@dzhechkov/harness-cli 0.4.5 → 0.5.0

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
@@ -125,6 +125,140 @@ point any tool at (a skill is just Markdown). Dedicated targets are on the [road
125
125
  **❌ No target applies:** `nemoclaw` (NVIDIA agent-safety runtime — not a code editor, no rules file) ·
126
126
  `v0` (UI-only, no repo file). *(`pi` is a real coder but reads `AGENTS.md` → covered by `agents-md`.)*
127
127
 
128
+ #### `--target` aliases and did-you-mean
129
+
130
+ `--target claude` used to be rejected outright — the canonical name is `claude-code`, and the error
131
+ just re-printed the list. All **eight** `--target`-taking commands (`init`, `verify`, `install`,
132
+ `compose`, `setup`, `upgrade`, `parity`, `feature-adr-setup`) now resolve the value first.
133
+
134
+ *(This sentence used to say "every" and name only seven: `dz parity` was missed, and shipped the
135
+ original defect verbatim. It is now checked rather than promised — `test/target-alias-cli.test.ts`
136
+ asserts that no command reads `--target` without reaching the resolver, and that every command whose
137
+ `--help` advertises `--target` appears in the per-command sweep.)*
138
+
139
+ **Accepted aliases** (a table, so adding one is a data edit):
140
+
141
+ | You type | You get | Why it is a row |
142
+ |----------|---------|-----------------|
143
+ | `claude` · `cc` | `claude-code` | the tool's everyday name |
144
+ | `agents` | `agents-md` | the everyday name of the `AGENTS.md` target |
145
+ | `gpt` · `openai` | `codex` | the vendor name, not the CLI's |
146
+
147
+ Case, padding and separators are handled **without** a row: `CLAUDE`, `Claude_Code`, `claudecode`
148
+ and ` claude-code ` all resolve to `claude-code`; `agentsmd` and `agents.md` resolve to `agents-md`.
149
+
150
+ **A typo is SUGGESTED, never silently accepted** — installing to the wrong target on a guess is worse
151
+ than one round-trip:
152
+
153
+ ```console
154
+ $ dz init --target clade-code --preset devops > out.txt # the refusal is on STDERR
155
+ dz init: unknown --target "clade-code" — did you mean "claude-code"?
156
+ --target must be one of: agents-md, claude-code, codex, copilot, cursor, gemini, hermes, openclaude, opencode, windsurf
157
+ $ echo $?
158
+ 1
159
+ $ wc -c out.txt
160
+ 0 out.txt # stdout stays a clean data channel, even on a refusal
161
+ ```
162
+
163
+ **Both lines go to stderr** — the refusal, like the alias note below, is diagnosis, not data. `dz
164
+ parity --target <bad> --json` likewise writes its structured error to stderr and leaves stdout empty,
165
+ so `| jq` is never handed a diagnostic.
166
+
167
+ An ambiguous prefix gets no guess at all (`--target co` could be `codex` or `copilot`), and neither
168
+ does nonsense (`--target totally-bogus` prints the plain list). When an alias IS accepted, the
169
+ substitution is announced **on stderr**, so piped stdout stays machine-readable:
170
+
171
+ ```console
172
+ $ dz init --target claude --preset devops > installed.txt
173
+ dz init: --target "claude" → claude-code (alias)
174
+ ```
175
+
176
+ **When this matters:** you are following a blog post or a teammate's snippet that says `--target claude`.
177
+ It now works, and you are told what it resolved to.
178
+
179
+ ### `dz list` — one broken skill never hides the rest
180
+
181
+ `dz list` used to abort on the FIRST unparseable `SKILL.md`: exit 1, an error naming neither the file
182
+ nor a count, and every other skill in the tree invisible. A pack you installed could blank your whole
183
+ listing. Now the loadable skills are listed on **stdout** and the unloadable ones are named on
184
+ **stderr**:
185
+
186
+ ```console
187
+ $ dz list --skills-dir .claude/skills
188
+ 105 skill(s) in /home/you/proj/.claude/skills:
189
+
190
+ api-design Designs REST and GraphQL APIs with OpenAPI specs…
191
+ …103 more…
192
+ test-writer Writes focused unit and integration tests…
193
+ $ echo $?
194
+ 1
195
+ ```
196
+
197
+ …with the diagnosis kept out of the data:
198
+
199
+ ```console
200
+ $ dz list --skills-dir .claude/skills 2>/dev/null # stdout only — clean, pipeable
201
+ 105 skill(s) in …
202
+
203
+ $ dz list --skills-dir .claude/skills 1>/dev/null # stderr only — the actionable part
204
+ dz list: 105 listed, 1 skipped in /home/you/proj/.claude/skills
205
+ ⚠ 1 skill(s) skipped (unparseable SKILL.md):
206
+ /home/you/proj/.claude/skills/broken-one/SKILL.md
207
+ SKILL.md must begin with a "---" frontmatter fence
208
+ (line 1: "# Broken One")
209
+ ```
210
+
211
+ The full contract:
212
+
213
+ | valid skills | skipped | stdout | stderr | exit |
214
+ |--------------|---------|--------|--------|------|
215
+ | >0 | 0 | the listing | *empty* | 0 |
216
+ | >0 | >0 | the listing of the valid ones | named summary | 1 |
217
+ | 0 | >0 | *nothing* | named summary (`0 listed, N skipped`) | 1 |
218
+ | 0 | 0 | *nothing* | `dz list: no skills found in <dir>` | 1 |
219
+
220
+ `dz init`, `dz install` and `dz sync` behave the same way — the good skills are installed, the bad ones
221
+ are named, and the command exits 1.
222
+
223
+ **Two failure kinds, two headers, two subjects.** A skill that will not PARSE and a skill that will not
224
+ WRITE are different accusations, so `dz init` reports them separately:
225
+
226
+ ```console
227
+ $ dz init --target claude-code --skills-dir ./skills --project ./proj
228
+ dz init --target claude-code: 1 skill(s), 1 file(s) written, 0 skipped
229
+ dz init: 1 installed, 1 failed to write # ← stderr
230
+ ✗ 1 skill(s) failed to install (compile/write error):
231
+ alpha
232
+ EEXIST: file already exists, mkdir './proj/.claude/skills/alpha'
233
+ ```
234
+
235
+ The header names the **target**, not `alpha/SKILL.md` — which is perfectly valid. (It used to print
236
+ `⚠ 1 skill(s) skipped (unparseable SKILL.md)` and quote `line 1: "---"`, a valid frontmatter fence, as
237
+ its evidence: a failure that names the wrong artifact is worse than an anonymous one.)
238
+
239
+ `dz install` renders the offending path **relative to the package**
240
+ and says so explicitly, because a `node_modules/**` path is not something you can act on:
241
+
242
+ ```console
243
+ $ dz install @someone/skills-pack
244
+ dz install @someone/skills-pack: 12 skill(s), 24 file(s) written, 0 skipped
245
+ dz install: @someone/skills-pack ships 1 unparseable skill(s) —
246
+ ⚠ 1 skill(s) skipped (unparseable SKILL.md):
247
+ skills/broken-one/SKILL.md
248
+ SKILL.md must begin with a "---" frontmatter fence
249
+ This is a defect in the package, not in your project.
250
+ Workaround: npx -y @someone/skills-pack init
251
+ ```
252
+
253
+ **When this matters:** a package you installed shipped a broken skill — you still see everything else,
254
+ you know exactly which file is at fault, and you know whose defect it is.
255
+
256
+ > **`dz` requires `@dzhechkov/harness-core >= 0.4.7`.** If you ever see
257
+ > `dz: needs @dzhechkov/harness-core >= 0.4.7, found 0.4.1`, a stale core was reused from a cache:
258
+ > `rm -rf ~/.npm/_npx && npx @dzhechkov/harness-cli@latest --version`. That named message replaced a
259
+ > bare `SyntaxError: … does not provide an export named 'GRADE_SUCCESS_FLOOR'` that used to kill even
260
+ > `dz --version`.
261
+
128
262
  > **"Nothing installs" / "no skills found"?** Update the CLI:
129
263
  > `cd /tmp && npm i -g @dzhechkov/harness-cli@latest`. Older global installs couldn't locate their
130
264
  > own bundled packs outside the monorepo — now fixed, so `dz registry`/`dz init`/`dz setup` work
@@ -132,7 +266,7 @@ point any tool at (a skill is just Markdown). Dedicated targets are on the [road
132
266
 
133
267
  ## User Journey — from install to mastery
134
268
 
135
- All 66 commands (MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases) mapped to a real workflow:
269
+ All 67 commands (MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases) mapped to a real workflow:
136
270
 
137
271
  ```
138
272
  DISCOVER → INSTALL → USE → CREATE → MAINTAIN → SHARE
@@ -1015,7 +1149,7 @@ green: re-sign reminder (`dz sign …` — the publish signature gate is refuse-
1015
1149
  before a multi-package npm release, or whenever a broken dist/bin must be impossible to ship; keep plain
1016
1150
  `dz publish` for routine pushes. `dz publish` itself is byte-identical whether or not release exists.
1017
1151
 
1018
- ### Skill Packs (23 packs · 179 skills)
1152
+ ### Skill Packs (18 packs · 189 skills)
1019
1153
 
1020
1154
  Each pack is an npm package — click through for the **full per-skill documentation** (what each skill does + how to trigger it). Install a whole pack with `dz install <pkg>`, or pick skills with `dz init --select` / a `--preset`.
1021
1155
 
@@ -1027,7 +1161,7 @@ Each pack is an npm package — click through for the **full per-skill documenta
1027
1161
  | [@dzhechkov/skills-qe](https://www.npmjs.com/package/@dzhechkov/skills-qe) | 20 | Quality engineering — test-gen, coverage, chaos, defect intelligence, QCSD swarms |
1028
1162
  | [@dzhechkov/skills-reasoning](https://www.npmjs.com/package/@dzhechkov/skills-reasoning) | 4 | Generic reasoning & code-quality — investigate (root-cause), solid (SOLID/TDD), karpathy-guidelines, agents-md-creator |
1029
1163
  | [@dzhechkov/skills-ecc](https://www.npmjs.com/package/@dzhechkov/skills-ecc) | 20 | Claude-Code engineering craft — agent architecture, autonomous loops, framework patterns |
1030
- | [@dzhechkov/skills-meta](https://www.npmjs.com/package/@dzhechkov/skills-meta) | 18 | Dev-process meta skills — explore, feature-adr, design-thinking, audit, skill-advisor |
1164
+ | [@dzhechkov/skills-meta](https://www.npmjs.com/package/@dzhechkov/skills-meta) | 19 | Dev-process meta skills — explore, feature-adr, design-thinking, audit, skill-advisor, loop-plan-author |
1031
1165
  | [@dzhechkov/skills-academic](https://www.npmjs.com/package/@dzhechkov/skills-academic) | 5 | Thesis-defense toolkit — dissertation review, questions, doc-check, defense eval |
1032
1166
  | [@dzhechkov/skills-news](https://www.npmjs.com/package/@dzhechkov/skills-news) | 3 | *dz-original* — news digests (`news-digest`) + delta watches (`news-monitor`) + bundled `goap-research-ed25519` verified-research backend (mandatory) |
1033
1167
  | [@dzhechkov/skills-idea2prd](https://www.npmjs.com/package/@dzhechkov/skills-idea2prd) | 1 | *dz-original* — `idea2prd-manual`: idea/problem → PRD+ADR+DDD+C4+Pseudocode+Tests+Completion (9 checkpoints); bundles the analyst trio as a sources.json-tracked vendor ([ADR-0001](https://github.com/djd1m/dz-harness-hub/blob/main/docs/adr/0001-skill-canonicalization-and-dependency-model.md)) |
@@ -1038,6 +1172,7 @@ Each pack is an npm package — click through for the **full per-skill documenta
1038
1172
  | [@dzhechkov/skills-taste](https://www.npmjs.com/package/@dzhechkov/skills-taste) | 1 | *imported (MIT)* — `design-taste-frontend`: anti-slop landing/portfolio/redesign framework (dials + pre-flight + GSAP skeletons); complements frontend-design (`dz init --select design-taste-frontend`) ([ADR-0002](https://github.com/djd1m/dz-harness-hub/blob/main/docs/adr/0002-product-and-design-expansion.md)) |
1039
1173
  | [@dzhechkov/skills-book-digitizer](https://www.npmjs.com/package/@dzhechkov/skills-book-digitizer) | 8 | *dz-original* — book → installable methodology pack: `digitize-book` (orchestrator) + ingest/extract/distill/pack/kb-index + `book-brain-register` (CP6 promote → cross-project brain) + `source-brain-ingest` (repo sibling). Verified provenance, IP-safe, resumable (`dz init --select digitize-book`) ([ADR-001](https://github.com/djd1m/dz-harness-hub/blob/main/features/book-knowledge-digitizer/03_adr/001-book-to-skillpack-pipeline.md)) |
1040
1174
  | [@dzhechkov/skills-12factor](https://www.npmjs.com/package/@dzhechkov/skills-12factor) | 12 | *generated by the digitizer, CC BY 4.0* — The Twelve-Factor App distilled into 12 decision-moment skills (one per factor). The first PUBLIC digitized-book pack; paraphrased (shingling-gated), routing-gated (every factor carries triggers), attributed (`NOTICE`) (`dz init --select 12factor-config-in-environment,…`) |
1175
+ | [@dzhechkov/skills-book-ai-apps](https://www.npmjs.com/package/@dzhechkov/skills-book-ai-apps) | 17 | *generated by the digitizer, CP5-published* — «Building Applications with AI Agents» (Albada, рус. пер.) distilled into 17 decision-moment skills across the whole agent-building arc: agent-fit & model choice, single-vs-multi, orchestration, tool design, knowledge & memory, context engineering, evaluation, probabilistic behaviour checks, release gates, improvement loops, drift, human-in-the-loop, agent UX, governance, security. Ships our page-anchored Knowledge Units, NOT the book text — shingling-gated at 0 uncited verbatim runs >=8 words; publication is the recorded CP5 owner decision; `trust_tier 1` (routing-gated, not human-reviewed) Since 0.2.2 it also ships `brain/ai-apps.sqlite`, the 223-KU knowledge slice: `dz brain add --from-pack @dzhechkov/skills-book-ai-apps` loads it into your `~/.dz/brain`, then `dz brain query --source ai-apps` answers in any project (`dz install @dzhechkov/skills-book-ai-apps --target claude-code`) |
1041
1176
 
1042
1177
  ### Available Presets (14)
1043
1178
 
@@ -1097,7 +1232,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
1097
1232
 
1098
1233
  Custom loops used to be born by copy-pasting a 1470-line workflow script; nothing deterministic
1099
1234
  checked the copy. The loop-designer meta-factory replaces that: a versioned typed plan
1100
- (`loop-plan/1`), a schema-driven generator, a 17-rule lint gate, and a local trace plane.
1235
+ (`loop-plan/1`), a schema-driven generator, an 18-rule lint gate, and a local trace plane.
1101
1236
 
1102
1237
  ```bash
1103
1238
  # 1. Scaffold a plan (pipeline | barrier | fanout | gate), edit the TODO prompts:
@@ -1116,7 +1251,7 @@ dz workflow render triage-loop.plan.json --o triage-loop.js
1116
1251
 
1117
1252
  # 4. Gate it (generated-loop CI mode; exit 0/1/3 — inconclusive is NEVER a pass):
1118
1253
  dz workflow-lint triage-loop.js --plan triage-loop.plan.json --require-plan
1119
- # → dz workflow-lint: PASS (mode=require-plan; 0 fail, 0 warn, 0 inconclusive over 17 rules)
1254
+ # → dz workflow-lint: PASS (mode=require-plan; 0 fail, 0 warn, 0 inconclusive over 18 rules)
1120
1255
 
1121
1256
  # 5. Run it via Workflow({scriptPath:'triage-loop.js', args:{traceDir:'/abs/run-dir'}}), then read
1122
1257
  # the run's own trace (seq-ordered; wallTime is diagnostic only):
@@ -1207,12 +1342,46 @@ dz workflow render audit.plan.json --o audit.loop.js
1207
1342
  # → wrote audit.loop.plan.json then audit.loop.js (exec-fp sha256:49feb8a2c1e17038…, blobs: trace)
1208
1343
 
1209
1344
  dz workflow-lint audit.loop.js --plan audit.loop.plan.json --require-plan
1210
- # → dz workflow-lint: PASS (mode=require-plan; 0 fail, 1 warn, 0 inconclusive over 17 rules)
1345
+ # → dz workflow-lint: PASS (mode=require-plan; 0 fail, 1 warn, 0 inconclusive over 18 rules)
1211
1346
  ```
1212
1347
 
1213
1348
  `inconclusive` is never a pass, and the rendered script keeps your hand edits across re-renders
1214
1349
  (USER regions are preserved).
1215
1350
 
1351
+ #### Declare each step's tool perimeter — `LoopStep.tools`
1352
+
1353
+ **When to use it:** any loop whose steps reach external systems (a ticket tracker, a wiki, a repo
1354
+ host) through MCP, and you want the intended perimeter written down, well-formed, and gated rather
1355
+ than living in a prompt someone will edit.
1356
+
1357
+ ```jsonc
1358
+ // in your plan.json — one array per DISPATCHING step (agent | gate)
1359
+ {"stepId":"discovery","kind":"agent","phase":"Discovery",
1360
+ "tools":["gitlab:read","jira:read","wiki:read"], "budget":{"maxAgents":1}},
1361
+ {"stepId":"verdict-gate","kind":"gate","phase":"Discovery","deps":["discovery"],
1362
+ "tools":[], "budget":{"maxAgents":1}}
1363
+ ```
1364
+
1365
+ `tools: []` is the **meaningful** value for a step that touches no external tool — absence is a lint
1366
+ finding, because silence is never permission:
1367
+
1368
+ ```bash
1369
+ dz workflow-lint my-loop.js --plan my-loop.plan.json --require-plan
1370
+ # → FAIL tool-perimeter-declared: dispatching step sa declares no `tools` perimeter — absence
1371
+ # FLAGS: silence is never permission (declare `tools: []` if the step touches no external tool)
1372
+ ```
1373
+
1374
+ A non-empty array is **enacted**, not decorative: it renders a fixed contract line into that step's
1375
+ prompt, and the lint rule cross-checks the script against the plan (so a plan edit you forgot to
1376
+ re-render fails too). Severity is staged — **WARN by default, FAIL only under `--require-plan`.**
1377
+
1378
+ **It is a DECLARATION, not enforcement.** `agent()` exposes no tool restriction; real enforcement
1379
+ lives at the MCP server. Do not read this field as a sandbox.
1380
+
1381
+ Worked consumer in this repo: `.claude/workflows/cfr-pipeline.js` — a 12-step, 6-gate Customer
1382
+ Feature Request pipeline with seven typed terminal exits, whose five source-touching stages each
1383
+ declare their perimeter (`features/cfr-pipeline/`).
1384
+
1216
1385
  #### Step 4 — run it (Claude Code), read it back (anywhere)
1217
1386
 
1218
1387
  In **Claude Code**, paste exactly this shape:
@@ -1342,9 +1511,9 @@ only at their documented scopes (plan + step), and `fanouts[].registry` items mu
1342
1511
  ItemKey domain the trace plane enforces — `trace.emit` can never decide whether a valid plan runs.
1343
1512
  Deferred options are on the loop-designer roadmap.
1344
1513
 
1345
- ## All Commands (66)
1514
+ ## All Commands (68)
1346
1515
 
1347
- *(66 MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases.)*
1516
+ *(67 MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases.)*
1348
1517
 
1349
1518
  ```
1350
1519
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
@@ -1399,7 +1568,7 @@ dz feature-adr-setup [--plan] [--from-spec <f>] [--apply] # guided project onb
1399
1568
  dz challenge --plan <plan.md> [--json] [--context-only] [--author <model>] # adversarial plan-gate (behind the `challenge-panel` skill): assemble a WIDE context pack (plan + vision + testing + map + degradations) + the fixed C1-C8 "break it" brief for a FRESH adversary (≠ plan author); advisory, never blocks
1400
1569
  dz routing [--stage <s>] [--json] # inspect the learned cost-optimal routing store: what `args.models.<stage>='auto-cost'` believes per (stage, complexity-tier, model) — gated attempts/successes/rate (feeds feature-adr model selection)
1401
1570
  dz bto-optimize --split|--plan|--select|--scope-check|--diff [--json] # deterministic engine behind /bto-optimize: hold-out split + hard-capped budget + no-regress-on-holdout winner selection (defeats judge-gaming); prose-only, diff-confirmed, never auto-writes
1402
- dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runner <cmd>] [--json] # §42 test-discrimination gate for feature-adr Step-8: run the ADR's property test in an isolated git worktree at pre-feature base — it MUST go red without the fix; a green is a false green (HIGH finding, advisory, never auto-aborts)
1571
+ dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runner <cmd>] [--timeout <ms>] [--json] # §42 test-discrimination gate for feature-adr Step-8: run the ADR's property test in an isolated git worktree at pre-feature base — it MUST go red without the fix. SEVEN verdicts, each gated on EXECUTION evidence: DISCRIMINATES · DISCRIMINATES_VIA_ERROR · NON_DISCRIMINATING (false green) · TEST_FILE_ABSENT · LOAD_ERROR_AT_BOTH_REVS · FAILS_AT_TIP · CANNOT_ISOLATE (+ typed reason). Advisory, never auto-aborts
1403
1572
  dz mutation-gate [--package <dir>] [--registry <file>] [--test-cmd "<cmd>"] [--only <id[,id]>] [--timeout <ms>] [--rebaseline per-entry|final] [--keep-scratch] [--json] # the mutation gate: for each NAMED protection in a declarative registry, copy the package to a scratch dir (shadow-repo layout, node_modules symlinked, git-initialized), verify the baseline is green, apply the entry's exact {find, replace} mutation, run the suite, REQUIRE red, restore — and require the red to be ATTRIBUTABLE to the protection: a mutated file that no longer parses is MUTATION_UNPARSEABLE, a failing count far above the entry's bound (maxFailing, default from observed) is OVER_FAILING, and a restored tree that does not reproduce green makes the entry INCONCLUSIVE (flaky suite). A mutation that does not apply, a green suite, or an inconclusive run is a FAILURE — never a skip. exit 0 all proven / 1 gate failed / 2 setup error
1404
1573
  dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict] [--author <model>] [--json] # portable Step-10 Delivery Gate: the `manual` form that travels to every shell target — prints the 4-plane review brief (regressions ‖ security ‖ code-quality ‖ product-honesty) + artifact probes; --findings classifies a fed-back review into a fail-closed ready|blocked hand-off (only cross-validated BLOCKER/HIGH count) and writes features/<slug>/10_delivery_review.md; --strict exits 1 on blocked
1405
1574
  dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json] # does .claude/skills/ actually REGISTER? --static = instant layout scan (CI-safe, no session): flags dirs that can never register; default also starts a real session and reads the authoritative system/init listing. exit 0 pass / 1 fail / 2 inconclusive — an unobservable registration is NEVER a pass
@@ -1422,10 +1591,12 @@ dz release [--filter <name>] [--affected] [--audit-dev] [--tag] [--pub
1422
1591
  dz auto-canonicalize --source <github-url> --pack <skills-pack>
1423
1592
  dz sync-upstream [--package <dir>] [--list] [--all]
1424
1593
  dz drift-check [--all] [--json] [--project <dir>] # CI gate: exit 1 on NEW shared-skill drift (baseline: .dz/drift-allowlist.json; --all incl .claude dogfood)
1594
+ dz agents-sync [--check] [--json] [--project <dir>] # sync anchored bearing rules into the root AGENTS.md policy fence; exit 0 sync / 1 drift / 3 inconclusive
1595
+ dz hooks-sync --target codex [--check] [--remove] [--json] # install + ARM the dz veto/recall hooks in $CODEX_HOME/hooks.json; exit 0 armed+trusted / 1 not armed / 3 inconclusive
1425
1596
  dz sync-canonical <skill> [--check] [--from <dir>] [--auto] [--project <dir>] # heal every copy from skills-meta/<skill> or --from; no canonical + --check = compare copies to each other (exit 1 on drift); no canonical + write = refuse unless --auto (LOUD, picks most-complete copy); --check writes nothing
1426
1597
  dz scout [--topics <list>] [--since <date>] [--deep] [--output <file>] [--diff] [--report]
1427
1598
  dz workflow init --name <n> [--pattern pipeline|barrier|fanout|gate] [--o <plan.json>] | validate <plan.json> [--json] | render <plan.json> --o <script.js> [--check] [--force] | blobs [--check] # loop-plan/1 authoring (the ADR-005 templates are retired)
1428
- dz workflow-lint <script.js> [--plan <plan.json>] [--require-plan|--legacy] [--json] # 17-rule deterministic gate; exit 0/1/3 — inconclusive is never a pass
1599
+ dz workflow-lint <script.js> [--plan <plan.json>] [--require-plan|--legacy] [--json] # 18-rule deterministic gate; exit 0/1/3 — inconclusive is never a pass
1429
1600
  dz workflow-trace <runDir|--slug <s>|--run <id>> [--invariants <plan.json>] [--html <out.html>] [--json] # timeline + SEQ invariant runner over the loop's own trace.jsonl
1430
1601
  dz workflow-trace export <run> --o <file> [--include-pairs --yes] [--strict] # one run's telemetry as ONE movable file
1431
1602
  dz workflow-trace import <bundle> --into <root> [--force] [--with-pairs] # reconstruct that run; fail-closed against clobbering
@@ -1440,6 +1611,44 @@ dz import-ecc [--local-path <dir>] [--select id,id,...] [--limit N] [--out
1440
1611
  dz help
1441
1612
  ```
1442
1613
 
1614
+ ## Global: `dz --version` / `-v` / `dz version`
1615
+
1616
+ ```bash
1617
+ dz --version # 0.4.8
1618
+ dz --version --json # {"name":"dz","version":"0.4.8","node":"v22.22.0","schemas":{"loopPlan":"loop-plan/1"}}
1619
+ ```
1620
+
1621
+ One line, exit 0. Unresolvable ⇒ the literal `unknown` and exit **1** — never a fabricated number,
1622
+ and never a zero exit for "I could not tell you".
1623
+
1624
+ This is a pre-dispatch GLOBAL FLAG, not a 67th command: the count above is unchanged, and a test
1625
+ derives it from the rendered `dz --help` command list so a reformat cannot move it silently.
1626
+
1627
+ **Why it exists.** `dz --version` used to print the entire USAGE manual and exit 0 (MEASURED
1628
+ 2026-08-17 on 0.4.5 — reproducer: `node dist/bin.js --version` before this change). Any wrapper
1629
+ guarding a version range — `@dzhechkov/loop-designer-plugin` is the first — would read exit 0 as "it
1630
+ answered me" while finding no version to parse, and then call a possibly-stale binary. Recognised
1631
+ only as the FIRST argument, so a later positional `-v` still belongs to its subcommand.
1632
+
1633
+ ## `dz skills-verify` also sees SLASH COMMANDS now
1634
+
1635
+ ```bash
1636
+ # does a plugin's skill AND its five commands actually register?
1637
+ dz skills-verify --dir /tmp/probe --plugin-dir packages/@dzhechkov/loop-designer-plugin \
1638
+ --expect loop-designer:loop-plan-author \
1639
+ --expect-commands loop-designer:init,loop-designer:validate,loop-designer:render,loop-designer:lint,loop-designer:trace
1640
+ ```
1641
+
1642
+ - `--plugin-dir <dir>` loads a plugin into the probe session (session-scoped, no marketplace). With
1643
+ no `--expect-commands`, the expectation DEFAULTS to the manifest's own `commands[]`; an unreadable
1644
+ manifest is refused rather than defaulted to an empty (vacuously passing) expectation.
1645
+ - `--expect-commands a,b` names the slash commands that must appear in the authoritative listing.
1646
+ - An **absent** `slash_commands` key is `inconclusive`, never an empty list — schema drift and "your
1647
+ commands did not load" are different facts and must not be collapsed.
1648
+ - `--static` now also PRINTS its advisories. A `.claude-plugin/plugin.json` under `.claude/skills/`
1649
+ previously produced `no layout problems found` and nothing else; the shape most likely to be a
1650
+ silent non-registration was invisible in the mode CI runs. It is reported, and still never fatal.
1651
+
1443
1652
  ### Grounding: three tiers + the token trade-off (`dz brain ground` / `dz brain expand`)
1444
1653
 
1445
1654
  The `dz brain ground` hook fires on **every turn**, so what it injects is a token trade-off. There are
@@ -2011,6 +2220,78 @@ dz sync-upstream --package packages/@dzhechkov/skills-devops # check one packag
2011
2220
 
2012
2221
  Discovers all skill packs with `sources.json`, fetches SKILL.md from origin repos, reports which skills have upstream changes.
2013
2222
 
2223
+ ### dz agents-sync — Codex starts with the repository's bearing rules
2224
+
2225
+ Use this after changing an anchored policy clause in `CLAUDE.md` or `.claude/rules/*.md`, and run
2226
+ the check form in CI. It updates only the `dz:policies` fence in the root `AGENTS.md`; authored
2227
+ content and the independent `dz:skills` fence remain untouched.
2228
+
2229
+ ```console
2230
+ $ dz agents-sync
2231
+ dz agents-sync: wrote — 9 policy section(s), 8097 bytes (24.71% of 32768)
2232
+
2233
+ $ dz agents-sync --check
2234
+ dz agents-sync: in sync — 9 policy section(s), 8097 bytes (24.71% of 32768)
2235
+ ```
2236
+
2237
+ Exit codes are **0** for synchronized, **1** for drift and **3** when fixed source evidence is
2238
+ missing or unreadable. `--check` never writes. Each section carries a 12-hex hash recomputed from
2239
+ its source anchor; this proves synchronization only — not that Codex read or obeyed the rule. The
2240
+ separate cold-start acceptance probe establishes runtime visibility.
2241
+
2242
+ ### dz hooks-sync — Codex runs the same veto and recall hooks Claude Code does
2243
+
2244
+ **When to use it.** Once per machine, after `npm i -g @dzhechkov/harness-cli`, if you drive Codex as
2245
+ well as Claude Code. It installs two hooks into the **user-global** `$CODEX_HOME/hooks.json`
2246
+ (default `~/.codex/hooks.json`) and **arms** them — Codex hooks are trust-gated, and an untrusted
2247
+ entry is silently never run. Re-run it after a dz upgrade; it is byte-idempotent, so an unchanged
2248
+ install rewrites nothing and the hook keeps its trust.
2249
+
2250
+ ```console
2251
+ $ dz hooks-sync --target codex
2252
+ dz hooks-sync: codex hooks installed and ARMED (trust: trusted) — ready
2253
+
2254
+ $ dz hooks-sync --target codex --check
2255
+ dz hooks-sync: codex hooks installed and ARMED (trust: trusted) — ready
2256
+
2257
+ $ dz hooks-sync --target codex --remove
2258
+ dz hooks-sync: removed 2 managed entr(ies) from /root/.codex/hooks.json
2259
+ ```
2260
+
2261
+ Exit codes are **0** for armed **and** trusted, **1** for not-armed / drift / a refusal, and **3**
2262
+ when the answer is inconclusive (including "no `codex` binary on PATH", where dz writes **nothing**).
2263
+ `--check` writes nothing and is **silent** in a home that never opted in.
2264
+
2265
+ **What the two hooks do.**
2266
+
2267
+ | Hook | Event | Behaviour |
2268
+ |---|---|---|
2269
+ | `dz-codex-veto.cjs` | `PreToolUse` | judges the shell command against one rule, `ssh-explicit-auth-weakening` |
2270
+ | `dz-codex-recall.cjs` | `UserPromptSubmit` | injects matching learned lessons and records the use with `runtime: "codex"` |
2271
+
2272
+ **The veto WARNS by default and never blocks.** A hit prints `DZ-VETO-WARN:` and exits 0. Enforcement
2273
+ is opt-in **per project**:
2274
+
2275
+ ```console
2276
+ $ cat .dz/config.json
2277
+ {"hooks": {"shellVeto": "block"}} # off | warn (default) | block
2278
+ ```
2279
+
2280
+ Only then does a hit exit 2 and stop the command. The rule fires only on tokens by which the command
2281
+ *explicitly* asks for weaker ssh authentication — `sshpass`, `-o PasswordAuthentication=yes`,
2282
+ `-o PubkeyAuthentication=no`, `-o PreferredAuthentications=…password…`. A bare `ssh myhost` whose
2283
+ identity comes from `~/.ssh/config` or `ssh-agent` is **allowed**: this guard is user-global, so a
2284
+ rule that blocks the normal secure case is not a guard, it is an outage.
2285
+
2286
+ **Radius.** Both helpers are INERT outside an opted-in dz project — the activation marker is a `.dz`
2287
+ directory, not `.git`. In a plain git checkout they take no decision, print nothing, and create
2288
+ nothing.
2289
+
2290
+ **Removal is conservative.** `--remove` deletes only entries whose command hash is recorded in dz's
2291
+ own manifest. An entry that merely *looks* like dz's is kept and reported — dz never deletes what it
2292
+ cannot prove it wrote. Foreign entries are preserved byte-for-byte by every operation, and a
2293
+ `hooks.json.bak-<ISO>` copy (newest 3 kept) is taken before each modifying write.
2294
+
2014
2295
  ### dz drift-check / dz sync-canonical — intra-monorepo skill-drift guard
2015
2296
 
2016
2297
  **What.** The same skill is physically duplicated across many packages (`packages/@dzhechkov/*/​<skill>/` + `.claude/skills/<skill>/`). These two commands make that duplication safe:
@@ -2684,23 +2965,56 @@ feature-adr Step-8 asserts the ADR's load-bearing safety property **has** a test
2684
2965
  false green — it may never exercise the property, so it would stay green even if the property regressed. The
2685
2966
  `§42` gate (learned from rUv's `cve-bench/evaluate.mjs`) proves the test **discriminates**: it runs the property
2686
2967
  test in a throwaway git worktree at the pre-feature base (no fix) and requires it to go **red**.
2968
+
2969
+ **Use it when** you are about to accept "the property has a test" as evidence — in Step-8 QE, in a review of
2970
+ someone else's fix, or before trusting a regression test you did not write. Run it against `HEAD` while your
2971
+ change is still uncommitted; that is what makes `HEAD` the pre-feature base.
2972
+
2687
2973
  ```bash
2688
2974
  # the Step-7 feature diff is uncommitted mid-pipeline, so HEAD is the pre-feature base
2689
2975
  dz discrimination-check --test packages/x/test/auth-property.test.ts --base HEAD --json
2976
+
2977
+ # human output, a longer suite, and a runner that is not vitest
2978
+ dz discrimination-check --test packages/x/test/auth-property.test.ts \
2979
+ --base HEAD --runner "node --test" --timeout 600000
2690
2980
  ```
2691
2981
  ```
2692
2982
  discrimination-check @ HEAD — verdict: NON_DISCRIMINATING
2693
2983
  ✗ packages/x/test/auth-property.test.ts: NON_DISCRIMINATING
2984
+ measurementValid: true · primaryAction: strengthen-test
2694
2985
 
2695
2986
  [high] non-discriminating property test (false green)
2696
- 1 property test(s) PASS at pre-feature base — they do not exercise the ADR safety property … (Advisory — the pipeline continues; the owner decides.)
2987
+ 1 property test(s) PASS at pre-feature base WITH execution evidence — they do not exercise the ADR safety
2988
+ property and would stay green if the fix regressed … Action: strengthen-test. (Advisory — the pipeline
2989
+ continues; the owner decides.)
2697
2990
  ```
2698
- Verdicts: `DISCRIMINATES` (red by assertion — good) · `DISCRIMINATES_VIA_ERROR` (couldn't load at base, e.g. a
2699
- brand-new module inferred) · `NON_DISCRIMINATING` (green at base a false green, HIGH finding) ·
2700
- `CANNOT_ISOLATE` (no runnable property test folds into "property untested"). It **never auto-aborts** (dz's
2701
- rule: a false gate kills trust)exit 0 on any verdict, exit 2 only on a usage/setup error. The worktree +
2702
- sanitation live in tested CLI code; base ref, paths, name filter, and runner are all injection-checked, and the
2703
- worktree is always removed. Step-8 runs this on the ADR Confirmation's `Required automated check` automatically.
2991
+
2992
+ **Every verdict is gated on EXECUTION evidence.** The gate reads the runner's own output shape (vitest /
2993
+ node --test) and requires it to demonstrate that the *named* test actually ran. An outcome nobody can prove
2994
+ degrades loudly instead of minting trust — MEASURED before this change, `--runner false` (a command that
2995
+ executes nothing and prints nothing) returned `DISCRIMINATES`; it now returns `CANNOT_ISOLATE` with
2996
+ reason `unrecognised-runner-output`.
2997
+
2998
+ | Verdict | Means | ✓/✗ | Action |
2999
+ |---|---|---|---|
3000
+ | `DISCRIMINATES` | assertion-red at base, execution-evidenced | ✓ | none |
3001
+ | `DISCRIMINATES_VIA_ERROR` | evidenced load error at base **and** an evidenced pass at TIP | ✓ | none (inference — note it) |
3002
+ | `NON_DISCRIMINATING` | evidenced pass at base — a proven false green | ✗ | `strengthen-test` |
3003
+ | `TEST_FILE_ABSENT` | the named check is not a regular file (stat+isFile, before any worktree) | ✗ | `create-missing-test` |
3004
+ | `LOAD_ERROR_AT_BOTH_REVS` | could not execute at EITHER rev — zero signal | ✗ | `fix-runner-invocation` |
3005
+ | `FAILS_AT_TIP` | the feature's own test is red **with** the feature present | ✗ | `fix-red-feature-test` |
3006
+ | `CANNOT_ISOLATE` | no established observation; the row carries a typed `reason` | ✗ | `map-a-test` / `fix-runner-invocation` |
3007
+
3008
+ `--json` carries the full reading: `perTest[]` (verdict + `reason`), the per-run `evidence` rows, `findings[]`
3009
+ (**one per distinct non-clean verdict** — the scalar `aggregate` can only name the worst), `measurementValid`
3010
+ (`true | false | 'partial'` — did the instrument measure at all), `primaryAction`, and `tipTree` (the live
3011
+ tree's HEAD + dirty-file count when a tip control ran). The singular `finding` is a **deprecated** alias for
3012
+ `findings[0]`; read `findings[]`. `--timeout <ms>` (default 300000) bounds each run — a timed-out run is
3013
+ `CANNOT_ISOLATE` reason `timeout`, never a pass.
3014
+
3015
+ It **never auto-aborts** (dz's rule: a false gate kills trust) — exit 0 on any verdict, exit 2 only on a
3016
+ usage/setup error. Base ref, paths, name filter, and runner are all injection-checked, and the worktree is
3017
+ always removed. Step-8 runs this on the ADR Confirmation's `Required automated check` automatically.
2704
3018
 
2705
3019
  ### `dz guard` — when a self-mutating operation should be refused, not regretted
2706
3020
 
@@ -3434,7 +3748,20 @@ npx @dzhechkov/p-replicator init
3434
3748
 
3435
3749
  ## Status
3436
3750
 
3437
- `v0.4.5` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3751
+ `v0.4.8` — staged (not yet published). Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3752
+
3753
+ New in 0.4.8 (feature `crossrt-1-agents-md`): `dz agents-sync` ports the fixed registry of bearing
3754
+ rules into an early root-`AGENTS.md` fence, `--check` exposes source drift to CI, and both surfaces
3755
+ report the measured Codex project-doc byte budget. A live cold-start probe, not file presence,
3756
+ remains the runtime acceptance gate.
3757
+
3758
+ New in this change (feature `dz-cli-defects`, slice A — three defects confirmed by RUNNING the CLI, then fixed):
3759
+
3760
+ - **`dz list` skips and collects** — one unparseable `SKILL.md` no longer hides the rest; the broken files are named on stderr and the exit code stays 1. Same for `dz init` / `dz install` / `dz sync`. [Details](#dz-list--one-broken-skill-never-hides-the-rest)
3761
+ - **`--target` aliases + did-you-mean** — `--target claude` (and `cc`, `agents`, `gpt`/`openai`) resolve; typos are suggested, never silently accepted. [Details](#--target-aliases-and-did-you-mean)
3762
+ - **A named refusal instead of a `SyntaxError`** — `dz` now requires `@dzhechkov/harness-core >= 0.4.7` (was `^0.4.0`, which npm could legally resolve to a core too old to link) and says so in words, before anything is imported.
3763
+
3764
+ Previously: the global `dz --version` / `-v` / `dz version` surface (one parseable line), and `dz skills-verify --plugin-dir` / `--expect-commands` so slash-command registration is gate-visible. Both exist for `@dzhechkov/loop-designer-plugin`, which requires `dz` in `^0.4`, verifies it at run time, and falls back to `npx -y @dzhechkov/harness-cli@^0.4` when the `dz` on PATH is stale, unparseable or missing.
3438
3765
 
3439
3766
  ## Claude Plugin
3440
3767
 
package/dist/bin.d.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * `dz` executable entry point.
4
+ *
5
+ * Two phases, and the split is load-bearing (feature dz-cli-defects, D5):
6
+ *
7
+ * phase 1 — ZERO `@dzhechkov/harness-core` imports: probe the installed core version
8
+ * and refuse with a NAMED message if it is below `MIN_CORE`;
9
+ * phase 2 — `await import('./cli.js')`, which is where the ~100-name static graph is
10
+ * linked. A STATIC import here would link that graph before any guard could
11
+ * run, which is exactly how a cached lower core produced a bare
12
+ * `SyntaxError: … does not provide an export named 'GRADE_SUCCESS_FLOOR'`
13
+ * with nothing pointing at the cause.
4
14
  */
5
15
  export {};
6
16
  //# sourceMappingURL=bin.d.ts.map
package/dist/bin.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;GAEG"}
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
package/dist/bin.js CHANGED
@@ -1,17 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * `dz` executable entry point.
4
+ *
5
+ * Two phases, and the split is load-bearing (feature dz-cli-defects, D5):
6
+ *
7
+ * phase 1 — ZERO `@dzhechkov/harness-core` imports: probe the installed core version
8
+ * and refuse with a NAMED message if it is below `MIN_CORE`;
9
+ * phase 2 — `await import('./cli.js')`, which is where the ~100-name static graph is
10
+ * linked. A STATIC import here would link that graph before any guard could
11
+ * run, which is exactly how a cached lower core produced a bare
12
+ * `SyntaxError: … does not provide an export named 'GRADE_SUCCESS_FLOOR'`
13
+ * with nothing pointing at the cause.
4
14
  */
5
- import { runCli } from './cli.js';
6
- const code = await runCli(process.argv.slice(2));
7
- // `process.exit()` here TRUNCATED large output on a pipe, silently and at exactly the
8
- // pipe buffer size. On a pipe (not a TTY, not a file) Node's stdout is ASYNCHRONOUS, so
9
- // `process.exit` discards whatever has not flushed yet: `dz recall --all --json > file`
10
- // wrote 122826 bytes while `dz recall --all --json | jq` got exactly 65536 and a parse
11
- // error. That is data loss in the documented sharing path, and silent — the exit code
12
- // was 0 and the JSON simply stopped mid-string.
13
- //
14
- // So: set the code and let the process end when the event loop drains, which is what
15
- // flushes stdout. `process.exitCode` preserves the status without the race.
16
- process.exitCode = code;
15
+ import { checkCoreCompat, describeMissingExportError, resolveInstalledCoreVersion, MIN_CORE } from './core-compat.js';
16
+ const foundCore = resolveInstalledCoreVersion(import.meta.url);
17
+ const compat = checkCoreCompat({ found: foundCore, min: MIN_CORE });
18
+ if (!compat.ok) {
19
+ // stderr, and no stack: the user needs one actionable line, not a trace.
20
+ console.error(compat.message);
21
+ process.exitCode = 1;
22
+ }
23
+ else {
24
+ let code;
25
+ try {
26
+ const { runCli } = await import('./cli.js');
27
+ code = await runCli(process.argv.slice(2));
28
+ }
29
+ catch (error) {
30
+ // The version probe fails OPEN, so a missing-binding link error can still reach us.
31
+ // Translate exactly that class into the named form; re-throw everything else
32
+ // unchanged so real bugs still surface.
33
+ const named = describeMissingExportError(error, foundCore);
34
+ if (named === null)
35
+ throw error;
36
+ console.error(named);
37
+ code = 1;
38
+ }
39
+ // `process.exit()` here TRUNCATED large output on a pipe, silently and at exactly the
40
+ // pipe buffer size. On a pipe (not a TTY, not a file) Node's stdout is ASYNCHRONOUS, so
41
+ // `process.exit` discards whatever has not flushed yet: `dz recall --all --json > file`
42
+ // wrote 122826 bytes while `dz recall --all --json | jq` got exactly 65536 and a parse
43
+ // error. That is data loss in the documented sharing path, and silent — the exit code
44
+ // was 0 and the JSON simply stopped mid-string.
45
+ //
46
+ // So: set the code and let the process end when the event loop drains, which is what
47
+ // flushes stdout. `process.exitCode` preserves the status without the race.
48
+ process.exitCode = code;
49
+ }
17
50
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEjD,sFAAsF;AACtF,wFAAwF;AACxF,wFAAwF;AACxF,uFAAuF;AACvF,sFAAsF;AACtF,gDAAgD;AAChD,EAAE;AACF,qFAAqF;AACrF,4EAA4E;AAC5E,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEtH,MAAM,SAAS,GAAG,2BAA2B,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/D,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;AAEpE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;IACf,yEAAyE;IACzE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;KAAM,CAAC;IACN,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,oFAAoF;QACpF,6EAA6E;QAC7E,wCAAwC;QACxC,MAAM,KAAK,GAAG,0BAA0B,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC3D,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,KAAK,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,GAAG,CAAC,CAAC;IACX,CAAC;IAED,sFAAsF;IACtF,wFAAwF;IACxF,wFAAwF;IACxF,uFAAuF;IACvF,sFAAsF;IACtF,gDAAgD;IAChD,EAAE;IACF,qFAAqF;IACrF,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC"}
package/dist/cli.d.ts CHANGED
@@ -7,6 +7,18 @@
7
7
  export interface CliIo {
8
8
  readonly cwd?: string;
9
9
  readonly write?: (line: string) => void;
10
+ /**
11
+ * Diagnostics sink — **stderr**, defaulting to `console.error`.
12
+ *
13
+ * Before feature dz-cli-defects `CliIo` had no stderr seam at all, so every
14
+ * diagnostic (including the top-level error handler) landed on stdout and
15
+ * `dz list > skills.txt` wrote the error INTO the data file. `write` stays "data
16
+ * only"; `writeErr` is "diagnosis only".
17
+ *
18
+ * There is deliberately **no** fall-back to `write`: a test that wants to assert on
19
+ * stderr must inject `writeErr`, or the assertion would be theatre.
20
+ */
21
+ readonly writeErr?: (line: string) => void;
10
22
  /**
11
23
  * Pre-read STDIN content (injectable so `dz brain ground`'s hook path is testable without
12
24
  * an actual pipe). When omitted, the CLI reads fd 0 synchronously — but only for the one
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAocH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA0lR9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoK5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsdH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAilS9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgM5E"}