@nodii/dev 0.5.0 → 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
@@ -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
 
@@ -93,7 +98,34 @@ nodii-dev heartbeat audit [SERVICE]
93
98
  nodii-dev design entries [--kind KIND] [--group GROUP] [--q TEXT]
94
99
  nodii-dev design entry KEY
95
100
  nodii-dev design guide PAGE_TYPE
101
+ ```
102
+
103
+ ### Design-system guidelines
96
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
+ ```
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
97
129
  nodii-dev figma lock FILE_KEY --title TEXT
98
130
  nodii-dev figma check FILE_KEY
99
131
  nodii-dev figma log-write CHANGE_SET --tool TOOL --nodes NODE[,NODE]
@@ -137,8 +169,83 @@ nodii-dev people refresh-names
137
169
  nodii-dev setup # preview files that would change
138
170
  nodii-dev setup --yes # merge and write the harness
139
171
  nodii-dev setup --check # exit non-zero when it has drifted
172
+
173
+ nodii-dev rulebook list [--section rulebook|services] [--kind KIND] [--q TEXT]
174
+ nodii-dev rulebook show CODE [--version V] # the current text, from Dev
175
+ nodii-dev rulebook diff CODE [--from V] [--to V]
176
+
177
+ nodii-dev docs check PATH... # local M-05 checks only
178
+ nodii-dev docs validate PATH... --class CLASS # Dev's dry run of the upload
179
+ nodii-dev docs upload PATH... --class ruling|conformance|editorial
180
+ [--base CODE=VERSION ...] [--decision DECISION.json]
181
+ [--conformance-ref PR|REPO@SHA] [--meaning-unchanged REASON]
182
+ [--wait [--wait-minutes N]]
183
+ nodii-dev docs ratify --domain KEY [--domain KEY ...] --decision DECISION.json [--wait]
184
+ nodii-dev docs parked [CODE] [--status pending|acknowledged]
185
+
186
+ nodii-dev harness install [--include-drafts] [--adopt PATH,...] [--dry-run]
187
+ nodii-dev harness update [--include-drafts] [--adopt PATH,...] [--dry-run]
188
+ nodii-dev harness update --check # exit 1 when Dev would write anything different
140
189
  ```
141
190
 
191
+ ## AD documents
192
+
193
+ AD documents (the rulebook and per-service feature docs) live in Dev, one
194
+ current text per document with its version history (M-05). `docs upload`
195
+ takes files or folders (folders are searched for `.md` files, skipping `_`
196
+ and `.` entries) and checks every document locally before sending anything:
197
+ front matter, the section order for its kind, unique clause ids that use the
198
+ document's code, no bare decision ids (`D123`; cite `pre:D123`), and the
199
+ parked (`Undecided`) items, which it lists. All problems are printed with
200
+ Dev's error codes, and nothing is sent until there are none. Dev then runs the
201
+ same checks, plus the ones that need its data, in one transaction.
202
+
203
+ - `--class` is required on every write (M05-8). A `ruling` carries its
204
+ decision (`--decision`, with `amends`), and the decision locks with the new
205
+ versions or nothing is written. An `editorial` write that changes a Rules
206
+ clause is refused unless `--meaning-unchanged REASON` is given (M05-10).
207
+ - A new document is based on nothing. For a document Dev already has, pass
208
+ the version you started from with `--base CODE=VERSION` (M05-11). The CLI
209
+ never fills it in from Dev, so a stale edit is refused instead of
210
+ overwriting newer text.
211
+ - **Parked items:** when a document has parked items the operator has not
212
+ acknowledged, Dev refuses the upload and records the list. The CLI prints
213
+ it and exits `4`. The operator acknowledges it in Dev (this CLI is an OAuth
214
+ app and cannot). Re-run the upload then, or pass `--wait` to poll and retry
215
+ once it is acknowledged.
216
+ - `docs ratify` is the bulk ratification mode: one decision locks v1.0 of
217
+ every draft document in the named domains, which then become AD (M05-2).
218
+ Upload the drafts first (`--class conformance`), then ratify domain by
219
+ domain. The decision file holds `title`, `context`, `options`, `ruling`,
220
+ `consequences` and `provenance`; Dev mints the `DEC-nnnn` id and generates
221
+ the amends. A domain that is already AD can be named again to ratify drafts
222
+ added to it since (a new feature doc in a ratified service).
223
+ - `docs validate` also prints Dev's warnings for each document (they never
224
+ block) and any problems with the decision's amends.
225
+
226
+ Like every other command, these reach Dev only through the platform edge
227
+ (`/devops/api/v1/documents`, `/domains`, `/harness`, …) with the OAuth login.
228
+
229
+ ## Agent harness
230
+
231
+ `harness install` writes the repository's agent harness as Dev generates it
232
+ from the ratified methodology: the `CLAUDE.md` and `AGENTS.md` pointer
233
+ blocks, one pointer skill per methodology document, the Claude Code hooks
234
+ merged into `.claude/settings.json`, and Dev's `.nodii/harness.json`. Every
235
+ generated file carries the methodology version it came from. Nothing about
236
+ the rules is authored locally. Run it in the service's own repository: it
237
+ installs one service at a time, and there is no fleet mode. `--include-drafts`
238
+ uses draft methodology while none is ratified.
239
+
240
+ The CLI records what it wrote in `.nodii/harness-state.json` and never touches
241
+ anything else. An existing file it did not write is refused, unless the
242
+ operator names it with `--adopt PATH` (a `CLAUDE.md` then keeps its text and
243
+ gains the managed block; `settings.json` keeps its other keys and hooks).
244
+ `harness update` re-syncs: it prints the methodology version change, which
245
+ source documents moved, and each file created, updated or removed. It
246
+ removes only files, blocks and hook entries it wrote, and it refuses (writing
247
+ nothing) when one of its files was edited by hand.
248
+
142
249
  ## Build lanes and repository setup
143
250
 
144
251
  `lane start` creates and starts a lane. It infers the repository, branch and
@@ -190,7 +297,9 @@ runtime, people, agents, inbox/gates, design, runbooks,
190
297
  doctrine and exceptions, changelog, health verdicts, approval kinds,
191
298
  notification registry, skills, questions, decisions, drift, requests,
192
299
  pipelines, runners, routing, usage, audit, reconciliation, cron, composite
193
- surfaces, service edges, lanes, swaps, ideas, and terminology. `api list` is the authoritative discovery view;
300
+ surfaces, service edges, lanes, swaps, ideas, terminology, and the AD era: `rulebook`
301
+ (documents, plus `rulebook versions CODE`), `ad-decisions`, `domains`, `standardizations`,
302
+ `archive` (`archive resolve --ref pre:D1366`), and `harness methodology`. `api list` is the authoritative discovery view;
194
303
  `api OPERATION_ID` makes every operation callable even without an alias.
195
304
 
196
305
  `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"], rulebook: "Doctrine", changelog: "Changelog", "health-verdicts": "HealthVerdicts",
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
- swaps: "Swaps", ideas: "Ideas", terminology: "Terminology",
103
+ swaps: "Swaps", ideas: "Ideas", terminology: "Terminology", credentials: "Credentials",
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
- "rulebook:list": "listDoctrine", "rulebook:show": "getDoctrine", "changelog:list": "listChangelog", "changelog:append": "appendChangelog",
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,14 +145,28 @@ 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
 
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
+
149
164
  export function findFirstClass(operations, group, action) {
150
165
  const aliased = ALIASES[`${group}:${action}`];
151
166
  if (aliased) return operations.find(x => x.operationId === aliased);
152
167
  const tags = Array.isArray(GROUPS[group]) ? GROUPS[group] : [GROUPS[group]];
153
- 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)));
154
170
  const normalized = action?.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).toLowerCase();
155
171
  return candidates.find(x => x.operationId.toLowerCase() === normalized || x.operationId.toLowerCase().endsWith(normalized));
156
172
  }
package/dist/cli.js CHANGED
@@ -24,6 +24,9 @@ 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";
29
+ import { changeSetCommand, guidelineCommand } from "./guidelines.js";
27
30
  import {
28
31
  ALIASES,
29
32
  GROUPS,
@@ -31,6 +34,7 @@ import {
31
34
  callOperation,
32
35
  findFirstClass,
33
36
  flagsToOperation,
37
+ refusePersonOnly,
34
38
  loadOpenApi,
35
39
  operationsFrom,
36
40
  parseParams,
@@ -56,9 +60,20 @@ Usage: nodii-dev <command> [options] [--json]
56
60
  drift file | request file
57
61
  decisions list|get|search
58
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
59
66
  figma lock|check|log-write|submit|apply|published|abandon|release
60
67
  hook claude-figma-pre|claude-figma-post|print-settings
61
68
  setup [--yes|--check] # dry-run by default
69
+ harness install|update [--include-drafts[=false]] [--adopt PATH,...] [--dry-run]
70
+ harness update --check # exit 1 when Dev would write anything different
71
+ docs check|validate|upload <path...> --class ruling|conformance|editorial
72
+ [--base CODE=VERSION] [--decision FILE] [--conformance-ref REF]
73
+ [--meaning-unchanged REASON] [--wait]
74
+ docs ratify --domain KEY [--domain KEY ...] --decision FILE [--wait]
75
+ docs parked [CODE] [--status pending|acknowledged]
76
+ rulebook list|show CODE [--version V]|diff CODE
62
77
 
63
78
  lane start <key> --title TEXT --kind KIND --side SIDE [--repo REPO] [--issue ISSUE]
64
79
  lane step <key> --side SIDE --start|--done|--block <step>
@@ -79,10 +94,15 @@ Resource commands cover every OpenAPI tag, including services, feature-docs,
79
94
  contracts, sagas, outbox-topics, tables, libraries, workers, projects, issues,
80
95
  backlogs, builds, changes, deploy-reports, runtime, people, agents, inbox,
81
96
  gate, design, runbooks, doctrine, changelog, health-verdicts, approval-kinds,
82
- notification-registry, skills, open-questions, decisions, drift, and requests.
97
+ notification-registry, skills, open-questions, decisions, drift, requests,
98
+ rulebook, ad-decisions, domains, standardizations, archive, and harness.
83
99
 
84
100
  Run a command with --help or see the package README for all flags.`;
85
101
 
102
+ // Verbs of the local-stack CLI, which shared this name until it became
103
+ // ./nodii-stack (operator G3-6, no alias). Named here only to point there.
104
+ 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"]);
105
+
86
106
  async function stdinText() {
87
107
  let value = "";
88
108
  for await (const chunk of stdin) value += chunk;
@@ -222,6 +242,7 @@ export async function main(argv = process.argv.slice(2)) {
222
242
  if (!action) throw new CliError("USAGE", "api needs an operationId or list", 2);
223
243
  const entry = operations.find((x) => x.operationId === action);
224
244
  if (!entry) throw new CliError("UNKNOWN_OPERATION", `No OpenAPI operationId: ${action}`, 2);
245
+ refusePersonOnly(entry.operationId);
225
246
  const parsed = await readBody(f.body);
226
247
  const request = buildOperationRequest(entry, parseParams(f.param), parsed?.value);
227
248
  return emit(await devRawRequest(request.method, request.path, parsed?.text), json);
@@ -248,6 +269,16 @@ export async function main(argv = process.argv.slice(2)) {
248
269
  }
249
270
  const emitCommand = async (result) => emit(result.payload, json, result.human);
250
271
  if (command === "setup") return emitCommand(await setupCommand(f));
272
+ if (command === "harness" && ["install", "update"].includes(action)) {
273
+ // `--include-drafts` is a switch: `--include-drafts false` would leave `false` here and turn drafts on.
274
+ if (rest.length) throw new CliError("USAGE", `harness ${action} takes no arguments, got: ${rest.join(" ")}. Write a flag value as --include-drafts=false.`, 2);
275
+ return emitCommand(await harnessCommand(action, f));
276
+ }
277
+ if (command === "docs") return emitCommand(await docsCommand(action, rest, f));
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));
251
282
  if (command === "lane") return emitCommand(await laneCommand(action, rest, f));
252
283
  if (command === "next") return emitCommand(await nextCommand(action, f));
253
284
  if (command === "built") return emitCommand(await builtCommand(action, rest, f));
@@ -568,7 +599,7 @@ export async function main(argv = process.argv.slice(2)) {
568
599
  }
569
600
  throw new CliError(
570
601
  "USAGE",
571
- `Unknown command: ${[command, action].filter(Boolean).join(" ")}`,
602
+ `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
603
  2,
573
604
  );
574
605
  }
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.5.0";
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");
@@ -105,10 +105,10 @@ export function requestUrl(config, path) {
105
105
  return `${config.edgeUrl.replace(/\/$/, "")}/devops${path}`;
106
106
  }
107
107
 
108
- export async function devRawRequest(method, path, bodyText, contentType = "application/json") {
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: "application/json" };
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
- const raw = await response.text();
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", "drafted"]);
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 [key, inline] = token.slice(2).split("=", 2);
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] =