@zenera/cli 1.1.6 → 1.1.8
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/dist/commands/index.js +2 -2
- package/dist/podman.d.ts +0 -9
- package/dist/podman.js +3 -10
- package/package.json +2 -2
- package/templates/editor/.github/skills/api-schema-index/SKILL.md +158 -16
- package/templates/editor/.github/skills/zen-cli/SKILL.md +1 -1
- package/templates/editor/.github/skills/zen-cli/references/rag.md +86 -7
package/dist/commands/index.js
CHANGED
|
@@ -48,8 +48,8 @@ export const EXTERNAL = {
|
|
|
48
48
|
},
|
|
49
49
|
rag: {
|
|
50
50
|
package: '@zenera/rag',
|
|
51
|
-
summary: '
|
|
52
|
-
usage: 'zen rag
|
|
51
|
+
summary: 'Retrieval over a corpus: index it, then ask it something.',
|
|
52
|
+
usage: 'zen rag <subject> <command> [args...]',
|
|
53
53
|
install: 'npm i -g @zenera/rag',
|
|
54
54
|
banner: { head: 'Zenera', accent: 'Rag', subtitle: 'Api Retrieval' },
|
|
55
55
|
},
|
package/dist/podman.d.ts
CHANGED
|
@@ -34,15 +34,6 @@ export interface PodmanStatus {
|
|
|
34
34
|
imagePresent?: boolean;
|
|
35
35
|
}
|
|
36
36
|
export declare function ensurePodmanReady(opts?: PodmanOptions): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Builds the project's Dockerfile under its tag.
|
|
39
|
-
*
|
|
40
|
-
* Run every time rather than skipped when the tag already exists, because the
|
|
41
|
-
* tag is a hash of the Dockerfile and its context and the engine's layer cache
|
|
42
|
-
* is a hash of the same thing plus the base image. Asking it is cheap, and it
|
|
43
|
-
* is the only thing that notices when `FROM node:24` starts meaning a different
|
|
44
|
-
* node:24.
|
|
45
|
-
*/
|
|
46
37
|
/**
|
|
47
38
|
* A build that ran and failed, rather than a host that could not be asked.
|
|
48
39
|
*
|
package/dist/podman.js
CHANGED
|
@@ -167,15 +167,6 @@ function parseMachines(stdout) {
|
|
|
167
167
|
// ---------------------------------------------------------------------------
|
|
168
168
|
// The image
|
|
169
169
|
// ---------------------------------------------------------------------------
|
|
170
|
-
/**
|
|
171
|
-
* Builds the project's Dockerfile under its tag.
|
|
172
|
-
*
|
|
173
|
-
* Run every time rather than skipped when the tag already exists, because the
|
|
174
|
-
* tag is a hash of the Dockerfile and its context and the engine's layer cache
|
|
175
|
-
* is a hash of the same thing plus the base image. Asking it is cheap, and it
|
|
176
|
-
* is the only thing that notices when `FROM node:24` starts meaning a different
|
|
177
|
-
* node:24.
|
|
178
|
-
*/
|
|
179
170
|
/**
|
|
180
171
|
* A build that ran and failed, rather than a host that could not be asked.
|
|
181
172
|
*
|
|
@@ -204,7 +195,9 @@ async function build(engine, spec, run, force = false) {
|
|
|
204
195
|
return;
|
|
205
196
|
}
|
|
206
197
|
}
|
|
207
|
-
note(dim(
|
|
198
|
+
note(dim(`${force ? 'rebuilding' : 'building'} the sandbox image from ${spec.dockerfile}`));
|
|
199
|
+
note(dim(' the agent runs its commands inside a container, so the image is needed first'));
|
|
200
|
+
note(dim(' this takes a few minutes; later runs reuse it until the Dockerfile changes'));
|
|
208
201
|
const built = await stream(engine, ['build', '--tag', spec.tag, '--file', spec.dockerfile, spec.context], run, BUILD_MS);
|
|
209
202
|
if (built.code !== 0) {
|
|
210
203
|
throw new BuildError(`could not build ${spec.dockerfile}`, EXIT.sandbox, last(built) || 'run the build by hand to see what the engine says');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenera/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Command-line front end for @zenera/neo: agentic projects you can run, share and commit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@inkjs/ui": "^2.0.0",
|
|
52
52
|
"ink": "^7.1.1",
|
|
53
53
|
"react": "^19.2.8",
|
|
54
|
-
"@zenera/neo": "^1.1.
|
|
54
|
+
"@zenera/neo": "^1.1.8",
|
|
55
55
|
"@anthropic-ai/sdk": "^0.120.0",
|
|
56
56
|
"@google/genai": "^2.18.0",
|
|
57
57
|
"@openrouter/sdk": "^1.2.80",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: api-schema-index
|
|
3
|
-
description: What a schema index is, how it finds the right call inside a large OpenAPI/Swagger document, and how to build and query one with `zen rag schema` (or `npx @zenera/cli`) — searching it by meaning, listing and grepping it exactly instead of reaching for shell `grep`/`rg`,
|
|
3
|
+
description: What a schema index is, how it finds the right call inside a large OpenAPI/Swagger document, and how to build and query one with `zen rag schema` (or `npx @zenera/cli`) — searching it by meaning, listing and grepping it exactly instead of reaching for shell `grep`/`rg`, tracing a field up to the operations that carry it, giving it to an agent as tools, and writing the project skill that a wired-in index requires.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# The schema index
|
|
7
7
|
|
|
8
8
|
A schema index is an OpenAPI/Swagger description turned into something that can
|
|
9
9
|
be **asked a question**. It is built once, on disk, and answered from without a
|
|
10
|
-
model: `zen rag schema index` writes it, and
|
|
10
|
+
model: `zen rag schema index` writes it, and six commands read it.
|
|
11
11
|
|
|
12
12
|
It exists because a real specification does not fit in a context window, and
|
|
13
13
|
grepping it does not help. The parts that answer "how do I reset a password?"
|
|
@@ -18,7 +18,7 @@ word "password" appears in forty places that are not the one you want.
|
|
|
18
18
|
## The commands
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
zen rag schema <index|search|list|grep|show|stats> [spec...]
|
|
21
|
+
zen rag schema <index|search|list|grep|trace|show|stats> [spec...]
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
| Command | Answers | Embedder? | Typical |
|
|
@@ -27,15 +27,16 @@ zen rag schema <index|search|list|grep|show|stats> [spec...]
|
|
|
27
27
|
| `search` | _what is this API's way to do X?_ | **yes** | seconds |
|
|
28
28
|
| `list` | _what methods/types/fields are there?_ | no | instant |
|
|
29
29
|
| `grep` | _does the string X appear anywhere?_ | no | instant |
|
|
30
|
+
| `trace` | _which call can reach this field?_ | no | instant |
|
|
30
31
|
| `show` | _print exactly these things_ | no | instant |
|
|
31
32
|
| `stats` | _what is in this index?_ | no | instant |
|
|
32
33
|
|
|
33
34
|
Only `search` ranks, and only `search` costs a network round trip — it embeds
|
|
34
|
-
the query before it can compare anything. The other
|
|
35
|
+
the query before it can compare anything. The other five read `graph.json` off
|
|
35
36
|
the disk and answer in milliseconds, so reach for `search` when the question is
|
|
36
|
-
vague and for `list`/`grep` when it is precise. If a search feels slow,
|
|
37
|
-
that one embedding call, not the index: near-zero CPU for several seconds
|
|
38
|
-
the tell.
|
|
37
|
+
vague and for `list`/`grep`/`trace` when it is precise. If a search feels slow,
|
|
38
|
+
it is that one embedding call, not the index: near-zero CPU for several seconds
|
|
39
|
+
is the tell.
|
|
39
40
|
|
|
40
41
|
> **`search` takes bare words as the query, not as a subcommand.**
|
|
41
42
|
> `zen rag schema search list methods` does not list anything — it runs a
|
|
@@ -138,6 +139,7 @@ zen rag schema index <spec...> [--embedding <ref>] [-o <dir>] [--batch <n>]
|
|
|
138
139
|
| `--embedding <ref>` | — | Which embedder makes the vectors. Omit it to see the choices |
|
|
139
140
|
| `-o`, `--out <dir>` | `./schema-db` | Where the index goes; `$ZEN_SCHEMA_DB` if that is set |
|
|
140
141
|
| `--batch <n>` | `96` | Texts per embedding request, and how often progress prints |
|
|
142
|
+
| `--no-sources` | — | Keep no copy of the documents; `show --source` rebuilds them |
|
|
141
143
|
| `--quiet` | — | No narration |
|
|
142
144
|
|
|
143
145
|
```sh
|
|
@@ -316,6 +318,7 @@ think it is?".
|
|
|
316
318
|
| ------------------------------------------------ | ---------------------------------------- |
|
|
317
319
|
| "how do I reset a password with this API?" | `search --method "reset a password"` |
|
|
318
320
|
| _anything you would have run `grep` for_ | `grep` / `list` — never the shell |
|
|
321
|
+
| "which call can reach this field?" | `trace <field>` |
|
|
319
322
|
| "what does the create-user request look like?" | `search --input-type "create user"` |
|
|
320
323
|
| "what operations exist under /users?" | `list methods --path "*/users*"` |
|
|
321
324
|
| "how many operations are there at all?" | `list methods` (or `--json` for `found`) |
|
|
@@ -325,9 +328,9 @@ think it is?".
|
|
|
325
328
|
| "is this index the right one?" | `stats` |
|
|
326
329
|
|
|
327
330
|
The rule: **a question about meaning is a `search`; a question about presence,
|
|
328
|
-
count or spelling is a `list` or a `grep
|
|
329
|
-
|
|
330
|
-
them are right.
|
|
331
|
+
count or spelling is a `list` or a `grep`; a question about reachability is a
|
|
332
|
+
`trace`.** Search cannot answer the last two, because a ranking always returns
|
|
333
|
+
its best guesses whether or not any of them are right.
|
|
331
334
|
|
|
332
335
|
### Exact matching, when the question is whether something exists
|
|
333
336
|
|
|
@@ -344,7 +347,7 @@ zen rag schema grep <pattern> [-d <dir>] [filters…]
|
|
|
344
347
|
| ------------------- | ------ | ---------------------------------------------- |
|
|
345
348
|
| `--name <p>` | both | Match the name. Repeatable |
|
|
346
349
|
| `--path <p>` | both | Match the route it sits on. Repeatable |
|
|
347
|
-
| `--regex` | both | Read
|
|
350
|
+
| `--regex` | both | Read the patterns as regular expressions |
|
|
348
351
|
| `--case-sensitive` | both | Stop ignoring case |
|
|
349
352
|
| `--source <name>` | both | Only one document, as `stats` names it |
|
|
350
353
|
| `--show-source` | both | Print which document each row came from |
|
|
@@ -375,12 +378,15 @@ so the two agree on what the API says. A pattern with `*` or `?` is a glob
|
|
|
375
378
|
matched against the whole string; a plain word is a substring, so `--name
|
|
376
379
|
password` finds `ResetPasswordPayload` and `--name "Password*"` finds nothing.
|
|
377
380
|
|
|
378
|
-
**`--regex`
|
|
379
|
-
|
|
381
|
+
**`--regex` is the only way to say "one of these"** — a glob has no alternation.
|
|
382
|
+
On `list` it turns every pattern into a regular expression. On `grep` it turns
|
|
383
|
+
the **pattern** into one; `--name` and `--path` stay globs-or-substrings there,
|
|
384
|
+
because they are always names:
|
|
380
385
|
|
|
381
386
|
```sh
|
|
382
387
|
zen rag schema list methods --regex --path "^/(users|teams)/"
|
|
383
388
|
zen rag schema list types --regex --name "(Request|Response)$"
|
|
389
|
+
zen rag schema grep "pass(word|phrase)" --regex --path "*/users*"
|
|
384
390
|
```
|
|
385
391
|
|
|
386
392
|
`--name` and `--path` are constraints on `grep` as well, which is what makes a
|
|
@@ -404,6 +410,63 @@ whole point of them.
|
|
|
404
410
|
zen rag schema grep token --ids-only | xargs zen rag schema show --format ts
|
|
405
411
|
```
|
|
406
412
|
|
|
413
|
+
### Upwards, from a field to the calls that carry it
|
|
414
|
+
|
|
415
|
+
Finding the field is half the job. The other half — which operation can
|
|
416
|
+
actually reach it — is a walk up the `$ref`s.
|
|
417
|
+
|
|
418
|
+
`search` does part of it: it stitches its seeds into one connected piece and
|
|
419
|
+
prints what each operation accepts and returns, so a lucky search does show the
|
|
420
|
+
call. But it joins only what **ranked**, only within `--max-hops` (3 by
|
|
421
|
+
default), and it never names the chain — and the call almost never repeats the
|
|
422
|
+
word, so `GET /users/{userId}` and `city` have nothing in common except the
|
|
423
|
+
edges between them. `trace` follows those edges instead of guessing at them:
|
|
424
|
+
exhaustive, and certain.
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
zen rag schema trace <pattern|id...> [-d <dir>] [filters…]
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```sh
|
|
431
|
+
zen rag schema trace city
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
Property:Address.city
|
|
436
|
+
GET /users/{userId} getUser output PublicUserProfile.address → Address.city
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
One command instead of three lookups and a guess: find the field, find what
|
|
440
|
+
holds `Address`, find what accepts _that_, and hope you followed every branch.
|
|
441
|
+
The last column is the whole route, so the shape of the call can be read off
|
|
442
|
+
the answer.
|
|
443
|
+
|
|
444
|
+
| Flag | Default | Meaning |
|
|
445
|
+
| ----------------- | ------------------ | ---------------------------------------------------- |
|
|
446
|
+
| `--kind <k>` | types + properties | `method`, `type` or `property`. Repeatable |
|
|
447
|
+
| `--direction <d>` | `any` | Only the calls that accept it, or that return it |
|
|
448
|
+
| `--max-hops <n>` | `8` | How far up to walk |
|
|
449
|
+
| `--limit <n>` | — | Trace at most n matching nodes |
|
|
450
|
+
| `--routes <n>` | — | Operations printed per node; `found` counts them all |
|
|
451
|
+
| `--ids-only` | — | Bare operation ids, one per line, for piping |
|
|
452
|
+
| `--regex` | — | Read the pattern as a regex; `--case-sensitive` too |
|
|
453
|
+
| `--source <name>` | — | Only nodes from one document |
|
|
454
|
+
| `--show-source` | — | Print which document each operation came from |
|
|
455
|
+
|
|
456
|
+
A bare word matches the way `list --name` does — a substring, or a glob when it
|
|
457
|
+
has `*` or `?`. A node id (`Type:User`) is taken as that node rather than as a
|
|
458
|
+
pattern. Operations are left out of a name match on purpose: they are where a
|
|
459
|
+
trace ends, not where one starts.
|
|
460
|
+
|
|
461
|
+
```sh
|
|
462
|
+
zen rag schema trace password --direction input
|
|
463
|
+
zen rag schema trace "*Settings" --kind type
|
|
464
|
+
zen rag schema trace mfa_secret --ids-only | xargs zen rag schema show --format openapi
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`no operation reaches it` is a real answer, and one worth having: the schema is
|
|
468
|
+
unreachable in this document, so no request will ever carry it.
|
|
469
|
+
|
|
407
470
|
### Instead of the shell
|
|
408
471
|
|
|
409
472
|
Every reflex that reaches for a shell tool has a command here that answers the
|
|
@@ -456,7 +519,7 @@ client or a mock payload from.
|
|
|
456
519
|
|
|
457
520
|
## Giving it to an agent
|
|
458
521
|
|
|
459
|
-
The same engine, as
|
|
522
|
+
The same engine, as six tools in the group `schema`. An agent takes them all
|
|
460
523
|
with `schema:*` in its `tools:`.
|
|
461
524
|
|
|
462
525
|
```ts
|
|
@@ -477,14 +540,17 @@ const project = await loadProject('./my-project', { tools: schemaTools(index) })
|
|
|
477
540
|
| `find_types_with_property` | every schema with a field of this name — exact lookup, no searching |
|
|
478
541
|
| `list_api` | methods, types or fields by name — complete, and counted in full |
|
|
479
542
|
| `grep_api` | every literal occurrence of a string — the way to prove absence |
|
|
543
|
+
| `trace_api` | up from a field or schema to the operations that carry it |
|
|
480
544
|
|
|
481
|
-
Only `search_api` ranks; the other
|
|
545
|
+
Only `search_api` ranks; the other five are exact. `find_types_with_property`
|
|
482
546
|
is the one for the repair loop. When `tsc` says `'password' does not exist in
|
|
483
547
|
type 'PublicUserProfile'`, the model does not need the word explained again —
|
|
484
548
|
it needs the list of types that _do_ have one, and embedding the word will only
|
|
485
549
|
rank the guess it already made near the top. `grep_api` is the same instinct
|
|
486
550
|
widened: it is how a model checks that a search returning nothing really means
|
|
487
|
-
there is nothing.
|
|
551
|
+
there is nothing. `trace_api` is the step after either of them: a field is of
|
|
552
|
+
no use until the call that carries it is known, and no ranking will find that
|
|
553
|
+
call — the operation and the field share no words, only edges.
|
|
488
554
|
|
|
489
555
|
`list_api` and `grep_api` take `name`, `path`, `regex` and `source`, so a
|
|
490
556
|
common word can be narrowed to one route or one document rather than read out
|
|
@@ -495,6 +561,82 @@ together, which one answered is part of the answer.
|
|
|
495
561
|
Tell the agent in its prompt to search before it writes a call, and to put the
|
|
496
562
|
intent in the narrow field. A model left to itself puts everything in `all`.
|
|
497
563
|
|
|
564
|
+
## Wiring it into a project means writing the project a skill
|
|
565
|
+
|
|
566
|
+
**Whenever an index is used by a Zenera project — as `schema:*` tools, or as
|
|
567
|
+
`zen rag` reachable from the agent's sandbox — write a skill for it in that
|
|
568
|
+
project.** Not optional, and not the same thing as passing the tools in.
|
|
569
|
+
|
|
570
|
+
Wiring alone leaves the model to infer everything that matters. A tool
|
|
571
|
+
description says what `grep_api` does; it cannot say that this index holds the
|
|
572
|
+
NSX policy API, that names are `snake_case`, that every route is under
|
|
573
|
+
`/policy/api/v1`, or that `list_api` is the right first move here because the
|
|
574
|
+
API has three hundred operations and search will hand back five. That is
|
|
575
|
+
project knowledge, and project knowledge belongs in a skill — where it is
|
|
576
|
+
loaded only when the model is actually working on this API, instead of sitting
|
|
577
|
+
in the system prompt of every run.
|
|
578
|
+
|
|
579
|
+
```
|
|
580
|
+
<project>/agents/skills/<api>-api/SKILL.md
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Frontmatter is `name` and `description`; the description is what the model
|
|
584
|
+
reads when choosing, so it must name the API and the questions it answers.
|
|
585
|
+
Add `tools: [search_api, list_api, grep_api, trace_api, describe_types,
|
|
586
|
+
find_types_with_property]` if the skill should be what unlocks them.
|
|
587
|
+
|
|
588
|
+
### What the skill has to say
|
|
589
|
+
|
|
590
|
+
| Section | Because |
|
|
591
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
592
|
+
| Which API | The name, the version, and the document it was built from |
|
|
593
|
+
| Where the index | `ZEN_SCHEMA_DB`, or the `-d` to pass — an agent cannot guess a path |
|
|
594
|
+
| Which command | Meaning → `search`; presence, spelling or a count → `list`/`grep`; which endpoint carries a field → `trace` |
|
|
595
|
+
| Never the shell | State it outright. `grep`/`rg`/`jq` on the spec is the default reflex |
|
|
596
|
+
| The conventions | Auth, base path, pagination, casing, error envelope — none of it is in the graph |
|
|
597
|
+
| Worked examples | Two or three, with **real operation and schema names from this index** |
|
|
598
|
+
| The repair loop | Compiler said the field is not on the type → `find_types_with_property`, then `trace` for the call that carries it |
|
|
599
|
+
|
|
600
|
+
Best practice, in order of how often it is got wrong:
|
|
601
|
+
|
|
602
|
+
1. **Use real names.** `list_api types --name "*Policy*"` with output the model
|
|
603
|
+
will actually see beats a generic `<TypeName>` placeholder, because the
|
|
604
|
+
names are the anchors it steers by.
|
|
605
|
+
2. **Say which command answers which question**, and say that a ranking cannot
|
|
606
|
+
prove absence. Left alone a model searches for everything, gets five ranked
|
|
607
|
+
guesses, and writes a call against the best of them.
|
|
608
|
+
3. **Keep it short.** A skill is prompt. One screen of routing rules and
|
|
609
|
+
conventions beats a transcription of this document — link to `zen rag
|
|
610
|
+
schema --help` for the flags.
|
|
611
|
+
4. **Re-index, then re-read the skill.** Both go stale against the same
|
|
612
|
+
change, and a skill quoting operations that no longer exist is worse than
|
|
613
|
+
none.
|
|
614
|
+
5. **One skill per API**, named after it. Two APIs in one skill and the model
|
|
615
|
+
mixes their conventions.
|
|
616
|
+
|
|
617
|
+
```md
|
|
618
|
+
---
|
|
619
|
+
name: billing-api
|
|
620
|
+
description: How to find the right call in the Acme Billing API (v2) — which schema
|
|
621
|
+
carries which field, and which endpoint accepts it. Use before writing any request.
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
# The Billing API
|
|
625
|
+
|
|
626
|
+
Indexed at `/assets/schema-db` (already in `$ZEN_SCHEMA_DB`). 214 operations,
|
|
627
|
+
all under `/v2`. Bearer token in `Authorization`; cursors, never page numbers.
|
|
628
|
+
|
|
629
|
+
- Vague question ("how do I cancel a subscription?") → `search_api`, intent in
|
|
630
|
+
the narrowest field: `methods`, not `all`.
|
|
631
|
+
- Does X exist, how is it spelled, how many are there → `list_api` / `grep_api`.
|
|
632
|
+
These are complete; a search is not, and cannot prove absence.
|
|
633
|
+
- Which endpoint carries this field → `trace_api`. Never guess the owner.
|
|
634
|
+
- Never `grep`/`rg`/`jq` the spec — the tools above are local and exact.
|
|
635
|
+
|
|
636
|
+
Worked: the invoice total is `Invoice.amount_due` (minor units), returned by
|
|
637
|
+
`GetInvoice` and `ListInvoices`; `trace_api of: amount_due` shows both.
|
|
638
|
+
```
|
|
639
|
+
|
|
498
640
|
## When it goes wrong
|
|
499
641
|
|
|
500
642
|
| Symptom | Cause |
|
|
@@ -28,7 +28,7 @@ zen sandbox [status|up|pull|clean|disk] [options]
|
|
|
28
28
|
zen version
|
|
29
29
|
|
|
30
30
|
zen faker <serve|build|cache> [spec...]
|
|
31
|
-
zen rag schema <index|search|list|grep|show|stats> [spec...]
|
|
31
|
+
zen rag schema <index|search|list|grep|trace|show|stats> [spec...]
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Read the reference before answering
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# API search — `zen rag`
|
|
2
2
|
|
|
3
3
|
```
|
|
4
|
-
zen rag schema <index|search|list|grep|show|stats> [spec...]
|
|
4
|
+
zen rag schema <index|search|list|grep|trace|show|stats> [spec...]
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
Provided by `@zenera/rag` — `npm i -g @zenera/rag` if `zen rag` says it is not
|
|
@@ -27,6 +27,8 @@ zen rag schema index <spec...> [--embedding <ref>] [-o <dir>] [--batch <n>]
|
|
|
27
27
|
| `--embedding <ref>` | — | Which embedder makes the vectors. Omit it to see the choices |
|
|
28
28
|
| `-o`, `--out <dir>` | `./schema-db` | Where the index goes; `$ZEN_SCHEMA_DB` if set |
|
|
29
29
|
| `--batch <n>` | `96` | Texts per embedding request, and how often progress prints |
|
|
30
|
+
| `--no-sources` | — | Keep no copy of the documents in the index |
|
|
31
|
+
| `--quiet` | — | No narration |
|
|
30
32
|
|
|
31
33
|
```
|
|
32
34
|
zen rag schema index openapi.yaml --embedding openai:text-embedding-3-small
|
|
@@ -162,7 +164,7 @@ read `graph.json` directly, with no embedder, no credential and no network.
|
|
|
162
164
|
| ------------------- | ------ | -------------------------------------------- |
|
|
163
165
|
| `--name <p>` | both | Match the name. Repeatable |
|
|
164
166
|
| `--path <p>` | both | Match the route it sits on. Repeatable |
|
|
165
|
-
| `--regex` | both | Read
|
|
167
|
+
| `--regex` | both | Read the patterns as regular expressions |
|
|
166
168
|
| `--case-sensitive` | both | Stop ignoring case |
|
|
167
169
|
| `--source <name>` | both | Only nodes from one document |
|
|
168
170
|
| `--show-source` | both | Print which document each row came from |
|
|
@@ -171,11 +173,18 @@ read `graph.json` directly, with no embedder, no credential and no network.
|
|
|
171
173
|
| `--kind <k>` | `grep` | `method`, `type` or `property`. Repeatable |
|
|
172
174
|
| `--ids-only` | `grep` | Just the ids, one per line, for piping |
|
|
173
175
|
| `--limit <n>` | both | Rows to print. `found` still counts them all |
|
|
176
|
+
| `--quiet` | both | No narration |
|
|
177
|
+
|
|
178
|
+
Under `--json`: `{found, truncated, rows}` from `list`, `{found, truncated,
|
|
179
|
+
matches}` from `grep`. `grep` takes **one** pattern — quote it if it has
|
|
180
|
+
spaces — and `list` **one** subject.
|
|
174
181
|
|
|
175
182
|
A pattern with `*` or `?` is a glob matched against the whole string; a plain
|
|
176
183
|
word is a substring. So `--name password` finds `ResetPasswordPayload`, and
|
|
177
184
|
`--name "Password*"` finds nothing, because nothing starts with it. `--regex`
|
|
178
|
-
makes it a regular expression instead — the only way to say "one of these"
|
|
185
|
+
makes it a regular expression instead — the only way to say "one of these". On
|
|
186
|
+
`list` it applies to every pattern; on `grep` it applies to the pattern, while
|
|
187
|
+
`--name` and `--path` stay globs-or-substrings:
|
|
179
188
|
|
|
180
189
|
```
|
|
181
190
|
zen rag schema list methods --regex --path "^/(users|teams)/"
|
|
@@ -198,12 +207,71 @@ No match exits 0 with nothing on stdout — that is the answer, and unlike an
|
|
|
198
207
|
empty search it is a reliable one. Under `--limit`, `found` is still the true
|
|
199
208
|
total, so a shortened answer never misreports how much there is.
|
|
200
209
|
|
|
210
|
+
## `trace`
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
zen rag schema trace <pattern|id...> [-d <dir>] [--kind <k>] [--direction <d>]
|
|
214
|
+
[--max-hops <n>] [--limit <n>] [--routes <n>]
|
|
215
|
+
[--ids-only] [--regex] [--case-sensitive] [--source <name>]
|
|
216
|
+
[--show-source]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The question `list` and `grep` leave you holding: you have found the field, so
|
|
220
|
+
**which call can reach it?** `trace` walks up the `$ref`s from every node of
|
|
221
|
+
that name to the operations that accept or return it, and prints the chain in
|
|
222
|
+
between. Also a plain graph read — no embedder, no credential.
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
zen rag schema trace city
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
Property:Address.city
|
|
230
|
+
GET /users/{userId} getUser output PublicUserProfile.address → Address.city
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
By hand that is three lookups and a guess: find the field, find what holds
|
|
234
|
+
`Address`, find what accepts _that_, and hope you followed every branch.
|
|
235
|
+
|
|
236
|
+
`search` does some of this already — it stitches its seeds into one connected
|
|
237
|
+
piece and prints what each operation accepts and returns. But it links only
|
|
238
|
+
what **ranked**, only within `--max-hops`, and it never names the chain; and
|
|
239
|
+
`getUser` and `city` share no word, so the field has to rank on its own and the
|
|
240
|
+
call has to land near it. `trace` follows the edges instead, which are already
|
|
241
|
+
there and are certain.
|
|
242
|
+
|
|
243
|
+
| Flag | Default | Meaning |
|
|
244
|
+
| ----------------- | ------------------ | ---------------------------------------------------- |
|
|
245
|
+
| `--kind <k>` | types + properties | `method`, `type` or `property`. Repeatable |
|
|
246
|
+
| `--direction <d>` | `any` | Keep only the calls that accept it, or return it |
|
|
247
|
+
| `--max-hops <n>` | `8` | How far up to walk |
|
|
248
|
+
| `--limit <n>` | — | Trace at most n matching nodes |
|
|
249
|
+
| `--routes <n>` | — | Operations printed per node; `found` counts them all |
|
|
250
|
+
| `--ids-only` | — | Bare operation ids, one per line, for piping |
|
|
251
|
+
| `--regex` | — | Read the pattern as a regex; `--case-sensitive` too |
|
|
252
|
+
| `--source <name>` | — | Only nodes from one document |
|
|
253
|
+
| `--show-source` | — | Print which document each operation came from |
|
|
254
|
+
|
|
255
|
+
A bare word is matched the way `list --name` matches it — substring, or a glob
|
|
256
|
+
when it has `*` or `?`. A node id (`Type:User`) is taken as that node and not
|
|
257
|
+
as a pattern. Operations are left out of a name match on purpose: they are
|
|
258
|
+
where a trace ends.
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
zen rag schema trace password --direction input
|
|
262
|
+
zen rag schema trace "*Settings" --kind type
|
|
263
|
+
zen rag schema trace mfa_secret --ids-only | xargs zen rag schema show --format openapi
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
A node nothing carries prints `no operation reaches it` — a real answer, and
|
|
267
|
+
one worth having: it means the schema is unreachable in this document.
|
|
268
|
+
|
|
201
269
|
## `show`
|
|
202
270
|
|
|
203
271
|
```
|
|
204
|
-
zen rag schema show [id...] [-d <dir>] [--format <f>]
|
|
272
|
+
zen rag schema show [id...] [-d <dir>] [--format <f>] [--exact]
|
|
205
273
|
[--method <name>] [--type <name>] [--source <name>]
|
|
206
|
-
[--show-source] [--
|
|
274
|
+
[--show-source] [--max-nodes <n>] [--no-docs] [--quiet]
|
|
207
275
|
```
|
|
208
276
|
|
|
209
277
|
Prints named nodes with no search in between. Needs no embedder and no
|
|
@@ -214,7 +282,16 @@ usually what you have. A bare name means exactly that name; add `*` to select
|
|
|
214
282
|
more than one. `--source <name>` takes a whole document, and `--show-source`
|
|
215
283
|
tags each node with the document it came from. `--exact` prints only what was
|
|
216
284
|
named instead of the neighbourhood around it — with `--format openapi` that is
|
|
217
|
-
a valid, self-contained slice of the specification.
|
|
285
|
+
a valid, self-contained slice of the specification. Without it the neighbours
|
|
286
|
+
are stitched in, up to `--max-nodes`.
|
|
287
|
+
|
|
288
|
+
`--source <name> --format openapi` with nothing else named prints the
|
|
289
|
+
**verbatim** document as it was indexed, unless the index was built
|
|
290
|
+
`--no-sources`, in which case it is rebuilt from the graph and says so.
|
|
291
|
+
|
|
292
|
+
A name that matches nothing is an **error**, not an empty answer: naming
|
|
293
|
+
something is a claim that it is there, and `list --name` is the command for
|
|
294
|
+
asking whether it is.
|
|
218
295
|
|
|
219
296
|
```
|
|
220
297
|
zen rag schema show --method GetCurrentUserInfo --format openapi --exact
|
|
@@ -241,10 +318,12 @@ documents it came from. Also needs no embedder.
|
|
|
241
318
|
| `find_types_with_property` | Every schema with a field of this name — exact lookup, no searching |
|
|
242
319
|
| `list_api` | Methods, types or fields by name — complete, and counted in full |
|
|
243
320
|
| `grep_api` | Every literal occurrence of a string — the way to prove absence |
|
|
321
|
+
| `trace_api` | Up from a field or schema to the operations that carry it |
|
|
244
322
|
|
|
245
323
|
Only `search_api` ranks. Reach for the others whenever the question is whether
|
|
246
324
|
something exists, because a search that returns nothing and a thing that is not
|
|
247
|
-
there look exactly the same.
|
|
325
|
+
there look exactly the same. `trace_api` is the one for after a field has been
|
|
326
|
+
found and the endpoint to call is what is actually wanted.
|
|
248
327
|
|
|
249
328
|
They share the group `schema`, so an agent takes them with `schema:*` in its
|
|
250
329
|
`tools:`.
|