hzl-cli 1.5.1 → 1.7.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.
Files changed (194) hide show
  1. package/README.md +307 -127
  2. package/dist/__tests__/integration/cli-integration.test.js +10 -9
  3. package/dist/__tests__/integration/cli-integration.test.js.map +1 -1
  4. package/dist/__tests__/integration/config.test.js +1 -1
  5. package/dist/__tests__/integration/config.test.js.map +1 -1
  6. package/dist/__tests__/integration/helpers.d.ts +1 -0
  7. package/dist/__tests__/integration/helpers.d.ts.map +1 -1
  8. package/dist/__tests__/integration/helpers.js +2 -0
  9. package/dist/__tests__/integration/helpers.js.map +1 -1
  10. package/dist/__tests__/integration/project-workflow.test.js +6 -5
  11. package/dist/__tests__/integration/project-workflow.test.js.map +1 -1
  12. package/dist/commands/config.test.js +2 -2
  13. package/dist/commands/config.test.js.map +1 -1
  14. package/dist/commands/doctor.d.ts +39 -0
  15. package/dist/commands/doctor.d.ts.map +1 -0
  16. package/dist/commands/doctor.js +249 -0
  17. package/dist/commands/doctor.js.map +1 -0
  18. package/dist/commands/doctor.test.d.ts +2 -0
  19. package/dist/commands/doctor.test.d.ts.map +1 -0
  20. package/dist/commands/doctor.test.js +54 -0
  21. package/dist/commands/doctor.test.js.map +1 -0
  22. package/dist/commands/export-events.js +3 -3
  23. package/dist/commands/export-events.js.map +1 -1
  24. package/dist/commands/export-events.test.js +2 -2
  25. package/dist/commands/export-events.test.js.map +1 -1
  26. package/dist/commands/init.d.ts +13 -7
  27. package/dist/commands/init.d.ts.map +1 -1
  28. package/dist/commands/init.js +97 -34
  29. package/dist/commands/init.js.map +1 -1
  30. package/dist/commands/init.test.js +83 -81
  31. package/dist/commands/init.test.js.map +1 -1
  32. package/dist/commands/lock.d.ts +39 -0
  33. package/dist/commands/lock.d.ts.map +1 -0
  34. package/dist/commands/lock.js +142 -0
  35. package/dist/commands/lock.js.map +1 -0
  36. package/dist/commands/lock.test.d.ts +2 -0
  37. package/dist/commands/lock.test.d.ts.map +1 -0
  38. package/dist/commands/lock.test.js +130 -0
  39. package/dist/commands/lock.test.js.map +1 -0
  40. package/dist/commands/project/create.js +3 -3
  41. package/dist/commands/project/create.js.map +1 -1
  42. package/dist/commands/project/create.test.js +2 -2
  43. package/dist/commands/project/create.test.js.map +1 -1
  44. package/dist/commands/project/delete.js +12 -12
  45. package/dist/commands/project/delete.js.map +1 -1
  46. package/dist/commands/project/delete.test.js +2 -2
  47. package/dist/commands/project/delete.test.js.map +1 -1
  48. package/dist/commands/project/list.js +4 -4
  49. package/dist/commands/project/list.js.map +1 -1
  50. package/dist/commands/project/list.test.js +2 -2
  51. package/dist/commands/project/list.test.js.map +1 -1
  52. package/dist/commands/project/rename.js +3 -3
  53. package/dist/commands/project/rename.js.map +1 -1
  54. package/dist/commands/project/rename.test.js +2 -2
  55. package/dist/commands/project/rename.test.js.map +1 -1
  56. package/dist/commands/project/show.js +4 -4
  57. package/dist/commands/project/show.js.map +1 -1
  58. package/dist/commands/project/show.test.js +2 -2
  59. package/dist/commands/project/show.test.js.map +1 -1
  60. package/dist/commands/sample-project.d.ts +10 -6
  61. package/dist/commands/sample-project.d.ts.map +1 -1
  62. package/dist/commands/sample-project.js +23 -13
  63. package/dist/commands/sample-project.js.map +1 -1
  64. package/dist/commands/sample-project.test.js +26 -24
  65. package/dist/commands/sample-project.test.js.map +1 -1
  66. package/dist/commands/stats.js +12 -12
  67. package/dist/commands/stats.js.map +1 -1
  68. package/dist/commands/stats.test.js +2 -2
  69. package/dist/commands/stats.test.js.map +1 -1
  70. package/dist/commands/status.d.ts +28 -0
  71. package/dist/commands/status.d.ts.map +1 -0
  72. package/dist/commands/status.js +88 -0
  73. package/dist/commands/status.js.map +1 -0
  74. package/dist/commands/status.test.d.ts +2 -0
  75. package/dist/commands/status.test.d.ts.map +1 -0
  76. package/dist/commands/status.test.js +47 -0
  77. package/dist/commands/status.test.js.map +1 -0
  78. package/dist/commands/sync.d.ts +47 -0
  79. package/dist/commands/sync.d.ts.map +1 -0
  80. package/dist/commands/sync.js +218 -0
  81. package/dist/commands/sync.js.map +1 -0
  82. package/dist/commands/sync.test.d.ts +2 -0
  83. package/dist/commands/sync.test.d.ts.map +1 -0
  84. package/dist/commands/sync.test.js +49 -0
  85. package/dist/commands/sync.test.js.map +1 -0
  86. package/dist/commands/task/add-dep.js +5 -5
  87. package/dist/commands/task/add-dep.js.map +1 -1
  88. package/dist/commands/task/add-dep.test.js +2 -2
  89. package/dist/commands/task/add-dep.test.js.map +1 -1
  90. package/dist/commands/task/add.js +3 -3
  91. package/dist/commands/task/add.js.map +1 -1
  92. package/dist/commands/task/add.test.js +2 -2
  93. package/dist/commands/task/add.test.js.map +1 -1
  94. package/dist/commands/task/archive.js +3 -3
  95. package/dist/commands/task/archive.js.map +1 -1
  96. package/dist/commands/task/archive.test.js +2 -2
  97. package/dist/commands/task/archive.test.js.map +1 -1
  98. package/dist/commands/task/checkpoint.js +3 -3
  99. package/dist/commands/task/checkpoint.js.map +1 -1
  100. package/dist/commands/task/checkpoint.test.js +2 -2
  101. package/dist/commands/task/checkpoint.test.js.map +1 -1
  102. package/dist/commands/task/claim.js +3 -3
  103. package/dist/commands/task/claim.js.map +1 -1
  104. package/dist/commands/task/claim.test.js +2 -2
  105. package/dist/commands/task/claim.test.js.map +1 -1
  106. package/dist/commands/task/comment.js +3 -3
  107. package/dist/commands/task/comment.js.map +1 -1
  108. package/dist/commands/task/comment.test.js +2 -2
  109. package/dist/commands/task/comment.test.js.map +1 -1
  110. package/dist/commands/task/complete.js +3 -3
  111. package/dist/commands/task/complete.js.map +1 -1
  112. package/dist/commands/task/complete.test.js +2 -2
  113. package/dist/commands/task/complete.test.js.map +1 -1
  114. package/dist/commands/task/history.js +3 -3
  115. package/dist/commands/task/history.js.map +1 -1
  116. package/dist/commands/task/history.test.js +2 -2
  117. package/dist/commands/task/history.test.js.map +1 -1
  118. package/dist/commands/task/list.js +4 -4
  119. package/dist/commands/task/list.js.map +1 -1
  120. package/dist/commands/task/list.test.js +2 -2
  121. package/dist/commands/task/list.test.js.map +1 -1
  122. package/dist/commands/task/move.js +3 -3
  123. package/dist/commands/task/move.js.map +1 -1
  124. package/dist/commands/task/move.test.js +2 -2
  125. package/dist/commands/task/move.test.js.map +1 -1
  126. package/dist/commands/task/next.js +3 -3
  127. package/dist/commands/task/next.js.map +1 -1
  128. package/dist/commands/task/next.test.js +2 -2
  129. package/dist/commands/task/next.test.js.map +1 -1
  130. package/dist/commands/task/release.js +3 -3
  131. package/dist/commands/task/release.js.map +1 -1
  132. package/dist/commands/task/release.test.js +2 -2
  133. package/dist/commands/task/release.test.js.map +1 -1
  134. package/dist/commands/task/remove-dep.js +3 -3
  135. package/dist/commands/task/remove-dep.js.map +1 -1
  136. package/dist/commands/task/remove-dep.test.js +2 -2
  137. package/dist/commands/task/remove-dep.test.js.map +1 -1
  138. package/dist/commands/task/reopen.js +3 -3
  139. package/dist/commands/task/reopen.js.map +1 -1
  140. package/dist/commands/task/reopen.test.js +2 -2
  141. package/dist/commands/task/reopen.test.js.map +1 -1
  142. package/dist/commands/task/search.js +4 -4
  143. package/dist/commands/task/search.js.map +1 -1
  144. package/dist/commands/task/search.test.js +2 -2
  145. package/dist/commands/task/search.test.js.map +1 -1
  146. package/dist/commands/task/set-status.js +3 -3
  147. package/dist/commands/task/set-status.js.map +1 -1
  148. package/dist/commands/task/set-status.test.js +2 -2
  149. package/dist/commands/task/set-status.test.js.map +1 -1
  150. package/dist/commands/task/show.js +3 -3
  151. package/dist/commands/task/show.js.map +1 -1
  152. package/dist/commands/task/show.test.js +2 -2
  153. package/dist/commands/task/show.test.js.map +1 -1
  154. package/dist/commands/task/steal.js +3 -3
  155. package/dist/commands/task/steal.js.map +1 -1
  156. package/dist/commands/task/steal.test.js +2 -2
  157. package/dist/commands/task/steal.test.js.map +1 -1
  158. package/dist/commands/task/stuck.js +4 -4
  159. package/dist/commands/task/stuck.js.map +1 -1
  160. package/dist/commands/task/stuck.test.js +2 -2
  161. package/dist/commands/task/stuck.test.js.map +1 -1
  162. package/dist/commands/task/update.js +3 -3
  163. package/dist/commands/task/update.js.map +1 -1
  164. package/dist/commands/task/update.test.js +2 -2
  165. package/dist/commands/task/update.test.js.map +1 -1
  166. package/dist/commands/validate.js +3 -3
  167. package/dist/commands/validate.js.map +1 -1
  168. package/dist/commands/validate.test.js +3 -3
  169. package/dist/commands/validate.test.js.map +1 -1
  170. package/dist/commands/which-db.d.ts +4 -4
  171. package/dist/commands/which-db.d.ts.map +1 -1
  172. package/dist/commands/which-db.js +8 -11
  173. package/dist/commands/which-db.js.map +1 -1
  174. package/dist/commands/which-db.test.js +9 -8
  175. package/dist/commands/which-db.test.js.map +1 -1
  176. package/dist/config.d.ts +18 -4
  177. package/dist/config.d.ts.map +1 -1
  178. package/dist/config.js +120 -13
  179. package/dist/config.js.map +1 -1
  180. package/dist/config.test.js +34 -110
  181. package/dist/config.test.js.map +1 -1
  182. package/dist/db.d.ts +16 -2
  183. package/dist/db.d.ts.map +1 -1
  184. package/dist/db.js +47 -17
  185. package/dist/db.js.map +1 -1
  186. package/dist/index.d.ts +2 -2
  187. package/dist/index.d.ts.map +1 -1
  188. package/dist/index.js +10 -2
  189. package/dist/index.js.map +1 -1
  190. package/dist/index.test.js +1 -1
  191. package/dist/index.test.js.map +1 -1
  192. package/dist/types.d.ts +22 -0
  193. package/dist/types.d.ts.map +1 -1
  194. package/package.json +6 -6
package/README.md CHANGED
@@ -1,164 +1,314 @@
1
- # HZL
1
+ # HZL (Hazel)
2
2
 
3
- **Lightweight task tracking for AI agents and swarms.**
3
+ **A shared task ledger for OpenClaw and poly-agent workflows.**
4
4
 
5
- HZL is a CLI-first task management system designed for solo developers working with multiple AI agents across multiple projects. It provides durable, local coordination without the overhead of team-oriented project management tools.
5
+ OpenClaw is great at doing work: running tools, coordinating sub-agents, and maintaining memory.
6
+ What it (and most agent tools) do not give you is a durable, shared backlog that survives:
6
7
 
7
- ## Why HZL?
8
+ - session boundaries
9
+ - crashes/reboots
10
+ - switching between different agent runtimes (Claude Code, Codex, Gemini, etc.)
8
11
 
9
- Most project management tools assume teams of humans collaborating on shared repositories. When you're a solo developer with AI agents as your primary collaborators, these tools become heavyweight:
12
+ HZL fills that gap: a lightweight, local-first task tracker that any agent can read/write.
10
13
 
11
- - **Repository-level storage is limiting.** You work across many repos and non-code projects. HZL stores tasks at the user level (`~/.hzl/data.db`), giving you one source of truth for all your work.
14
+ Using OpenClaw? Start here: [OpenClaw integration](#openclaw-integration)
12
15
 
13
- - **Agents need machine-readable interfaces.** HZL's CLI is optimized for programmatic use with `--json` output, atomic operations, and deterministic selection policies that agents can rely on.
16
+ Not using OpenClaw? Jump to: [Using HZL with Claude Code, Codex, Gemini CLI, or any coding agent](#using-hzl-with-claude-code-codex-gemini-cli-or-any-coding-agent)
14
17
 
15
- - **Concurrent agents need coordination.** Multiple agents working in parallel can atomically claim tasks, preventing conflicts. No heartbeats required—checkpoints let agents recover each other's work.
18
+ HZL provides:
16
19
 
17
- - **Active work, not archival.** HZL is designed for coordinating current projects, not storing years of task history. Track recent work, see progress stats, then let completed tasks fade.
20
+ - Projects and tasks
21
+ - Dependencies (`B` waits for `A`)
22
+ - Checkpoints (progress snapshots you can resume from)
23
+ - Leases (time-limited claims for multi-agent coordination)
24
+ - Event history (audit trail)
25
+ - Cloud Sync (multi-device/multi-agent synchronization with Turso)
26
+ - A CLI + JSON output that agents can script against
18
27
 
19
- ## Design Principles
28
+ Data is stored in SQLite. Default location: `$XDG_DATA_HOME/hzl/data.db` (fallback `~/.local/share/hzl/data.db`); Windows: `%LOCALAPPDATA%\\hzl\\data.db`.
20
29
 
21
- 1. **Tracker, not orchestrator.** HZL is a dumb ledger. It tracks work state—it doesn't orchestrate, prioritize, or decide what agents should do. Orchestration belongs elsewhere: in your control agents, workflow tools, or the agents themselves. This separation is intentional. HZL stays simple and reliable because it does one thing well.
30
+ ---
22
31
 
23
- 2. **Events are truth.** Every change is an append-only event. Current state is a projection. Full audit trails, reconstructable history.
32
+ ## Why another task tracker?
24
33
 
25
- 3. **Local-first.** SQLite with WAL mode. No network dependency. Works offline.
34
+ Because most task trackers are built for humans.
26
35
 
27
- 4. **Hierarchical but simple.** Projects contain tasks. Tasks can have subtasks and dependencies. That's it.
36
+ HZL is built for agents:
28
37
 
29
- ## Non-Goals
38
+ - It is backend-first, not UI-first. Think "task database with a CLI," not "another Trello."
39
+ - It is model-agnostic. Your tasks live outside any one vendor's memory or chat history.
40
+ - It is multi-agent safe. Leases prevent orphaned work and enable clean handoffs.
41
+ - It is resumable. Checkpoints let an agent crash, reboot, or swap models and keep going.
30
42
 
31
- HZL intentionally does not do these things:
43
+ If you already have a favorite human todo app, keep it.
44
+ If you need a shared task state that multiple agents can read/write, that is HZL.
32
45
 
33
- - **Orchestration.** HZL doesn't spawn agents, manage their lifecycles, assign work, or decide what should happen next. If you need a control agent that spawns sub-agents, that logic lives in your agent—not in HZL.
46
+ ---
34
47
 
35
- - **Task decomposition.** HZL won't break down "build the app" into subtasks. Humans or agents create the task hierarchy; HZL just tracks it.
48
+ ## Where HZL fits
36
49
 
37
- - **Smart scheduling.** `hzl next` uses simple, deterministic rules (priority, then FIFO). There's no learning, no load balancing, no routing based on agent capabilities. If you need smarter task selection, your orchestration layer decides and claims by ID.
50
+ ### 1) OpenClaw orchestrator + sub-agents
38
51
 
39
- - **Team collaboration.** No permissions, roles, notifications, or multi-user features. HZL assumes a single developer working with their agents.
52
+ ```mermaid
53
+ flowchart LR
54
+ You[You] --> OC["OpenClaw (orchestrator)"]
55
+ OC --> Tools["OpenClaw tools<br/>(browser, exec, email, etc.)"]
56
+ OC <--> HZL[(HZL task ledger)]
57
+ OC --> S1[Claude Code]
58
+ OC --> S2[Codex / other]
59
+ S1 <--> HZL
60
+ S2 <--> HZL
61
+ ```
40
62
 
41
- - **Cloud sync.** Local SQLite by design. If you want sync, export/import or backup to your own storage.
63
+ OpenClaw coordinates the work. HZL is the shared, durable task board that OpenClaw and its sub-agents can use across sessions.
42
64
 
43
- ## Installation
65
+ ### 2) Any poly-agent system (no OpenClaw required)
44
66
 
45
- Requires Node.js 22.14+.
67
+ ```mermaid
68
+ flowchart LR
69
+ U[You] --> O["Orchestrator (human or agent)"]
70
+ O <--> HZL[(HZL)]
71
+ O --> C[Claude Code]
72
+ O --> X[Codex]
73
+ O --> G[Gemini]
74
+ C <--> HZL
75
+ X <--> HZL
76
+ G <--> HZL
77
+ ```
46
78
 
47
- ```bash
48
- npm install -g hzl-cli
79
+ Same idea: once you are switching tools/models, you need a shared ledger.
80
+
81
+ ### 3) One agent, many sessions
82
+
83
+ ```mermaid
84
+ flowchart LR
85
+ U[You] --> A["Coding agent<br>(Claude Code, Codex, etc)"]
86
+ A <--> HZL
87
+ A --> R[Repo / files]
49
88
  ```
50
89
 
51
- Or from source:
90
+ Use HZL to persist "what's next" and "what changed" between sessions.
52
91
 
53
- ```bash
54
- git clone https://github.com/tmchow/hzl.git
55
- cd hzl
56
- npm install
57
- npm run build
58
- npm link packages/hzl-cli
92
+ ### 4) HZL as the backend for your own UI
93
+
94
+ ```mermaid
95
+ flowchart LR
96
+ UI[Lightweight web app] --> HZL
97
+ Agents[Agents + scripts] --> HZL
59
98
  ```
60
99
 
61
- ## Quick Start
100
+ If you want a human-friendly interface, build one. HZL stays the durable backend that both humans and agents can use.
62
101
 
63
- ```bash
64
- # Initialize the database
65
- hzl init
102
+ ---
66
103
 
67
- # Create a project
68
- hzl project create myapp
104
+ ## Quickstart
69
105
 
70
- # Create tasks
71
- hzl task add "Set up authentication" -P myapp --priority 2 --tags backend,auth
72
- hzl task add "Write API tests" -P myapp --depends-on <task-id>
106
+ ### Install
73
107
 
74
- # List available work
75
- hzl task list --project myapp --available
108
+ Requires Node.js 22.14+.
76
109
 
77
- # Claim and work
78
- hzl task next --project myapp
79
- hzl task claim <task-id> --author agent-1
80
- hzl task checkpoint <task-id> "Completed OAuth flow"
81
- hzl task complete <task-id>
110
+ ```bash
111
+ npm install -g hzl-cli
112
+ hzl init
82
113
  ```
83
114
 
84
- ## Key Commands
115
+ ### Enable Cloud Sync (Optional)
85
116
 
86
- ### Task Lifecycle
117
+ Sync with a Turso database for multi-device/multi-agent access:
87
118
 
88
119
  ```bash
89
- hzl task add <title> -P <project> # Create a task
90
- hzl task list [--project] [--status] # List tasks
91
- hzl task next [--project] # Show next claimable task
92
- hzl task claim <id> # Claim a task
93
- hzl task complete <id> # Mark done
120
+ hzl init --sync-url libsql://<db>.turso.io --auth-token <token>
94
121
  ```
95
122
 
96
- ### For Agents
123
+ ### Create a project and tasks
97
124
 
98
125
  ```bash
99
- # All commands support --json for structured output
100
- hzl task list --project myapp --available --json
101
- hzl task claim <id> --author agent-1 --json
126
+ hzl project create portland-trip
102
127
 
103
- # Checkpoints let agents recover each other's work
104
- hzl task checkpoint <id> "step-3-complete" --data '{"files":["a.ts","b.ts"]}'
105
- hzl task show <id> --json # Get task details + history
128
+ hzl task add "Check calendars for March weekends" -P portland-trip --priority 5
129
+ hzl task add "Research neighborhoods + activities" -P portland-trip --priority 4
130
+ hzl task add "Shortlist 2-3 weekend options" -P portland-trip --priority 3 \
131
+ --depends-on <calendar-task-id> --depends-on <research-task-id>
106
132
  ```
107
133
 
108
- ### Stuck Task Recovery
134
+ ### Work with checkpoints
109
135
 
110
136
  ```bash
111
- hzl task claim <id> --lease 30 # Claim with 30-minute lease
112
- hzl task stuck # Find tasks with expired leases
113
- hzl task steal <id> --if-expired # Reclaim expired work
137
+ hzl task claim <calendar-task-id> --author trevin-agent
138
+ hzl task checkpoint <calendar-task-id> "Found 3 options: Mar 7-9, 14-16, 21-23"
139
+ hzl task complete <calendar-task-id>
114
140
  ```
115
141
 
116
- ### Human Oversight
142
+ ### Link to supporting documents
143
+
144
+ Tasks stay lightweight. Use `--links` to reference design docs, brainstorms, or specs:
117
145
 
118
146
  ```bash
119
- hzl project list # See all projects
120
- hzl task show <id> # Task details + history
121
- hzl task comment <id> "guidance..." # Add steering comments
147
+ # Create a task that links to context documents
148
+ hzl task add "Implement auth flow per design" -P myapp --priority 3 \
149
+ --links docs/designs/auth-flow.md \
150
+ --links docs/brainstorm/2026-01-auth-options.md
151
+
152
+ # The agent reads linked files for context, task stays focused on the work
153
+ hzl task show <id> --json
154
+ # → { "links": ["docs/designs/auth-flow.md", "https://somedomain/resource.md"], ... }
122
155
  ```
123
156
 
124
- ### Dependencies
157
+ This pattern keeps tasks actionable while pointing agents to richer context stored elsewhere.
158
+
159
+ ### Use JSON output when scripting
125
160
 
126
161
  ```bash
127
- hzl task add-dep <task> <depends-on> # Task waits for dependency
128
- hzl task remove-dep <task> <dep> # Remove dependency
129
- hzl validate # Check for cycles
162
+ hzl task show <id> --json
163
+ hzl task next --project portland-trip --json
130
164
  ```
131
165
 
132
- ## Configuration
166
+ ---
167
+
168
+ ## Core concepts (the stuff that matters)
169
+
170
+ ### Tasks are units of work, not reminders
171
+
172
+ HZL is optimized for "do work, report progress, unblock the next step."
173
+
174
+ If you need time-based reminders, pair HZL with a scheduler (cron, OpenClaw cron, etc.).
175
+
176
+ ### Checkpoints are progress snapshots
177
+
178
+ A checkpoint is a compact, durable record of what happened:
179
+
180
+ - what you tried
181
+ - what you found
182
+ - what's still missing
183
+ - links, commands, or file paths needed to resume
184
+
185
+ ### Dependencies encode ordering
186
+
187
+ Dependencies are how an agent avoids premature work:
188
+
189
+ - "Don't search flights before you know dates."
190
+ - "Don't open a PR before tests pass."
191
+
192
+ ### Leases make multi-agent handoffs reliable
193
+
194
+ Leases are time-limited claims:
195
+
196
+ - A worker agent claims a task with `--lease 30`
197
+ - If it disappears, the lease expires
198
+ - Another agent can detect stuck work and take over
199
+
200
+ ### Cloud Sync & Offline-First
201
+
202
+ HZL uses a **local-first** architecture. You always read/write to a fast local database. Sync happens in the background via Turso/libSQL.
203
+
204
+ ```mermaid
205
+ flowchart TD
206
+ CLI[User / Agent CLI]
207
+ subgraph Local["Local Machine"]
208
+ Cache[(cache.db<br/>Reads)]
209
+ Events[(events.db<br/>Writes)]
210
+ Sync[Sync Engine]
211
+ end
212
+ Cloud[(Turso / Cloud)]
213
+
214
+ CLI -->|Read| Cache
215
+ CLI -->|Write| Events
216
+ Events -->|Rebuild| Cache
217
+ Events <-->|Sync| Sync
218
+ Sync <-->|Replication| Cloud
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Patterns
133
224
 
134
- HZL stores configuration in `~/.hzl/config.json`. The config file is created automatically when you run `hzl init`.
225
+ ### Pattern: Poly-agent backlog (recommended)
135
226
 
136
- To use a custom database location:
227
+ Conventions that help:
228
+
229
+ - Use consistent author IDs: `openclaw`, `claude-code`, `codex`, `gemini`, etc.
230
+ - Claim tasks before work.
231
+ - Checkpoint whenever you learn something that would be painful to rediscover.
232
+
233
+ Example handoff:
137
234
 
138
235
  ```bash
139
- hzl init --db ~/my-project/tasks.db
236
+ # Orchestrator creates task
237
+ hzl task add "Implement REST API endpoints" -P myapp --priority 2
238
+ TASK_ID=<id>
239
+
240
+ # Worker agent claims with a lease
241
+ hzl task claim "$TASK_ID" --author claude-code --lease 30
242
+ hzl task checkpoint "$TASK_ID" "Endpoints scaffolded; next: auth middleware"
243
+ hzl task complete "$TASK_ID"
140
244
  ```
141
245
 
142
- Subsequent commands will automatically use this database.
246
+ ### Pattern: Personal todo list (it works, but bring your own UI)
247
+
248
+ HZL can track personal tasks and has the advantage of centralizing agent and personal tasks.
249
+ This enables scenarios like OpenClaw assigning you tasks without needing to sync with other todo systems.
250
+
251
+ HZL itself is not trying to be a polished, human-first todo app. You bring other pieces for that.
252
+
253
+ If you want a todo app, build or use a UI:
254
+
255
+ - a tiny web app
256
+ - a TUI wrapper
257
+ - a menu bar widget
258
+
259
+ HZL stays the storage layer and concurrency-safe ledger underneath.
260
+
261
+ ---
262
+
263
+ ## Using HZL with Claude Code, Codex, Gemini CLI, or any coding agent
264
+
265
+ **Why HZL when your agent already has task tracking?**
143
266
 
144
- **Config resolution order (highest to lowest priority):**
145
- 1. `--db` flag
146
- 2. `HZL_DB` environment variable
147
- 3. `~/.hzl/config.json`
148
- 4. Default: `~/.hzl/data.db`
267
+ Many coding agents (like Claude Code) have built-in task management. HZL complements rather than replaces it:
149
268
 
150
- ## Environment Variables
269
+ - **Cross-agent workflows**: If you use Claude Code *and* Codex *and* Gemini, each has its own task system. HZL is the shared ledger they can all read/write.
270
+ - **Session persistence**: Built-in task trackers often reset between sessions. HZL persists to disk (and optionally to cloud).
271
+ - **Orchestration**: When one agent delegates to another, HZL provides the handoff mechanism (claim, checkpoint, complete).
272
+ - **Backup**: With cloud sync enabled, your task state survives machine failures.
151
273
 
152
- | Variable | Description |
153
- |----------|-------------|
154
- | `HZL_DB` | Override database location |
155
- | `HZL_CONFIG` | Override config file location (default: `~/.hzl/config.json`) |
156
- | `HZL_AUTHOR` | Default author for claims/comments |
157
- | `HZL_AGENT_ID` | Default agent identifier |
274
+ If you only ever use one agent and never need cross-session persistence, the built-in tracker is fine. Once you need durability or multi-agent coordination, reach for HZL.
158
275
 
159
- ## Claude Code Marketplace
276
+ If your coding agent supports an instruction file (for example `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, etc.), add a short policy so the agent reaches for HZL consistently.
160
277
 
161
- HZL includes a [Claude Code](https://claude.ai/code) plugin marketplace with skills that help AI agents work effectively with HZL.
278
+ ### Drop-in policy snippet
279
+
280
+ ```md
281
+ ### HZL task ledger (durable, cross-agent task tracking)
282
+
283
+ HZL is a shared task database that persists across sessions and works across different agents.
284
+ Use it when work spans multiple sessions, involves handoffs to other agents, or needs durable backup.
285
+ If this agent has built-in task tracking, HZL complements it for cross-agent and multi-session work.
286
+
287
+ When to use HZL:
288
+ - Multi-session work that must survive restarts
289
+ - Coordination with other agents (Claude Code ↔ Codex ↔ Gemini, etc.)
290
+ - Work delegated to sub-agents that need explicit handoff
291
+ - When you need task state backed up to cloud
292
+
293
+ Workflow:
294
+ 1) Create or use an HZL project for the work.
295
+ 2) Break work into tasks with dependencies. Use `--links` to reference design docs or specs.
296
+ 3) Claim tasks before work and checkpoint after meaningful progress.
297
+ 4) Use `--json` when producing output another tool will parse.
298
+
299
+ Key commands:
300
+ - `hzl project create <name>`
301
+ - `hzl task add "<title>" -P <project> [--depends-on <id>] [--links <file>]`
302
+ - `hzl task claim <id> --author <agent-id> [--lease 30]`
303
+ - `hzl task checkpoint <id> "<progress + next step>"`
304
+ - `hzl task complete <id>`
305
+ ```
306
+
307
+ That snippet is intentionally short. The goal is consistency, not ceremony.
308
+
309
+ ### Claude Code marketplace (optional)
310
+
311
+ HZL includes a Claude Code plugin marketplace with skills that help agents work effectively with HZL.
162
312
 
163
313
  ```bash
164
314
  # Add the marketplace
@@ -170,62 +320,92 @@ HZL includes a [Claude Code](https://claude.ai/code) plugin marketplace with ski
170
320
 
171
321
  See [`packages/hzl-marketplace`](./packages/hzl-marketplace) for details.
172
322
 
173
- ## Related Projects
323
+ ---
324
+
325
+ ## OpenClaw integration
174
326
 
175
- - [Beads](https://github.com/steveyegge/beads) - Steve Yegge's task management for agents
176
- - [beads-rust](https://github.com/Dicklesworthstone/beads_rust) - Rust port of Beads
327
+ OpenClaw is a self-hosted AI assistant that can coordinate tools and sub-agents.
328
+ HZL fits well as the task ledger that OpenClaw (and its sub-agents) can share.
177
329
 
178
- HZL takes a different approach: user-level storage, CLI-first for agents, and designed for solo developers coordinating multiple agents across projects.
330
+ ### Quick start (recommended)
179
331
 
180
- ## Development
332
+ Copy/paste this into an OpenClaw chat (single prompt):
181
333
 
182
- ```bash
183
- npm install
184
- npm run build
185
- npm test
186
- npm run lint
334
+ ```
335
+ Install HZL from https://github.com/tmchow/hzl and run hzl init. Install the HZL skill from https://www.clawhub.ai/tmchow/hzl. Then append the HZL policy from https://raw.githubusercontent.com/tmchow/hzl/main/docs/openclaw/tools-prompt.md to my TOOLS.md.
336
+ ```
337
+
338
+ ### Manual setup
339
+
340
+ 1) Install HZL on the machine running OpenClaw:
187
341
 
188
- # Try the sample project
189
- hzl sample-project
342
+ ```bash
343
+ npm install -g hzl-cli
344
+ hzl init
190
345
  ```
191
346
 
192
- ---
347
+ 2) Install the HZL skill from https://www.clawhub.ai/tmchow/hzl
348
+ Skill source (for reference only): **[`docs/openclaw/skill-hzl.md`](./docs/openclaw/skill-hzl.md)**
193
349
 
194
- ## CLAUDE.md / AGENTS.md Snippet
350
+ 3) Teach OpenClaw when to use HZL (important):
351
+ - Copy/paste from: **[`docs/openclaw/tools-prompt.md`](./docs/openclaw/tools-prompt.md)**
352
+ - Or tell OpenClaw to add this policy to `TOOLS.md`:
195
353
 
196
- Copy this into your project's `CLAUDE.md` or `AGENTS.md`:
354
+ ```
355
+ HZL is a tool available to you for task management in certain cases. I want you to add this information to your TOOLS.md in the right way so you remember how to use it:
356
+ https://raw.githubusercontent.com/tmchow/hzl/main/docs/openclaw/tools-prompt.md
357
+ ```
197
358
 
198
- ````markdown
199
- ## Task Management
359
+ ---
200
360
 
201
- This project uses [HZL](https://github.com/tmchow/hzl) for task tracking.
361
+ ## When to use HZL (and when not to)
202
362
 
203
- ### Choosing a project name
363
+ ### Use HZL when:
204
364
 
205
- Use a **stable identifier** you can always derive:
365
+ - work has multiple steps and you want explicit sequencing
366
+ - work spans multiple sessions (resume tomorrow with confidence)
367
+ - you are coordinating multiple agents or model providers
368
+ - you need durable status reporting (done / in progress / blocked / next)
369
+ - you want a task ledger your own UI can sit on top of
206
370
 
207
- - **Working in a repo?** Use the repository name (e.g., `hzl`, `my-app`)
208
- - **Long-lived agent?** Use your agent identity (e.g., `openclaw`, `kalids-openclaw`)
371
+ ### Consider something else when:
209
372
 
210
- Projects group related work. Don't create per-feature projects—keep them long-lived. If no project is specified, tasks default to `inbox`.
373
+ - you need time-based reminders or notifications (use a scheduler + a notifier)
374
+ - you need rich human workflow features (due dates, recurring tasks, calendar views)
375
+ - you are tracking an org-wide backlog (GitHub/Jira/etc. may be a better fit)
211
376
 
212
- ### Commands
377
+ ---
378
+
379
+ ## CLI reference (short)
213
380
 
214
381
  ```bash
382
+ # Setup
383
+ hzl init # Initialize database (add --sync-url for cloud)
384
+
215
385
  # Projects
216
- hzl project list # List all projects
217
- hzl project rename <old> <new> # Rename a project
386
+ hzl project create <name> # Create a project
387
+ hzl project list # List all projects
218
388
 
219
389
  # Tasks
220
- hzl task add "Task title" -P <project> # Create task
221
- hzl task next --project <project> --json # Show next available
222
- hzl task show <task-id> --json # Task details + history
223
- hzl task checkpoint <task-id> "<name>" # Save progress
224
- hzl task complete <task-id> # Mark done
225
- ```
390
+ hzl task add "<title>" -P <project> # Create task (--depends-on, --links, --priority)
391
+ hzl task list --project <project> # List tasks (--available for claimable only)
392
+ hzl task next --project <project> # Get highest priority available task
393
+
394
+ # Working
395
+ hzl task claim <id> --author <name> # Claim task (--lease <minutes> for expiry)
396
+ hzl task checkpoint <id> "<message>" # Save progress snapshot
397
+ hzl task complete <id> # Mark done
226
398
 
227
- Use `--json` for structured output. HZL handles atomic claiming.
228
- ````
399
+ # Coordination
400
+ hzl task stuck # Find expired leases
401
+ hzl task steal <id> --if-expired # Take over abandoned task
402
+ hzl task show <id> --json # Task details (--json for scripting)
403
+
404
+ # Diagnostics
405
+ hzl sync # Sync with cloud (if configured)
406
+ hzl status # Show database and sync state
407
+ hzl doctor # Health checks
408
+ ```
229
409
 
230
410
  ---
231
411
 
@@ -3,8 +3,8 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import { fileURLToPath } from 'url';
6
- import Database from 'better-sqlite3';
7
- import { initializeDb, closeDb } from '../../db.js';
6
+ import Database from 'libsql';
7
+ import { initializeDbFromPath, closeDb } from '../../db.js';
8
8
  import { createTestContext, hzlJson, hzlMayFail, } from './helpers.js';
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
@@ -32,7 +32,7 @@ describe('CLI Integration Tests', () => {
32
32
  it('creates database file', () => {
33
33
  const result = hzlJson(ctx, 'init');
34
34
  expect(result.created).toBe(true);
35
- expect(result.path).toBe(ctx.dbPath);
35
+ expect(result.eventsDbPath).toBe(ctx.dbPath);
36
36
  expect(fs.existsSync(ctx.dbPath)).toBe(true);
37
37
  });
38
38
  it('is idempotent', () => {
@@ -44,15 +44,15 @@ describe('CLI Integration Tests', () => {
44
44
  });
45
45
  it('returns path information', () => {
46
46
  const result = hzlJson(ctx, 'init');
47
- expect(result.path).toBe(ctx.dbPath);
47
+ expect(result.eventsDbPath).toBe(ctx.dbPath);
48
48
  expect(result.created).toBe(true);
49
49
  });
50
50
  });
51
51
  describe('which-db command', () => {
52
52
  it('returns resolved database path', () => {
53
53
  const result = hzlJson(ctx, 'which-db');
54
- expect(result.path).toBe(ctx.dbPath);
55
- expect(result.source).toBe('cli');
54
+ expect(result.eventsDbPath).toBe(ctx.dbPath);
55
+ expect(result.cacheDbPath).toBe(ctx.cachePath);
56
56
  });
57
57
  });
58
58
  describe('task lifecycle round-trip', () => {
@@ -107,14 +107,14 @@ describe('CLI Integration Tests', () => {
107
107
  const addResult = hzlJson(ctx, `task add-dep ${task2.task_id} ${task1.task_id}`);
108
108
  expect(addResult.task_id).toBe(task2.task_id);
109
109
  expect(addResult.depends_on_id).toBe(task1.task_id);
110
- const db = new Database(ctx.dbPath);
110
+ const db = new Database(ctx.cachePath);
111
111
  const deps = db
112
112
  .prepare('SELECT depends_on_id FROM task_dependencies WHERE task_id = ?')
113
113
  .all(task2.task_id);
114
114
  expect(deps.map((d) => d.depends_on_id)).toContain(task1.task_id);
115
115
  db.close();
116
116
  hzlJson(ctx, `task remove-dep ${task2.task_id} ${task1.task_id}`);
117
- const dbAfter = new Database(ctx.dbPath);
117
+ const dbAfter = new Database(ctx.cachePath);
118
118
  const depsAfter = dbAfter
119
119
  .prepare('SELECT depends_on_id FROM task_dependencies WHERE task_id = ?')
120
120
  .all(task2.task_id);
@@ -243,6 +243,7 @@ describe('CLI Integration Tests', () => {
243
243
  expect(fs.existsSync(exportPath)).toBe(true);
244
244
  const content = fs.readFileSync(exportPath, 'utf-8');
245
245
  const lines = content.trim().split('\n').filter((line) => line.length > 0);
246
+ // 1 project_created (inbox) + 2 task_created events
246
247
  expect(lines).toHaveLength(3);
247
248
  const events = lines.map((line) => JSON.parse(line));
248
249
  expect(events[0].type).toBe('project_created');
@@ -283,7 +284,7 @@ describe('CLI Integration Tests', () => {
283
284
  const task = addTask('inbox', 'Stuck task');
284
285
  hzlJson(ctx, `task set-status ${task.task_id} ready`);
285
286
  const pastLease = new Date(Date.now() - 60000).toISOString();
286
- const services = initializeDb(ctx.dbPath);
287
+ const services = initializeDbFromPath(ctx.dbPath);
287
288
  try {
288
289
  services.taskService.claimTask(task.task_id, {
289
290
  author: 'stalled-agent',