@zenera/cli 1.1.14 → 1.1.16

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.
Files changed (32) hide show
  1. package/README.md +563 -206
  2. package/dist/banner.d.ts +5 -1
  3. package/dist/banner.js +2 -2
  4. package/dist/command.d.ts +5 -0
  5. package/dist/commands/init.js +46 -2
  6. package/dist/main.js +10 -6
  7. package/dist/validate.js +1 -1
  8. package/package.json +2 -2
  9. package/templates/editor/.github/copilot-instructions.md +253 -218
  10. package/templates/editor/.github/prompts/new-agent.prompt.md +6 -6
  11. package/templates/editor/.github/prompts/new-skill.prompt.md +7 -7
  12. package/templates/editor/.github/prompts/review-project.prompt.md +1 -1
  13. package/templates/editor/.github/prompts/sync-with-spec.prompt.md +26 -26
  14. package/templates/editor/.github/skills/zen-cli/SKILL.md +62 -6
  15. package/templates/editor/.github/skills/zen-cli/references/check.md +3 -3
  16. package/templates/editor/.github/skills/zen-cli/references/faker.md +14 -14
  17. package/templates/editor/.github/skills/zen-cli/references/frame.md +6 -6
  18. package/templates/editor/.github/skills/zen-cli/references/inspect.md +3 -3
  19. package/templates/editor/.github/skills/zen-cli/references/keys.md +9 -9
  20. package/templates/editor/.github/skills/zen-cli/references/memory.md +11 -11
  21. package/templates/editor/.github/skills/zen-cli/references/models.md +8 -8
  22. package/templates/editor/.github/skills/zen-cli/references/projects.md +3 -3
  23. package/templates/editor/.github/skills/zen-cli/references/rag.md +191 -56
  24. package/templates/editor/.github/skills/zen-cli/references/run.md +3 -3
  25. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +8 -8
  26. package/templates/editor/.github/skills/zen-memory/SKILL.md +50 -50
  27. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +71 -48
  28. package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +67 -67
  29. package/templates/editor/.vscode/settings.json +5 -2
  30. package/templates/project/INSTRUCTIONS.md +1 -1
  31. package/templates/project/assets/README.md +1 -1
  32. package/templates/project/sandbox/Dockerfile.tmpl +3 -0
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: zen-rag-docs
3
- description: What a document index is, how to build one from a folder of `.md` files with `zen rag docs index` (or `npx @zenera/cli`), and how to ask it something — searching by meaning and narrowing to a release, a heading or a table, listing and grepping it exactly instead of reaching for shell `grep`/`rg`, reading a section back verbatim, giving it to an agent as tools, and writing the project skill that a wired-in index requires. For an OpenAPI description instead of prose, see `zen-rag-schema`.
3
+ description: What a document index is, how to build one from a folder of `.md` files with `zen rag docs index` (or `npx @zenera/cli`), and how to ask it something - searching by meaning and narrowing to a release, a heading or a table, listing and grepping it exactly instead of reaching for shell `grep`/`rg`, reading a section back verbatim, giving it to an agent as tools, and writing the project skill that a wired-in index requires. For an OpenAPI description instead of prose, see `zen-rag-schema`.
4
4
  ---
5
5
 
6
6
  # The document index
7
7
 
8
8
  A document index is a pile of markdown turned into something that can be
9
- **asked a question**. It is built once, on disk, and answered from — and what
9
+ **asked a question**. It is built once, on disk, and answered from - and what
10
10
  comes back is not a summary but the documents themselves: the passages that
11
11
  matched, quoted verbatim, with their line numbers.
12
12
 
@@ -31,7 +31,7 @@ zen rag docs <index|search|list|grep|show|stats> [path...]
31
31
  | `show` | _print this section, verbatim_ | no | instant |
32
32
  | `stats` | _what is in this index?_ | no | instant |
33
33
 
34
- Only `search` ranks, and only `search` costs a network round trip — it embeds
34
+ Only `search` ranks, and only `search` costs a network round trip - it embeds
35
35
  the query before it can compare anything. The other four read `outline.json`
36
36
  and the copies in `sources/` off the disk and answer in milliseconds. Reach for
37
37
  `search` when the question is vague and for `list`/`grep`/`show` when it is
@@ -62,10 +62,10 @@ Three things, and the third is the point.
62
62
 
63
63
  ```
64
64
  docs-db/
65
- ├── README.md what this index holds — a live progress report while it builds
66
- ├── manifest.json written LAST — its absence means "not indexed"
65
+ ├── README.md what this index holds - a live progress report while it builds
66
+ ├── manifest.json written LAST - its absence means "not indexed"
67
67
  ├── outline.json headings and tables, read whole
68
- ├── sources/ the documents, verbatim — where every quoted line comes from
68
+ ├── sources/ the documents, verbatim - where every quoted line comes from
69
69
  └── lance/ the chunks
70
70
  ```
71
71
 
@@ -86,7 +86,7 @@ Chunking follows the markdown rather than a character count: a paragraph, a
86
86
  list, a fenced code block, the frontmatter. Every chunk knows the heading it
87
87
  sits under, which is what makes `--section` a filter and not a hope.
88
88
 
89
- Tables are indexed **twice over** — once as a descriptor carrying the caption
89
+ Tables are indexed **twice over** - once as a descriptor carrying the caption
90
90
  and the column names, and once per row, with the header row travelling
91
91
  alongside so the columns are still named wherever a row lands. A row too wide
92
92
  to be one chunk is cut into column groups with the key column repeated. That is
@@ -99,7 +99,7 @@ Every chunk gets a `kind`: `paragraph`, `list`, `table`, `table_row`, `code`,
99
99
  ## Installing
100
100
 
101
101
  `zen rag` ships in `@zenera/rag`, a sibling of the CLI. It has no binary of its
102
- own — installing it adds the `rag` subcommand to `zen`, which is also where the
102
+ own - installing it adds the `rag` subcommand to `zen`, which is also where the
103
103
  credentials already live.
104
104
 
105
105
  ```sh
@@ -138,7 +138,7 @@ That is the whole thing: point it at a directory and it walks it.
138
138
 
139
139
  `.md`, `.markdown`, `.txt` and `.text` are read; `.txt` and `.text` are treated
140
140
  as prose with no headings. Anything else is not a document and is not indexed.
141
- Hidden directories and `node_modules` are skipped — a file nobody can see is
141
+ Hidden directories and `node_modules` are skipped - a file nobody can see is
142
142
  not one anybody meant to index. A file over 16 MB is skipped as a data dump and
143
143
  said so on stderr, next to the per-document table.
144
144
 
@@ -166,13 +166,13 @@ would land on the same name are deduped rather than merged.
166
166
 
167
167
  | Flag | Default | Meaning |
168
168
  | -------------------- | ----------- | ------------------------------------------------------------ |
169
- | `--embedding <ref>` | — | Which embedder makes the vectors. Omit it to see the choices |
169
+ | `--embedding <ref>` | - | Which embedder makes the vectors. Omit it to see the choices |
170
170
  | `-o`, `--out <dir>` | `./docs-db` | Where the index goes; `$ZEN_DOCS_DB` if that is set |
171
171
  | `--batch <n>` | `96` | Texts per embedding request, and how often progress prints |
172
172
  | `--chunk-tokens <n>` | `384` | Target chunk size. Leave it alone unless you have a reason |
173
- | `--quiet` | — | No narration |
173
+ | `--quiet` | - | No narration |
174
174
 
175
- The embedding reference names a provider first — `openai:text-embedding-3-small`,
175
+ The embedding reference names a provider first - `openai:text-embedding-3-small`,
176
176
  not a bare model id. Credentials come from the `zen` keyring (`zen key ls`), and
177
177
  a real environment variable always wins.
178
178
 
@@ -184,14 +184,14 @@ the output directory and nothing else**, so `DIR=$(zen rag docs index …)` work
184
184
  everything else is stderr.
185
185
 
186
186
  This is the one command here that spends money and time: it embeds every chunk.
187
- While it runs, the output directory narrates itself — `README.md` is a live
187
+ While it runs, the output directory narrates itself - `README.md` is a live
188
188
  progress report, rewritten at most every five seconds, replaced on completion by
189
189
  a description of what the index turned out to hold, and left saying so if the
190
190
  build dies. A `.lock` names the process, so a second build of the same directory
191
191
  is refused unless the lock is stale.
192
192
 
193
193
  Rebuild the index when the documents change. Nothing watches them, and a stale
194
- index is a confident wrong answer — worse here than anywhere, because the quoted
194
+ index is a confident wrong answer - worse here than anywhere, because the quoted
195
195
  lines will look exactly as authoritative as they did when they were true.
196
196
 
197
197
  ## Which index gets read
@@ -199,7 +199,7 @@ lines will look exactly as authoritative as they did when they were true.
199
199
  Every reading command takes `-d`, `--dir`. Without one:
200
200
 
201
201
  1. `$ZEN_DOCS_DB`, if it is set. **Set this once** instead of typing `-d` on
202
- every command — `export ZEN_DOCS_DB=/assets/docs-db`.
202
+ every command - `export ZEN_DOCS_DB=/assets/docs-db`.
203
203
  2. Otherwise the **nearest index** to the working directory: here, then a short
204
204
  way down into it, then up a level and again, stopping at your home
205
205
  directory. The one chosen is named on stderr as it is used, so an answer is
@@ -207,7 +207,7 @@ Every reading command takes `-d`, `--dir`. Without one:
207
207
  3. Otherwise `./docs-db`, which is only so the error names the directory you
208
208
  were expecting.
209
209
 
210
- What is looked for is a `manifest.json`, and the search is **scoped by kind** —
210
+ What is looked for is a `manifest.json`, and the search is **scoped by kind** -
211
211
  a `docs` index and a `schema` index can sit in the same tree without either
212
212
  shadowing the other, and nothing searches for a directory literally called
213
213
  `docs-db`. That is just the name a new one is given.
@@ -229,7 +229,7 @@ zen rag docs search "how are rate limits counted"
229
229
  The answer is the corpus quoting itself:
230
230
 
231
231
  ```
232
- ## acme_4.2.0/api/routing.md — 9 of 148 lines
232
+ ## acme_4.2.0/api/routing.md - 9 of 148 lines
233
233
 
234
234
  5 | ## Rate limits
235
235
  7 | Requests are counted per tenant and rejected past the limit.
@@ -247,7 +247,7 @@ with. The numbers are what `show` and `read_docs` take.
247
247
 
248
248
  Nobody finds the paragraph they want on the first ask. They search, see it is
249
249
  the wrong release, and search again inside one. So the second call is the same
250
- question with a narrowing on it — not a different command.
250
+ question with a narrowing on it - not a different command.
251
251
 
252
252
  | Flag | Narrows to |
253
253
  | ------------------------ | ------------------------------------------------------------------------------------ |
@@ -273,28 +273,47 @@ earlier answer printed. A title that appears in four documents becomes four
273
273
  scopes, not a guess at one.
274
274
 
275
275
  **`--mode text` is the one to remember.** `hybrid` blends meaning and wording,
276
- which is right for a question; when what you have is an exact string — an error
277
- message, a header name, a flag — `text` is exact wording only, and meaning can
276
+ which is right for a question; when what you have is an exact string - an error
277
+ message, a header name, a flag - `text` is exact wording only, and meaning can
278
278
  only dilute it.
279
279
 
280
280
  The narrowings are resolved against the manifest and the outline **before** the
281
281
  store is touched, so a `--file` pattern that matches nothing says so instead of
282
282
  quietly searching everything.
283
283
 
284
+ ### One wording per leg
285
+
286
+ `hybrid` asks two retrievers the same sentence, and they read it differently:
287
+ the full-text leg wants the words the documents use, the vector leg wants the
288
+ question stated in full. When you know both - the exact header name _and_ what
289
+ you actually want to know about it - say each one:
290
+
291
+ ```sh
292
+ zen rag docs search --text-query "Retry-After" \
293
+ --vector-query "how long to wait before trying the request again"
294
+ ```
295
+
296
+ Both legs run and the results fuse as usual. Either flag alone runs that leg
297
+ alone, so `--text-query` by itself is `--mode text` with its own wording.
298
+
299
+ They **replace** the positional text rather than narrowing it, and they settle
300
+ the mode by existing. `search "x" --text-query "y"` and `--text-query "y"
301
+ --mode vector` are both refused, because each says two things at once.
302
+
284
303
  ### Shaping the answer
285
304
 
286
305
  | Flag | Default | Meaning |
287
306
  | -------------------- | ------- | ----------------------------------------- |
288
- | `-d`, `--dir <dir>` | found | Which index — see "Which index gets read" |
307
+ | `-d`, `--dir <dir>` | found | Which index - see "Which index gets read" |
289
308
  | `--embedding <ref>` | index's | Must be the one the index was built with |
290
309
  | `--limit <n>` | `8` | Passages kept |
291
310
  | `-B`, `--before <n>` | `0` | Extra lines quoted before each passage |
292
311
  | `-A`, `--after <n>` | `0` | Extra lines quoted after each passage |
293
312
  | `--max-lines <n>` | `400` | A ceiling on the whole answer |
294
- | `--no-numbers` | — | Quote the lines without their numbers |
295
- | `--hits` | — | One row per passage instead of the text |
296
- | `--interactive` | — | Prompt, search, narrow. Needs a terminal |
297
- | `--quiet` | — | No narration |
313
+ | `--no-numbers` | - | Quote the lines without their numbers |
314
+ | `--hits` | - | One row per passage instead of the text |
315
+ | `--interactive` | - | Prompt, search, narrow. Needs a terminal |
316
+ | `--quiet` | - | No narration |
298
317
 
299
318
  `--hits` is the fast triage: it prints where the passages are without spending
300
319
  the lines on them, and you follow up with `show`.
@@ -302,7 +321,7 @@ the lines on them, and you follow up with `show`.
302
321
  ### As a machine interface
303
322
 
304
323
  Non-interactive search is a tool, not an afterthought. `--json` is a stable
305
- shape — `{matches, files, scope, mode, considered, truncated}` — no terminal is
324
+ shape - `{matches, files, scope, mode, considered, truncated}` - no terminal is
306
325
  needed, and **an empty result exits 0**: a caller must never have to tell
307
326
  "nothing matched" from "the index is missing" by parsing stderr.
308
327
 
@@ -330,7 +349,7 @@ quit
330
349
 
331
350
  ## Reading it without searching
332
351
 
333
- None of these need an embedder or a credential — they are plain reads of the
352
+ None of these need an embedder or a credential - they are plain reads of the
334
353
  outline and the copies on disk.
335
354
 
336
355
  ```
@@ -365,7 +384,7 @@ zen rag docs stats
365
384
  ```
366
385
 
367
386
  `list files` reports the name, title, format, and how many lines, sections,
368
- tables and chunks each document holds — which is also the fastest way to learn
387
+ tables and chunks each document holds - which is also the fastest way to learn
369
388
  the names your `--file` patterns will be matched against. `list sections` is a
370
389
  table of contents with line spans; `list tables` names every table's caption,
371
390
  columns and row count, which is how you find the limits table without reading
@@ -374,14 +393,14 @@ the prose around it.
374
393
  `grep` reports `found` as the **true total** even when `--limit` cuts the rows,
375
394
  so unlike a search it can answer whether a string appears at all. Every hit
376
395
  carries the innermost heading it sits under, so a match has a place. Nothing
377
- matching exits 0 with empty stdout — and that emptiness is trustworthy, which is
396
+ matching exits 0 with empty stdout - and that emptiness is trustworthy, which is
378
397
  the whole point of it.
379
398
 
380
399
  `show` prints a document, a named section, or a line range, verbatim, with a
381
400
  line-number gutter (`--no-numbers` to drop it). It is the natural follow-up to
382
401
  every search: find the passage, then read what is actually around it.
383
402
 
384
- `stats` says what is in an index and what built it — the documents, the counts,
403
+ `stats` says what is in an index and what built it - the documents, the counts,
385
404
  the embedding model. It is the fastest way to answer "is this index the one I
386
405
  think it is?".
387
406
 
@@ -390,7 +409,7 @@ think it is?".
390
409
  | The question | The command |
391
410
  | ------------------------------------------------- | ----------------------------------------------- |
392
411
  | "how does this thing handle retries?" | `search "how are retries handled"` |
393
- | _anything you would have run `grep` for_ | `grep` / `list` — never the shell |
412
+ | _anything you would have run `grep` for_ | `grep` / `list` - never the shell |
394
413
  | "does `X-Request-Id` appear anywhere?" | `grep X-Request-Id` |
395
414
  | "what does the rate-limits section actually say?" | `show <file> --section "Rate limits"` |
396
415
  | "which documents are even in here?" | `list files` |
@@ -415,7 +434,7 @@ Add `-d <dir>` when the index is not the nearest one, or name it once with
415
434
  | `grep -rn "Retry-After" docs/` | `zen rag docs grep "Retry-After"` |
416
435
  | `rg -i retry --glob 'api/**'` | `zen rag docs grep retry --file "api/**"` |
417
436
  | `grep -E "X-RateLimit-\w+"` | `zen rag docs grep "X-RateLimit-\w+" --regex` |
418
- | `grep -c` / `wc -l` | `--json`, and read `found` — it counts past `--limit` |
437
+ | `grep -c` / `wc -l` | `--json`, and read `found` - it counts past `--limit` |
419
438
  | `find docs -name '*.md'` | `zen rag docs list files` |
420
439
  | `grep '^#' file.md` | `zen rag docs list sections --file file.md` |
421
440
  | `sed -n '40,80p' file.md` | `zen rag docs show file.md --lines 40-80` |
@@ -429,7 +448,7 @@ zen rag docs list sections --file "api/**" --depth 3
429
448
  ```
430
449
 
431
450
  If none of these fits the question, the question is about meaning, and the
432
- answer is `search` — still not the shell.
451
+ answer is `search` - still not the shell.
433
452
 
434
453
  ## Giving it to an agent
435
454
 
@@ -450,12 +469,12 @@ const project = await loadProject('./my-project', { tools: docs.docsTools(index)
450
469
  | Tool | For |
451
470
  | ------------- | ------------------------------------------------------------ |
452
471
  | `search_docs` | the passages that match, quoted with their line numbers |
453
- | `list_docs` | the documents, their headings, or their tables — no search |
454
- | `grep_docs` | every matching line, counted in full — no search |
472
+ | `list_docs` | the documents, their headings, or their tables - no search |
473
+ | `grep_docs` | every matching line, counted in full - no search |
455
474
  | `read_docs` | a section or a line range, verbatim and with nothing omitted |
456
475
 
457
476
  Only `search_docs` ranks; the other three are exact, because a model told "no
458
- results" by a vector search has learned nothing — a ranking returns the top of a
477
+ results" by a vector search has learned nothing - a ranking returns the top of a
459
478
  list, so an empty answer and an absent thing look identical. `grep_docs` is how
460
479
  "it is not written down anywhere" can actually be concluded.
461
480
 
@@ -466,18 +485,22 @@ or `kind: ["table"]`. Those are parameters and not separate tools, so narrowing
466
485
  costs one call instead of three. `exclude_ids` takes the ids from an earlier
467
486
  answer, so asking again moves on instead of repeating itself.
468
487
 
488
+ `text_query` and `vector_query` are the tool's version of the two flags above:
489
+ send one to run that leg alone, or both to word each half of a hybrid search.
490
+ They replace `query`, so sending it as well is refused.
491
+
469
492
  Every answer carries line numbers and `read_docs` takes them. That is the loop
470
493
  the whole subject exists for: **find the passage, read around it, then edit the
471
494
  file the passage came from.**
472
495
 
473
- Tell the agent in its prompt to call `list_docs` once before its first search —
496
+ Tell the agent in its prompt to call `list_docs` once before its first search -
474
497
  the document names are what every `files` pattern is matched against, and a
475
498
  model guessing at them narrows to nothing and concludes the corpus is empty.
476
499
 
477
500
  ## Wiring it into a project means writing the project a skill
478
501
 
479
- **Whenever an index is used by a Zenera project — as `docs:*` tools, or as
480
- `zen rag` reachable from the agent's sandbox — write a skill for it in that
502
+ **Whenever an index is used by a Zenera project - as `docs:*` tools, or as
503
+ `zen rag` reachable from the agent's sandbox - write a skill for it in that
481
504
  project.** Not optional, and not the same thing as passing the tools in.
482
505
 
483
506
  Wiring alone leaves the model to infer everything that matters. A tool
@@ -486,7 +509,7 @@ ACME 4.1 and 4.2 manuals side by side, that every answer must be pinned to a
486
509
  release with `files`, that the API reference lives under `*/api/**` and the
487
510
  task guides under `*/guides/**`, or that the numbers anybody actually wants are
488
511
  in tables and so `kind: ["table_row"]` is the right first move. That is project
489
- knowledge, and project knowledge belongs in a skill — where it is loaded only
512
+ knowledge, and project knowledge belongs in a skill - where it is loaded only
490
513
  when the model is working on this corpus, instead of sitting in the system
491
514
  prompt of every run.
492
515
 
@@ -504,7 +527,7 @@ what unlocks them.
504
527
  | Section | Because |
505
528
  | ------------------ | ----------------------------------------------------------------------------------- |
506
529
  | Which corpus | What it is, which version(s), and when it was indexed |
507
- | Where the index | `ZEN_DOCS_DB`, or the `-d` to pass — an agent cannot guess a path |
530
+ | Where the index | `ZEN_DOCS_DB`, or the `-d` to pass - an agent cannot guess a path |
508
531
  | The document names | The shape `--file`/`files` patterns match, with two real examples |
509
532
  | Which command | Meaning → `search`; presence or a count → `grep`; what it says → `show`/`read_docs` |
510
533
  | Never the shell | State it outright. `grep`/`rg`/`cat` on the tree is the default reflex |
@@ -522,7 +545,7 @@ Best practice, in order of how often it is got wrong:
522
545
  3. **Name the narrowing that matters here.** Two releases in one index is the
523
546
  common case, and an unpinned search silently mixes them.
524
547
  4. **Keep it short.** A skill is prompt. One screen of routing rules beats a
525
- transcription of this document — link to `zen rag docs --help` for the flags.
548
+ transcription of this document - link to `zen rag docs --help` for the flags.
526
549
  5. **Re-index, then re-read the skill.** Both go stale against the same change,
527
550
  and a skill quoting headings that no longer exist is worse than none.
528
551
  6. **One skill per corpus**, named after it. Two corpora in one skill and the
@@ -531,7 +554,7 @@ Best practice, in order of how often it is got wrong:
531
554
  ```md
532
555
  ---
533
556
  name: acme-docs
534
- description: How to find an answer in the ACME 4.1 and 4.2 manuals — which document says it,
557
+ description: How to find an answer in the ACME 4.1 and 4.2 manuals - which document says it,
535
558
  what it says verbatim, and which release it is true of. Use before answering anything
536
559
  about ACME behaviour, limits or API routes.
537
560
  ---
@@ -550,7 +573,7 @@ reference and `guides/` for the task documentation.
550
573
  - Limits and defaults are in tables → add `kind: ["table_row"]`.
551
574
  - Then `read_docs` the lines around the hit before answering. Never answer from
552
575
  the excerpt alone.
553
- - Never `grep`/`rg`/`cat` the tree — the tools above are local and exact.
576
+ - Never `grep`/`rg`/`cat` the tree - the tools above are local and exact.
554
577
 
555
578
  Worked: per-route rate limits are the table under "Rate limits" in
556
579
  `acme_4.2.0/api/routing.md` (lines 24-31); the retry envelope is described two
@@ -561,13 +584,13 @@ sections down. Cite the document name and the line numbers.
561
584
 
562
585
  | Symptom | Cause |
563
586
  | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
564
- | "not installed" | `@zenera/rag` is not resolvable — install it, or use the two-`--package` npx form |
587
+ | "not installed" | `@zenera/rag` is not resolvable - install it, or use the two-`--package` npx form |
565
588
  | `nothing to index` | No `.md`/`.markdown`/`.txt`/`.text` under those paths, or they are all hidden |
566
- | A document you expected is missing | Hidden directory, `node_modules`, an unlisted extension, or over 16 MB — check stderr |
589
+ | A document you expected is missing | Hidden directory, `node_modules`, an unlisted extension, or over 16 MB - check stderr |
567
590
  | Refused for a different embedding | The index records the model that built it; re-index or pass the right `--embedding` |
568
591
  | No manifest / not an index | A build that did not finish. `manifest.json` is written last on purpose |
569
- | A build is refused | `.lock` — another build is running. It is taken over when the process is gone |
570
- | `provider "openai": no api key` | `zen key ls` — the keyring, or a real environment variable |
592
+ | A build is refused | `.lock` - another build is running. It is taken over when the process is gone |
593
+ | `provider "openai": no api key` | `zen key ls` - the keyring, or a real environment variable |
571
594
  | A usage error before any credential is asked for | Deliberate: everything about the invocation is checked first, so a typo is a typo |
572
595
  | `no document matched --file` | The pattern is matched against the document **name**; `list files` prints them |
573
596
  | Nothing matched | Exit 0 with an empty answer. Try fewer words, or `--mode text` for an exact string |