@nodii/dev 0.6.1 → 0.6.2

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
@@ -98,7 +98,34 @@ nodii-dev heartbeat audit [SERVICE]
98
98
  nodii-dev design entries [--kind KIND] [--group GROUP] [--q TEXT]
99
99
  nodii-dev design entry KEY
100
100
  nodii-dev design guide PAGE_TYPE
101
+ ```
102
+
103
+ ### Design-system guidelines
104
+
105
+ Edit an entry's guideline through Dev, not with signed scripts. A proposal is
106
+ a guideline change set; a person approves or rejects it in Dev (nobody decides
107
+ their own), and the approved edit becomes a design-system PR.
108
+
109
+ ```bash
110
+ nodii-dev design guideline list [--drafted] [--kind KIND] [--q TEXT] [--all]
111
+ nodii-dev design guideline show ENTRY > ENTRY.md # the editable Markdown form
112
+ nodii-dev design guideline propose ENTRY --file ENTRY.md [--summary TEXT] [--dry-run]
113
+ nodii-dev design changeset list [--entry KEY] [--state in_review] [--limit N]
114
+ nodii-dev design changeset show GCS-001
115
+ ```
101
116
 
117
+ The file has one `## <list>` section per statement list (`whenToUse`,
118
+ `whenNotToUse`, `dos`, `donts`, and for an active guideline `anatomy`,
119
+ `states.states`, `states.loading`, `states.empty`, `states.error`, `copy`,
120
+ `composition`, `data.notes`), one `- ` bullet per item, and `(drafted)` after
121
+ the list name when the text still awaits a real source. A line that is an
122
+ HTML comment is ignored (`<!--` inside an item stays in its text), and a line
123
+ break inside an item reads back as a space. The `#` title, as `show` prints it,
124
+ names the entry: `propose` refuses a file titled for another one. `propose` sends
125
+ only the lists that differ from Dev's current guideline, and `--dry-run`
126
+ prints them without creating anything.
127
+
128
+ ```bash
102
129
  nodii-dev figma lock FILE_KEY --title TEXT
103
130
  nodii-dev figma check FILE_KEY
104
131
  nodii-dev figma log-write CHANGE_SET --tool TOOL --nodes NODE[,NODE]
package/dist/api.js CHANGED
@@ -100,7 +100,7 @@ export const GROUPS = {
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
- swaps: "Swaps", ideas: "Ideas", terminology: "Terminology",
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",
106
106
  };
@@ -154,11 +154,19 @@ export const ALIASES = {
154
154
  "terminology:list": "listTerminology", "terminology:show": "getTerminologyTerm", "terminology:create": "createTerminologyTerm", "terminology:update": "updateTerminologyTerm",
155
155
  };
156
156
 
157
+ // Decisions a person makes in Dev (operator authority): nodii-dev never sends them.
158
+ export const PERSON_ONLY = new Set(["approveDesignGuidelineChangeSet", "rejectDesignGuidelineChangeSet", "mergeDesignRelease"]);
159
+ export function refusePersonOnly(operationId) {
160
+ if (PERSON_ONLY.has(operationId))
161
+ throw new CliError("PERSON_ONLY", `${operationId} is a person's decision in Dev; nodii-dev does not send it.`, 2);
162
+ }
163
+
157
164
  export function findFirstClass(operations, group, action) {
158
165
  const aliased = ALIASES[`${group}:${action}`];
159
166
  if (aliased) return operations.find(x => x.operationId === aliased);
160
167
  const tags = Array.isArray(GROUPS[group]) ? GROUPS[group] : [GROUPS[group]];
161
- const candidates = operations.filter(x => x.tags.some(t => tags.includes(t)));
168
+ // A person-only operation is never picked by name, so a guessed verb cannot reach it.
169
+ const candidates = operations.filter(x => !PERSON_ONLY.has(x.operationId) && x.tags.some(t => tags.includes(t)));
162
170
  const normalized = action?.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).toLowerCase();
163
171
  return candidates.find(x => x.operationId.toLowerCase() === normalized || x.operationId.toLowerCase().endsWith(normalized));
164
172
  }
package/dist/cli.js CHANGED
@@ -26,6 +26,7 @@ import { heartbeatCurrentLane, postHook, preHook, settingsSnippet, workflowSugge
26
26
  import { setupCommand } from "./setup.js";
27
27
  import { harnessCommand } from "./harness.js";
28
28
  import { docsCommand, rulebookCommand } from "./upload.js";
29
+ import { changeSetCommand, guidelineCommand } from "./guidelines.js";
29
30
  import {
30
31
  ALIASES,
31
32
  GROUPS,
@@ -33,6 +34,7 @@ import {
33
34
  callOperation,
34
35
  findFirstClass,
35
36
  flagsToOperation,
37
+ refusePersonOnly,
36
38
  loadOpenApi,
37
39
  operationsFrom,
38
40
  parseParams,
@@ -58,6 +60,9 @@ Usage: nodii-dev <command> [options] [--json]
58
60
  drift file | request file
59
61
  decisions list|get|search
60
62
  design entries|entry|guide
63
+ design guideline list [--drafted] [--all] | show ENTRY
64
+ design guideline propose ENTRY --file FILE.md [--summary TEXT] [--dry-run]
65
+ design changeset list [--entry KEY] [--state STATE] | show ID
61
66
  figma lock|check|log-write|submit|apply|published|abandon|release
62
67
  hook claude-figma-pre|claude-figma-post|print-settings
63
68
  setup [--yes|--check] # dry-run by default
@@ -237,6 +242,7 @@ export async function main(argv = process.argv.slice(2)) {
237
242
  if (!action) throw new CliError("USAGE", "api needs an operationId or list", 2);
238
243
  const entry = operations.find((x) => x.operationId === action);
239
244
  if (!entry) throw new CliError("UNKNOWN_OPERATION", `No OpenAPI operationId: ${action}`, 2);
245
+ refusePersonOnly(entry.operationId);
240
246
  const parsed = await readBody(f.body);
241
247
  const request = buildOperationRequest(entry, parseParams(f.param), parsed?.value);
242
248
  return emit(await devRawRequest(request.method, request.path, parsed?.text), json);
@@ -270,6 +276,9 @@ export async function main(argv = process.argv.slice(2)) {
270
276
  }
271
277
  if (command === "docs") return emitCommand(await docsCommand(action, rest, f));
272
278
  if (command === "rulebook" && ["list", "show", "diff"].includes(action)) return emitCommand(await rulebookCommand(action, rest, f));
279
+ // Guideline change sets: approving or rejecting one is a person's action in Dev.
280
+ if (command === "design" && action === "guideline") return emitCommand(await guidelineCommand(rest[0], rest.slice(1), f));
281
+ if (command === "design" && action === "changeset") return emitCommand(await changeSetCommand(rest[0], rest.slice(1), f));
273
282
  if (command === "lane") return emitCommand(await laneCommand(action, rest, f));
274
283
  if (command === "next") return emitCommand(await nextCommand(action, f));
275
284
  if (command === "built") return emitCommand(await builtCommand(action, rest, f));
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.1";
7
+ export const VERSION = "0.6.2";
8
8
  export const configPath = () =>
9
9
  process.env.NODII_DEV_CONFIG_PATH ??
10
10
  join(homedir(), ".config/nodii-dev/config.json");
@@ -162,7 +162,7 @@ export async function checkLogin() {
162
162
  export const encode = (value) => encodeURIComponent(value);
163
163
  // Switches never take the next token as their value, so
164
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"]);
165
+ const SWITCHES = new Set(["json", "wait", "dry-run", "check", "all", "fleet", "help", "yes", "mine", "no-browser", "include-drafts", "drafted"]);
166
166
  export function parseArgs(argv) {
167
167
  const flags = {};
168
168
  const args = [];