@zenera/cli 1.1.16 → 1.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -12
- package/dist/catalog.js +19 -20
- package/dist/commands/check.js +5 -1
- package/dist/commands/init.js +41 -6
- package/dist/commands/run.js +4 -0
- package/dist/engine.js +6 -0
- package/dist/env.d.ts +20 -0
- package/dist/env.js +65 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +2 -0
- package/dist/sandbox.d.ts +9 -0
- package/dist/sandbox.js +23 -3
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +56 -15
- package/dist/validate.js +57 -15
- package/package.json +2 -2
- package/templates/editor/.github/copilot-instructions.md +256 -91
- package/templates/editor/.github/prompts/review-project.prompt.md +53 -14
- package/templates/editor/.github/prompts/sync-with-spec.prompt.md +208 -21
- package/templates/editor/.github/prompts/update-spec-from-feedback.prompt.md +142 -0
- package/templates/editor/.github/skills/zen-cli/references/check.md +6 -3
- package/templates/editor/.github/skills/zen-cli/references/frame.md +5 -3
- package/templates/editor/.github/skills/zen-cli/references/projects.md +19 -2
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +6 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +102 -30
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +115 -0
- package/templates/editor/.github/skills/zen-review/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +119 -0
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +104 -0
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +197 -0
- package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +200 -0
- package/templates/editor/.vscode/settings.json +44 -4
- package/templates/parts/memory-terms.yaml.tmpl +16 -0
- package/templates/parts/memory.yaml.tmpl +12 -0
- package/templates/project/SPECIFICATION.md.tmpl +7 -0
- package/templates/project/agents/memory-instructions.md +278 -0
- package/templates/project/agents.yaml.tmpl +9 -0
- package/templates/project/env +22 -0
- /package/templates/project/{INSTRUCTIONS.md → agents/instructions.md} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zen-spec-sync
|
|
3
|
+
description: Running `/sync-with-spec` as an incremental pass - the committed `.spec-sync` baseline of what the last pass applied, how to scope a pass from the specification and feedback diffs and from what moved in the project itself, the four checks a narrow pass may never skip, the iteration history format, and why the baseline is written last. Load before every `/sync-with-spec`, including the first one on a project that has no baseline yet.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Syncing a project with its specification, one change at a time
|
|
7
|
+
|
|
8
|
+
`SPECIFICATION.md` is the intent and the files are the implementation, so
|
|
9
|
+
bringing the two together is a pass over the whole project. Done from nothing
|
|
10
|
+
every time, that pass re-reads every prompt, re-derives every decision the last
|
|
11
|
+
one already made, and re-raises questions someone has already answered.
|
|
12
|
+
|
|
13
|
+
A pass is worth repeating cheaply, so it keeps a record of **what it applied**.
|
|
14
|
+
The next pass diffs the specification against that record and works the
|
|
15
|
+
difference. What it must not do is confuse a narrow diff with a narrow
|
|
16
|
+
obligation: the record scopes what you _read and edit_, never what you _verify_.
|
|
17
|
+
|
|
18
|
+
## What this owns
|
|
19
|
+
|
|
20
|
+
Everything is committed. `.spec-sync/` is a record, not scratch - `.tmp/` is
|
|
21
|
+
scratch.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
.spec-sync/
|
|
25
|
+
├── baseline/
|
|
26
|
+
│ ├── SPECIFICATION.md the spec the last completed pass applied
|
|
27
|
+
│ ├── SPECIFICATION-FEEDBACK.md the questions it had open, if any
|
|
28
|
+
│ ├── agents.yaml the wiring it left behind
|
|
29
|
+
│ ├── agents/ and every instruction, prompt and skill
|
|
30
|
+
│ ├── applied.txt the history entry this baseline belongs to
|
|
31
|
+
│ └── manifest.txt sha256 of every file above
|
|
32
|
+
└── history/
|
|
33
|
+
└── 2026-09-13T104500Z.md one file per pass, never edited after
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The baseline mirrors the paths it records, so `baseline/agents/skills/x/SKILL.md`
|
|
37
|
+
is that skill as the last completed pass left it. Both sides are there on
|
|
38
|
+
purpose: the specification and the feedback say what was **asked for**, the
|
|
39
|
+
wiring and the `agents/` tree say what was **built**, and a pass has to know
|
|
40
|
+
which of the two moved.
|
|
41
|
+
|
|
42
|
+
The baseline is **not a copy of the current specification**. It is the
|
|
43
|
+
specification as the last completed pass left it, which is what makes the
|
|
44
|
+
difference between the two mean "work nobody has done yet".
|
|
45
|
+
|
|
46
|
+
`zen init` creates `baseline/` and `history/` empty, and git does not carry an
|
|
47
|
+
empty directory, so **the presence of `.spec-sync/` proves nothing**. The
|
|
48
|
+
manifest is the signal: no `manifest.txt` means no pass has ever completed.
|
|
49
|
+
|
|
50
|
+
## Deciding the size of the pass
|
|
51
|
+
|
|
52
|
+
Run this first, from the project root:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
.github/skills/zen-spec-sync/scripts/snapshot.sh status
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It prints one `mode:` line, the `applied:` stamp the baseline belongs to, and a
|
|
59
|
+
`changed:` line per file that differs - specification, feedback, `agents.yaml`
|
|
60
|
+
or anything under `agents/`:
|
|
61
|
+
|
|
62
|
+
| Mode | Means | Do |
|
|
63
|
+
| ------------- | ------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
64
|
+
| `full` | no manifest - no pass has ever completed | the whole prompt as written, then write the first baseline |
|
|
65
|
+
| `incremental` | a baseline is there and the files differ | scope from the four inputs below |
|
|
66
|
+
| `tampered` | a baseline file does not match its own sha | treat as `full`, and say in the chat which file and that the baseline was not trusted |
|
|
67
|
+
|
|
68
|
+
`tampered` is either a baseline edited by hand or a pass that died between
|
|
69
|
+
copying the files and writing the manifest. Both mean the record is not
|
|
70
|
+
evidence. Do not rewrite `manifest.txt` to make the message go away - that
|
|
71
|
+
asserts a pass happened that did not.
|
|
72
|
+
|
|
73
|
+
`snapshot.sh diff` then prints the unified diffs, and is where the pass
|
|
74
|
+
actually starts.
|
|
75
|
+
|
|
76
|
+
## The four scope inputs
|
|
77
|
+
|
|
78
|
+
An incremental pass is scoped by the union of these. Write the union down before
|
|
79
|
+
editing anything, and report it in the chat as the pass's scope.
|
|
80
|
+
|
|
81
|
+
1. **The `SPECIFICATION.md` diff.** The primary input: added, changed and
|
|
82
|
+
deleted requirements. A deleted requirement is work too - what implemented it
|
|
83
|
+
is now an **extra**, to be reported under `➕` rather than quietly deleted.
|
|
84
|
+
2. **The `SPECIFICATION-FEEDBACK.md` diff.** The file is a questionnaire the
|
|
85
|
+
human answers by ticking one answer's `[ ]` box or deleting the answers they
|
|
86
|
+
do not want. A question that has moved under `✅ Answered` was folded into the
|
|
87
|
+
specification by `/update-spec-from-feedback`, and its **Still to implement**
|
|
88
|
+
line is the work. A question with a box marked `[x]`, or one answer left
|
|
89
|
+
under it, and no `✅` entry was answered and never folded in - implement it,
|
|
90
|
+
put its text into `SPECIFICATION.md`, and say so. A new question someone else
|
|
91
|
+
raised is work too. Close what this pass resolves rather than re-raising it.
|
|
92
|
+
3. **What the last pass left open.** The `Still open` section of the entry named
|
|
93
|
+
by `applied:`, in `.spec-sync/history/`. A specification line that has not
|
|
94
|
+
changed since the baseline is **not therefore implemented**: the baseline
|
|
95
|
+
records what the pass applied, and a pass can complete with items open. This
|
|
96
|
+
is the only place that says which.
|
|
97
|
+
4. **The implementation diff.** `agents.yaml` and every file under `agents/` -
|
|
98
|
+
instructions, prompts and skills. This side is what was built rather than
|
|
99
|
+
what was asked for, so it never narrows a pass: it says what moved under you
|
|
100
|
+
since the last one. An agent, tool grant, handoff, model, house rule, prompt
|
|
101
|
+
or skill edited outside a pass **widens** the pass to everything that names
|
|
102
|
+
what changed, and the edit is itself an item - either the specification
|
|
103
|
+
covers it, and you say which line, or it does not, and it is an **extra**
|
|
104
|
+
reported under `➕`.
|
|
105
|
+
|
|
106
|
+
## The four checks a narrow pass may never skip
|
|
107
|
+
|
|
108
|
+
However small the diff:
|
|
109
|
+
|
|
110
|
+
1. **`zen check` passes.** Run it before the first edit and again after the
|
|
111
|
+
last.
|
|
112
|
+
2. **`scripts/_setup.sh` has run to completion in this session, and re-runs
|
|
113
|
+
clean.** A one-line specification change can still break a setup step, and a
|
|
114
|
+
pass that did not run it does not know.
|
|
115
|
+
3. **§9 of the copilot instructions, worked as a checklist**, group by group.
|
|
116
|
+
4. **The capability obligations in §0.1.** For every capability the project has
|
|
117
|
+
turned on, load that capability's skill and re-check what it requires - in
|
|
118
|
+
particular that `agents/memory-instructions.md` is still byte-identical to
|
|
119
|
+
`.github/skills/zen-memory/references/memory-instructions.md`. `zen init` and
|
|
120
|
+
`zen open` rewrite the reference, so this drifts without anybody editing the
|
|
121
|
+
project.
|
|
122
|
+
|
|
123
|
+
A narrow diff narrows reading and editing. It does not narrow verification.
|
|
124
|
+
|
|
125
|
+
## What else an incremental pass still owes
|
|
126
|
+
|
|
127
|
+
The reverse pass of §2 - reading each line and naming the specification item it
|
|
128
|
+
serves - runs over every file this pass touched, plus every file the
|
|
129
|
+
implementation diff named. It is the only pass that finds a line contradicting
|
|
130
|
+
another file, and an edit made this session, or made by hand since the last
|
|
131
|
+
pass, is exactly where a new contradiction comes from.
|
|
132
|
+
|
|
133
|
+
## The history entry
|
|
134
|
+
|
|
135
|
+
One file per pass, `.spec-sync/history/<stamp>.md`, where the stamp is UTC and
|
|
136
|
+
filename-safe: `date -u +%Y-%m-%dT%H%M%SZ`. Never edit a file that is already
|
|
137
|
+
there; a pass that did nothing still writes one saying so.
|
|
138
|
+
|
|
139
|
+
These headings, in this order, because the next pass reads them:
|
|
140
|
+
|
|
141
|
+
```markdown
|
|
142
|
+
# Sync pass 2026-09-13T104500Z
|
|
143
|
+
|
|
144
|
+
- **Mode** - incremental
|
|
145
|
+
- **Spec sha** - 3f9a… (`shasum -a 256 SPECIFICATION.md`)
|
|
146
|
+
|
|
147
|
+
## Scope
|
|
148
|
+
|
|
149
|
+
What drove the pass: the diff hunks, the feedback entries, what the last pass
|
|
150
|
+
left open. `full` if it was a full pass.
|
|
151
|
+
|
|
152
|
+
## Changed
|
|
153
|
+
|
|
154
|
+
One row per file changed, and the specification item it closes.
|
|
155
|
+
|
|
156
|
+
## Feedback
|
|
157
|
+
|
|
158
|
+
Entries opened and entries closed, by number.
|
|
159
|
+
|
|
160
|
+
## Still open
|
|
161
|
+
|
|
162
|
+
Every specification item this pass did **not** close, with its
|
|
163
|
+
`SPECIFICATION-FEEDBACK.md` anchor. Empty means empty - this is what the next
|
|
164
|
+
pass trusts.
|
|
165
|
+
|
|
166
|
+
## Checks
|
|
167
|
+
|
|
168
|
+
`zen check`, each `scripts/_setup.sh` step, and the §9 groups: pass or fail.
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Write the baseline last
|
|
172
|
+
|
|
173
|
+
The one rule that must not be got wrong.
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
.github/skills/zen-spec-sync/scripts/snapshot.sh commit
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
runs **after** `zen check` passes, **after** `scripts/_setup.sh` has completed,
|
|
180
|
+
and **after** the history file is on disk. It takes the newest entry in
|
|
181
|
+
`.spec-sync/history/` - the stamps sort chronologically by name - records it in
|
|
182
|
+
`applied.txt`, and writes `manifest.txt` last of all. It refuses to run when
|
|
183
|
+
there is no history file, and when the newest one is the entry `applied.txt`
|
|
184
|
+
already names: that means this pass wrote no entry of its own.
|
|
185
|
+
|
|
186
|
+
A pass that moves the baseline early and then fails records a specification as
|
|
187
|
+
applied that was never applied - and the next pass diffs against it, sees
|
|
188
|
+
nothing, and skips the work for good. Failing with the baseline unmoved costs
|
|
189
|
+
one repeated pass; failing with it moved costs a requirement that is never
|
|
190
|
+
implemented and that nothing will report. Say in the chat which history file was
|
|
191
|
+
written and that the baseline moved.
|
|
192
|
+
|
|
193
|
+
## The first pass on a project that has no baseline
|
|
194
|
+
|
|
195
|
+
`mode: full`, so run the prompt as written, end to end. Then write the history
|
|
196
|
+
file and commit the baseline exactly as any other pass does - a project only
|
|
197
|
+
becomes incremental by having completed one full pass.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# The committed record of what the last completed /sync-with-spec pass applied.
|
|
4
|
+
#
|
|
5
|
+
# snapshot.sh status full | incremental | tampered, and what differs
|
|
6
|
+
# snapshot.sh diff the difference the next pass has to work
|
|
7
|
+
# snapshot.sh commit move the baseline on, once the pass has finished
|
|
8
|
+
#
|
|
9
|
+
# Run it from anywhere; it finds the project root from its own location, which
|
|
10
|
+
# `zen init` and `zen open` fix. The baseline is written last and only last —
|
|
11
|
+
# see .github/skills/zen-spec-sync/SKILL.md for why that is the whole point.
|
|
12
|
+
|
|
13
|
+
set -eu
|
|
14
|
+
cd "$(dirname "$0")/../../../.."
|
|
15
|
+
|
|
16
|
+
STATE=.spec-sync
|
|
17
|
+
BASE="$STATE/baseline"
|
|
18
|
+
HISTORY="$STATE/history"
|
|
19
|
+
MANIFEST="$BASE/manifest.txt"
|
|
20
|
+
SCRATCH=.tmp/spec-sync
|
|
21
|
+
|
|
22
|
+
# The intent: what the project is meant to be, and what was asked back about it.
|
|
23
|
+
INTENT="SPECIFICATION.md SPECIFICATION-FEEDBACK.md"
|
|
24
|
+
|
|
25
|
+
# The implementation: the wiring, and every instruction, prompt and skill file
|
|
26
|
+
# under agents/. One of those edited since the last pass is a change the pass
|
|
27
|
+
# has to account for, the same as a changed requirement is.
|
|
28
|
+
WIRING=agents.yaml
|
|
29
|
+
TREE=agents
|
|
30
|
+
|
|
31
|
+
# The history entry this baseline belongs to, hashed like the rest of it.
|
|
32
|
+
APPLIED=applied.txt
|
|
33
|
+
|
|
34
|
+
if [ ! -f "$WIRING" ] && [ ! -f SPECIFICATION.md ]; then
|
|
35
|
+
echo "snapshot.sh: no agents.yaml or SPECIFICATION.md in $PWD" >&2
|
|
36
|
+
exit 2
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Git cannot carry an empty directory, so these are made rather than expected.
|
|
40
|
+
mkdir -p "$BASE" "$HISTORY"
|
|
41
|
+
|
|
42
|
+
# Every path the record covers, on the working-tree side.
|
|
43
|
+
present() {
|
|
44
|
+
{
|
|
45
|
+
for file in $INTENT $WIRING; do
|
|
46
|
+
if [ -f "$file" ]; then echo "$file"; fi
|
|
47
|
+
done
|
|
48
|
+
if [ -d "$TREE" ]; then find "$TREE" -type f ! -name .DS_Store; fi
|
|
49
|
+
} | sort -u
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# The same, from both sides, so a file added or deleted since the last pass is
|
|
53
|
+
# a difference rather than a path nobody walks.
|
|
54
|
+
scope() {
|
|
55
|
+
{
|
|
56
|
+
present
|
|
57
|
+
for file in $INTENT $WIRING; do
|
|
58
|
+
if [ -f "$BASE/$file" ]; then echo "$file"; fi
|
|
59
|
+
done
|
|
60
|
+
if [ -d "$BASE/$TREE" ]; then
|
|
61
|
+
(cd "$BASE" && find "$TREE" -type f ! -name .DS_Store)
|
|
62
|
+
fi
|
|
63
|
+
} | sort -u
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# A file that is not there counts as an empty one, so adding or deleting it
|
|
67
|
+
# reads as a difference rather than as an error.
|
|
68
|
+
side() {
|
|
69
|
+
if [ -f "$1" ]; then echo "$1"; else echo /dev/null; fi
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
differs() {
|
|
73
|
+
! cmp -s "$(side "$BASE/$1")" "$(side "$1")"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Which baseline files no longer hash to what the manifest says they do.
|
|
77
|
+
# `FAILED` covers a changed file; `FAILED open or read` covers a missing one.
|
|
78
|
+
broken() {
|
|
79
|
+
(cd "$BASE" && shasum -a 256 -c manifest.txt 2>/dev/null || true) |
|
|
80
|
+
sed -n 's/: FAILED.*$//p'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# The stamps sort chronologically by name, which is the point of the format —
|
|
84
|
+
# an mtime would make a fast pass look like no pass at all.
|
|
85
|
+
newest() {
|
|
86
|
+
ls "$HISTORY"/*.md 2>/dev/null | sort | tail -1 || true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
status() {
|
|
90
|
+
if [ ! -f "$MANIFEST" ]; then
|
|
91
|
+
echo "mode: full"
|
|
92
|
+
echo "reason: no baseline, so no pass has ever completed here"
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
failed="$(broken)"
|
|
96
|
+
if [ -n "$failed" ]; then
|
|
97
|
+
echo "mode: tampered"
|
|
98
|
+
while IFS= read -r file; do
|
|
99
|
+
if [ -n "$file" ]; then echo "unsound: $BASE/$file"; fi
|
|
100
|
+
done <<EOF
|
|
101
|
+
$failed
|
|
102
|
+
EOF
|
|
103
|
+
echo "reason: the baseline does not match its own manifest"
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
echo "mode: incremental"
|
|
107
|
+
if [ -f "$BASE/$APPLIED" ]; then
|
|
108
|
+
echo "applied: $(cat "$BASE/$APPLIED")"
|
|
109
|
+
fi
|
|
110
|
+
while IFS= read -r file; do
|
|
111
|
+
if [ -n "$file" ] && differs "$file"; then
|
|
112
|
+
echo "changed: $file"
|
|
113
|
+
fi
|
|
114
|
+
done <<EOF
|
|
115
|
+
$(scope)
|
|
116
|
+
EOF
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
show_diff() {
|
|
120
|
+
while IFS= read -r file; do
|
|
121
|
+
if [ -n "$file" ] && differs "$file"; then
|
|
122
|
+
diff -u -L "baseline/$file" -L "$file" \
|
|
123
|
+
"$(side "$BASE/$file")" "$(side "$file")" || true
|
|
124
|
+
fi
|
|
125
|
+
done <<EOF
|
|
126
|
+
$(scope)
|
|
127
|
+
EOF
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
commit() {
|
|
131
|
+
entry="$(newest)"
|
|
132
|
+
if [ -z "$entry" ]; then
|
|
133
|
+
echo "snapshot.sh: write $HISTORY/<stamp>.md before moving the baseline" >&2
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
stamp="$(basename "$entry" .md)"
|
|
137
|
+
if [ -f "$BASE/$APPLIED" ] && [ "$stamp" = "$(cat "$BASE/$APPLIED")" ]; then
|
|
138
|
+
echo "snapshot.sh: the baseline already names $stamp — this pass wrote no entry" >&2
|
|
139
|
+
exit 1
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
staged="$(present)"
|
|
143
|
+
rm -rf "$SCRATCH"
|
|
144
|
+
mkdir -p "$SCRATCH"
|
|
145
|
+
echo "$stamp" >"$SCRATCH/$APPLIED"
|
|
146
|
+
: >"$SCRATCH/manifest.txt"
|
|
147
|
+
while IFS= read -r file; do
|
|
148
|
+
if [ -n "$file" ]; then
|
|
149
|
+
mkdir -p "$SCRATCH/$(dirname "$file")"
|
|
150
|
+
cp "$file" "$SCRATCH/$file"
|
|
151
|
+
fi
|
|
152
|
+
done <<EOF
|
|
153
|
+
$staged
|
|
154
|
+
EOF
|
|
155
|
+
while IFS= read -r file; do
|
|
156
|
+
if [ -n "$file" ]; then
|
|
157
|
+
(cd "$SCRATCH" && shasum -a 256 "$file") >>"$SCRATCH/manifest.txt"
|
|
158
|
+
fi
|
|
159
|
+
done <<EOF
|
|
160
|
+
$APPLIED
|
|
161
|
+
$staged
|
|
162
|
+
EOF
|
|
163
|
+
|
|
164
|
+
while IFS= read -r file; do
|
|
165
|
+
if [ -n "$file" ]; then
|
|
166
|
+
mkdir -p "$BASE/$(dirname "$file")"
|
|
167
|
+
mv "$SCRATCH/$file" "$BASE/$file"
|
|
168
|
+
fi
|
|
169
|
+
done <<EOF
|
|
170
|
+
$APPLIED
|
|
171
|
+
$staged
|
|
172
|
+
EOF
|
|
173
|
+
# Everything in scope that still exists has just been staged, so a baseline
|
|
174
|
+
# file with no working copy is one this pass deleted.
|
|
175
|
+
while IFS= read -r file; do
|
|
176
|
+
if [ -n "$file" ] && [ "$file" != "$APPLIED" ] && [ ! -f "$file" ]; then
|
|
177
|
+
rm -f "$BASE/$file"
|
|
178
|
+
fi
|
|
179
|
+
done <<EOF
|
|
180
|
+
$(cd "$BASE" && find . -type f ! -name manifest.txt | sed 's|^\./||')
|
|
181
|
+
EOF
|
|
182
|
+
find "$BASE" -mindepth 1 -type d -empty -delete
|
|
183
|
+
# Last, so an interrupted commit leaves a baseline that reports itself as
|
|
184
|
+
# tampered rather than one that claims a pass it never finished.
|
|
185
|
+
mv "$SCRATCH/manifest.txt" "$MANIFEST"
|
|
186
|
+
rm -rf "$SCRATCH"
|
|
187
|
+
|
|
188
|
+
echo "applied: $stamp"
|
|
189
|
+
echo "recorded: $(printf '%s\n' "$staged" | sed '/^$/d' | wc -l | tr -d ' ') files"
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
case "${1:-status}" in
|
|
193
|
+
status) status ;;
|
|
194
|
+
diff) show_diff ;;
|
|
195
|
+
commit) commit ;;
|
|
196
|
+
*)
|
|
197
|
+
echo "usage: snapshot.sh [status|diff|commit]" >&2
|
|
198
|
+
exit 2
|
|
199
|
+
;;
|
|
200
|
+
esac
|
|
@@ -1,11 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"chat.useNestedAgentsMdFiles": false,
|
|
3
3
|
"chat.tools.terminal.enableAutoApprove": true,
|
|
4
|
-
"chat.tools.terminal.ignoreDefaultAutoApproveRules": true,
|
|
5
4
|
"chat.tools.edits.autoApprove": true,
|
|
6
5
|
"chat.tools.terminal.autoApprove": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
6
|
+
"awk": true,
|
|
7
|
+
"basename": true,
|
|
8
|
+
"cat": true,
|
|
9
|
+
"cd": true,
|
|
10
|
+
"comm": true,
|
|
11
|
+
"cut": true,
|
|
12
|
+
"date": true,
|
|
13
|
+
"diff": true,
|
|
14
|
+
"dirname": true,
|
|
15
|
+
"du": true,
|
|
16
|
+
"echo": true,
|
|
17
|
+
"env": true,
|
|
18
|
+
"file": true,
|
|
19
|
+
"find": true,
|
|
20
|
+
"grep": true,
|
|
21
|
+
"head": true,
|
|
22
|
+
"jq": true,
|
|
23
|
+
"ls": true,
|
|
24
|
+
"nl": true,
|
|
25
|
+
"printf": true,
|
|
26
|
+
"pwd": true,
|
|
27
|
+
"realpath": true,
|
|
28
|
+
"rg": true,
|
|
29
|
+
"sed": true,
|
|
30
|
+
"sort": true,
|
|
31
|
+
"stat": true,
|
|
32
|
+
"tail": true,
|
|
33
|
+
"tr": true,
|
|
34
|
+
"tree": true,
|
|
35
|
+
"uniq": true,
|
|
36
|
+
"wc": true,
|
|
37
|
+
"which": true,
|
|
38
|
+
"tsc": true,
|
|
39
|
+
"zen": true,
|
|
40
|
+
"zn": true,
|
|
41
|
+
"zenera": true,
|
|
42
|
+
"/^git (branch|diff|log|show|status)\\b/": true,
|
|
43
|
+
"/^npm (test|run (build|format:check|lint|test|typecheck))\\b/": true,
|
|
44
|
+
"/\\bfind\\b[^|;]*\\s-(exec|execdir|ok|okdir|delete)\\b/": false,
|
|
45
|
+
"/\\bsed\\b[^|;]*\\s-[A-Za-z]*i/": false,
|
|
46
|
+
"/\\bawk\\b[^|;]*\\b(system|print\\s*>|printf\\s*>)/": false,
|
|
47
|
+
"/^\\.?\\/?scripts\\/[A-Za-z0-9_.-]+\\.sh\\b/": true,
|
|
48
|
+
"/^\\.?\\/?\\.github\\/skills\\/[A-Za-z0-9_-]+\\/scripts\\/[A-Za-z0-9_.-]+\\.sh\\b/": true,
|
|
49
|
+
"podman": true
|
|
10
50
|
}
|
|
11
51
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# What the agents work out, kept with the project rather than in the session
|
|
2
|
+
# that found it out. `agents/memory-instructions.md` is what they read about
|
|
3
|
+
# using it, and `zen memory` is how you look at it from the outside.
|
|
4
|
+
#
|
|
5
|
+
# Recall ranks by term overlap here, which finds a memory phrased the way the
|
|
6
|
+
# question was and misses the rest. This project's provider publishes no
|
|
7
|
+
# embeddings API; add one that does and point the store at it to rank by
|
|
8
|
+
# meaning instead:
|
|
9
|
+
#
|
|
10
|
+
# memory:
|
|
11
|
+
# embedding: main
|
|
12
|
+
#
|
|
13
|
+
# embeddings:
|
|
14
|
+
# main: openai:text-embedding-3-small
|
|
15
|
+
memory:
|
|
16
|
+
dir: memory
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# What the agents work out, kept with the project rather than in the session
|
|
2
|
+
# that found it out. `agents/memory-instructions.md` is what they read about
|
|
3
|
+
# using it, and `zen memory` is how you look at it from the outside.
|
|
4
|
+
memory:
|
|
5
|
+
dir: memory
|
|
6
|
+
# Recall by meaning rather than by the words a question happened to reuse.
|
|
7
|
+
# The width is learned from the first response, so swapping this model
|
|
8
|
+
# later is refused rather than quietly mixing two vector spaces.
|
|
9
|
+
embedding: main
|
|
10
|
+
|
|
11
|
+
embeddings:
|
|
12
|
+
main: {{embedding}}
|
|
@@ -18,18 +18,25 @@ something in it — and report what was done in enough detail to be checked.
|
|
|
18
18
|
- `default` — the entry point and the only agent; nobody to hand work to.
|
|
19
19
|
- Establishes what it is looking at before it acts: reads a file before
|
|
20
20
|
changing it, runs a command rather than predicting its output.
|
|
21
|
+
- Remembers what it worked out, so a later session starts from it rather
|
|
22
|
+
than from nothing.
|
|
21
23
|
|
|
22
24
|
## Tools and boundaries
|
|
23
25
|
|
|
24
26
|
- Files — read, search, create, edit, move, delete. The workspace is the only
|
|
25
27
|
writable thing, symlinks included.
|
|
26
28
|
- Shell — commands run in the container, on that same workspace.
|
|
29
|
+
- Memory — a graph of what earlier runs established, recalled before new
|
|
30
|
+
questions and added to deliberately. What goes in it, and what does not, is
|
|
31
|
+
`agents/memory-instructions.md`.
|
|
27
32
|
- `assets/` is reference material, and read-only.
|
|
28
33
|
{{web}}
|
|
29
34
|
|
|
30
35
|
## Environment
|
|
31
36
|
|
|
32
37
|
- Model: `{{modelRef}}`, one for the whole project.
|
|
38
|
+
- Memory: `memory/`, kept with the project and committed except for its
|
|
39
|
+
vectors, which are derived.
|
|
33
40
|
- Sandbox: built from `sandbox/Dockerfile`, persists between runs, and can
|
|
34
41
|
reach the network. What the work always needs belongs in the image, not in an
|
|
35
42
|
instruction that reinstalls it on every run.
|