hafez 1.0.1
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/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/cli/batch-errors.d.ts +24 -0
- package/dist/cli/batch-errors.js +236 -0
- package/dist/cli/commands-schema.d.ts +3 -0
- package/dist/cli/commands-schema.js +80 -0
- package/dist/cli/commands.d.ts +133 -0
- package/dist/cli/commands.js +628 -0
- package/dist/cli/format.d.ts +9 -0
- package/dist/cli/format.js +158 -0
- package/dist/cli/help-agent.d.ts +6 -0
- package/dist/cli/help-agent.js +158 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +304 -0
- package/dist/cli/resolve-vault.d.ts +13 -0
- package/dist/cli/resolve-vault.js +124 -0
- package/dist/cli/schema-introspect.d.ts +70 -0
- package/dist/cli/schema-introspect.js +372 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/contracts.d.ts +26 -0
- package/dist/contracts.js +87 -0
- package/dist/db.d.ts +68 -0
- package/dist/db.js +663 -0
- package/dist/digest.d.ts +29 -0
- package/dist/digest.js +94 -0
- package/dist/export-okf.d.ts +12 -0
- package/dist/export-okf.js +176 -0
- package/dist/git.d.ts +12 -0
- package/dist/git.js +355 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +909 -0
- package/dist/knowledge-index.d.ts +3 -0
- package/dist/knowledge-index.js +135 -0
- package/dist/merge.d.ts +1 -0
- package/dist/merge.js +176 -0
- package/dist/migrate-knowledge-v2.d.ts +11 -0
- package/dist/migrate-knowledge-v2.js +158 -0
- package/dist/migrate-next-actions.d.ts +6 -0
- package/dist/migrate-next-actions.js +32 -0
- package/dist/migrate-types.d.ts +11 -0
- package/dist/migrate-types.js +142 -0
- package/dist/parser.d.ts +18 -0
- package/dist/parser.js +68 -0
- package/dist/query.d.ts +24 -0
- package/dist/query.js +167 -0
- package/dist/schema.d.ts +9 -0
- package/dist/schema.js +59 -0
- package/dist/sections.d.ts +54 -0
- package/dist/sections.js +297 -0
- package/dist/templates.d.ts +8 -0
- package/dist/templates.js +39 -0
- package/dist/types.d.ts +321 -0
- package/dist/types.js +11 -0
- package/dist/vault.d.ts +12 -0
- package/dist/vault.js +45 -0
- package/package.json +67 -0
- package/skill/SKILL.md +190 -0
package/dist/vault.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/vault.ts
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
// Prevent gray-matter from auto-coercing YAML date strings into JS Date objects
|
|
7
|
+
const MATTER_OPTIONS = { engines: { yaml: (s) => yaml.load(s, { schema: yaml.JSON_SCHEMA }) } };
|
|
8
|
+
export function slugify(name) {
|
|
9
|
+
return name
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/['']/g, '')
|
|
12
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
13
|
+
.replace(/^-|-$/g, '')
|
|
14
|
+
.replace(/-{2,}/g, '-');
|
|
15
|
+
}
|
|
16
|
+
export function resolveFilePath(vaultPath, slug, kind) {
|
|
17
|
+
const dir = kind === 'entity' ? 'entities' : 'knowledge';
|
|
18
|
+
return join(vaultPath, dir, `${slug}.md`);
|
|
19
|
+
}
|
|
20
|
+
export function slugExists(vaultPath, slug) {
|
|
21
|
+
return existsSync(resolveFilePath(vaultPath, slug, 'entity'))
|
|
22
|
+
|| existsSync(resolveFilePath(vaultPath, slug, 'knowledge'));
|
|
23
|
+
}
|
|
24
|
+
// Fields that must always be arrays when present (may be scalar in hand-edited YAML)
|
|
25
|
+
const ARRAY_FIELDS = ['related', 'tags', 'domain'];
|
|
26
|
+
function normalizeFrontmatter(fm) {
|
|
27
|
+
for (const field of ARRAY_FIELDS) {
|
|
28
|
+
if (field in fm && typeof fm[field] === 'string') {
|
|
29
|
+
fm[field] = [fm[field]];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return fm;
|
|
33
|
+
}
|
|
34
|
+
export function parseFilePath(path) {
|
|
35
|
+
const content = readFileSync(path, 'utf-8');
|
|
36
|
+
const { data, content: body } = matter(content, MATTER_OPTIONS);
|
|
37
|
+
return { frontmatter: normalizeFrontmatter(data), body: body.trim() };
|
|
38
|
+
}
|
|
39
|
+
export function parseContent(content) {
|
|
40
|
+
const { data, content: body } = matter(content, MATTER_OPTIONS);
|
|
41
|
+
return { frontmatter: normalizeFrontmatter(data), body: body.trim() };
|
|
42
|
+
}
|
|
43
|
+
export function serializeFile(frontmatter, body) {
|
|
44
|
+
return matter.stringify(body.endsWith('\n') ? body : body + '\n', frontmatter);
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hafez",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"hafez": "./dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"skill",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"install:local": "mkdir -p ~/.local/bin && ln -sf \"$(pwd)/dist/cli.js\" ~/.local/bin/hafez && rm -f ~/.claude/skills/hafez/SKILL.md && mkdir -p ~/.claude/skills/hafez && cp \"$(pwd)/skill/SKILL.md\" ~/.claude/skills/hafez/SKILL.md",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"prepare": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"better-sqlite3": "^12.6.2",
|
|
28
|
+
"gray-matter": "^4.0.3",
|
|
29
|
+
"js-yaml": "^4.1.1",
|
|
30
|
+
"simple-git": "^3.33.0",
|
|
31
|
+
"zod": "^4.3.6"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
35
|
+
"@types/js-yaml": "^4.0.9",
|
|
36
|
+
"@types/node": "^25.4.0",
|
|
37
|
+
"tsx": "^4.21.0",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"vitest": "^4.0.18"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"description": "Agent-native personal knowledge vault: markdown + YAML frontmatter, SQLite FTS5 search, git-backed",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"knowledge-base",
|
|
45
|
+
"knowledge-vault",
|
|
46
|
+
"agent",
|
|
47
|
+
"ai-agents",
|
|
48
|
+
"claude-code",
|
|
49
|
+
"markdown",
|
|
50
|
+
"frontmatter",
|
|
51
|
+
"second-brain",
|
|
52
|
+
"cli",
|
|
53
|
+
"sqlite",
|
|
54
|
+
"fts5"
|
|
55
|
+
],
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22"
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/shrvn-r/hafez.git"
|
|
62
|
+
},
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/shrvn-r/hafez/issues"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://github.com/shrvn-r/hafez#readme"
|
|
67
|
+
}
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hafez
|
|
3
|
+
description: >
|
|
4
|
+
Hafez vault — personal knowledge base of captures, entities, projects,
|
|
5
|
+
and knowledge notes (with subtypes: insight, plan). Use when the user says /hafez, asks to query the vault
|
|
6
|
+
("what am I working on?", "show active projects", "what's stale?"), wants to
|
|
7
|
+
read or create an entity, or references Hafez directly. Also use at session
|
|
8
|
+
start to load context for identifiable topics, and at session end to record
|
|
9
|
+
a digest. Even if the user just casually mentions a project by name,
|
|
10
|
+
use this skill to check if it's a Hafez entity.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Hafez — Vault Operations
|
|
14
|
+
|
|
15
|
+
All operations use the `hafez` CLI. No file manipulation — the CLI handles reads, writes, validation, and git.
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
## CLI Reference
|
|
20
|
+
|
|
21
|
+
Run `hafez help --agent` for the complete, always-current API reference including all commands, flags, batch schema, and valid enum values.
|
|
22
|
+
|
|
23
|
+
Quick reference for common operations:
|
|
24
|
+
|
|
25
|
+
| Situation | Command |
|
|
26
|
+
|-----------|---------|
|
|
27
|
+
| Read an entity | `hafez read <slug>` |
|
|
28
|
+
| Active projects | `hafez query --filter active` |
|
|
29
|
+
| Stale items | `hafez query --filter stale` |
|
|
30
|
+
| Full-text search | `hafez search "<terms>"` |
|
|
31
|
+
| Search knowledge only | `hafez search "<terms>" --kind knowledge` |
|
|
32
|
+
| Create entity | `hafez create entity "<name>" --type project [--purpose "text"] [--description "text"] [--resource "uri"] [--brief "text"] [--add-action "task"]` |
|
|
33
|
+
| Quick capture | `hafez capture "<name>"` |
|
|
34
|
+
| List inbox captures | `hafez query --filter capture` |
|
|
35
|
+
| Update entity | `hafez update <slug> --brief "text" --add-action "task"` |
|
|
36
|
+
| Complete action | `hafez update <slug> --complete-action "substring"` |
|
|
37
|
+
| Session log | `hafez update <slug> --log "progress: summary" --agent claude` |
|
|
38
|
+
| Link entities | `hafez link <slug> <target> --relation related` |
|
|
39
|
+
| Promote | `hafez promote <slug> <target>` (capture→entity/project/knowledge, entity→project) |
|
|
40
|
+
| Add synthesis to knowledge | `hafez update <slug> --synthesis "text"` |
|
|
41
|
+
| Add evidence to knowledge | `hafez update <slug> --add-evidence "text"` |
|
|
42
|
+
| Add source to knowledge | `hafez update <slug> --add-source "url"` |
|
|
43
|
+
| Export OKF bundle | `hafez export --okf [--out <dir>]` (re-export overwrites, never deletes) |
|
|
44
|
+
| Digest session → batch | `echo '{...}' \| hafez digest \| hafez batch` |
|
|
45
|
+
|
|
46
|
+
Note: `--insight` is an alias for `--synthesis`.
|
|
47
|
+
|
|
48
|
+
## Session Start — Context Loading
|
|
49
|
+
|
|
50
|
+
When a topic is identifiable from the user's first message:
|
|
51
|
+
|
|
52
|
+
1. Run `hafez read <slug>` to load entity context
|
|
53
|
+
2. If the entity has a `parent`, also read the parent at frontmatter depth: `hafez read <parent> --depth frontmatter`
|
|
54
|
+
3. Search for related knowledge (`hafez search "<topic>" --kind knowledge`) and read any relevant note: `hafez read <knowledge-slug>`
|
|
55
|
+
4. Present a brief context line: "Loaded context for {name} (status: {status}, next: {next_action})"
|
|
56
|
+
|
|
57
|
+
Don't load context speculatively. Only when you can identify a specific entity slug or topic.
|
|
58
|
+
|
|
59
|
+
## Mid-Session — Working With the Vault
|
|
60
|
+
|
|
61
|
+
| Situation | Action |
|
|
62
|
+
|-----------|--------|
|
|
63
|
+
| User asks "what am I working on?" | `hafez query --filter active` |
|
|
64
|
+
| User asks about stale items | `hafez query --filter stale` |
|
|
65
|
+
| User mentions a project by name | `hafez read <slug>` |
|
|
66
|
+
| New workstream emerges | `hafez create entity "<name>" --type project [--brief "context"] [--add-action "task"]` |
|
|
67
|
+
| Quick thought, no classification | `hafez capture "<name>"` |
|
|
68
|
+
| Promote capture to entity/project | `hafez promote <slug> entity` |
|
|
69
|
+
| Insight worth preserving | Search first (`hafez search "<topic>"`), then create or update |
|
|
70
|
+
| Entity relates to another | `hafez link <slug> <target> --relation related` |
|
|
71
|
+
| Thread belongs to a project | `hafez link <slug> <parent> --relation parent` |
|
|
72
|
+
| Knowledge needs new synthesis | `hafez update <slug> --synthesis "text"` |
|
|
73
|
+
| Knowledge backed by new evidence | `hafez update <slug> --add-evidence "text"` |
|
|
74
|
+
| Knowledge has a source to cite | `hafez update <slug> --add-source "url"` |
|
|
75
|
+
| Need children of a project | `hafez query --parent <slug>` |
|
|
76
|
+
| Need related entities/knowledge | `hafez query --related-to <slug>` |
|
|
77
|
+
| Search for something | `hafez search "<terms>"` |
|
|
78
|
+
| Set handoff context | `hafez update <slug> --brief "context"` |
|
|
79
|
+
| Set one-line description | `hafez update <slug> --description "summary"` |
|
|
80
|
+
| Set resource URI (entity) | `hafez update <slug> --resource "https://..."` |
|
|
81
|
+
| Add a next action | `hafez update <slug> --add-action "task"` |
|
|
82
|
+
| Complete a next action | `hafez update <slug> --complete-action "task"` |
|
|
83
|
+
| Rebuild stale index | `hafez index rebuild` |
|
|
84
|
+
| Pull latest from remote | `hafez sync` |
|
|
85
|
+
|
|
86
|
+
### Ingest Reflex — Before Creating New Knowledge
|
|
87
|
+
|
|
88
|
+
Before creating a new knowledge note, always check if one already exists:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
hafez search "<topic>"
|
|
92
|
+
hafez search "<topic>" --kind knowledge
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If a related note exists, update it rather than creating a duplicate. Use `--synthesis` to revise the main insight text, `--add-evidence` to append supporting evidence, or `--add-source` to cite a new source.
|
|
96
|
+
|
|
97
|
+
Only create a new note when no existing knowledge covers the topic.
|
|
98
|
+
|
|
99
|
+
### Wiki Links in Knowledge Notes
|
|
100
|
+
|
|
101
|
+
When writing or updating knowledge note prose (brief, synthesis, insight body), use `[[wiki links]]` to reference related entities and knowledge naturally. This makes the vault navigable as a wiki:
|
|
102
|
+
|
|
103
|
+
- Reference related projects: `[[project-slug]]`
|
|
104
|
+
- Reference other knowledge: `[[knowledge-slug]]`
|
|
105
|
+
- Reference people or areas: `[[entity-slug]]`
|
|
106
|
+
|
|
107
|
+
The CLI does not enforce link syntax — write them naturally in the prose content you pass to `--synthesis`, `--brief`, or `--insight`.
|
|
108
|
+
|
|
109
|
+
### Deliberate Ingest Pattern
|
|
110
|
+
|
|
111
|
+
When processing a source (document, conversation, research) to extract knowledge:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# 1. Search for existing coverage
|
|
115
|
+
hafez search "<topic>" --kind knowledge
|
|
116
|
+
|
|
117
|
+
# 2a. If exists — update it
|
|
118
|
+
hafez update <slug> --synthesis "Revised insight" --add-source "url"
|
|
119
|
+
hafez update <slug> --add-evidence "New evidence from source"
|
|
120
|
+
|
|
121
|
+
# 2b. If new — create it
|
|
122
|
+
hafez create knowledge "<name>" --related slug1,slug2
|
|
123
|
+
|
|
124
|
+
# 3. Then update the note body via --synthesis with wiki links to related notes
|
|
125
|
+
hafez update <slug> --synthesis "Insight text referencing [[related-slug]]"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Batch Operations
|
|
129
|
+
|
|
130
|
+
**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.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
echo '[
|
|
134
|
+
{"op":"update","slug":"foo","fields":{"status":"done"}},
|
|
135
|
+
{"op":"update","slug":"foo","fields":{"session_log":{"type":"progress","summary":"Shipped it","agent":"claude"}}},
|
|
136
|
+
{"op":"update","slug":"bar","fields":{"add_action":"Write tests"}},
|
|
137
|
+
{"op":"create","kind":"entity","name":"New Thing","fields":{"type":"project","brief":"Context here.","add_action":"First task"}},
|
|
138
|
+
{"op":"create","kind":"knowledge","name":"Insight","fields":{"related":["foo"]}},
|
|
139
|
+
{"op":"create","kind":"session","name":"2026-04-06 Session","fields":{"synthesis":"Summary text"}},
|
|
140
|
+
{"op":"update","slug":"k1","fields":{"synthesis":"Updated synthesis text"}}
|
|
141
|
+
]' | hafez batch
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Batch output includes created slugs: `Batch complete: 3 operations (created: new-thing, insight)`
|
|
145
|
+
|
|
146
|
+
For a single operation, individual commands are fine — they auto-sync.
|
|
147
|
+
|
|
148
|
+
## Session End — Digest
|
|
149
|
+
|
|
150
|
+
At the end of a working session (or a natural breakpoint), record what happened:
|
|
151
|
+
|
|
152
|
+
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>"`.
|
|
153
|
+
2. If no entities were touched (pure tooling work with no vault relevance), skip the digest.
|
|
154
|
+
3. Pipe session context through digest into batch (`session_date` is today's real date):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
echo '{
|
|
158
|
+
"entities_touched": ["slug-1", "slug-2"],
|
|
159
|
+
"decisions": ["Decision made", "Another decision"],
|
|
160
|
+
"narrative": "One to three sentence cross-cutting summary.",
|
|
161
|
+
"session_date": "2026-07-31",
|
|
162
|
+
"agent": "claude"
|
|
163
|
+
}' | hafez digest | hafez batch
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Digest adds a session log entry to each touched entity and creates a session note linking them all. Authoritative field docs: `hafez schema digest`.
|
|
167
|
+
|
|
168
|
+
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.
|
|
169
|
+
|
|
170
|
+
**Mid-session updates and the digest coexist.** Point-in-time updates (`--status done`, `--complete-action`, brief changes) happen as they happen — don't defer them to session end. Digest adds the overall narrative on top. Do NOT hand-write session log entries (`--log`) right before digesting — digest builds the session summary itself. Use `--log` mid-session only for significant milestones (deployed, merged, discovered a bug).
|
|
171
|
+
|
|
172
|
+
## Knowledge Maintenance
|
|
173
|
+
|
|
174
|
+
Knowledge notes are the primary durable output of sessions — not a side effect. Treat the vault as a living wiki.
|
|
175
|
+
|
|
176
|
+
**Active maintenance pattern:**
|
|
177
|
+
1. At session start: search for knowledge relevant to the topic
|
|
178
|
+
2. During work: when insights emerge, search before creating — update existing notes first
|
|
179
|
+
3. Use `[[wiki links]]` in knowledge prose to connect related concepts
|
|
180
|
+
4. At session end: review what was learned and update or create knowledge notes
|
|
181
|
+
|
|
182
|
+
**Periodic lint** (run at session end or on demand):
|
|
183
|
+
```bash
|
|
184
|
+
hafez validate
|
|
185
|
+
hafez query --kind knowledge --filter stale
|
|
186
|
+
```
|
|
187
|
+
Surface orphaned notes (no related links) and stale knowledge for review or archival.
|
|
188
|
+
|
|
189
|
+
**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.
|
|
190
|
+
|