hafez 1.4.0 → 2.0.0
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 +26 -33
- package/dist/apply.d.ts +5 -2
- package/dist/apply.js +225 -114
- package/dist/batch-ops.d.ts +89 -26
- package/dist/batch-ops.js +176 -18
- package/dist/cli/args.d.ts +43 -5
- package/dist/cli/args.js +69 -8
- package/dist/cli/batch-errors.js +19 -11
- package/dist/cli/block-hook.d.ts +9 -0
- package/dist/cli/block-hook.js +90 -0
- package/dist/cli/commands-schema.js +2 -0
- package/dist/cli/commands.d.ts +4 -3
- package/dist/cli/commands.js +300 -99
- package/dist/cli/format.d.ts +7 -3
- package/dist/cli/format.js +27 -10
- package/dist/cli/index.d.ts +18 -4
- package/dist/cli/index.js +72 -30
- package/dist/cli/install.d.ts +53 -0
- package/dist/cli/install.js +297 -0
- package/dist/cli/onboard.d.ts +15 -2
- package/dist/cli/onboard.js +265 -90
- package/dist/cli/resolve-vault.d.ts +11 -4
- package/dist/cli/resolve-vault.js +13 -11
- package/dist/cli/schema-introspect.js +4 -3
- package/dist/cli/upgrade.d.ts +13 -4
- package/dist/cli/upgrade.js +90 -44
- package/dist/closest.d.ts +8 -0
- package/dist/closest.js +65 -0
- package/dist/contracts.d.ts +17 -1
- package/dist/contracts.js +44 -0
- package/dist/db.d.ts +15 -2
- package/dist/db.js +69 -48
- package/dist/digest.d.ts +6 -4
- package/dist/digest.js +28 -17
- package/dist/document.d.ts +41 -3
- package/dist/document.js +218 -62
- package/dist/export-okf.d.ts +13 -0
- package/dist/export-okf.js +11 -7
- package/dist/harness.d.ts +58 -0
- package/dist/harness.js +50 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +94 -104
- package/dist/merge.js +7 -5
- package/dist/near-match.d.ts +9 -0
- package/dist/near-match.js +34 -0
- package/dist/stale.d.ts +41 -0
- package/dist/stale.js +43 -0
- package/dist/stopwords.d.ts +6 -0
- package/dist/stopwords.js +25 -0
- package/dist/surfaces/items.d.ts +366 -0
- package/dist/surfaces/items.js +523 -0
- package/dist/surfaces/surfaces.d.ts +32 -0
- package/dist/surfaces/surfaces.js +230 -0
- package/dist/types.d.ts +45 -16
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +109 -0
- package/dist/vault.js +1 -1
- package/package.json +4 -3
- package/skill/hafez/SKILL.md +68 -0
- package/skill/hafez-save/SKILL.md +29 -0
- package/dist/cli/help-agent.d.ts +0 -1
- package/dist/cli/help-agent.js +0 -210
- package/skill/SKILL.md +0 -137
package/dist/cli/help-agent.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
// src/cli/help-agent.ts
|
|
2
|
-
//
|
|
3
|
-
// `hafez help --agent` — the prompt-gate. Plain text, no ANSI, no markdown
|
|
4
|
-
// decoration. Designed for an LLM with zero Hafez context to read once per
|
|
5
|
-
// session and then produce valid batch JSON on first try.
|
|
6
|
-
//
|
|
7
|
-
// Hand-written sections: Preamble + Hard rules (prose, free-form).
|
|
8
|
-
// Generated sections (wrapped in <!-- SECTION:name --> markers so tests can
|
|
9
|
-
// parse them): op-catalog, critical-enums, batch-examples, digest.
|
|
10
|
-
//
|
|
11
|
-
// Generated sections derive from the Op Spec table (batch-ops.ts via
|
|
12
|
-
// schema-introspect.ts), so they cannot drift from the Zod schemas. Do NOT
|
|
13
|
-
// enumerate enum values inside the preamble or hard rules — those come from
|
|
14
|
-
// schema-introspect.ts.
|
|
15
|
-
import { listOps, getEnumValues, getOpExample, getOpSchema } from './schema-introspect.js';
|
|
16
|
-
const PREAMBLE = `# Hafez — Agent Prompt Gate
|
|
17
|
-
|
|
18
|
-
Hafez is your persistent knowledge graph for projects, entities, captures,
|
|
19
|
-
knowledge notes, and session notes. The \`hafez\` CLI is the ONLY supported
|
|
20
|
-
write path into the vault — never edit files under \`entities/\`, \`knowledge/\`,
|
|
21
|
-
or \`sessions/\` by hand. Every mutation goes through the CLI so the git history,
|
|
22
|
-
SQLite index, and link integrity stay in sync.
|
|
23
|
-
|
|
24
|
-
Mental model:
|
|
25
|
-
|
|
26
|
-
- An \`entity\` is tracked work with substance — a bug, a feature, a decision.
|
|
27
|
-
Entities have status, can have children, and carry a session log. The three
|
|
28
|
-
entity types are \`capture\` (raw inbox), \`entity\` (standalone tracked item),
|
|
29
|
-
and \`project\` (scoped effort with deliverables, can have child entities).
|
|
30
|
-
- A \`knowledge\` note is an insight or a plan. Insights distil a pattern or
|
|
31
|
-
principle and mature through added evidence. Plans are structured steps
|
|
32
|
-
toward a goal. Knowledge is searchable, linkable, and timestamped.
|
|
33
|
-
- A \`session\` note records what happened in a working session and references
|
|
34
|
-
the entities it touched. Session notes are created via \`create/session\`
|
|
35
|
-
and are not indexed in SQLite — they are for narrative, not query.
|
|
36
|
-
- \`capture\` is the quick-inbox path: grab a link or thought, no structure.
|
|
37
|
-
A capture can later be promoted to entity, project, or knowledge.
|
|
38
|
-
- Entities relate to each other through links: \`parent\` (hierarchy) or
|
|
39
|
-
\`related\` (soft reference). Use \`link\` / \`unlink\` ops to manage them.
|
|
40
|
-
- Every meaningful update should include a session_log entry so the history
|
|
41
|
-
of the work is preserved. Sessions are how future-you reconstructs why a
|
|
42
|
-
decision was made.
|
|
43
|
-
|
|
44
|
-
Single source of truth: the exact field names, enum values, and op signatures
|
|
45
|
-
below are generated at runtime from the live Zod schema. If a field is not
|
|
46
|
-
listed here, it does not exist — do not invent fields.
|
|
47
|
-
|
|
48
|
-
## Hard rules (read before writing a batch)
|
|
49
|
-
|
|
50
|
-
- ALWAYS batch 2+ mutations into one \`hafez batch\` call. Many single-op
|
|
51
|
-
calls fragment git history and clobber the vault index's atomicity guarantee.
|
|
52
|
-
- NEVER invent field names. If a field is not in \`hafez schema <op>\`, it
|
|
53
|
-
does not exist. Unknown fields are rejected by the validator with a pointer
|
|
54
|
-
to the right schema.
|
|
55
|
-
- NEVER write to vault files directly. Always go through the CLI.
|
|
56
|
-
- STRONGLY PREFERRED: include a \`session_log\` on update ops that reflect
|
|
57
|
-
meaningful work. The validator does not enforce this — omitting it is a
|
|
58
|
-
code smell, not a hard error.
|
|
59
|
-
- When in doubt, run \`hafez schema <op>\` before writing a batch. Cheap,
|
|
60
|
-
deterministic, no side effects.
|
|
61
|
-
- Use \`hafez batch --dry-run\` to validate a payload without touching the
|
|
62
|
-
vault. Same validation as a real apply, zero filesystem mutation.
|
|
63
|
-
|
|
64
|
-
## Slug contract (create-then-link in one batch)
|
|
65
|
-
|
|
66
|
-
Slugs are deterministic: slug = slugify(name) — lowercase, non-alphanumeric
|
|
67
|
-
runs collapse to single \`-\`, edges trimmed (\`"Auth Refactor!"\` →
|
|
68
|
-
\`auth-refactor\`). A batch executes sequentially, so later ops may reference
|
|
69
|
-
the slugs of earlier creates in the same batch (\`create\` then \`link\`/\`update\`
|
|
70
|
-
works in one payload). Collisions hard-error — a wrong slug prediction can
|
|
71
|
-
never silently hit an existing document. To verify predictions first:
|
|
72
|
-
\`hafez batch --dry-run\` reports the derived slug for every create op.
|
|
73
|
-
`;
|
|
74
|
-
function renderOpCatalog() {
|
|
75
|
-
const lines = ['<!-- SECTION:op-catalog -->'];
|
|
76
|
-
lines.push('Ops (run `hafez schema <name>` for the full field list):');
|
|
77
|
-
for (const op of listOps()) {
|
|
78
|
-
const desc = op.description ? ` — ${op.description}` : '';
|
|
79
|
-
lines.push(` ${op.name}${desc}`);
|
|
80
|
-
lines.push(` → hafez schema ${op.name}`);
|
|
81
|
-
}
|
|
82
|
-
lines.push('<!-- /SECTION:op-catalog -->');
|
|
83
|
-
return lines.join('\n');
|
|
84
|
-
}
|
|
85
|
-
function renderCriticalEnums() {
|
|
86
|
-
const entries = [
|
|
87
|
-
['update.fields.status', 'update.fields.status'],
|
|
88
|
-
['update.fields.session_log.type', 'update.fields.session_log.type'],
|
|
89
|
-
['update.fields.confidence', 'update.fields.confidence'],
|
|
90
|
-
['create-entity.fields.type', 'create-entity.fields.type'],
|
|
91
|
-
['create-knowledge.fields.subtype', 'create-knowledge.fields.subtype'],
|
|
92
|
-
['link.relation', 'link.relation'],
|
|
93
|
-
];
|
|
94
|
-
const lines = ['<!-- SECTION:critical-enums -->'];
|
|
95
|
-
lines.push('Critical enums (value lists — values are authoritative):');
|
|
96
|
-
for (const [label, path] of entries) {
|
|
97
|
-
const values = getEnumValues(path);
|
|
98
|
-
if (!values)
|
|
99
|
-
continue;
|
|
100
|
-
lines.push(` ${label} = [${values.join(', ')}]`);
|
|
101
|
-
}
|
|
102
|
-
lines.push('<!-- /SECTION:critical-enums -->');
|
|
103
|
-
return lines.join('\n');
|
|
104
|
-
}
|
|
105
|
-
function renderBatchExamples() {
|
|
106
|
-
const lines = ['<!-- SECTION:batch-examples -->'];
|
|
107
|
-
lines.push('Batch JSON shape — wrap one or more ops in an array:');
|
|
108
|
-
lines.push('');
|
|
109
|
-
lines.push(' echo \'[...]\' | hafez batch # POSIX shells');
|
|
110
|
-
lines.push(' hafez batch --file payload.json # any platform (PowerShell 5.1 cannot pipe stdin)');
|
|
111
|
-
lines.push('');
|
|
112
|
-
lines.push('Minimal example per op (canonical field names; aliased keys stripped):');
|
|
113
|
-
lines.push('');
|
|
114
|
-
for (const op of listOps()) {
|
|
115
|
-
const example = getOpExample(op.name);
|
|
116
|
-
if (!example)
|
|
117
|
-
continue;
|
|
118
|
-
lines.push(`# ${op.name}`);
|
|
119
|
-
lines.push(JSON.stringify(example, null, 2));
|
|
120
|
-
lines.push('');
|
|
121
|
-
}
|
|
122
|
-
lines.push('<!-- /SECTION:batch-examples -->');
|
|
123
|
-
return lines.join('\n');
|
|
124
|
-
}
|
|
125
|
-
function renderDigestSection() {
|
|
126
|
-
const lines = ['<!-- SECTION:digest -->'];
|
|
127
|
-
lines.push('Session digest — turn end-of-session context into a batch payload.');
|
|
128
|
-
lines.push('NOT a batch op: it reads its own JSON and prints batch JSON.');
|
|
129
|
-
lines.push('');
|
|
130
|
-
lines.push(" echo '<digest-json>' | hafez digest | hafez batch");
|
|
131
|
-
lines.push(' # No stdin pipes (PowerShell 5.1): hafez digest --file summary.json > ops.json,');
|
|
132
|
-
lines.push(' # then hafez batch --file ops.json');
|
|
133
|
-
lines.push('');
|
|
134
|
-
lines.push('Input fields (`hafez schema digest` for the full schema):');
|
|
135
|
-
const schema = getOpSchema('digest');
|
|
136
|
-
for (const [name, field] of Object.entries(schema?.fields ?? {})) {
|
|
137
|
-
const req = field.optional ? 'optional' : 'required';
|
|
138
|
-
const desc = field.description ? ` — ${field.description}` : '';
|
|
139
|
-
lines.push(` ${name} (${req})${desc}`);
|
|
140
|
-
}
|
|
141
|
-
lines.push('');
|
|
142
|
-
lines.push('Example input:');
|
|
143
|
-
lines.push(JSON.stringify(getOpExample('digest'), null, 2));
|
|
144
|
-
lines.push('<!-- /SECTION:digest -->');
|
|
145
|
-
return lines.join('\n');
|
|
146
|
-
}
|
|
147
|
-
const UPGRADE_SECTION = `## Upgrading hafez itself
|
|
148
|
-
|
|
149
|
-
\`hafez upgrade\` — checks whether the installed hafez is outdated and prints
|
|
150
|
-
the steps. It NEVER installs: the steps install code and overwrite the user's
|
|
151
|
-
agent instructions, so show them verbatim, get an explicit yes, and only then
|
|
152
|
-
run them. Re-run to verify. Every check outcome exits 0 — being outdated is a
|
|
153
|
-
report, not a gate — so read the output, not the exit code. A non-zero exit
|
|
154
|
-
means the invocation was wrong (exit 2, \`upgrade\` takes no arguments), never
|
|
155
|
-
that a check failed. Not to be confused with \`hafez update\`, which updates
|
|
156
|
-
an entity.
|
|
157
|
-
|
|
158
|
-
\`--json\` keys: current, latest (null when unverified), comparison
|
|
159
|
-
(behind|equal|ahead|unknown), registryReachable, npmMajor, install
|
|
160
|
-
(registry|dev-checkout), skill (present|absent), skillSource, skillTarget,
|
|
161
|
-
alternativeInstall, steps. \`steps\` is the action list in execution order;
|
|
162
|
-
\`kind: 'command'\` entries are executable, \`kind: 'action'\` entries are
|
|
163
|
-
prose — do not shell out to them. \`alternativeInstall\` is mutually exclusive
|
|
164
|
-
with the install command in \`steps\`; never run both.
|
|
165
|
-
|
|
166
|
-
Checks the npm registry only (override with \`HAFEZ_REGISTRY_URL\`), so a
|
|
167
|
-
just-cut GitHub Release can read as \`equal\` until it is published.
|
|
168
|
-
|
|
169
|
-
`;
|
|
170
|
-
const FOOTER = `## Lookup pointers
|
|
171
|
-
|
|
172
|
-
- \`hafez schema\` — list all ops
|
|
173
|
-
- \`hafez schema <op>\` — full JSON schema for one op
|
|
174
|
-
- \`hafez schema <op> --examples\` — working JSON examples per enum value
|
|
175
|
-
- \`hafez batch --dry-run\` — validate without touching the vault
|
|
176
|
-
`;
|
|
177
|
-
/**
|
|
178
|
-
* Render the full prompt-gate output. Token budget: 1500-3000 tokens when
|
|
179
|
-
* measured as char/3.5. The drift test in tests/cli-help-agent.test.ts
|
|
180
|
-
* enforces the ceiling.
|
|
181
|
-
*/
|
|
182
|
-
const EXIT_CODES_SECTION = `## Exit codes
|
|
183
|
-
|
|
184
|
-
| code | meaning |
|
|
185
|
-
|------|---------|
|
|
186
|
-
| 0 | success |
|
|
187
|
-
| 1 | not found / unknown error |
|
|
188
|
-
| 2 | validation or usage error (bad flags, bad payload, core rejected the write) |
|
|
189
|
-
| 3 | git push failed — the write IS committed locally |
|
|
190
|
-
| 4 | write succeeded but the auto-sync after it failed |
|
|
191
|
-
| 5 | slug already exists |
|
|
192
|
-
| 6 | git commit failed — nothing was written to history |
|
|
193
|
-
| 7 | vault locked by another hafez process — retry when it finishes |`;
|
|
194
|
-
export function renderAgentHelp() {
|
|
195
|
-
return [
|
|
196
|
-
PREAMBLE,
|
|
197
|
-
renderOpCatalog(),
|
|
198
|
-
'',
|
|
199
|
-
renderCriticalEnums(),
|
|
200
|
-
'',
|
|
201
|
-
renderBatchExamples(),
|
|
202
|
-
'',
|
|
203
|
-
renderDigestSection(),
|
|
204
|
-
'',
|
|
205
|
-
EXIT_CODES_SECTION,
|
|
206
|
-
'',
|
|
207
|
-
UPGRADE_SECTION,
|
|
208
|
-
FOOTER,
|
|
209
|
-
].join('\n');
|
|
210
|
-
}
|
package/skill/SKILL.md
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hafez
|
|
3
|
-
description: >
|
|
4
|
-
Hafez vault — personal knowledge base of captures, entities, projects, and
|
|
5
|
-
knowledge notes (subtypes: insight, plan). Use when the user mentions Hafez
|
|
6
|
-
(or types /hafez), asks what's in the vault ("what am I working on?", "what's
|
|
7
|
-
stale?"), wants to create or update an entity or knowledge note, at session
|
|
8
|
-
start to load context for an identifiable topic, at session end to record a
|
|
9
|
-
digest, when a casually mentioned project name might be a vault entity, or
|
|
10
|
-
when the user wants to update the tool itself ("update hafez", "upgrade
|
|
11
|
-
hafez", "is hafez outdated").
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# Hafez — Vault Operations
|
|
15
|
-
|
|
16
|
-
All operations go through the `hafez` CLI — it owns reads, writes, validation, and git. Vault files are never edited directly.
|
|
17
|
-
|
|
18
|
-
If `hafez` is not installed, or `hafez init` reports no vault, stop and follow the setup in the project README (github.com/shrvn-r/hafez) before using this skill. If the vault exists but is empty, run `hafez onboard` and follow what it prints.
|
|
19
|
-
|
|
20
|
-
## Situation → Command
|
|
21
|
-
|
|
22
|
-
`hafez help --agent` is the complete, always-current API reference (all commands, flags, batch schema, enum values). This table covers the common situations; consult `help --agent` for anything beyond it.
|
|
23
|
-
|
|
24
|
-
| Situation | Command |
|
|
25
|
-
|-----------|---------|
|
|
26
|
-
| Read an entity (incl. a project the user mentions) | `hafez read <slug>` |
|
|
27
|
-
| "What am I working on?" / active projects | `hafez query --filter active` |
|
|
28
|
-
| Stale items | `hafez query --filter stale` |
|
|
29
|
-
| Full-text search | `hafez search "<terms>"` (`--kind knowledge` to narrow) |
|
|
30
|
-
| List inbox captures | `hafez query --filter capture` |
|
|
31
|
-
| Children of a project | `hafez query --parent <slug>` |
|
|
32
|
-
| Related entities/knowledge | `hafez query --related-to <slug>` |
|
|
33
|
-
| New workstream | `hafez create entity "<name>" --type project [--brief "context"] [--add-action "task"]` (full flag list in `help --agent`; `--purpose` is project-only) |
|
|
34
|
-
| New plan | `hafez create knowledge "<name>" --subtype plan [--status active\|done]` (plans have Goal/Steps/Dependencies — no `--synthesis`; `--status` is plan-only) |
|
|
35
|
-
| Quick thought, no classification yet | `hafez capture "<name>"` |
|
|
36
|
-
| Promote | `hafez promote <slug> <target>` (capture→entity/project/knowledge, entity→project) |
|
|
37
|
-
| Set handoff context / summary / URI | `hafez update <slug> --brief "text"` / `--description "summary"` / `--resource "https://..."` |
|
|
38
|
-
| Add / complete a next action | `hafez update <slug> --add-action "task"` / `--complete-action "substring"` |
|
|
39
|
-
| Significant milestone (deployed, merged, bug found) | `hafez update <slug> --log "progress: summary" --agent claude` |
|
|
40
|
-
| Link entities | `hafez link <slug> <target> --relation related` (or `parent` when a thread belongs to a project) |
|
|
41
|
-
| Revise a knowledge insight | `hafez update <slug> --synthesis "text"` (`--insight` is an alias) |
|
|
42
|
-
| Back knowledge with evidence / a source | `hafez update <slug> --add-evidence "text"` / `--add-source "url"` |
|
|
43
|
-
| Digest session → batch | `echo '{...}' \| hafez digest \| hafez batch` (no pipes? `--file` on both) |
|
|
44
|
-
|
|
45
|
-
## Session Start — Context Loading
|
|
46
|
-
|
|
47
|
-
Load context only when a specific entity slug or topic is identifiable from the user's first message:
|
|
48
|
-
|
|
49
|
-
1. Run `hafez read <slug>` to load entity context
|
|
50
|
-
2. If the entity has a `parent`, also read the parent at frontmatter depth: `hafez read <parent> --depth frontmatter`
|
|
51
|
-
3. Search for related knowledge (`hafez search "<topic>" --kind knowledge`) and read any relevant note: `hafez read <knowledge-slug>`
|
|
52
|
-
4. Present a brief context line: "Loaded context for {name} (status: {status}, next: {next_action})"
|
|
53
|
-
|
|
54
|
-
## Search-First Ingest
|
|
55
|
-
|
|
56
|
-
A new knowledge note is a last resort. When an insight emerges or a source (document, conversation, research) is being processed:
|
|
57
|
-
|
|
58
|
-
1. Search for existing coverage: `hafez search "<topic>"`, narrowing with `--kind knowledge`.
|
|
59
|
-
2. A note covers the topic → update it: `--synthesis` to revise the main insight, `--add-evidence` to append support, `--add-source` to cite.
|
|
60
|
-
3. Nothing covers it → create it linked from birth (`hafez create knowledge "<name>" --related slug1,slug2`), then write the body via `--synthesis`.
|
|
61
|
-
|
|
62
|
-
Write `[[wiki links]]` in any knowledge prose (`--brief`, `--synthesis`) to reference related projects, entities, and notes — the CLI does not enforce link syntax; write them naturally in the text. This is what keeps the vault navigable as a wiki.
|
|
63
|
-
|
|
64
|
-
**Knowledge boundary:** before writing an insight down, ask where it belongs. Useful beyond the current session or repo → vault knowledge note. Specific to one repo's code or workflow → that repo's own docs (CLAUDE.md, README, comments), not the vault.
|
|
65
|
-
|
|
66
|
-
## Batch Operations
|
|
67
|
-
|
|
68
|
-
**Rule: Use `hafez batch` when making 2+ mutating calls.** Individual commands auto-commit after each call (and push, when the vault has a remote). Running multiple individual commands back-to-back causes git push races on synced vaults. Batch does all operations in one commit + one push. For a single operation, individual commands are fine — they auto-sync.
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
echo '[
|
|
72
|
-
{"op":"update","slug":"foo","fields":{"status":"done"}},
|
|
73
|
-
{"op":"update","slug":"foo","fields":{"session_log":{"type":"progress","summary":"Shipped it","agent":"claude"}}},
|
|
74
|
-
{"op":"update","slug":"bar","fields":{"add_action":"Write tests"}},
|
|
75
|
-
{"op":"create","kind":"entity","name":"New Thing","fields":{"type":"project","brief":"Context here.","add_action":"First task"}},
|
|
76
|
-
{"op":"create","kind":"knowledge","name":"Insight","fields":{"related":["foo"]}},
|
|
77
|
-
{"op":"create","kind":"session","name":"2026-04-06 Session","fields":{"synthesis":"Summary text"}},
|
|
78
|
-
{"op":"update","slug":"k1","fields":{"synthesis":"Updated synthesis text"}}
|
|
79
|
-
]' | hafez batch
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Batch output includes created slugs: `Batch complete: 3 operations (created: new-thing, insight)`
|
|
83
|
-
|
|
84
|
-
On shells that can't pipe stdin to native executables (Windows PowerShell 5.1), write the payload to a file and use `hafez batch --file payload.json` — same JSON, same behavior. (`cmd /c "hafez batch < payload.json"` also works.)
|
|
85
|
-
|
|
86
|
-
**Slug contract.** Slugs derive deterministically from names: lowercase, non-alphanumeric runs become single `-` (`"Auth Refactor!"` → `auth-refactor`). Batch executes sequentially, so later ops can reference the slugs of earlier creates in the same payload — create-then-link works in one batch. Collisions hard-error, so a wrong prediction can't touch the wrong document; `hafez batch --dry-run` reports the derived slug for every create op if you want to verify first.
|
|
87
|
-
|
|
88
|
-
## Session End — Digest
|
|
89
|
-
|
|
90
|
-
At the end of a working session (or a natural breakpoint), record what happened:
|
|
91
|
-
|
|
92
|
-
1. Identify the entities touched this session — read, updated, or created, or whose code was worked on. If unsure whether a name maps to an entity, run `hafez search "<term>"`.
|
|
93
|
-
2. If no entities were touched (pure tooling work with no vault relevance), skip the digest.
|
|
94
|
-
3. Pipe session context through digest into batch (`session_date` is today's real date):
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
echo '{
|
|
98
|
-
"entities_touched": ["slug-1", "slug-2"],
|
|
99
|
-
"decisions": ["Decision made", "Another decision"],
|
|
100
|
-
"narrative": "One to three sentence cross-cutting summary.",
|
|
101
|
-
"session_date": "2026-07-31",
|
|
102
|
-
"agent": "claude"
|
|
103
|
-
}' | hafez digest | hafez batch
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
On shells that can't pipe (PowerShell 5.1), use files instead — digest stays inspectable in the middle: `hafez digest --file summary.json > ops.json`, then `hafez batch --file ops.json`.
|
|
107
|
-
|
|
108
|
-
Digest adds a session log entry to each touched entity and creates a session note linking them all. Authoritative field docs: `hafez schema digest`.
|
|
109
|
-
|
|
110
|
-
Digest itself is pure — it only prints batch JSON; `hafez batch` is what writes. If you are unsure the slugs exist, run `hafez digest` alone first: unknown slugs warn on stderr (they are omitted from the output). Fix `entities_touched`, regenerate, and pipe to batch **once** — re-running the full pipe after a successful batch creates a duplicate session note.
|
|
111
|
-
|
|
112
|
-
**Mid-session updates and the digest coexist.** Point-in-time updates (`--status done`, `--complete-action`, brief changes) happen as they happen — digest adds the overall narrative on top. Digest builds the session summary itself, so the session's closing log entry comes from digest; reserve hand-written `--log` for significant mid-session milestones (deployed, merged, discovered a bug).
|
|
113
|
-
|
|
114
|
-
Session end is also when knowledge gets written: review what was learned and run Search-First Ingest on anything worth keeping. Knowledge notes are the primary durable output of sessions — not a side effect.
|
|
115
|
-
|
|
116
|
-
## Periodic Lint
|
|
117
|
-
|
|
118
|
-
Run at session end or on demand:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
hafez validate
|
|
122
|
-
hafez query --kind knowledge --sort-by last_reinforced --sort-order asc --limit 20
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Surface orphaned notes (no related links) and the least-recently-reinforced knowledge for review or archival. (`--filter` is entity status — knowledge notes have none, so the query command rejects it under `--kind knowledge`.)
|
|
126
|
-
|
|
127
|
-
## Updating Hafez Itself
|
|
128
|
-
|
|
129
|
-
"update hafez" means the **tool**. `hafez update` is an entity update — a different thing entirely. Don't confuse them.
|
|
130
|
-
|
|
131
|
-
1. Run `hafez upgrade`. It checks and prints; it never installs.
|
|
132
|
-
2. Show the user the printed commands **verbatim** and wait for an explicit yes. They install code and overwrite the user's own agent instructions file — never run them unattended, and never run the tarball alternative alongside the npm install.
|
|
133
|
-
3. Run the approved steps, then re-run `hafez upgrade` to verify.
|
|
134
|
-
|
|
135
|
-
If `hafez upgrade` fails with the literal prefix `Unknown command:`, the installed hafez predates it. Install the current version, re-copy the bundled skill, then run `hafez upgrade` — the README's Updating section (github.com/shrvn-r/hafez) has the exact commands.
|
|
136
|
-
|
|
137
|
-
`hafez upgrade` also prints the current integration snippet. Compare it against the Hafez block in the user's global instructions file and offer to reconcile differences — don't rewrite their file unasked.
|