@zenera/cli 1.1.21 → 1.1.23

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 (62) hide show
  1. package/README.md +40 -3
  2. package/dist/archive.d.ts +139 -0
  3. package/dist/archive.js +376 -0
  4. package/dist/banner.d.ts +33 -24
  5. package/dist/banner.js +25 -6
  6. package/dist/commands/check.js +1 -0
  7. package/dist/commands/export.d.ts +3 -0
  8. package/dist/commands/export.js +255 -0
  9. package/dist/commands/import.d.ts +3 -0
  10. package/dist/commands/import.js +173 -0
  11. package/dist/commands/index.d.ts +5 -1
  12. package/dist/commands/index.js +13 -1
  13. package/dist/commands/init.js +1 -0
  14. package/dist/commands/inspect.js +1 -0
  15. package/dist/commands/key.js +1 -0
  16. package/dist/commands/list.js +1 -0
  17. package/dist/commands/memory.js +118 -10
  18. package/dist/commands/open.js +1 -0
  19. package/dist/commands/run.js +3 -0
  20. package/dist/commands/sandbox.js +1 -0
  21. package/dist/commands/version.js +1 -0
  22. package/dist/lib.d.ts +2 -1
  23. package/dist/lib.js +2 -1
  24. package/dist/meta.d.ts +1 -1
  25. package/dist/meta.js +5 -28
  26. package/dist/resolve.js +19 -12
  27. package/dist/sandbox.js +1 -0
  28. package/dist/scaffold.d.ts +8 -0
  29. package/dist/scaffold.js +7 -2
  30. package/dist/term.d.ts +2 -0
  31. package/dist/term.js +2 -0
  32. package/dist/tui/markdown.d.ts +16 -0
  33. package/dist/tui/markdown.js +259 -0
  34. package/dist/tui/theme.js +1 -1
  35. package/dist/tui/wrap.d.ts +1 -1
  36. package/dist/validate.js +150 -4
  37. package/package.json +12 -6
  38. package/templates/editor/.github/copilot-instructions.md +62 -33
  39. package/templates/editor/.github/prompts/spec-sync-project.prompt.md +56 -3
  40. package/templates/editor/.github/skills/zen-cli/SKILL.md +21 -16
  41. package/templates/editor/.github/skills/zen-cli/references/check.md +6 -0
  42. package/templates/editor/.github/skills/zen-cli/references/export.md +107 -0
  43. package/templates/editor/.github/skills/zen-cli/references/memory.md +51 -13
  44. package/templates/editor/.github/skills/zen-cli/references/projects.md +1 -0
  45. package/templates/editor/.github/skills/zen-cli/references/rag.md +43 -3
  46. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +30 -2
  47. package/templates/editor/.github/skills/zen-instructions/SKILL.md +232 -0
  48. package/templates/editor/.github/skills/zen-memory/SKILL.md +2 -1
  49. package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +684 -0
  50. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +56 -23
  51. package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +55 -28
  52. package/templates/editor/.github/skills/zen-review/SKILL.md +17 -1
  53. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +257 -0
  54. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +16 -3
  55. package/templates/editor/.vscode/agents.schema.json +8 -3
  56. package/templates/project/SPECIFICATION.md.tmpl +4 -1
  57. package/templates/project/agents/files-instructions.md +86 -0
  58. package/templates/project/agents/fork-instructions.md +17 -0
  59. package/templates/project/agents/memory-instructions.md +37 -0
  60. package/templates/project/agents.yaml.tmpl +3 -3
  61. package/templates/project/gitignore +12 -1
  62. package/templates/project/scripts/_setup.sh +68 -11
@@ -45,12 +45,22 @@ not reconstruct `zen` arguments from memory or call `--help` to discover them.
45
45
  **Every runtime capability a project turns on carries obligations that are not
46
46
  visible in `agents.yaml` and that `zen check` does not report.** Before
47
47
  reviewing or changing a project, list the capabilities it has turned on and load
48
- the editor skill for each one - `zen-memory` for a `memory:` block, `zen-rag-
49
- schema` or `zen-rag-docs` for an index, `zen-cli` always. A capability that is
50
- already configured and already passing `zen check` is exactly the case that
51
- looks finished and is not: the obligation lives in the skill, so a review that
52
- never opens the skill cannot find what is missing. This applies whatever
53
- prompted the change - a task scoped to `SPECIFICATION.md` does not narrow it.
48
+ the editor skill for each one - `zen-memory` for a `memory:` block,
49
+ `zen-sandbox` for sandbox configuration or shell tools, `zen-rag-schema` or
50
+ `zen-rag-docs` for an index, `zen-cli` always. A capability that is already
51
+ configured and already passing `zen check` is exactly the case that looks
52
+ finished and is not: the obligation lives in the skill, so a review that never
53
+ opens the skill cannot find what is missing. This applies whatever prompted the
54
+ change - a task scoped to `SPECIFICATION.md` does not narrow it.
55
+
56
+ **Load `zen-instructions` before writing, editing, reviewing or deleting any
57
+ `agents/*instructions.md`.** They are the house rules, prepended to every
58
+ agent's prompt, and three of them are `zen`'s own copies rather than the
59
+ project's: an edit made inside one is lost at the next `zen check --fix`, `zen
60
+ init` or `zen open`, with no diff to show for it. The skill is what says which
61
+ three those are, where a project's own rules on the same subjects go instead,
62
+ how `requires:` conditions a document, and what filename order decides. None of
63
+ that is visible in the file being edited.
54
64
 
55
65
  First determine whether the target project uses RAG: a `zen rag` command in an
56
66
  agent prompt or skill, RAG tools supplied by its host, or a documentation or
@@ -222,6 +232,7 @@ my-project/
222
232
  ├── agents.yaml who exists, what they may reach for
223
233
  ├── agents/
224
234
  │ ├── instructions.md house rules, prepended to every agent
235
+ │ ├── files-instructions.md and more - how files are handled
225
236
  │ ├── fork-instructions.md more of them - any `<topic>-instructions.md`
226
237
  │ ├── memory-instructions.md and more - how the graph is used
227
238
  │ ├── tools-instructions.md and more - how tools are called
@@ -631,9 +642,19 @@ follow. So:
631
642
  - Anything only one agent needs goes in that agent's prompt instead. If you find
632
643
  yourself writing "if you are the router…", you are in the wrong file.
633
644
 
634
- Target 20–60 lines each. If one exceeds ~100, split the stable half out into a
645
+ Target 20-60 lines each. If one exceeds ~100, split the stable half out into a
635
646
  preloaded skill.
636
647
 
648
+ Four of these documents are **`zen`'s, not the project's** -
649
+ `tools-instructions.md`, `files-instructions.md`, `memory-instructions.md` and
650
+ `fork-instructions.md`. They are replaced by `zen check --fix`, and a copy that
651
+ differs is reported as `rules.stale`. This project's own rules on any of those
652
+ subjects go in a topic file beside them - `agents/files-policy-instructions.md`,
653
+ `agents/memory-policy-instructions.md` and the like, carrying the same
654
+ `requires:`. The `zen-instructions` skill is the full treatment: the two owners,
655
+ the naming and ordering that keeps a policy after the rules it qualifies, and
656
+ every finding these files can produce. Load it before touching one.
657
+
637
658
  ### 3.3 `agents/prompts/<name>.md`
638
659
 
639
660
  Plain Markdown, no frontmatter. This is the agent's _job description_, appended
@@ -644,7 +665,7 @@ Structure that works:
644
665
  ```markdown
645
666
  <one sentence: who this agent is and what it owns>
646
667
 
647
- <what it does, as a short numbered procedure or 3–5 rules>
668
+ <what it does, as a short numbered procedure or 3-5 rules>
648
669
 
649
670
  <what it must NOT do - especially the neighbouring agent's job>
650
671
 
@@ -677,7 +698,7 @@ already did, §3.2), never repeats one of them, and never describes the runtime.
677
698
  Every line should be an instruction the model can act on or a boundary it can
678
699
  check itself against.
679
700
 
680
- Target 10–40 lines. A 200-line prompt is a skill catalog that has not been split
701
+ Target 10-40 lines. A 200-line prompt is a skill catalog that has not been split
681
702
  yet.
682
703
 
683
704
  ### 3.4 Skills
@@ -798,21 +819,21 @@ Three rules follow:
798
819
 
799
820
  - **Grant the narrowest set the job needs.** An agent that only reviews should
800
821
  not be holding the tools that overwrite files.
801
- - **Grant by group, then subtract.** Write `workspace:*` and take away what this
802
- agent must not have - not a hand-picked list of names. The tools in a group are
803
- designed to be used together, and a list assembled one name at a time leaves out
804
- the companion the model reaches for next: `write_file` without `apply_patch`
805
- turns every one-line edit into a whole-file rewrite, and `read_file` without
806
- `list_dir` and `find_files` leaves it guessing at paths. A group also tracks the
807
- runtime - a tool added to it later reaches the agent, where an enumerated list
808
- silently stays behind.
822
+ - **Grant by group, then subtract.** Write `files:*` (or `workspace:*`) and take
823
+ away what this agent must not have - not a hand-picked list of names. The tools
824
+ in a group are designed to be used together, and a list assembled one name at
825
+ a time leaves out the companion the model reaches for next: `write_file`
826
+ without `apply_patch` turns every one-line edit into a whole-file rewrite, and
827
+ `read_file` without `list_dir` and `find_files` leaves it guessing at paths. A
828
+ group also tracks the runtime - a tool added to it later reaches the agent,
829
+ where an enumerated list silently stays behind.
809
830
  - **Say in the prompt when to reach for what.** A granted tool the prompt never
810
831
  mentions is used at the model's discretion, which is not the same as never.
811
832
 
812
833
  Skills can own tools too - `tools:` in a skill's frontmatter (§3.4) names tools
813
834
  that refuse to run until that skill is active.
814
835
 
815
- ### 3.6 The workspace tools (`workspace:*`)
836
+ ### 3.6 The file tools (`files:*`, alias `workspace:*`)
816
837
 
817
838
  `zen run` builds this set for you, rooted at the session's workspace directory.
818
839
  Nothing else reaches the file system, so an agent whose `tools:` does not name
@@ -841,27 +862,27 @@ container involved there is no second name and everything stays relative.
841
862
 
842
863
  **Selecting them.** A `tools:` entry is a selector, not only a name:
843
864
 
844
- | Selector | Selects |
845
- | --------------------- | ------------------------------------------------- |
846
- | `read_file` | that one tool |
847
- | `workspace:read_file` | the same tool, written out in full |
848
- | `workspace:*` | every tool in the group |
849
- | `'*'` | every tool the runtime provides |
850
- | `-<any>` | removes what it matches from the selection so far |
865
+ | Selector | Selects |
866
+ | ------------------------------- | ------------------------------------------------- |
867
+ | `read_file` | that one tool |
868
+ | `files:read_file` | the same tool, written out in full |
869
+ | `files:*` (alias `workspace:*`) | every tool in the group |
870
+ | `'*'` | every tool the runtime provides |
871
+ | `-<any>` | removes what it matches from the selection so far |
851
872
 
852
873
  ```yaml
853
874
  agents:
854
875
  - name: editor
855
- tools: [workspace:*]
876
+ tools: [files:*]
856
877
 
857
878
  - name: reviewer
858
879
  # Everything except the four that can change something.
859
- tools: [workspace:*, -write_file, -apply_patch, -move_file, -delete_file]
880
+ tools: [files:*, -write_file, -apply_patch, -move_file, -delete_file]
860
881
  ```
861
882
 
862
883
  Selectors apply in the order written, so a `-` line reads as an exception to the
863
884
  line above it. Quote a lone `'*'`: unquoted, YAML reads it as an alias and
864
- refuses the file. `workspace:*` needs no quoting. There is no name globbing -
885
+ refuses the file. `files:*` (and `workspace:*`) needs no quoting. There is no name globbing -
865
886
  `read_*` is an unknown tool, because a selector should track a declared set, not
866
887
  a naming habit.
867
888
 
@@ -983,12 +1004,12 @@ sandbox:
983
1004
 
984
1005
  agents:
985
1006
  - name: builder
986
- tools: [workspace:*, sandbox:*]
1007
+ tools: [files:*, sandbox:*]
987
1008
  sandbox:
988
1009
  image: docker.io/library/node:22-bookworm-slim
989
1010
  memory: 8192
990
1011
  - name: analyst
991
- tools: [workspace:*, sandbox:*] # shares the project's container
1012
+ tools: [files:*, sandbox:*] # shares the project's container
992
1013
  ```
993
1014
 
994
1015
  **Write `persist: true` unless you have a reason not to.** By default the
@@ -1053,7 +1074,7 @@ over several pages, and wastes it when a specific document is wanted.
1053
1074
  ```yaml
1054
1075
  agents:
1055
1076
  - name: researcher
1056
- tools: [exa:*, workspace:*]
1077
+ tools: [exa:*, files:*]
1057
1078
 
1058
1079
  - name: fact-checker
1059
1080
  # Find and read, but never let a model on the far side do the reasoning.
@@ -1839,6 +1860,9 @@ input`, or tools quietly unused). This is not a config error and `zen check`
1839
1860
  publishes no embeddings API. Point it at another provider - §3.1.1.
1840
1861
  - **Changing `dimensions:` on an embedding invalidates every stored vector.**
1841
1862
  Widths are not comparable, so anything already indexed has to be re-embedded.
1863
+ For a `zen rag` index that is `zen rag <subject> restore --dimensions <n>`,
1864
+ which rebuilds from the copies the index already holds rather than from the
1865
+ documents; the same command with `--embedding <ref>` moves it to another model.
1842
1866
 
1843
1867
  ### 7.8 How to choose, in practice
1844
1868
 
@@ -1940,9 +1964,13 @@ Before finishing any change here:
1940
1964
  - [ ] Nothing duplicated between a house-rules file and an agent prompt
1941
1965
  - [ ] Each `agents/<topic>-instructions.md` is about one capability, and is true
1942
1966
  of every agent it reaches - `requires:` if it is about one of them
1967
+ - [ ] If any agent has file tools, `agents/files-instructions.md` is present - §3.6
1943
1968
  - [ ] If any agent has `fork:`, `agents/fork-instructions.md` is present - §6.4
1944
- - [ ] No `rules.stale`: the three files that are `zen`'s rather than yours say
1969
+ - [ ] No `rules.stale`: the four files that are `zen`'s rather than yours say
1945
1970
  what this version of the runtime does, and `zen check --fix` replaces them
1971
+ - [ ] Any change to an `agents/*instructions.md` was made with the
1972
+ `zen-instructions` skill loaded, and nothing of this project's was written
1973
+ into one of `zen`'s - §3.2
1946
1974
  - [ ] Every tool and agent referenced by its exact name
1947
1975
  - [ ] Failure paths stated for every instruction that can fail
1948
1976
  - [ ] No facts, rates or figures embedded in a prompt
@@ -1984,7 +2012,7 @@ candidates; these are the judgements to make about each one)**
1984
2012
  - [ ] No agent holds `write_file` without `apply_patch`, or `read_file` without
1985
2013
  `list_dir` and `find_files`
1986
2014
  - [ ] An agent that only reads is not holding `write_file`, `apply_patch`,
1987
- `move_file` or `delete_file` - subtract them from `workspace:*`
2015
+ `move_file` or `delete_file` - subtract them from `files:*` (or `workspace:*`)
1988
2016
  - [ ] `sandbox:*` is granted only where a shell is actually needed
1989
2017
  - [ ] `sandbox.persist: true`, unless a throwaway rootfs is wanted on purpose
1990
2018
  - [ ] The `sandbox:` image carries what the work needs, rather than the prompt
@@ -2056,6 +2084,7 @@ candidates; these are the judgements to make about each one)**
2056
2084
  | Works through N independent items serially | `fork:` on that agent, and a prompt line - §6.4 |
2057
2085
  | Forks when the steps actually depend | Prompt line: branches cannot see each other |
2058
2086
  | Forks a sequence, or loses a branch's working | `agents/fork-instructions.md` is missing or empty - §6.4 |
2087
+ | Edits files blindly or breaks patches | `agents/files-instructions.md` is missing or empty - §3.6 |
2059
2088
  | Slow and expensive on trivial cases | Demote that agent's model tier / reasoning effort |
2060
2089
  | Fails only on genuinely hard cases | Promote that agent's tier, or split the hard path out |
2061
2090
  | Shows no reasoning while it works | Turn summaries on for that model - §7.6 |
@@ -31,7 +31,7 @@ because a prompt or skill someone changed outside a pass still has to be
31
31
  accounted for against the specification.
32
32
 
33
33
  The scope decides what you **read and edit**. It never decides what you
34
- **verify**: §6, §7 and the four checks in the skill run at full size on every
34
+ **verify**: §6, §7 and the five checks in the skill run at full size on every
35
35
  pass, however small the diff.
36
36
 
37
37
  ## 1. Read before changing anything
@@ -115,7 +115,10 @@ the model to compute or remember. Specifically:
115
115
  in it to the version of `zen` in hand. Read that version with `zen --version`
116
116
  and write it into the `npm install -g` line, so the CLI an agent runs inside
117
117
  the container is the same one that built it. An unpinned name drifts away from
118
- the host on the next image build; a stale pin is a mismatch nothing reports.
118
+ the host on the next image build; a stale pin leaves the container holding an
119
+ older `zen` than the session outside it. `zen check` reports both, as
120
+ `sandbox.pin.absent` and `sandbox.pin.stale`, but will not repair either - the
121
+ Dockerfile is the project's file, so this edit is yours to make.
119
122
 
120
123
  Never delete an **extra** without saying so. Report it, say why the
121
124
  specification does not cover it, and leave it unless I tell you to remove it.
@@ -148,9 +151,51 @@ not a paragraph of instructions for a human.
148
151
  to a temporary path and moving it into place so an interrupted run never leaves
149
152
  a half-built artefact behind. `scripts/_setup.sh --force` redoes everything,
150
153
  and reaches each step as `$FORCE=1`.
154
+ - **Every step answers a dry run.** `scripts/_setup.sh --check` reaches each step
155
+ as `$CHECK=1` and means: make the same "already done" test, then do none of the
156
+ work. Exit **3** when the answer is yes, otherwise say what is missing and exit
157
+ **4**, which the runner reports as `needs setup`. This is the one command that
158
+ says whether the project can run without spending the download and the
159
+ embedding to find out, so the line a step prints under `$CHECK` is the whole
160
+ value of it: name the artefact and the command that produces it. Pass a tool's
161
+ own words through rather than writing a second opinion: `zen rag docs ready`
162
+ without `--quiet` already prints what is wrong and the `restore` or `index`
163
+ that fixes it. A check must not assume an earlier step has run: a missing
164
+ prerequisite is a 4 of its own, not a failure, or the first unbuilt thing
165
+ hides everything after it.
166
+ - **A step's "already done" test must name something that survives a clone.**
167
+ Git-ignored output does not. A rag index commits its `manifest.json` and
168
+ ignores `lance/`, so `[ -f "$OUT/manifest.json" ]` is true on a fresh checkout
169
+ that cannot search at all - every step reports `skipped` and the failure
170
+ surfaces much later, as a search error nobody connects to setup. Ask the tool
171
+ rather than guessing at files: `zen rag docs ready --dir "$OUT" --quiet` exits
172
+ 0 when the index can answer and 3 when it cannot, and
173
+ `zen rag docs restore --dir "$OUT"` re-embeds it from the copies it already
174
+ holds instead of indexing the documents again. A step that builds an index
175
+ looks like this:
176
+
177
+ ```sh
178
+ if [ "${FORCE:-0}" = 0 ] && zen rag docs ready --dir "$OUT" --quiet; then
179
+ echo "$OUT is already built"
180
+ exit 3
181
+ fi
182
+ if [ "${CHECK:-0}" = 1 ]; then
183
+ zen rag docs ready --dir "$OUT" || true # its reason and its fix
184
+ exit 4
185
+ fi
186
+ if [ "${FORCE:-0}" = 0 ] && [ -f "$OUT/manifest.json" ]; then
187
+ zen rag docs restore --dir "$OUT" # committed index, ignored vectors
188
+ exit 0
189
+ fi
190
+ rm -rf .tmp/docs-db
191
+ zen rag docs index assets/docs --embedding <ref> --out .tmp/docs-db
192
+ rm -rf "$OUT" && mv .tmp/docs-db "$OUT"
193
+ ```
194
+
151
195
  - **It must be watchable.** `scripts/_setup.sh` tees each step's output to
152
196
  `.tmp/logs/setup-<step>.log`, prints a heartbeat line while a long step runs,
153
- and finishes with one line per step: `ok`, `skipped`, or `failed`.
197
+ and finishes with one line per step: `ok`, `skipped`, `needs setup`, or
198
+ `failed`.
154
199
  - **Everything transient goes under `.tmp/`** - logs, scratch files, downloads,
155
200
  test output, and the temporary path a step writes to before moving its
156
201
  artefact into place. Nothing else in the tree is a scratch directory, and
@@ -354,6 +399,14 @@ background and follow the logs instead of waiting blind:
354
399
  for the same reason, stop and raise it in `SPECIFICATION-FEEDBACK.md`.
355
400
  - Prove re-entrancy: once it has succeeded, run `scripts/_setup.sh` once more and
356
401
  check that every step reports `skipped` rather than rebuilding.
402
+ - Prove the dry run: `scripts/_setup.sh --check` must then report every step
403
+ `skipped` and exit 0. `needs setup` for something setup has just built means
404
+ the step's two tests disagree, and one of them is wrong.
405
+ - **`skipped` on a machine that has never built it is a bug, not a pass.** A
406
+ step that tests for a committed file skips on a fresh clone and leaves the
407
+ project unusable. For every index the project builds, check it can actually
408
+ answer - `zen rag docs ready --dir <dir>` - and fix the step's test if the
409
+ answer disagrees with what setup reported.
357
410
 
358
411
  ## 7. Verify and report
359
412
 
@@ -24,13 +24,15 @@ zen key <ls|add|use|check|rm|show|env> [ref] [options]
24
24
  zen models <providers|ls|search|show|test|pick> [ref] [options]
25
25
  zen check [name|dir] [--project <name|dir>] [--no-sandbox] [--no-models] [--strict] [--quiet]
26
26
  zen inspect [run] [--session <id>] [--open] [--rebuild] [--serve [port]]
27
- zen memory [stats|ls|show|export|forget] [args] [options]
27
+ zen memory [stats|ls|show|export|merge|forget] [args] [options]
28
+ zen export [project] [--out <file>] [--no-vectors] [--no-memory] [--force]
29
+ zen import <file.zip> [dir] [--name <name>] [--force] [--no-register]
28
30
  zen sandbox [status|up|pull|clean|disk] [options]
29
31
  zen version
30
32
 
31
33
  zen faker <serve|build|cache> [spec...]
32
- zen rag schema <index|search|list|grep|trace|show|stats> [spec...]
33
- zen rag docs <index|search|list|grep|show|stats> [path...]
34
+ zen rag schema <index|restore|ready|search|list|grep|trace|show|stats> [spec...]
35
+ zen rag docs <index|restore|ready|search|list|grep|show|stats> [path...]
34
36
  ```
35
37
 
36
38
  ## Read the reference before answering
@@ -39,19 +41,20 @@ The full reference lives next to this file, one document per part of the command
39
41
  line. **Read the one that covers the question before answering it** - do not
40
42
  guess a flag, and do not read them all.
41
43
 
42
- | The question is about | Read |
43
- | ------------------------------------------------------------------- | --------------------------------------- |
44
- | Global flags, `--json`, exit codes, environment, where files live | [frame.md](./references/frame.md) |
45
- | Creating, finding or opening a project | [projects.md](./references/projects.md) |
46
- | Running: the TUI, one-shot answers, sessions, workspaces, overrides | [run.md](./references/run.md) |
47
- | Validating: `zen check`, what it can and cannot see | [check.md](./references/check.md) |
48
- | API keys, providers, the keyring, "no credential" errors | [keys.md](./references/keys.md) |
49
- | Which models exist, whether one works, recovering from a refusal | [models.md](./references/models.md) |
50
- | The container shell commands run in, images, `persist` | [sandbox.md](./references/sandbox.md) |
51
- | Run reports, trajectories, what a session directory holds | [inspect.md](./references/inspect.md) |
52
- | What the agents remember, why something was recalled, forgetting | [memory.md](./references/memory.md) |
53
- | `zen faker` - a mock API from an OpenAPI/Swagger document | [faker.md](./references/faker.md) |
54
- | `zen rag` - searching an API description, or a pile of markdown | [rag.md](./references/rag.md) |
44
+ | The question is about | Read |
45
+ | ------------------------------------------------------------------- | --------------------------------------------------------------- |
46
+ | Global flags, `--json`, exit codes, environment, where files live | [frame.md](./references/frame.md) |
47
+ | Creating, finding or opening a project | [projects.md](./references/projects.md) |
48
+ | Running: the TUI, one-shot answers, sessions, workspaces, overrides | [run.md](./references/run.md) |
49
+ | Validating: `zen check`, what it can and cannot see | [check.md](./references/check.md) |
50
+ | API keys, providers, the keyring, "no credential" errors | [keys.md](./references/keys.md) |
51
+ | Which models exist, whether one works, recovering from a refusal | [models.md](./references/models.md) |
52
+ | The container shell commands run in, images, `persist` | [sandbox.md](./references/sandbox.md) (and `zen-sandbox` skill) |
53
+ | Run reports, trajectories, what a session directory holds | [inspect.md](./references/inspect.md) |
54
+ | What the agents remember, why something was recalled, forgetting | [memory.md](./references/memory.md) |
55
+ | Sharing a project: archives, what travels, importing one safely | [export.md](./references/export.md) |
56
+ | `zen faker` - a mock API from an OpenAPI/Swagger document | [faker.md](./references/faker.md) |
57
+ | `zen rag` - searching an API description, or a pile of markdown | [rag.md](./references/rag.md) |
55
58
 
56
59
  ## The short version
57
60
 
@@ -62,6 +65,8 @@ zen run the TUI
62
65
  zen run "what changed?" one answer, this directory as the workspace
63
66
  zen inspect --open what the model was actually given
64
67
  zen memory export --open the whole memory graph, as a page
68
+ zen export the project as a zip you can send
69
+ zen import a.zip unpack one somebody sent, run nothing in it
65
70
  zen key ls --check which credentials still work
66
71
  zen models test <ref> whether one model actually answers
67
72
  zen models pick --embedding the first embedder that does, on stdout
@@ -51,6 +51,11 @@ checked too. A word that is neither is a usage error (exit 2), not a report.
51
51
  exist, and - unless `--no-sandbox` - the image **builds** and one command runs
52
52
  in it, against a temporary directory rather than your workspace. No container
53
53
  engine at all is a warning, not an error.
54
+ - The `@zenera/*` the Dockerfile installs are pinned to **this** `zen`. The pin
55
+ is written once, by `zen init`, and nothing rewrites it afterwards, so it falls
56
+ behind the host on the next upgrade and the container ends up holding a
57
+ different `zen` than the session outside it. Read rather than run, so this one
58
+ is reported with `--no-sandbox` too.
54
59
 
55
60
  ### Findings
56
61
 
@@ -66,6 +71,7 @@ fork.unknown skills.missing skill.unloadable / .no-skill-md
66
71
  skills.no-catalog skills.unreachable skill.unused
67
72
  assets.missing assets.overbroad sandbox.dockerfile.missing
68
73
  sandbox.build sandbox.smoke sandbox.start / .unchecked
74
+ sandbox.pin.stale sandbox.pin.absent
69
75
  provider.invalid model.none model.unresolvable
70
76
  model.refused model.unreachable model.unusable
71
77
  embedding.refused embedding.unreachable embedding.unusable
@@ -0,0 +1,107 @@
1
+ # Sharing a project - `zen export` and `zen import`
2
+
3
+ A project is a directory, so moving one is a zip - except that half of the
4
+ directory belongs to that machine rather than to the project. These two write
5
+ and read the other half.
6
+
7
+ ## `zen export`
8
+
9
+ ```
10
+ zen export [project] [--out <file>] [--no-vectors] [--no-memory] [--force]
11
+ ```
12
+
13
+ Aliases: `pack`, `archive`, `share`.
14
+
15
+ Writes `./<project>-<stamp>.zip` in the working directory unless `--out` names
16
+ somewhere else. The stamp means two exports in one afternoon are two files, not
17
+ one silently replaced; an `--out` that already exists is a usage error until
18
+ `--force`.
19
+
20
+ | Flag | Meaning |
21
+ | -------------- | ------------------------------------------------------------ |
22
+ | `--out <file>` | Where the archive goes |
23
+ | `--no-vectors` | Leave out `assets/**/lance/` - much smaller, needs a restore |
24
+ | `--no-memory` | Leave out `memory/` entirely |
25
+ | `--force` | Overwrite the target, and export past a held memory lock |
26
+
27
+ ### What travels
28
+
29
+ Everything that is the project: `agents.yaml`, the `agents/` tree,
30
+ `SPECIFICATION.md` and its feedback, `assets/`, `memory/`, `sandbox/`,
31
+ `scripts/`, and the editor files (`.github/`, `.vscode/`).
32
+
33
+ What does not: `sessions/`, `.tmp/`, `.git/`, `node_modules/`, lock files,
34
+ `.DS_Store`, any `*.zip` at the top of the project (the default destination
35
+ puts one there, and a second export must not pack the first), and `.env`.
36
+ Symbolic links are counted and never followed. A zip that is genuine project
37
+ material lives under `assets/`, where this rule does not look.
38
+
39
+ **The values in `.env` never travel, behind no flag.** The names do, as a
40
+ `.env.example` with every value blank and every comment kept - so the other end
41
+ knows which credentials to supply without being handed any.
42
+
43
+ ### The vectors travel by default
44
+
45
+ This is the decision the command is built around. A rag index without its
46
+ `lance/` tree looks built and cannot search, and memory has no fallback at all:
47
+ a graph without `vectors.f32` recalls by term overlap until every node is
48
+ written again. So the default archive works when it is opened.
49
+
50
+ `--no-vectors` makes a much smaller one for someone who will run
51
+ `zen rag <subject> restore` on arrival. The manifest records the choice and
52
+ `zen import` prints the restore step when it was taken.
53
+
54
+ ### What it refuses
55
+
56
+ Only one thing: memory held by a live run, because a graph copied mid-write is
57
+ an archive that opens and is wrong. `--force` overrides it, and
58
+ `--no-memory` sidesteps it.
59
+
60
+ A project that fails `zen check` is exported anyway, with a warning. Sending
61
+ someone a broken project to ask for help is a legitimate reason to export one.
62
+
63
+ `--json` prints `{file, bytes, files, project, root, vectors, memory, skipped, ok}`.
64
+
65
+ ## `zen import`
66
+
67
+ ```
68
+ zen import <file.zip> [dir] [--name <name>] [--force] [--no-register]
69
+ ```
70
+
71
+ Aliases: `unpack`.
72
+
73
+ Unpacks into `./<project>` unless a directory is named, and registers it so
74
+ `zen list` and `zen open` find it by name. A target directory with anything in
75
+ it is refused until `--force`.
76
+
77
+ | Flag | Meaning |
78
+ | --------------- | --------------------------------------------------- |
79
+ | `--name <name>` | Register under this name - use it when one is taken |
80
+ | `--force` | Write into a directory that is not empty |
81
+ | `--no-register` | Unpack and leave the registry alone |
82
+
83
+ A zip without `zenera-export.json` at its root is a usage error: only an archive
84
+ `zen export` wrote can be imported.
85
+
86
+ ### It treats the archive as hostile
87
+
88
+ Every path, size and mode in a zip is a claim made by whoever sent it. So:
89
+ entry paths that are absolute, contain `..`, or fall outside the single
90
+ directory the manifest names are refused; an entry claiming to be a symbolic
91
+ link is refused; the entry count and unpacked size are bounded; and file modes
92
+ are replaced rather than honoured (`0644`, and `0755` for `*.sh` only).
93
+
94
+ **Nothing in the archive is executed** - not `scripts/_setup.sh`, not the
95
+ Dockerfile, not a line of the `.github/` tree. The commands to run next are
96
+ printed instead, so they can be read before they are typed.
97
+
98
+ ### After importing
99
+
100
+ ```sh
101
+ cp .env.example .env # and fill in the credentials; they were not in the archive
102
+ zen check <name> # what this machine is still missing
103
+ zen rag <subject> restore # only when the archive was made with --no-vectors
104
+ zen open <name>
105
+ ```
106
+
107
+ `--json` prints `{dir, name, files, bytes, registered, from, vectors, memory}`.
@@ -1,7 +1,7 @@
1
1
  # Memory - `zen memory`
2
2
 
3
3
  ```
4
- zen memory [stats|ls|show|export|forget] [args] [options]
4
+ zen memory [stats|ls|show|export|merge|forget] [args] [options]
5
5
  ```
6
6
 
7
7
  Alias: `mem`.
@@ -24,20 +24,24 @@ skill.
24
24
  | `zen memory ls` | Nodes, newest first. Changes nothing |
25
25
  | `zen memory show <id>` | One node in full, with what it links to |
26
26
  | `zen memory export [f]` | The whole graph as one HTML page |
27
+ | `zen memory merge <dir…>` | Fold other memories into this one |
27
28
  | `zen memory forget <id…>` | Remove nodes, their vectors and their files |
28
29
 
29
- | Flag | Meaning |
30
- | ----------------------- | -------------------------------------------- |
31
- | `--project <name\|dir>` | Which project. Inferred from the directory |
32
- | `--dir <dir>` | Read this memory directory instead |
33
- | `--kind <name>` | Only this kind of node |
34
- | `--audience <name>` | Only nodes committed under this label |
35
- | `--files` | Only nodes that remember a file |
36
- | `--stale` | Only nodes something has superseded |
37
- | `--limit <n>` | Rows to list. Default 30 |
38
- | `--out <file>` | Where `export` writes. Default `memory.html` |
39
- | `--open` | Open the exported page |
40
- | `--yes` | Do not ask before removing |
30
+ | Flag | Meaning |
31
+ | ----------------------- | --------------------------------------------- |
32
+ | `--project <name\|dir>` | Which project. Inferred from the directory |
33
+ | `--dir <dir>` | Read this memory directory instead |
34
+ | `--kind <name>` | Only this kind of node |
35
+ | `--audience <name>` | Only nodes committed under this label |
36
+ | `--files` | Only nodes that remember a file |
37
+ | `--stale` | Only nodes something has superseded |
38
+ | `--limit <n>` | Rows to list. Default 30 |
39
+ | `--out <file>` | Where `export` writes. Default `memory.html` |
40
+ | `--open` | Open the exported page |
41
+ | `--dry-run` | Say what `merge` would do, and stop |
42
+ | `--no-dedupe` | Keep memories `merge` would otherwise fold |
43
+ | `--force` | Let `merge` pick a winner where copies differ |
44
+ | `--yes` | Do not ask before removing or merging |
41
45
 
42
46
  ## A graph that is not the project's
43
47
 
@@ -90,6 +94,40 @@ It is a single file with no server behind it, so it attaches to a bug report.
90
94
  The only network it does is fetching Mermaid; without that it degrades to a
91
95
  working list and detail view.
92
96
 
97
+ ## Merging
98
+
99
+ The lock is per directory, so two runs cannot warm the same memory at once.
100
+ They each warm their own, and `merge` puts the results back together:
101
+
102
+ ```sh
103
+ zen memory merge .tmp/warmup-*/memory into the project’s
104
+ zen memory merge a/memory b/memory --dir merged/memory into a named one
105
+ ```
106
+
107
+ Sources are positional and the target is `--dir`, or the project you are in —
108
+ the same shape as everywhere else here. The shell expands the glob, so a
109
+ twelve-way fan-out is still one word. `merge` is the one subcommand that will
110
+ write a memory into existence: a target that is not there yet is created.
111
+
112
+ It is offline and contacts no model, which is why every side has to have been
113
+ embedded with the same model already. Node ids are kept, so the interesting
114
+ case is the same id on both sides:
115
+
116
+ - **Same revision, same content** — a shared ancestor, because the runs started
117
+ from a copy of the same memory. Use counts and timestamps reconcile to the
118
+ larger of the two, never the sum, so merging a source twice changes nothing.
119
+ - **Anything else** — a divergence. The whole merge is refused, nothing is
120
+ written, and every conflicting id is listed with both revisions. `--force`
121
+ takes the highest revision instead.
122
+
123
+ Memories that are not the same node but say the same thing fold together on the
124
+ same rule a commit uses for duplicates — near-identical by cosine, same kind,
125
+ same audience, never one that remembers a file. `--no-dedupe` turns that off.
126
+ Edges follow whatever their ends folded onto, and an edge whose ends both
127
+ landed on the same memory is dropped.
128
+
129
+ `--dry-run` reports all of it and writes nothing.
130
+
93
131
  ## Forgetting
94
132
 
95
133
  ```sh
@@ -21,6 +21,7 @@ What it writes:
21
21
  zenera.json { version, name }
22
22
  agents.yaml the configuration
23
23
  agents/instructions.md house rules, prepended to every agent
24
+ agents/files-instructions.md more of them, for agents that work with files
24
25
  agents/fork-instructions.md more of them, for agents that can fork
25
26
  agents/memory-instructions.md more of them, for agents with the memory store
26
27
  agents/tools-instructions.md more of them, about how tools are called