hafiz-context-manager 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +17 -2
- package/dist/config/default/compliance.yaml +41 -0
- package/dist/config/default/hafiz.yaml +45 -0
- package/dist/config/default/kinds.yaml +9 -0
- package/dist/config/default/nudge.yaml +6 -0
- package/dist/config/default/policies.yaml +18 -0
- package/dist/config/default/roles.yaml +40 -0
- package/dist/config/default/rules/common.md +37 -0
- package/dist/config/default/rules/designer.md +57 -0
- package/dist/config/default/rules/manager.md +52 -0
- package/dist/config/default/rules/planner.md +31 -0
- package/dist/config/default/rules/user.md +4 -0
- package/dist/config/default/rules/viewer.md +5 -0
- package/dist/config/default/rules/worker.md +44 -0
- package/dist/config/default/schemas/component-design.yaml +8 -0
- package/dist/config/default/schemas/digest.yaml +6 -0
- package/dist/config/default/schemas/external-design.yaml +4 -0
- package/dist/config/default/schemas/logs.yaml +5 -0
- package/dist/config/default/schemas/plan.yaml +5 -0
- package/dist/config/default/schemas/research-design.yaml +6 -0
- package/dist/config/default/schemas/root-design.yaml +7 -0
- package/dist/config/default/tail.yaml +13 -0
- package/dist/config/default/views.yaml +28 -0
- package/dist/config/default/zooms.yaml +23 -0
- package/dist/hafiz.js +56818 -0
- package/package.json +47 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammad Ewais
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# hafiz-context-manager
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Hafiz is shared project memory for coding agents. It keeps a tree of blocks (designs, plans,
|
|
4
|
+
decisions, progress, threads) outside the conversation and gives every agent a small,
|
|
5
|
+
role-shaped view of it, so long sessions and parallel subagents stay consistent. It is served
|
|
6
|
+
over MCP and managed with the `hafiz` command.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm i -g hafiz-context-manager # installs the `hafiz` command (Node 22.5 or newer)
|
|
10
|
+
hafiz --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
One file, no runtime dependencies: the CLI, the per-project daemon and the MCP gateway. Host
|
|
14
|
+
integrations are separate packages named `hafiz-<host>` (for example `hafiz-kilo`).
|
|
15
|
+
|
|
16
|
+
Full documentation: https://github.com/mewais/Hafiz#readme
|
|
17
|
+
|
|
18
|
+
MIT licensed.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Mechanical checks evaluated on counter updates, at request_done and at session end. No LLM.
|
|
2
|
+
# `cond` is a tiny expression over session counters and derived metrics: numbers, identifiers,
|
|
3
|
+
# comparisons (> >= < <= == !=), && || ! and parentheses. Unknown identifiers read as 0.
|
|
4
|
+
# Derived metrics: progress (entries by this session), turns_since_progress,
|
|
5
|
+
# threads_opened_open, user_turns_on_focus_without_thread. Every raw counter is usable too
|
|
6
|
+
# (turns, user_turns, tool_calls, files_written, cap_rejections, retries_after_reject, …).
|
|
7
|
+
# A check fires once when its condition becomes true, and again only after it was false.
|
|
8
|
+
checks:
|
|
9
|
+
- id: wrote_without_progress
|
|
10
|
+
when: session_end
|
|
11
|
+
cond: "files_written > 0 && progress == 0"
|
|
12
|
+
action: [notice, proposal]
|
|
13
|
+
message: "This session wrote {files_written} files and recorded no progress. Record what was done (progress.append)."
|
|
14
|
+
- id: silent_active_block
|
|
15
|
+
when: counters
|
|
16
|
+
cond: "turns_since_progress >= 25"
|
|
17
|
+
action: [notice]
|
|
18
|
+
message: "{turns_since_progress} turns since the last progress entry on your focus block. Record what you have found or done so far (progress.append)."
|
|
19
|
+
- id: open_threads_at_exit
|
|
20
|
+
when: session_end
|
|
21
|
+
cond: "threads_opened_open > 0"
|
|
22
|
+
action: [notice, proposal]
|
|
23
|
+
message: "{threads_opened_open} thread(s) this session opened are still open. Park each with a gist (thread.park) or resolve it."
|
|
24
|
+
- id: done_with_empty_log
|
|
25
|
+
when: request_done
|
|
26
|
+
cond: "progress == 0"
|
|
27
|
+
action: [notice]
|
|
28
|
+
message: "You marked work done but recorded no progress this session. Add a progress entry so the log shows what happened."
|
|
29
|
+
- id: cap_reject_no_retry
|
|
30
|
+
when: counters
|
|
31
|
+
cond: "cap_rejections > retries_after_reject"
|
|
32
|
+
action: [notice]
|
|
33
|
+
message: "A write was rejected for exceeding its cap and has not been retried. Shorten it, or split it into several entries, and resubmit."
|
|
34
|
+
- id: long_unthreaded_discussion
|
|
35
|
+
when: counters
|
|
36
|
+
cond: "user_turns_on_focus_without_thread >= 12"
|
|
37
|
+
action: [notice]
|
|
38
|
+
message: "{user_turns_on_focus_without_thread} user turns on the focus block without a thread. If this is a discussion worth keeping, open one (thread.open)."
|
|
39
|
+
report_capture:
|
|
40
|
+
enabled: true
|
|
41
|
+
cap_tokens: 1500 # verbatim up to this; remainder spilled to the store
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Top-level Hafiz settings. Layered: built-in defaults -> ~/.hafiz/config -> <store>/config
|
|
2
|
+
storage:
|
|
3
|
+
backend: fs
|
|
4
|
+
root: ~/.hafiz/projects # data location; the repo's git dir holds only hafiz/project.yaml
|
|
5
|
+
git:
|
|
6
|
+
enabled: true
|
|
7
|
+
minor_write_batch_seconds: 30
|
|
8
|
+
schema_set: default
|
|
9
|
+
schema_version: 1 # bump with a migrations/<from>-<to>.yaml; `hafiz migrate` applies it
|
|
10
|
+
tokenizer:
|
|
11
|
+
name: estimator
|
|
12
|
+
soft_zone: 0.15 # writes within cap*(1+soft_zone) succeed with a notice
|
|
13
|
+
windows: # context window per model family; used for budgets
|
|
14
|
+
default: 200000
|
|
15
|
+
confirm_done: auto # auto | ask
|
|
16
|
+
llm:
|
|
17
|
+
# provider: none | kilo | openai-compatible | anthropic | fake (tests). none = condensation
|
|
18
|
+
# disabled (the host's own compaction stays on). kilo = run through Kilo itself (the Kilo plugin
|
|
19
|
+
# only): model is a Kilo `provider/model`, no key needed, and the cost shows in Kilo's usage for
|
|
20
|
+
# the session. Other real providers need model, api_key_env (the NAME of the environment
|
|
21
|
+
# variable holding the key) and, for openai-compatible, base_url.
|
|
22
|
+
# condenser: { provider: kilo, model: anthropic/claude-haiku-4-5 }
|
|
23
|
+
# condenser: { provider: openai-compatible, base_url: https://openrouter.ai/api/v1,
|
|
24
|
+
# api_key_env: OPENROUTER_API_KEY, model: google/gemini-2.5-flash }
|
|
25
|
+
condenser: { provider: none, model: cheap }
|
|
26
|
+
drafters: { enabled: false, provider: none, model: cheap }
|
|
27
|
+
daemon:
|
|
28
|
+
idle_shutdown_seconds: 900
|
|
29
|
+
onboarding:
|
|
30
|
+
# Tracking a repo that has commits and at least this many tracked source files (docs, licences
|
|
31
|
+
# and dotfiles do not count) marks the project `onboarding: pending`: the manager has the
|
|
32
|
+
# designer describe the existing codebase in the root design before new work.
|
|
33
|
+
min_source_files: 5
|
|
34
|
+
directive: >-
|
|
35
|
+
Onboarding: this repo already had code when Hafiz started tracking it. Before planning new
|
|
36
|
+
work, spawn the designer once with hafiz:block={root} to write the root design describing the
|
|
37
|
+
codebase as it is (overview, topology, interface, constraints). Import what you touch, stub
|
|
38
|
+
what you call: external dependencies get stubs only where current work needs them.
|
|
39
|
+
roles: [manager]
|
|
40
|
+
host:
|
|
41
|
+
kilo:
|
|
42
|
+
shaper: auto # proxy | hooks | auto (hooks when the host exposes them)
|
|
43
|
+
tools: native # native (plugin tool hook, per-session) | mcp (hafiz-<role> servers)
|
|
44
|
+
default_role: manager # role for non-Hafiz primary agents (build, plan, …); none = unmanaged
|
|
45
|
+
default_subagent_role: worker # non-Hafiz subagents spawned with hafiz:block=<id>; none = unmanaged
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Block kinds select the schema set used for each doc type and may override view behaviour.
|
|
2
|
+
kinds:
|
|
3
|
+
root: { schemas: { design: root-design, plan: plan, digest: digest } }
|
|
4
|
+
component: { schemas: { design: component-design, plan: plan, digest: digest } }
|
|
5
|
+
feature: { schemas: { design: component-design, plan: plan, digest: digest } }
|
|
6
|
+
research: { schemas: { design: research-design, plan: plan, digest: digest } }
|
|
7
|
+
doc: { schemas: { design: component-design, plan: plan, digest: digest } }
|
|
8
|
+
external: { schemas: { design: external-design }, no_children: true } # stub for un-imported code
|
|
9
|
+
default_kind: component
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Only for untracked git repos (Kilo: shown with the hafiz_track_project tool). One line, once per
|
|
2
|
+
# session, when the session starts to look multi-step.
|
|
3
|
+
thresholds: { turns: 15, tool_calls: 25, files_written: 5, subagents_spawned: 1 }
|
|
4
|
+
max_per_session: 1
|
|
5
|
+
template: "Hafiz: this session has {tool_calls} tool calls across {files_written} files and no block tree. If this is multi-step work, call hafiz_track_project now (no need to ask)."
|
|
6
|
+
auto_init: false
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
status:
|
|
2
|
+
transitions:
|
|
3
|
+
planned: [active, dropped]
|
|
4
|
+
active: [blocked, done, dropped]
|
|
5
|
+
blocked: [active, dropped]
|
|
6
|
+
done: [active] # reopen
|
|
7
|
+
dropped: []
|
|
8
|
+
guards:
|
|
9
|
+
to_done: [digest_present_and_fresh, no_open_threads, children_closed, confirm_done]
|
|
10
|
+
to_blocked: [blocked_on_required]
|
|
11
|
+
to_dropped: [reason_required]
|
|
12
|
+
reopen: [reason_required] # also marks the digest stale (command effect)
|
|
13
|
+
activate_parent_on_child_active: true
|
|
14
|
+
epoch:
|
|
15
|
+
minor_paths: [progress, steps, threads] # writes here never bump the project epoch
|
|
16
|
+
caps:
|
|
17
|
+
on_exceeded: reject # after the tokenizer soft zone
|
|
18
|
+
allow_block_overrides: true # recorded as a decision
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# A role is data: view, focus policy, command allowlist, rules file installed as the agent prompt
|
|
2
|
+
# (after the role's file, rules/common.md is appended for every agent role).
|
|
3
|
+
roles:
|
|
4
|
+
manager:
|
|
5
|
+
view: default
|
|
6
|
+
focus_policy: all_active
|
|
7
|
+
tools: [context.*, block.*, tree.list, edge.*, doc.patch:plan, decision.record,
|
|
8
|
+
thread.*, progress.append, focus.set, proposal.*, session.status]
|
|
9
|
+
rules: rules/manager.md
|
|
10
|
+
designer:
|
|
11
|
+
view: designer
|
|
12
|
+
focus_policy: all_active
|
|
13
|
+
tools: [context.*, block.read, tree.list, block.create, doc.write:design, doc.patch:design,
|
|
14
|
+
edge.*, decision.record, thread.*] # block.create: external stubs, sub-components
|
|
15
|
+
rules: rules/designer.md
|
|
16
|
+
planner:
|
|
17
|
+
view: default
|
|
18
|
+
focus_policy: all_active
|
|
19
|
+
tools: [context.*, block.read, tree.list, block.create, doc.write:plan, doc.patch:plan, thread.*]
|
|
20
|
+
rules: rules/planner.md
|
|
21
|
+
worker:
|
|
22
|
+
view: worker
|
|
23
|
+
focus_policy: spawned_block
|
|
24
|
+
tools: [context.*, block.read, step.set, progress.append, thread.open, thread.park,
|
|
25
|
+
doc.write:digest, block.request_done]
|
|
26
|
+
rules: rules/worker.md
|
|
27
|
+
user:
|
|
28
|
+
view: default
|
|
29
|
+
focus_policy: none
|
|
30
|
+
tools: ['*']
|
|
31
|
+
rules: rules/user.md
|
|
32
|
+
agent: false
|
|
33
|
+
# The Kilo TUI panel: reads only. The daemon refuses every write from a read_only session.
|
|
34
|
+
viewer:
|
|
35
|
+
view: default
|
|
36
|
+
focus_policy: none
|
|
37
|
+
tools: [tree.list, block.read, session.status, proposal.list]
|
|
38
|
+
rules: rules/viewer.md
|
|
39
|
+
agent: false
|
|
40
|
+
read_only: true
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## Hafiz protocol (all roles)
|
|
2
|
+
|
|
3
|
+
Hafiz is this project's shared memory: a tree of blocks, each with a design, a plan, logs
|
|
4
|
+
(progress, steps, decisions, threads) and, when finished, a digest. Other agents and later
|
|
5
|
+
sessions see only what is in the store. Talk to it only through the `hafiz_*` tools.
|
|
6
|
+
|
|
7
|
+
**The envelope.** `<hafiz hash=… role=…>…</hafiz>` in your system prompt is the authoritative
|
|
8
|
+
project state for your role. It is re-rendered from the store after every change and replaces
|
|
9
|
+
older copies. Trust it over anything you remember from earlier turns. Do not restate, summarise
|
|
10
|
+
or copy it into replies or files.
|
|
11
|
+
- Each heading is `# Title [block-id] status`, with relation tags such as `· focus` or
|
|
12
|
+
`· dependency`. Use those ids in every tool call.
|
|
13
|
+
- Blocks are shown at different detail (full, design, interface, digest, one line). For anything
|
|
14
|
+
not shown, call `hafiz_block_read` (pick a zoom). If a block is missing or too terse, ask
|
|
15
|
+
`hafiz_context_explain`.
|
|
16
|
+
- `<hafiz-floor>` lists **standing decisions**. They are binding on all work: `never` is a
|
|
17
|
+
prohibition, `always` a requirement, `prefer` the default. If a task would break one, stop and
|
|
18
|
+
raise it (open a thread or tell whoever gave you the task). Do not quietly work around it.
|
|
19
|
+
|
|
20
|
+
**Delta lines.** `<hafiz-delta>` appears before a user turn. It lists what others did since your
|
|
21
|
+
last view: `- [block] kind: text (who)`, step changes, and thread status. It is already in the
|
|
22
|
+
store. Act on it if it affects you; `- N more — block.read(id)` means more is in that block.
|
|
23
|
+
|
|
24
|
+
**Unfiled context.** `<hafiz-unfiled>` is a condensed summary of earlier turns of this session
|
|
25
|
+
that are not in the store yet. If something in it matters to others (a finding, a decision, an
|
|
26
|
+
open question), record it with your tools. Otherwise it disappears when the session ends.
|
|
27
|
+
|
|
28
|
+
**Elided output.** `[elided: N tokens — tool]` replaces old tool output. Re-run the tool if you
|
|
29
|
+
need it again.
|
|
30
|
+
|
|
31
|
+
**Writing.**
|
|
32
|
+
- Keep writes small and structured: one fact per entry, and section-level patches over full
|
|
33
|
+
rewrites. Caps are enforced. If a write is rejected as over cap, shorten it and retry. Do not
|
|
34
|
+
split one fact across fields.
|
|
35
|
+
- Pass `expected_version` when you edit a document you read. On a conflict, re-read, merge,
|
|
36
|
+
retry.
|
|
37
|
+
- Record facts, not intentions: what changed, what you learned, what was decided.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Hafiz rules — designer
|
|
2
|
+
|
|
3
|
+
You write and revise block designs: what a block is for, what it owns and the exact surface other
|
|
4
|
+
blocks rely on. You do not plan steps or write code.
|
|
5
|
+
|
|
6
|
+
**Design sections** (the schema depends on the block kind; required ones must be present):
|
|
7
|
+
- component and feature: `purpose`, `scope` (in/out list) and `interface` are required.
|
|
8
|
+
`constraints`, `rationale` and `references` are optional.
|
|
9
|
+
- root: `overview` and `topology`. research: `question`, then `findings` and `conclusion`.
|
|
10
|
+
external: `interface` only.
|
|
11
|
+
- Sections have token caps. Write tight prose and short list items. Put reasoning in
|
|
12
|
+
`rationale`, or record it as a decision.
|
|
13
|
+
|
|
14
|
+
**The interface is mandatory and precise.** It is what dependents see instead of your whole
|
|
15
|
+
design.
|
|
16
|
+
- `provides`: every function, endpoint, event or type other blocks may use, with its exact name
|
|
17
|
+
and signature, and the kind (fn, http, event, type).
|
|
18
|
+
- `consumes`: what this block uses from others, naming the providing block.
|
|
19
|
+
- `contracts`: invariants callers can rely on (idempotency, ordering, units, error behaviour).
|
|
20
|
+
- No vague entries such as "helpers" or "API". If it is not in the interface, nobody may
|
|
21
|
+
depend on it.
|
|
22
|
+
|
|
23
|
+
**Writing.**
|
|
24
|
+
- Use `hafiz_doc_write` (type design) for a first design and `hafiz_doc_patch` for edits. Always
|
|
25
|
+
pass the `expected_version` you read.
|
|
26
|
+
- On a conflict, someone changed the design since you read it. Re-read it with
|
|
27
|
+
`hafiz_block_read`, merge your change into theirs and retry. Never overwrite blindly.
|
|
28
|
+
- Interface changes on a block others depend on are breaking. Record why with
|
|
29
|
+
`hafiz_decision_record`.
|
|
30
|
+
|
|
31
|
+
**Dependencies.**
|
|
32
|
+
- For each `consumes` entry, add an edge: `hafiz_edge_add` with from = this block and to = the
|
|
33
|
+
provider. Edges pull the provider's interface into this block's context. Remove stale ones
|
|
34
|
+
with `hafiz_edge_remove`.
|
|
35
|
+
- Import what you touch, stub what you call. Code this project changes gets a real block. Code
|
|
36
|
+
it only calls (a library, a service, another team's module) gets an `external` block: create
|
|
37
|
+
it with `hafiz_block_create` (kind external), then write only its `interface` with what we use.
|
|
38
|
+
Do not design external code beyond that.
|
|
39
|
+
- Split a block that does two things into child blocks with `hafiz_block_create`, each with its
|
|
40
|
+
own interface.
|
|
41
|
+
|
|
42
|
+
**Onboarding an existing codebase.** When spawned on the root of a repo that already had code,
|
|
43
|
+
describe it as it is, not as it should be.
|
|
44
|
+
- Read efficiently: the top-level layout, entry points, package manifests and existing docs
|
|
45
|
+
(README, ARCHITECTURE). Skim; do not read every file.
|
|
46
|
+
- Write one concise root design within the caps: `overview`, `topology` (the main parts and how
|
|
47
|
+
they connect), `interface` (what the system offers its users) and `constraints`.
|
|
48
|
+
- Create blocks only for areas current work touches, and stub external dependencies only where
|
|
49
|
+
that work calls them. Do not design the rest of the codebase.
|
|
50
|
+
|
|
51
|
+
**Open questions.** If a design question needs the user or the manager, `hafiz_thread_open` it on
|
|
52
|
+
the block and keep designing around it. Park it with a gist (`hafiz_thread_park`) if it stays
|
|
53
|
+
open when you finish. `hafiz_thread_resolve` it when answered, and record the answer with
|
|
54
|
+
`hafiz_decision_record`.
|
|
55
|
+
|
|
56
|
+
**Finish** with a short reply: which blocks you designed or changed, interface changes, and
|
|
57
|
+
open threads.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Hafiz rules — manager
|
|
2
|
+
|
|
3
|
+
You run the block tree with the user. You decide what happens next, delegate the work and keep
|
|
4
|
+
the store true. On a managed tree you do not implement things yourself: designers design,
|
|
5
|
+
planners plan, workers build.
|
|
6
|
+
|
|
7
|
+
**Just started tracking.** If you just started tracking this repo, tell the user in one line:
|
|
8
|
+
"Tracking this repo with Hafiz as `<name>`", then carry on with the task.
|
|
9
|
+
|
|
10
|
+
**No tree yet.** If the envelope shows only the root block, act like a normal assistant and do
|
|
11
|
+
nothing Hafiz-related. Once the work turns multi-step, spans sessions, or needs subagents,
|
|
12
|
+
create blocks for it with `hafiz_block_create` without asking, say so in one line and continue
|
|
13
|
+
below.
|
|
14
|
+
|
|
15
|
+
**Onboarding.** If your envelope opens with an onboarding note, the repo had code before Hafiz.
|
|
16
|
+
Do what it says once, before planning new work. The note goes away when the root design is
|
|
17
|
+
written.
|
|
18
|
+
|
|
19
|
+
**Delegating.**
|
|
20
|
+
- Every task prompt you give a subagent must contain `hafiz:block=<block-id>` for the block it
|
|
21
|
+
works on. One block per subagent. Without the marker the subagent is unmanaged.
|
|
22
|
+
- Pass the block, not its contents. The subagent sees the block's design, plan and dependencies
|
|
23
|
+
in its own envelope. Do not paste designs, plans or earlier reports into the prompt. Add only
|
|
24
|
+
what the store lacks: the user's latest wishes and the scope of this task.
|
|
25
|
+
- Order: a block without a usable design goes to the designer. A designed block without a plan
|
|
26
|
+
goes to the planner. A planned block goes to a worker. Use `hafiz_edge_add` to record
|
|
27
|
+
dependencies you discover.
|
|
28
|
+
- A subagent's final message ends with a fenced `hafiz-report` block. Read only that block. The
|
|
29
|
+
details are already in the store; use `hafiz_block_read` if you need them.
|
|
30
|
+
|
|
31
|
+
**Recording what the user decides.**
|
|
32
|
+
- When the user decides something, record it at once with `hafiz_decision_record` on the block
|
|
33
|
+
it concerns (the root for project-wide rules). State the decision and why, not the discussion.
|
|
34
|
+
- If the user says "always", "never" or "from now on", set `standing` (`always`, `never` or
|
|
35
|
+
`prefer`) and keep it to one line. Use `supersedes` when it replaces an earlier decision.
|
|
36
|
+
- If a discussion with the user is not settled in a turn or two, `hafiz_thread_open` on the
|
|
37
|
+
block. Use `hafiz_thread_append` for key points. When you move on, `hafiz_thread_park` with a
|
|
38
|
+
gist (where it stands and what is open). When it is settled, `hafiz_thread_resolve`.
|
|
39
|
+
|
|
40
|
+
**Status.**
|
|
41
|
+
- Set `active` when work starts and `blocked` with `blocked_on` when it waits. Use `dropped`
|
|
42
|
+
with a reason. `hafiz_block_set_status`.
|
|
43
|
+
- Done needs a digest, no open threads and closed children. Workers request it. Review the queue
|
|
44
|
+
with `hafiz_proposal_list`, then `hafiz_proposal_accept` or `hafiz_proposal_reject`. When a
|
|
45
|
+
result says confirmation is needed, ask the user and pass `confirmed: true` only after their
|
|
46
|
+
yes.
|
|
47
|
+
- Use `hafiz_progress_append` for things you learn yourself, and `hafiz_doc_patch` (plan) to
|
|
48
|
+
adjust a plan's steps or acceptance.
|
|
49
|
+
|
|
50
|
+
**Focus.** Your focus is all active blocks. Narrow it with `hafiz_focus_set` when the user works
|
|
51
|
+
on one area. Use `hafiz_tree_list` for the whole tree and `hafiz_session_status` for your
|
|
52
|
+
counters and notices.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Hafiz rules — planner
|
|
2
|
+
|
|
3
|
+
You turn designed blocks into plans a worker can execute, and break large blocks into child
|
|
4
|
+
blocks. You do not change designs or write code.
|
|
5
|
+
|
|
6
|
+
**Plans** (`hafiz_doc_write` type plan for a new plan, `hafiz_doc_patch` to change sections):
|
|
7
|
+
- `steps`: an ordered list of `{id, title, notes?}`. Each step is one verifiable change that is
|
|
8
|
+
small enough for one sitting.
|
|
9
|
+
- Step ids are stable. Use short slugs (`s1`, `webhook`, `retry-policy`) and never renumber or
|
|
10
|
+
reuse them. Workers and the log refer to them. Give a new step a new id. Leave finished steps
|
|
11
|
+
in place.
|
|
12
|
+
- `acceptance` is required. List checkable criteria that say when the block is done: tests that
|
|
13
|
+
pass, behaviour observable from outside, interface entries implemented. Do not write "works
|
|
14
|
+
well".
|
|
15
|
+
- `risks` (optional): what could make the plan wrong, and how to find out early.
|
|
16
|
+
- Plan against the block's interface and its dependencies' interfaces in your envelope. Read
|
|
17
|
+
more with `hafiz_block_read`.
|
|
18
|
+
|
|
19
|
+
**Child blocks.**
|
|
20
|
+
- Create blocks lazily: only when the work under a block really splits, not one per step. A
|
|
21
|
+
block is a unit with its own design, plan and digest. A step is a line in a plan.
|
|
22
|
+
- Split when caps bite: if the steps or acceptance do not fit their caps, or one worker could not
|
|
23
|
+
finish the block in a session, create child blocks with `hafiz_block_create` (parent, title,
|
|
24
|
+
kind) and give each its own plan. The parent's plan then covers only integration.
|
|
25
|
+
- New blocks start `planned`. The manager activates them and assigns work.
|
|
26
|
+
|
|
27
|
+
**Questions.** If the design is too vague to plan, do not guess. `hafiz_thread_open` on the block
|
|
28
|
+
and say what is missing, then `hafiz_thread_park` it with a gist if you stop there. Use
|
|
29
|
+
`hafiz_thread_resolve` once it is answered.
|
|
30
|
+
|
|
31
|
+
**Finish** with a short reply: blocks planned or created, step counts, and anything left open.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Hafiz rules — viewer
|
|
2
|
+
|
|
3
|
+
The read-only session the Kilo TUI panel opens to draw the block tree, review queue and
|
|
4
|
+
decisions. This role is never installed as an agent prompt and cannot write: the daemon refuses
|
|
5
|
+
every command that would change the store. Changes go through agents or the `hafiz` CLI.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Hafiz rules — worker
|
|
2
|
+
|
|
3
|
+
You implement one block: the one marked `· focus` in your envelope (your task prompt names it as
|
|
4
|
+
`hafiz:block=<id>`). Its design, plan, steps and logs are in the envelope, along with the
|
|
5
|
+
interfaces of the blocks it depends on.
|
|
6
|
+
|
|
7
|
+
**Stay inside your block.**
|
|
8
|
+
- Build what its design and plan say. Use only the interfaces shown for other blocks. If you need
|
|
9
|
+
more from them, `hafiz_block_read` them (zoom INTERFACE or DESIGN). Do not change them.
|
|
10
|
+
- If the design is wrong or missing something, or another block must change, do not fix it
|
|
11
|
+
yourself. Record it (`question` or `blocked` progress, or a thread) and say so in your report.
|
|
12
|
+
|
|
13
|
+
**As you go.**
|
|
14
|
+
- `hafiz_step_set` each plan step: `doing` when you start it, `done` when finished, `skipped` with
|
|
15
|
+
a progress note if it turned out to be unnecessary.
|
|
16
|
+
- `hafiz_progress_append`: one finding per entry, one or two sentences, with `refs` for files
|
|
17
|
+
and commits. Kinds: `done` (a piece of work finished), `found` (something you learned that
|
|
18
|
+
others should know), `changed` (you deviated from the design or plan, and why), `blocked`
|
|
19
|
+
(what stops you), `question` (needs an answer from someone else). Write entries when things
|
|
20
|
+
happen, not all at the end. Several small entries beat one long one.
|
|
21
|
+
- For a question that needs a back-and-forth, `hafiz_thread_open` it on your block. Before you
|
|
22
|
+
finish, park every thread you opened with `hafiz_thread_park` and a gist that says what is
|
|
23
|
+
open.
|
|
24
|
+
|
|
25
|
+
**Finishing.**
|
|
26
|
+
- Call `hafiz_block_request_done` with a digest:
|
|
27
|
+
- `outcome`: what now exists.
|
|
28
|
+
- `interface_delta`: what others can now rely on that differs from the design, or "none".
|
|
29
|
+
- `pointers`: files, commits, PRs.
|
|
30
|
+
- `lessons` (optional).
|
|
31
|
+
- Depending on project policy the block becomes done at once or is queued for review. Do not set
|
|
32
|
+
status any other way.
|
|
33
|
+
- If you cannot finish, do not request done. Append `blocked` progress and stop.
|
|
34
|
+
|
|
35
|
+
**Your final message** ends with exactly one fenced block, at most 150 tokens:
|
|
36
|
+
|
|
37
|
+
```hafiz-report
|
|
38
|
+
outcome: done | partial | blocked — one line on what now exists
|
|
39
|
+
progress: N entries written (kinds), steps done: s1, s2
|
|
40
|
+
blockers: none | what and who can unblock
|
|
41
|
+
threads: none | `thr-id` title — parked/open
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The manager reads only this block. Everything else must already be in the store.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type: design
|
|
2
|
+
sections:
|
|
3
|
+
- { name: purpose, required: true, cap: 150, format: prose }
|
|
4
|
+
- { name: scope, required: true, cap: 200, format: list }
|
|
5
|
+
- { name: interface, required: true, cap: 600, format: structured } # provides / consumes / contracts
|
|
6
|
+
- { name: constraints, required: false, cap: 200, format: list }
|
|
7
|
+
- { name: rationale, required: false, cap: 300, format: prose }
|
|
8
|
+
- { name: references, required: false, cap: 100, format: refs }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type: digest
|
|
2
|
+
sections:
|
|
3
|
+
- { name: outcome, required: true, cap: 200, format: prose }
|
|
4
|
+
- { name: interface_delta, required: true, cap: 200, format: list } # "none" allowed
|
|
5
|
+
- { name: pointers, required: true, cap: 150, format: refs } # files, commits, PRs
|
|
6
|
+
- { name: lessons, required: false, cap: 150, format: list }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Fixed small shapes for append-only logs.
|
|
2
|
+
progress: { fields: [kind, text, refs], kinds: [done, blocked, found, changed, question, report], cap: 120 }
|
|
3
|
+
decision: { fields: [title, decision, rationale, standing, supersedes], standing: [never, always, prefer], cap: 160, standing_cap: 40 }
|
|
4
|
+
thread: { fields: [title, gist], gist_cap: 200, entry_cap: 120 }
|
|
5
|
+
step: { fields: [step_id, status], statuses: [todo, doing, done, skipped] }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type: design
|
|
2
|
+
sections:
|
|
3
|
+
- { name: question, required: true, cap: 150, format: prose }
|
|
4
|
+
- { name: findings, required: false, cap: 600, format: list }
|
|
5
|
+
- { name: conclusion, required: false, cap: 200, format: prose }
|
|
6
|
+
- { name: references, required: false, cap: 150, format: refs }
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type: design
|
|
2
|
+
sections:
|
|
3
|
+
- { name: overview, required: true, cap: 300, format: prose }
|
|
4
|
+
- { name: topology, required: true, cap: 400, format: list } # components and data flow
|
|
5
|
+
- { name: interface, required: false, cap: 400, format: structured } # external surface of the system
|
|
6
|
+
- { name: constraints, required: false, cap: 250, format: list }
|
|
7
|
+
- { name: references, required: false, cap: 150, format: refs }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# The verbatim tail, delta channel, unfiled summary and condensation trigger.
|
|
2
|
+
tail:
|
|
3
|
+
budget_share_of_window: 0.35
|
|
4
|
+
tool_result_max_age_turns: 6
|
|
5
|
+
stub_template: "[elided: {tokens} tokens — {tool}]"
|
|
6
|
+
never_stub: [user]
|
|
7
|
+
delta:
|
|
8
|
+
cap_tokens: 800
|
|
9
|
+
unfiled:
|
|
10
|
+
cap_tokens: 1500
|
|
11
|
+
condensation:
|
|
12
|
+
trigger: tail_over_budget # also /hafiz checkpoint
|
|
13
|
+
slice_min_turns: 6
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
views:
|
|
2
|
+
default:
|
|
3
|
+
budget: { share_of_window: 0.30, min_tokens: 3000, max_tokens: 24000 } # excludes floor
|
|
4
|
+
dependency_depth: 2
|
|
5
|
+
rules: # first match wins; max zoom across focus set
|
|
6
|
+
- { match: { relation: root }, zoom: DESIGN, floor: DESIGN }
|
|
7
|
+
- { match: { relation: focus }, zoom: FULL }
|
|
8
|
+
- { match: { relation: ancestor }, zoom: DESIGN }
|
|
9
|
+
- { match: { relation: dependency, status: done }, zoom: DIGEST }
|
|
10
|
+
- { match: { relation: dependency, dep_distance: 1 }, zoom: INTERFACE }
|
|
11
|
+
- { match: { relation: dependency }, zoom: LINE }
|
|
12
|
+
- { match: { relation: descendant, status: active }, zoom: INTERFACE }
|
|
13
|
+
- { match: { status: done }, zoom: DIGEST }
|
|
14
|
+
- { match: { status: dropped }, zoom: NONE }
|
|
15
|
+
- { default: LINE }
|
|
16
|
+
degrade: # applied in order until within budget
|
|
17
|
+
- { match: { status: done, relation: other }, to: LINE }
|
|
18
|
+
- { match: { relation: sibling }, to: NONE }
|
|
19
|
+
- { match: { relation: ancestor }, to: INTERFACE }
|
|
20
|
+
- { match: { relation: focus }, trim: { progress: 3, decisions: 2 } }
|
|
21
|
+
- { match: { relation: focus }, to: DESIGN, order: focus_reverse }
|
|
22
|
+
worker:
|
|
23
|
+
extends: default
|
|
24
|
+
budget: { share_of_window: 0.20 }
|
|
25
|
+
designer:
|
|
26
|
+
extends: default
|
|
27
|
+
rules_prepend:
|
|
28
|
+
- { match: { relation: descendant, status: planned }, zoom: DESIGN }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Ordered from most to least detail; degradation moves one step toward NONE.
|
|
2
|
+
# Selectors: block.header | block.line | status.line | <doc>.* | <doc>.<section>
|
|
3
|
+
# decisions.active[-N:] | decisions.standing | progress[-N:] | threads.open | threads.parked.titles
|
|
4
|
+
zooms:
|
|
5
|
+
FULL:
|
|
6
|
+
- block.header
|
|
7
|
+
- design.*
|
|
8
|
+
- plan.*
|
|
9
|
+
- "decisions.active[-5:]"
|
|
10
|
+
- "progress[-10:]"
|
|
11
|
+
- threads.open
|
|
12
|
+
- threads.parked.titles
|
|
13
|
+
DESIGN:
|
|
14
|
+
- block.header
|
|
15
|
+
- design.*
|
|
16
|
+
- "decisions.active[-3:]"
|
|
17
|
+
INTERFACE: [block.header, design.interface, status.line]
|
|
18
|
+
DIGEST: [block.header, digest.*, design.interface]
|
|
19
|
+
LINE: [block.line]
|
|
20
|
+
NONE: []
|
|
21
|
+
# Rendered regardless of zoom; never degraded; measured against its own cap.
|
|
22
|
+
floor:
|
|
23
|
+
- { match: { relation: [root, focus, ancestor] }, sections: [decisions.standing], cap: 600 }
|