@zenera/cli 1.1.21 → 1.1.22

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 (49) hide show
  1. package/README.md +3 -3
  2. package/dist/banner.d.ts +33 -24
  3. package/dist/banner.js +25 -6
  4. package/dist/commands/check.js +1 -0
  5. package/dist/commands/init.js +1 -0
  6. package/dist/commands/inspect.js +1 -0
  7. package/dist/commands/key.js +1 -0
  8. package/dist/commands/list.js +1 -0
  9. package/dist/commands/memory.js +1 -0
  10. package/dist/commands/open.js +1 -0
  11. package/dist/commands/run.js +3 -0
  12. package/dist/commands/sandbox.js +1 -0
  13. package/dist/commands/version.js +1 -0
  14. package/dist/lib.d.ts +2 -1
  15. package/dist/lib.js +2 -1
  16. package/dist/meta.d.ts +1 -1
  17. package/dist/meta.js +5 -28
  18. package/dist/resolve.js +19 -12
  19. package/dist/sandbox.js +1 -0
  20. package/dist/scaffold.js +3 -1
  21. package/dist/term.d.ts +2 -0
  22. package/dist/term.js +2 -0
  23. package/dist/tui/markdown.d.ts +16 -0
  24. package/dist/tui/markdown.js +259 -0
  25. package/dist/tui/theme.js +1 -1
  26. package/dist/tui/wrap.d.ts +1 -1
  27. package/dist/validate.js +82 -3
  28. package/package.json +2 -2
  29. package/templates/editor/.github/copilot-instructions.md +62 -33
  30. package/templates/editor/.github/prompts/spec-sync-project.prompt.md +31 -1
  31. package/templates/editor/.github/skills/zen-cli/SKILL.md +15 -15
  32. package/templates/editor/.github/skills/zen-cli/references/projects.md +1 -0
  33. package/templates/editor/.github/skills/zen-cli/references/rag.md +37 -3
  34. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +30 -2
  35. package/templates/editor/.github/skills/zen-instructions/SKILL.md +232 -0
  36. package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +554 -0
  37. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +56 -23
  38. package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +55 -28
  39. package/templates/editor/.github/skills/zen-review/SKILL.md +17 -1
  40. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +257 -0
  41. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +15 -3
  42. package/templates/editor/.vscode/agents.schema.json +8 -3
  43. package/templates/project/SPECIFICATION.md.tmpl +2 -1
  44. package/templates/project/agents/files-instructions.md +86 -0
  45. package/templates/project/agents/fork-instructions.md +17 -0
  46. package/templates/project/agents/memory-instructions.md +37 -0
  47. package/templates/project/agents.yaml.tmpl +3 -3
  48. package/templates/project/gitignore +12 -1
  49. package/templates/project/scripts/_setup.sh +19 -4
@@ -0,0 +1,554 @@
1
+ ---
2
+ name: zen-memory-warmup
3
+ description: Pre-populating an agent project's memory graph through warm-up queries before production or deployment — isolating scratch memory and workspaces in timestamped .tmp directories, using stronger reasoning models, validating machine-readable JSON output, categorizing queries with limits, shuffling, and dry-run execution, inspecting the graph with zen memory stats/ls, atomic promotion into the project, and generating automated population scripts in scripts/memory_warmup.sh.
4
+ ---
5
+
6
+ # Memory Warmup
7
+
8
+ Memory warmup is the process of pre-populating an agent project's knowledge graph
9
+ before deploying it or exposing it to end users.
10
+
11
+ In Zenera Neo, an agent's memory graph (`memory/`) starts completely empty. A cold
12
+ start means early runs cannot recall prior approaches, domain facts, verified
13
+ plans, or tool schemas. Warming up the memory addresses this by executing a
14
+ curated set of representative domain queries ahead of time, allowing agents to
15
+ synthesize, connect, and commit high-value knowledge nodes.
16
+
17
+ ---
18
+
19
+ ## Why Warm Up Memory?
20
+
21
+ 1. **Eliminate Cold Starts**: Instead of failing or struggling through initial
22
+ discovery, the agent recalls established patterns, known schemas, and
23
+ proven plans on its very first user-facing turn.
24
+ 2. **Higher-Quality Graph via Stronger Models**: You can run warmup turns using
25
+ a more capable reasoning model (`--model`) than your day-to-day runtime
26
+ model. A smarter model creates cleaner abstractions, links causal relations
27
+ accurately, and produces superior plans.
28
+ 3. **Consistency & Alignment**: Foundational conventions, domain boundaries,
29
+ and operational rules are seeded as durable `fact` and `plan` nodes rather
30
+ than rediscovered inconsistently across random user sessions.
31
+
32
+ ---
33
+
34
+ ## Core Architecture & Safety Rules
35
+
36
+ Warmup is an offline staging process. It must adhere to five safety rules:
37
+
38
+ ### 1. Never warm up directly into the project's active `memory/`
39
+
40
+ If a warmup query fails, hallucinates, or aborts mid-turn, committing directly
41
+ to the project's memory directory corrupts the knowledge graph. Always point
42
+ `zen run` to a timestamped staging directory in `.tmp/` via `--memory` so that
43
+ repeated or concurrent runs never collide:
44
+
45
+ ```sh
46
+ STAMP="$(date +%Y%m%d-%H%M%S)"
47
+ --memory ".tmp/warmup-$STAMP/memory"
48
+ ```
49
+
50
+ ### 2. Isolate workspaces in `.tmp/` with per-run timestamps
51
+
52
+ Warmup queries often instruct agents to investigate repositories, write test
53
+ scripts, or run commands. Running them in the actual project or repository
54
+ working directory risks modifying tracked files or creating untracked clutter.
55
+
56
+ Always isolate the workspace per run into a fresh, timestamped subdirectory
57
+ under `.tmp/`:
58
+
59
+ ```sh
60
+ --workspace ".tmp/warmup-$STAMP/workspaces/run-01-$(date +%s)"
61
+ ```
62
+
63
+ Using timestamps on both the root staging directory and each individual run
64
+ workspace ensures:
65
+
66
+ - Concurrent or repeated warmup attempts never overwrite each other's state.
67
+ - Stale or corrupted files from an earlier failed run cannot leak into subsequent
68
+ queries.
69
+ - Every turn begins from a completely pristine, deterministic working directory.
70
+
71
+ ### 3. Use stronger models during population
72
+
73
+ Day-to-day execution might use a fast, cost-effective model (e.g.
74
+ `google:gemini-2.5-flash` or `anthropic:claude-3-5-haiku`). Warmup runs are
75
+ performed infrequently, so it makes sense to use a flagship reasoning model
76
+ (e.g. `anthropic:claude-sonnet-4-5`, `openai:gpt-5`, or
77
+ `google:gemini-3.5-flash`):
78
+
79
+ ```sh
80
+ --model anthropic:claude-sonnet-4-5
81
+ ```
82
+
83
+ The superior graph structure created by the flagship model is then recalled and
84
+ utilized by the runtime model.
85
+
86
+ ### 4. Execute non-interactively with `--json`
87
+
88
+ Using `--json` guarantees:
89
+
90
+ - Interactive TUI and confirmation prompts are disabled (assumes `--yes`).
91
+ - Progress narration on stderr is silenced.
92
+ - Machine-readable JSON output on stdout containing `stopReason`, `usage`,
93
+ `mounts`, run artifact paths (`run.dir`, `run.output`), and the answer.
94
+ - The population script can programmatically verify whether the run finished
95
+ successfully (`stopReason === "final"`).
96
+
97
+ ### 5. Validate before promotion
98
+
99
+ Track graph accumulation by inspecting `zen memory stats --dir ...` in between
100
+ warmup queries, and do not overwrite the project's `memory/` until the final
101
+ graph in `.tmp/warmup-<timestamp>/memory` has been verified to contain expected
102
+ nodes and vector embeddings.
103
+
104
+ ---
105
+
106
+ ## The Warmup Cycle
107
+
108
+ ```mermaid
109
+ flowchart TD
110
+ A[Curated Seed Queries] --> B["Initialize .tmp/warmup-<timestamp>/memory"]
111
+ B --> C["For each query: Create fresh .tmp/warmup-<timestamp>/workspaces/run-N-<timestamp>"]
112
+ C --> D["zen run --memory .tmp/warmup-<timestamp>/memory --workspace .tmp/.../run-N-<timestamp> --model <model> --json"]
113
+ D --> E{Check stopReason & exit code}
114
+ E -- Success --> F[Accumulate nodes in staging graph]
115
+ E -- Failure --> G[Log error & abort or retry]
116
+ F --> T["Log timing: query duration & total elapsed"]
117
+ T --> S["Inspect progress: zen memory stats --dir .tmp/warmup-<timestamp>/memory"]
118
+ S --> H{More queries?}
119
+ H -- Yes --> C
120
+ H -- No --> I["Validate: zen memory stats --dir .tmp/warmup-<timestamp>/memory"]
121
+ I --> J{Graph healthy?}
122
+ J -- Yes --> K["Backup existing memory & promote .tmp/warmup-<timestamp>/memory to memory/"]
123
+ J -- No --> L[Inspect with zen memory ls / export & discard]
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Query Categories, Sampling & Dry-Run Preview
129
+
130
+ Warmup suites should be organized across distinct domain categories rather than
131
+ an unstructured flat list of ad-hoc prompts.
132
+
133
+ ### Recommended Categories
134
+
135
+ | Category | Objective | Example Prompt |
136
+ | :------------- | :----------------------------------------------------------- | :------------------------------------------------------------------------- |
137
+ | `architecture` | Map high-level layout, component boundaries, and data flow | "Investigate project architecture and summarize component flow." |
138
+ | `workflows` | Formulate multi-step operational plans for common tasks | "Formulate standard operational plans for triaging common tasks." |
139
+ | `integrations` | Index external services, APIs, and credentials | "Identify external services, APIs, and key credential dependencies." |
140
+ | `errors` | Capture error handling, recovery routines, and failure modes | "Analyze error handling strategies, recovery routines, and failure modes." |
141
+ | `conventions` | Record coding styles, house rules, and constraints | "Document project-specific coding standards and house rules." |
142
+
143
+ ### Why Sampling, Shuffling, and Limits Matter
144
+
145
+ 1. **Balanced Graph Coverage**: If a warmup suite contains 20+ queries, running
146
+ them sequentially with a limit (e.g. `--limit 4`) would only execute the
147
+ first category (`architecture`), leaving workflows and integrations
148
+ completely cold.
149
+ 2. **`--shuffle`**: Randomizes the query sequence before limits are applied,
150
+ ensuring that a limited budget run draws an even cross-section across
151
+ different categories.
152
+ 3. **`--category <name>`**: Enables focused warmup on a single subsystem (e.g.
153
+ warming up only `integrations` after adding new API capabilities).
154
+ 4. **`--limit <n>`**: Prevents unintended token expenditure by capping the total
155
+ number of queries executed during test or verification passes.
156
+
157
+ ### Dry Run Mode (`--dry-run`)
158
+
159
+ Warming up memory with frontier reasoning models (`--model`) can be resource
160
+ intensive. A `--dry-run` flag is essential for:
161
+
162
+ - Verifying resolved staging paths and backup targets.
163
+ - Inspecting which queries and categories were selected after filtering,
164
+ shuffling, and limits.
165
+ - Checking the exact `zen run ...` commands that would be executed.
166
+ - Guaranteeing that zero files are written, zero directories are created, and
167
+ zero model tokens are billed.
168
+
169
+ ---
170
+
171
+ ## Generating the Warmup Script in `scripts/`
172
+
173
+ A project should maintain a dedicated warmup script located in `scripts/`
174
+ (e.g. `scripts/memory_warmup.sh` or `scripts/memory-warmup.sh`).
175
+
176
+ The script should automate the entire pipeline:
177
+
178
+ 1. Accept command-line flags:
179
+ - `--dry-run`: Preview selected queries, categories, and commands without executing.
180
+ - `--limit <n>`: Run at most $n$ queries.
181
+ - `--shuffle`: Randomize query order before applying limits.
182
+ - `--category <name>`: Filter queries to a specific category.
183
+ - `--model <provider:model>`: Override model with a reasoning flagship.
184
+ - `--incremental`: Copy existing project memory into staging before warming up.
185
+ - `-y`, `--yes`: Promote memory without interactive confirmation.
186
+ 2. Create timestamped staging paths under `.tmp/` (e.g.
187
+ `.tmp/warmup-$(date +%Y%m%d-%H%M%S)/`) and timestamped per-query workspaces.
188
+ 3. Filter, shuffle, and sample from categorized query definitions.
189
+ 4. If `--dry-run` is active, print the execution plan and exit 0 before touching disk.
190
+ 5. Invoke `zen run` with `--memory`, `--workspace`, `--model`, and `--json`.
191
+ 6. Parse each outcome with `jq`, halt if a turn fails, and report per-query execution time and total elapsed time since start.
192
+ 7. Print graph statistics via `zen memory stats --dir ...` between queries to track graph growth, followed by a final validation.
193
+ 8. Safely backup and replace the project's `memory/` directory with the warmed graph.
194
+
195
+ ### Recommended Script Template (`scripts/memory_warmup.sh`)
196
+
197
+ When generating a warmup script for a project, use the following production-ready
198
+ pattern:
199
+
200
+ ```bash
201
+ #!/usr/bin/env bash
202
+ #
203
+ # scripts/memory_warmup.sh — Pre-populate project memory using curated seed queries.
204
+ #
205
+ # Usage:
206
+ # ./scripts/memory_warmup.sh [options]
207
+ #
208
+ # Options:
209
+ # --dry-run Preview selected queries and commands without executing
210
+ # --limit <n> Maximum number of queries to run
211
+ # --shuffle Randomize query order before applying limits
212
+ # --category <name> Filter queries to a specific category
213
+ # --model <ref> Override model (e.g. anthropic:claude-sonnet-4-5)
214
+ # --incremental Seed staging memory with current memory/ before runs
215
+ # -y, --yes Assume yes for memory promotion
216
+ # -h, --help Show this help
217
+ #
218
+ # Examples:
219
+ # ./scripts/memory_warmup.sh --dry-run
220
+ # ./scripts/memory_warmup.sh --dry-run --category workflows --limit 2
221
+ # ./scripts/memory_warmup.sh --shuffle --limit 5 --model anthropic:claude-sonnet-4-5
222
+ # ./scripts/memory_warmup.sh --incremental
223
+ #
224
+
225
+ set -euo pipefail
226
+
227
+ # Find project root regardless of invocation directory
228
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
229
+ cd "$ROOT"
230
+
231
+ # Default configuration
232
+ MODEL="${ZEN_WARMUP_MODEL:-}"
233
+ INCREMENTAL=false
234
+ ASSUME_YES=false
235
+ DRY_RUN=false
236
+ LIMIT=0
237
+ SHUFFLE=false
238
+ CATEGORY_FILTER=""
239
+
240
+ # Helper to format seconds as human-readable duration
241
+ format_duration() {
242
+ local total_sec=$1
243
+ local hours=$((total_sec / 3600))
244
+ local mins=$(((total_sec % 3600) / 60))
245
+ local secs=$((total_sec % 60))
246
+ if [ $hours -gt 0 ]; then
247
+ echo "${hours}h ${mins}m ${secs}s"
248
+ elif [ $mins -gt 0 ]; then
249
+ echo "${mins}m ${secs}s"
250
+ else
251
+ echo "${secs}s"
252
+ fi
253
+ }
254
+
255
+ # Parse arguments
256
+ while [[ $# -gt 0 ]]; do
257
+ case "$1" in
258
+ --dry-run)
259
+ DRY_RUN=true
260
+ shift
261
+ ;;
262
+ --limit)
263
+ LIMIT="$2"
264
+ shift 2
265
+ ;;
266
+ --shuffle)
267
+ SHUFFLE=true
268
+ shift
269
+ ;;
270
+ --category)
271
+ CATEGORY_FILTER="$2"
272
+ shift 2
273
+ ;;
274
+ --model)
275
+ MODEL="$2"
276
+ shift 2
277
+ ;;
278
+ --incremental)
279
+ INCREMENTAL=true
280
+ shift
281
+ ;;
282
+ -y|--yes)
283
+ ASSUME_YES=true
284
+ shift
285
+ ;;
286
+ -h|--help)
287
+ echo "Usage: ./scripts/memory_warmup.sh [options]"
288
+ echo ""
289
+ echo "Options:"
290
+ echo " --dry-run Preview selected queries and commands without executing"
291
+ echo " --limit <n> Maximum number of queries to run"
292
+ echo " --shuffle Randomize query order before applying limits"
293
+ echo " --category <name> Filter queries to a specific category"
294
+ echo " --model <ref> Override model (e.g. anthropic:claude-sonnet-4-5)"
295
+ echo " --incremental Seed staging memory with current memory/ before runs"
296
+ echo " -y, --yes Assume yes for memory promotion"
297
+ echo " -h, --help Show this help"
298
+ exit 0
299
+ ;;
300
+ *)
301
+ echo "Error: Unknown option $1" >&2
302
+ exit 2
303
+ ;;
304
+ esac
305
+ done
306
+
307
+ STAMP="$(date +%Y%m%d-%H%M%S)"
308
+ SCRATCH_DIR=".tmp/warmup-$STAMP"
309
+ STAGING_MEM="$SCRATCH_DIR/memory"
310
+ WORKSPACES_DIR="$SCRATCH_DIR/workspaces"
311
+ PROJECT_MEM="memory"
312
+
313
+ # Define curated seed queries structured by category ("category|prompt")
314
+ ALL_QUERIES=(
315
+ "architecture|Investigate the project architecture and summarize the main components and data flow."
316
+ "architecture|Analyze module boundaries, dependency graphs, and core interfaces."
317
+ "workflows|Formulate standard operational plans for triaging and resolving common domain tasks."
318
+ "workflows|Trace the end-to-end user request lifecycle and hand-off points."
319
+ "integrations|Identify external services, APIs, and key credential dependencies."
320
+ "integrations|Verify database and storage schemas, access patterns, and persistence rules."
321
+ "errors|Analyze error handling strategies, recovery routines, and failure modes."
322
+ "errors|Identify retry policies, rate limits, and fallback behaviors."
323
+ "conventions|Document project-specific coding standards, house rules, and commit policies."
324
+ )
325
+
326
+ # 1. Filter by category if requested
327
+ FILTERED=()
328
+ for item in "${ALL_QUERIES[@]}"; do
329
+ cat="${item%%|*}"
330
+ if [ -z "$CATEGORY_FILTER" ] || [ "$cat" = "$CATEGORY_FILTER" ]; then
331
+ FILTERED+=("$item")
332
+ fi
333
+ done
334
+
335
+ if [ ${#FILTERED[@]} -eq 0 ]; then
336
+ echo "Error: No queries matched category '$CATEGORY_FILTER'." >&2
337
+ exit 1
338
+ fi
339
+
340
+ # 2. Shuffle if requested
341
+ if [ "$SHUFFLE" = true ]; then
342
+ TEMP=()
343
+ if command -v python3 >/dev/null 2>&1; then
344
+ while IFS= read -r line; do
345
+ [ -n "$line" ] && TEMP+=("$line")
346
+ done < <(printf '%s\n' "${FILTERED[@]}" | python3 -c 'import sys, random; lines = [l for l in sys.stdin.read().splitlines() if l]; random.shuffle(lines); print("\n".join(lines))')
347
+ elif command -v shuf >/dev/null 2>&1; then
348
+ while IFS= read -r line; do
349
+ [ -n "$line" ] && TEMP+=("$line")
350
+ done < <(printf '%s\n' "${FILTERED[@]}" | shuf)
351
+ elif sort -R </dev/null >/dev/null 2>&1; then
352
+ while IFS= read -r line; do
353
+ [ -n "$line" ] && TEMP+=("$line")
354
+ done < <(printf '%s\n' "${FILTERED[@]}" | sort -R)
355
+ else
356
+ TEMP=("${FILTERED[@]}")
357
+ fi
358
+ FILTERED=("${TEMP[@]}")
359
+ fi
360
+
361
+ # 3. Apply limit if requested
362
+ SELECTED=()
363
+ if [ "$LIMIT" -gt 0 ] && [ "$LIMIT" -lt "${#FILTERED[@]}" ]; then
364
+ for ((i = 0; i < LIMIT; i++)); do
365
+ SELECTED+=("${FILTERED[i]}")
366
+ done
367
+ else
368
+ SELECTED=("${FILTERED[@]}")
369
+ fi
370
+
371
+ # 4. Handle Dry Run mode
372
+ if [ "$DRY_RUN" = true ]; then
373
+ echo "=== DRY RUN: Previewing Warmup Plan ==="
374
+ echo "Project root: $ROOT"
375
+ echo "Staging memory: $STAGING_MEM"
376
+ echo "Target memory: $PROJECT_MEM"
377
+ echo "Model override: ${MODEL:-<default>}"
378
+ echo "Category filter: ${CATEGORY_FILTER:-<all>}"
379
+ echo "Shuffled: $SHUFFLE"
380
+ echo "Limit: ${LIMIT:-none}"
381
+ echo "Selected queries: ${#SELECTED[@]} of ${#ALL_QUERIES[@]}"
382
+ echo ""
383
+ echo "Planned queries:"
384
+ for item in "${SELECTED[@]}"; do
385
+ cat="${item%%|*}"
386
+ q="${item#*|}"
387
+ printf ' [%-14s] %s\n' "$cat" "$q"
388
+ done
389
+ echo ""
390
+ echo "Sample command that would execute:"
391
+ SAMPLE_Q="${SELECTED[0]#*|}"
392
+ echo " zen run --memory \"$STAGING_MEM\" --workspace \"$WORKSPACES_DIR/run-1-<timestamp>\" ${MODEL:+--model \"$MODEL\" }--json \"$SAMPLE_Q\""
393
+ echo " zen memory stats --dir \"$STAGING_MEM\""
394
+ echo ""
395
+ echo "Post-run validation that would run:"
396
+ echo " zen memory stats --dir \"$STAGING_MEM\""
397
+ echo ""
398
+ echo "Dry run complete. No files created, no commands executed."
399
+ exit 0
400
+ fi
401
+
402
+ WARMUP_START=$(date +%s)
403
+
404
+ echo "=== Zenera Neo Memory Warmup ==="
405
+ echo "Project root: $ROOT"
406
+ echo "Staging directory: $SCRATCH_DIR"
407
+ if [ -n "$MODEL" ]; then
408
+ echo "Model override: $MODEL"
409
+ fi
410
+ echo "Selected queries: ${#SELECTED[@]}"
411
+ echo ""
412
+
413
+ # Cleanup scratch on trap unless preserved for inspection on failure
414
+ cleanup() {
415
+ local rc=$?
416
+ if [ $rc -ne 0 ]; then
417
+ echo "" >&2
418
+ echo "Warmup FAILED (exit code $rc)." >&2
419
+ echo "Staging files preserved for inspection at: $SCRATCH_DIR" >&2
420
+ fi
421
+ }
422
+ trap cleanup EXIT
423
+
424
+ # Prepare staging memory
425
+ mkdir -p "$STAGING_MEM" "$WORKSPACES_DIR"
426
+
427
+ if [ "$INCREMENTAL" = true ] && [ -d "$PROJECT_MEM" ]; then
428
+ echo "Copying existing memory for incremental warmup..."
429
+ cp -R "$PROJECT_MEM/." "$STAGING_MEM/"
430
+ fi
431
+
432
+ echo "Running ${#SELECTED[@]} warmup queries..."
433
+ echo "----------------------------------------"
434
+
435
+ COUNT=0
436
+ for item in "${SELECTED[@]}"; do
437
+ COUNT=$((COUNT + 1))
438
+ CATEGORY="${item%%|*}"
439
+ QUERY="${item#*|}"
440
+
441
+ RUN_STAMP="$(date +%s)"
442
+ RUN_WORKSPACE="$WORKSPACES_DIR/run-$COUNT-$RUN_STAMP"
443
+ mkdir -p "$RUN_WORKSPACE"
444
+
445
+ echo "[$COUNT/${#SELECTED[@]}] [$CATEGORY] \"$QUERY\""
446
+ echo " -> Workspace: $RUN_WORKSPACE"
447
+
448
+ # Assemble arguments
449
+ CMD=(zen run --memory "$STAGING_MEM" --workspace "$RUN_WORKSPACE" --json)
450
+ if [ -n "$MODEL" ]; then
451
+ CMD+=(--model "$MODEL")
452
+ fi
453
+ CMD+=("$QUERY")
454
+
455
+ QUERY_START=$(date +%s)
456
+
457
+ # Run query and capture JSON output
458
+ JSON_OUT="$("${CMD[@]}")"
459
+ EXIT_CODE=$?
460
+
461
+ QUERY_END=$(date +%s)
462
+ QUERY_DURATION=$((QUERY_END - QUERY_START))
463
+ TOTAL_ELAPSED=$((QUERY_END - WARMUP_START))
464
+
465
+ if [ $EXIT_CODE -ne 0 ]; then
466
+ echo " -> Command exited with code $EXIT_CODE (query: $(format_duration $QUERY_DURATION), total elapsed: $(format_duration $TOTAL_ELAPSED))" >&2
467
+ exit $EXIT_CODE
468
+ fi
469
+
470
+ # Parse outcome via jq if available
471
+ if command -v jq >/dev/null 2>&1; then
472
+ STOP_REASON=$(echo "$JSON_OUT" | jq -r '.stopReason // empty')
473
+ AGENT=$(echo "$JSON_OUT" | jq -r '.agent // empty')
474
+ echo " -> Completed: agent=$AGENT stopReason=$STOP_REASON"
475
+
476
+ if [ "$STOP_REASON" != "final" ]; then
477
+ echo " -> Unexpected stop reason: $STOP_REASON" >&2
478
+ exit 1
479
+ fi
480
+ else
481
+ echo " -> Turn completed successfully."
482
+ fi
483
+
484
+ echo " -> Timing: query took $(format_duration $QUERY_DURATION) | elapsed since start: $(format_duration $TOTAL_ELAPSED)"
485
+
486
+ # Print memory stats between queries to track graph growth
487
+ if [ -f "$STAGING_MEM/manifest.json" ]; then
488
+ echo ""
489
+ echo "Memory stats after query $COUNT:"
490
+ zen memory stats --dir "$STAGING_MEM"
491
+ else
492
+ echo " -> Memory stats: No memories committed yet."
493
+ fi
494
+ echo ""
495
+ done
496
+
497
+ echo "----------------------------------------"
498
+ TOTAL_RUN_TIME=$(( $(date +%s) - WARMUP_START ))
499
+ echo "All warmup queries finished successfully in $(format_duration $TOTAL_RUN_TIME)."
500
+ echo ""
501
+
502
+ # Validate staging memory
503
+ echo "Validating staging memory graph:"
504
+ zen memory stats --dir "$STAGING_MEM"
505
+ echo ""
506
+
507
+ # Confirmation and promotion
508
+ if [ "$ASSUME_YES" = false ]; then
509
+ read -r -p "Promote staging memory to '$PROJECT_MEM'? [y/N] " CONFIRM
510
+ if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
511
+ echo "Warmup cancelled. Staging files left in $SCRATCH_DIR."
512
+ exit 0
513
+ fi
514
+ fi
515
+
516
+ # Backup existing project memory if present
517
+ if [ -d "$PROJECT_MEM" ]; then
518
+ BACKUP_MEM="memory.bak.$(date +%Y%m%d-%H%M%S)"
519
+ echo "Backing up current memory to $BACKUP_MEM..."
520
+ mv "$PROJECT_MEM" "$BACKUP_MEM"
521
+ fi
522
+
523
+ # Promote staging memory to project root
524
+ echo "Promoting warmed memory to $PROJECT_MEM..."
525
+ mv "$STAGING_MEM" "$PROJECT_MEM"
526
+
527
+ # Clean up remaining scratch workspaces
528
+ rm -rf "$SCRATCH_DIR"
529
+
530
+ echo "Warmup complete! Project memory is ready."
531
+ ```
532
+
533
+ ---
534
+
535
+ ## Best Practices for Seed Queries
536
+
537
+ When authoring or generating queries for the warmup script:
538
+
539
+ 1. **Group by Category**:
540
+ - Organize queries into clear domain categories (`architecture`, `workflows`,
541
+ `integrations`, `errors`, `conventions`) so test runs can be sampled or
542
+ targeted easily.
543
+ 2. **Use `--dry-run` First**:
544
+ - Always verify candidate queries, model overrides, and category distribution
545
+ with `--dry-run` before initiating a live warmup.
546
+ 3. **Prompts That Induce Plan Formation**:
547
+ - Instruct the agent to "analyze and determine the recommended pattern for..." so
548
+ it stores durable `plan` and `fact` nodes rather than transient output.
549
+ 4. **Verify Node Vectorisation**:
550
+ - Confirm in `zen memory stats --dir ...` that vector count equals node count
551
+ (ensuring your embedding model is configured and active).
552
+ 5. **Keep Seed Queries Under Version Control**:
553
+ - Store the seed prompts inside the script or an adjacent `scripts/warmup-queries.txt`
554
+ so warmup is reproducible across environments and model upgrades.
@@ -19,17 +19,19 @@ hits for `limit` are not an answer; the eight lines around one of them are.
19
19
  ## The commands
20
20
 
21
21
  ```
22
- zen rag docs <index|search|list|grep|show|stats> [path...]
22
+ zen rag docs <index|restore|ready|search|list|grep|show|stats> [path...]
23
23
  ```
24
24
 
25
- | Command | Answers | Embedder? | Typical |
26
- | -------- | ------------------------------------------ | --------- | ------- |
27
- | `index` | builds the thing | yes | minutes |
28
- | `search` | _where does this corpus talk about X?_ | **yes** | seconds |
29
- | `list` | _what documents/headings/tables are here?_ | no | instant |
30
- | `grep` | _does the string X appear anywhere?_ | no | instant |
31
- | `show` | _print this section, verbatim_ | no | instant |
32
- | `stats` | _what is in this index?_ | no | instant |
25
+ | Command | Answers | Embedder? | Typical |
26
+ | --------- | ------------------------------------------ | --------- | ------- |
27
+ | `index` | builds the thing | yes | minutes |
28
+ | `restore` | _put the vectors back_ | **yes** | minutes |
29
+ | `ready` | _can this index answer at all?_ | no | instant |
30
+ | `search` | _where does this corpus talk about X?_ | **yes** | seconds |
31
+ | `list` | _what documents/headings/tables are here?_ | no | instant |
32
+ | `grep` | _does the string X appear anywhere?_ | no | instant |
33
+ | `show` | _print this section, verbatim_ | no | instant |
34
+ | `stats` | _what is in this index?_ | no | instant |
33
35
 
34
36
  Only `search` ranks, and only `search` costs a network round trip - it embeds
35
37
  the query before it can compare anything. The other four read `outline.json`
@@ -80,6 +82,35 @@ the answer.)
80
82
  `manifest.json` records **which embedder made the vectors**, so a search with a
81
83
  different model is refused rather than answered with noise.
82
84
 
85
+ ### What survives a clone
86
+
87
+ `lance/` is binary, large, and rebuildable, so a project commits the index and
88
+ git-ignores the vectors. What a fresh checkout holds is therefore an index that
89
+ **looks built and cannot answer**: the manifest is there, `sources/` is there,
90
+ and the first search fails deep in the store with `holds no searchable table`.
91
+
92
+ Two commands close that gap, and neither needs the original documents:
93
+
94
+ ```sh
95
+ zen rag docs ready --dir assets/docs-db # exit 0 searchable, exit 3 not
96
+ zen rag docs restore --dir assets/docs-db # re-embed from sources/
97
+ ```
98
+
99
+ `ready` answers with its **exit code**, so a setup step branches on it without
100
+ parsing anything; `--json` gives the same answer as `{ ready, state, embedding }`
101
+ for a caller that wants the detail. `restore` reads the documents back out of
102
+ `sources/`, re-embeds them with the model the manifest names, and renames the
103
+ result into place - so a failed restore leaves the old index untouched, at the
104
+ cost of room for both while it runs.
105
+
106
+ > **Never test for a built index with `[ -f .../manifest.json ]`.** The manifest
107
+ > is committed and the vectors are not, so that test is true on exactly the
108
+ > machine where it is wrong. `zen rag docs ready` is the test.
109
+
110
+ `restore` is also how an index **changes embedder**: `--embedding <ref>`
111
+ re-embeds everything and rewrites the manifest, so later searches ask for the
112
+ new model. Same corpus, same names, same chunk ids - only the vectors are new.
113
+
83
114
  ### How a document is cut up
84
115
 
85
116
  Chunking follows the markdown rather than a character count: a paragraph, a
@@ -582,17 +613,19 @@ sections down. Cite the document name and the line numbers.
582
613
 
583
614
  ## When it goes wrong
584
615
 
585
- | Symptom | Cause |
586
- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
587
- | "not installed" | `@zenera/rag` is not resolvable - install it, or use the two-`--package` npx form |
588
- | `nothing to index` | No `.md`/`.markdown`/`.txt`/`.text` under those paths, or they are all hidden |
589
- | A document you expected is missing | Hidden directory, `node_modules`, an unlisted extension, or over 16 MB - check stderr |
590
- | Refused for a different embedding | The index records the model that built it; re-index or pass the right `--embedding` |
591
- | No manifest / not an index | A build that did not finish. `manifest.json` is written last on purpose |
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 |
594
- | A usage error before any credential is asked for | Deliberate: everything about the invocation is checked first, so a typo is a typo |
595
- | `no document matched --file` | The pattern is matched against the document **name**; `list files` prints them |
596
- | Nothing matched | Exit 0 with an empty answer. Try fewer words, or `--mode text` for an exact string |
597
- | Answers quoting text that is no longer there | Nothing watches the documents. Re-index after they change |
598
- | A search took ten seconds, using no CPU | One embedding round trip, not the index. `list`/`grep`/`show` make none |
616
+ | Symptom | Cause |
617
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
618
+ | "not installed" | `@zenera/rag` is not resolvable - install it, or use the two-`--package` npx form |
619
+ | `nothing to index` | No `.md`/`.markdown`/`.txt`/`.text` under those paths, or they are all hidden |
620
+ | A document you expected is missing | Hidden directory, `node_modules`, an unlisted extension, or over 16 MB - check stderr |
621
+ | Refused for a different embedding | The index records the model that built it; pass the right `--embedding`, or move it with `restore --embedding` |
622
+ | No manifest / not an index | A build that did not finish. `manifest.json` is written last on purpose |
623
+ | `holds no searchable table` | A manifest with no vectors - a clone, since `lance/` is git-ignored. `restore` it |
624
+ | A step reported `skipped` but nothing can search | The step tested for `manifest.json` instead of asking `ready` |
625
+ | A build is refused | `.lock` - another build is running. It is taken over when the process is gone |
626
+ | `provider "openai": no api key` | `zen key ls` - the keyring, or a real environment variable |
627
+ | A usage error before any credential is asked for | Deliberate: everything about the invocation is checked first, so a typo is a typo |
628
+ | `no document matched --file` | The pattern is matched against the document **name**; `list files` prints them |
629
+ | Nothing matched | Exit 0 with an empty answer. Try fewer words, or `--mode text` for an exact string |
630
+ | Answers quoting text that is no longer there | Nothing watches the documents. Re-index after they change |
631
+ | A search took ten seconds, using no CPU | One embedding round trip, not the index. `list`/`grep`/`show` make none |