@nodii/dev 0.4.1 → 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 CHANGED
@@ -13,35 +13,43 @@ nodii-dev login # opens the approval page in your browser
13
13
  nodii-dev login --no-browser # prints the link and code instead
14
14
  nodii-dev login --scope devops.issues.read,devops.issues.write # narrower grant
15
15
  nodii-dev agent register --name my-agent
16
-
17
- # Temporary compatibility path:
18
- printf '%s' "$PLANNING_HUB_HMAC_SECRET" | nodii-dev login --person PERSON_ID --secret-stdin
19
- nodii-dev login --hmac --person PERSON_ID --from-aws
20
16
  ```
21
17
 
22
- OAuth is the default: the CLI signs in as the platform tenant's nodii-dev app
23
- (`NODII_DEV_OAUTH_CLIENT_ID` or `--client-id` overrides the built-in public
24
- client id). With no `--scope`, auth grants the app's full ceiling: every live
25
- `devops.*` key except `devops.releases.promote`, which is never granted. Pass
26
- `--hmac` to keep the interim path. Login uses RFC 8628 and
27
- opens the verification page on an interactive terminal unless `--no-browser`
28
- is passed. Refresh tokens are stored only in macOS Keychain or Linux Secret
29
- Service (`secret-tool`); the CLI refuses plaintext fallback. Access tokens stay
30
- in memory. OAuth API calls use the platform edge `/devops` route and send the
31
- registered `x-agent-id`. `logout` revokes the refresh chain before erasing local
18
+ `nodii-dev` talks only to Dev, through the platform edge
19
+ (`https://platform.nucleus-cloud.in/devops/*`), signed in as the platform
20
+ tenant's nodii-dev OAuth app (D1368). There is no other host and no other way
21
+ to sign in. `NODII_DEV_OAUTH_CLIENT_ID` or `--client-id` overrides the built-in
22
+ public client id. With no `--scope`, auth grants the app's full ceiling: every
23
+ live `devops.*` key except `devops.releases.promote`, which is never granted.
24
+ Login uses the RFC 8628 device flow and opens the verification page on an
25
+ interactive terminal unless `--no-browser` is passed. Refresh tokens are
26
+ stored only in macOS Keychain or Linux Secret Service (`secret-tool`); the CLI
27
+ refuses plaintext fallback. Access tokens stay in memory. Every API call goes
28
+ to the edge's `/devops` prefix with the OAuth bearer and the registered
29
+ `x-agent-id`. `logout` revokes the refresh chain before erasing local
32
30
  credentials. `--auth-url`, `--edge-url`, and `--tenant-hint` select another real
33
31
  deployment.
34
32
 
33
+ Upgrading from 0.4.x: the shared-secret login (`login --hmac`, `--person`,
34
+ `--secret-stdin`, `--from-aws`, `--hub-url`) was removed in 0.5.0, and `login`
35
+ refuses those flags. The first command you run after upgrading deletes the old
36
+ config and its stored secret (Keychain item or file) without printing it, then
37
+ asks you to run `nodii-dev login`.
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
+
35
44
  Every command accepts `--json`; without it, output is formatted for humans.
36
- Hub failures print the hub error code to stderr and exit non-zero.
45
+ Dev failures print Dev's error code to stderr and exit non-zero.
37
46
 
38
47
  ## Commands
39
48
 
40
49
  ```text
41
- nodii-dev login [--no-browser]
42
- nodii-dev login --hmac --person ID (--secret-stdin | --from-aws)
50
+ nodii-dev login [--no-browser] [--scope KEYS]
43
51
  nodii-dev logout
44
- nodii-dev whoami
52
+ nodii-dev whoami # OAuth identity plus the registered agent
45
53
  nodii-dev config show
46
54
 
47
55
  nodii-dev api list [--tag TAG] [--json]
@@ -134,17 +142,92 @@ nodii-dev people refresh-names
134
142
  nodii-dev setup # preview files that would change
135
143
  nodii-dev setup --yes # merge and write the harness
136
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
137
162
  ```
138
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
+
139
222
  ## Build lanes and repository setup
140
223
 
141
224
  `lane start` creates and starts a lane. It infers the repository, branch and
142
225
  worktree from git unless overridden, and resolves `--issue` keys to the UUIDs
143
- required by the hub. Lane keys are passed directly to every lane and Built
226
+ required by Dev. Lane keys are passed directly to every lane and Built
144
227
  acknowledgement route. `next` reads the lane's generated
145
228
  API steps and prints the exact next `lane step` command. Server refusals,
146
229
  including operator-only actions, workflow blocks, lane limits and Built items
147
- that are not on dev, retain the hub error code/message and exit non-zero.
230
+ that are not on dev, retain Dev's error code/message and exit non-zero.
148
231
 
149
232
  `setup` installs the Claude Code workflow harness in the current repository.
150
233
  It writes `.claude/skills/nodii-build-workflow/SKILL.md`, merges SessionStart,
@@ -152,15 +235,16 @@ Stop and Bash PostToolUse hooks into `.claude/settings.json`, and maintains one
152
235
  marked section in an existing `CLAUDE.md` or `AGENTS.md` (creating `CLAUDE.md`
153
236
  when neither exists). Existing settings and hooks are preserved. The default
154
237
  is a read-only preview; `--yes` applies, and `--check` is suitable for drift
155
- checks. Setup also checks `whoami` and prints `nodii-dev login` guidance without
156
- exposing credential details.
238
+ checks. Setup also checks the OAuth login (a token refresh, then the registered
239
+ agent) and prints `nodii-dev login` guidance without exposing credential
240
+ details.
157
241
 
158
- Heartbeat notes are optional and limited by the hub to 500 characters.
242
+ Heartbeat notes are optional and limited by Dev to 500 characters.
159
243
  Prioritize and deprioritize require a 1–500 character reason, which the CLI
160
- sends to the hub. `lane review` records the review gate.
244
+ sends to Dev. `lane review` records the review gate.
161
245
 
162
246
  The CLI does not duplicate authorization policy. It sends requests and prints
163
- the hub's refusal unchanged. Stop, Built acknowledgement, and machine
247
+ Dev's refusal unchanged. Stop, Built acknowledgement, and machine
164
248
  create/update/coordinator changes require a staff person login. Machine agent
165
249
  assignment is for coordinators; lane-limit and repository-grant changes are for
166
250
  the target agent's coordinator or staff. Agent registration does not accept
@@ -177,19 +261,23 @@ nodii-dev requests resolve UUID --commit-sha abc1234 --resolution-md "shipped"
177
261
  nodii-dev open-questions answer UUID --decision-id D1420
178
262
  ```
179
263
 
180
- The first-class resource families cover every live OpenAPI tag: services,
264
+ The CLI bundles Dev's OpenAPI document as the edge routes it (the edge does not
265
+ serve Dev's own document), so `api list` works offline and lists only
266
+ operations the edge exposes. The first-class resource families cover every tag: services,
181
267
  feature docs, contracts (gRPC, REST, outbox, sagas, tables), libraries,
182
268
  workers, projects, slices, SQL lookups, issues, backlogs, builds, changes,
183
- deploy reports, runtime, people, agents, inbox/gates, design, runbooks,
269
+ runtime, people, agents, inbox/gates, design, runbooks,
184
270
  doctrine and exceptions, changelog, health verdicts, approval kinds,
185
271
  notification registry, skills, questions, decisions, drift, requests,
186
272
  pipelines, runners, routing, usage, audit, reconciliation, cron, composite
187
- surfaces, service edges, lanes, swaps, ideas, and terminology. `api list` is the authoritative discovery view;
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;
188
276
  `api OPERATION_ID` makes every operation callable even without an alias.
189
277
 
190
278
  `figma check` exits `0` when allowed and `3` when denied. The pre-hook permits
191
279
  non-Figma tools and allowlisted Figma reads, but defaults to denying every
192
- Figma write if the file key is absent, the hub cannot be reached, or the lock
280
+ Figma write if the file key is absent, Dev cannot be reached, or the lock
193
281
  is denied. It exits `2`, which makes Claude Code block the call. The post-hook
194
282
  best-effort logs the write and never blocks. Per D1433, Codex remains read-only
195
283
  for Figma; this package does not write to Figma itself.
package/dist/api.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { stdin } from "node:process";
3
- import { CliError, encode, hubRawRequest, readConfig } from "./core.js";
3
+ import { CliError, devRawRequest, encode } from "./core.js";
4
4
 
5
5
  const methods = new Set(["get", "post", "put", "patch", "delete", "head", "options"]);
6
- export async function loadOpenApi(fetcher = fetch) {
7
- const { hubUrl } = await readConfig();
8
- const response = await fetcher(`${hubUrl.replace(/\/$/, "")}/openapi.json`, { headers: { accept: "application/json" } });
9
- if (!response.ok) throw new CliError("OPENAPI_UNAVAILABLE", `Cannot load OpenAPI: HTTP ${response.status}`);
10
- return response.json();
6
+ // The Dev API as the platform edge routes it, bundled with the package (the
7
+ // edge does not serve Dev's own document). Refresh: `npm run openapi:snapshot`.
8
+ let bundled;
9
+ export async function loadOpenApi() {
10
+ bundled ??= JSON.parse(await readFile(new URL("./devops-openapi.json", import.meta.url), "utf8"));
11
+ return bundled;
11
12
  }
12
13
 
13
14
  export function operationsFrom(spec) {
@@ -86,20 +87,22 @@ export function flagsToOperation(entry, positional, flags) {
86
87
 
87
88
  export async function callOperation(entry, params, body) {
88
89
  const request = buildOperationRequest(entry, params, body);
89
- return hubRawRequest(request.method, request.path, body === undefined ? undefined : JSON.stringify(body));
90
+ return devRawRequest(request.method, request.path, body === undefined ? undefined : JSON.stringify(body));
90
91
  }
91
92
 
92
93
  export const GROUPS = {
93
94
  services: "Services", "feature-docs": "FeatureDocs", contracts: ["GrpcInterfaces", "RestInterfaces", "Outbox", "OutboxTopics", "Sagas", "Tables"],
94
95
  sagas: "Sagas", "outbox-topics": ["Outbox", "OutboxTopics"], tables: "Tables", libraries: "Libraries", workers: "Workers",
95
- projects: "Projects", issues: "Issues", backlog: "Backlogs", backlogs: "Backlogs", builds: "Delivery", changes: "Delivery", "deploy-reports": "Runtime",
96
+ projects: "Projects", issues: "Issues", backlog: "Backlogs", backlogs: "Backlogs", builds: "Delivery", changes: "Delivery",
96
97
  runtime: "Runtime", people: "People", agents: "Agents", inbox: "Inbox", gate: "Inbox", design: "Design", runbooks: "Runbooks",
97
- doctrine: ["Doctrine", "DoctrineExceptions"], rulebook: "Doctrine", changelog: "Changelog", "health-verdicts": "HealthVerdicts",
98
+ doctrine: ["Doctrine", "DoctrineExceptions"], changelog: "Changelog", "health-verdicts": "HealthVerdicts",
98
99
  "approval-kinds": "ApprovalKinds", "notification-registry": "Notification registry", skills: "Skills", "open-questions": "OpenQuestions",
99
100
  decisions: "Decisions", drift: "Drift", requests: "Requests", pipelines: "Pipelines", slices: "Slices", "sql-lookups": "SqlLookups",
100
101
  runners: "Runners", routing: "Routing", usage: "Usage", audit: "AuditLog", reconciliation: "Reconciliation", cron: "Cron", health: "Health",
101
102
  composite: "Composite", "service-edges": "ServiceEdges", lanes: "Lanes",
102
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",
103
106
  };
104
107
 
105
108
  export const ALIASES = {
@@ -123,12 +126,12 @@ export const ALIASES = {
123
126
  "projects:list": "listServiceProjects", "projects:show": "getProject", "projects:create": "createProject", "projects:update": "updateProject",
124
127
  "builds:list": "listBuilds", "builds:show": "getBuild", "builds:report": "reportBuild",
125
128
  "changes:list": "listChanges", "changes:show": "getChange", "changes:mark": "markChange",
126
- "deploy-reports:report": "reportDeployment", "runtime:list": "listRuntimeInventory", "runtime:show": "getServiceRuntime",
129
+ "runtime:list": "listRuntimeInventory", "runtime:show": "getServiceRuntime",
127
130
  "people:list": "listPeople", "people:show": "getPerson", "agents:list": "listAgents", "agents:show": "getAgent",
128
131
  "gate:fleet": "getFleetGate", "gate:service": "getServiceGate", "gate:dry-run": "getGateDryRun",
129
132
  "runbooks:list": "listRunbooks", "runbooks:show": "getRunbook", "runbooks:create": "registerRunbook", "runbooks:update": "updateRunbook",
130
133
  "doctrine:list": "listDoctrine", "doctrine:show": "getDoctrine", "doctrine:create": "createDoctrine", "doctrine:revise": "reviseDoctrine",
131
- "rulebook:list": "listDoctrine", "rulebook:show": "getDoctrine", "changelog:list": "listChangelog", "changelog:append": "appendChangelog",
134
+ "changelog:list": "listChangelog", "changelog:append": "appendChangelog",
132
135
  "health-verdicts:list": "listHealthVerdicts", "health-verdicts:post": "postHealthVerdict",
133
136
  "approval-kinds:list": "listApprovalKinds", "approval-kinds:show": "getApprovalKind", "approval-kinds:create": "createApprovalKind",
134
137
  "notification-registry:list": "listNotificationEvents", "notification-registry:show": "getNotificationEvent", "notification-registry:propose": "proposeNotificationEvent",
@@ -142,6 +145,12 @@ export const ALIASES = {
142
145
  "service-edges:list": "listServiceEdges", "service-edges:show": "getServiceEdgesForService",
143
146
  "swaps:list": "listSwaps", "swaps:show": "getSwap", "swaps:create": "createSwap", "swaps:close": "closeSwap",
144
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",
145
154
  "terminology:list": "listTerminology", "terminology:show": "getTerminologyTerm", "terminology:create": "createTerminologyTerm", "terminology:update": "updateTerminologyTerm",
146
155
  };
147
156