@hasna/todos 0.9.4 → 0.9.6
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 +250 -44
- package/dist/cli/index.js +7 -1
- package/dist/index.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
# @hasna/todos
|
|
2
2
|
|
|
3
|
-
Universal task management for AI coding agents. CLI + MCP server +
|
|
3
|
+
Universal task management for AI coding agents. CLI + MCP server + library, all sharing a single SQLite database.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **CLI** with interactive TUI (React/Ink) and JSON output
|
|
8
|
-
- **MCP server** for Claude, Codex, Gemini, and any MCP-compatible agent
|
|
7
|
+
- **CLI** with interactive TUI (React/Ink) and JSON output mode
|
|
8
|
+
- **MCP server** (29 tools) for Claude, Codex, Gemini, and any MCP-compatible agent
|
|
9
9
|
- **Library** for programmatic access from Node.js/Bun
|
|
10
|
+
- **Agent registration** with short UUID identity system
|
|
11
|
+
- **Task lists** for organizing tasks into named containers (backlog, sprint-1, bugs)
|
|
12
|
+
- **Task prefixes** with auto-incrementing short IDs per project (e.g., `APP-00001`)
|
|
13
|
+
- **Plans** as execution groups, separate from task list containers
|
|
10
14
|
- **SQLite** with WAL mode, optimistic locking, and automatic migrations
|
|
11
15
|
- Task dependencies with cycle detection
|
|
12
|
-
- Exclusive agent locking with auto-expiry
|
|
16
|
+
- Exclusive agent locking with 30-minute auto-expiry
|
|
13
17
|
- Full-text search across tasks
|
|
14
18
|
- Project auto-detection from git repositories
|
|
15
19
|
- Subtask hierarchies with cascade deletion
|
|
20
|
+
- Bidirectional sync with Claude Code, Codex, Gemini task lists
|
|
16
21
|
|
|
17
22
|
## Installation
|
|
18
23
|
|
|
@@ -20,31 +25,126 @@ Universal task management for AI coding agents. CLI + MCP server + web dashboard
|
|
|
20
25
|
bun add -g @hasna/todos
|
|
21
26
|
```
|
|
22
27
|
|
|
28
|
+
Or with npm:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g @hasna/todos
|
|
32
|
+
```
|
|
33
|
+
|
|
23
34
|
## Quick Start
|
|
24
35
|
|
|
25
36
|
```bash
|
|
37
|
+
# Register your agent (get a short UUID for identity)
|
|
38
|
+
todos init my-agent
|
|
39
|
+
|
|
26
40
|
# Create a task
|
|
27
41
|
todos add "Fix login bug" --priority high --tags bug,auth
|
|
28
42
|
|
|
29
|
-
# List tasks
|
|
43
|
+
# List active tasks
|
|
30
44
|
todos list
|
|
31
45
|
|
|
32
|
-
# Start working on a task
|
|
46
|
+
# Start working on a task (claims + locks it)
|
|
33
47
|
todos start <id>
|
|
34
48
|
|
|
35
49
|
# Mark complete
|
|
36
50
|
todos done <id>
|
|
37
51
|
|
|
38
|
-
#
|
|
39
|
-
todos
|
|
52
|
+
# Create a task list
|
|
53
|
+
todos lists --add "sprint-1"
|
|
54
|
+
|
|
55
|
+
# Add task to a list
|
|
56
|
+
todos add "Build search" --list <list-id>
|
|
40
57
|
|
|
41
58
|
# Register MCP server with AI agents
|
|
42
59
|
todos mcp --register all
|
|
60
|
+
|
|
61
|
+
# Launch interactive TUI
|
|
62
|
+
todos
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Agent Registration
|
|
66
|
+
|
|
67
|
+
Agents register once to get a persistent 8-character UUID. This ID is used to track task ownership, locking, and activity.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Register (idempotent — same name returns same ID)
|
|
71
|
+
todos init claude
|
|
72
|
+
# Agent registered:
|
|
73
|
+
# ID: 56783129
|
|
74
|
+
# Name: claude
|
|
75
|
+
|
|
76
|
+
# Use the ID on future commands
|
|
77
|
+
todos add "Fix bug" --agent 56783129
|
|
78
|
+
|
|
79
|
+
# List all registered agents
|
|
80
|
+
todos agents
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Registration is idempotent: calling `init` with the same name returns the existing agent and updates `last_seen_at`.
|
|
84
|
+
|
|
85
|
+
## Task Lists
|
|
86
|
+
|
|
87
|
+
Task lists are named containers for organizing tasks (like folders). They're separate from plans.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Create a task list
|
|
91
|
+
todos lists --add "backlog"
|
|
92
|
+
todos lists --add "sprint-1" --slug sprint-1 -d "Current sprint"
|
|
93
|
+
|
|
94
|
+
# List all task lists
|
|
95
|
+
todos lists
|
|
96
|
+
|
|
97
|
+
# Add tasks to a list
|
|
98
|
+
todos add "Build feature" --list <list-id>
|
|
99
|
+
|
|
100
|
+
# Filter tasks by list
|
|
101
|
+
todos list --list <list-id>
|
|
102
|
+
|
|
103
|
+
# Move a task to a different list
|
|
104
|
+
todos update <task-id> --list <list-id>
|
|
105
|
+
|
|
106
|
+
# Delete a list (tasks keep their data, just lose the list association)
|
|
107
|
+
todos lists --delete <list-id>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Task lists can be project-scoped or standalone. Slugs must be unique within a project.
|
|
111
|
+
|
|
112
|
+
## Task Prefixes & Short IDs
|
|
113
|
+
|
|
114
|
+
Every project gets an auto-generated prefix (e.g., "APP" from "My App"). When tasks are created under a project, they get a short ID prepended to the title:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Project "My App" has prefix "MYA"
|
|
118
|
+
todos add "Fix login bug"
|
|
119
|
+
# Creates: "MYA-00001: Fix login bug"
|
|
120
|
+
|
|
121
|
+
todos add "Add dark mode"
|
|
122
|
+
# Creates: "MYA-00002: Add dark mode"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Custom prefixes can be set when creating a project. Counters auto-increment per project.
|
|
126
|
+
|
|
127
|
+
## Plans
|
|
128
|
+
|
|
129
|
+
Plans are execution groups for organizing work. A task can belong to both a task list AND a plan.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Create a plan
|
|
133
|
+
todos plans --add "v2.0 Release"
|
|
134
|
+
|
|
135
|
+
# Show plan details
|
|
136
|
+
todos plans --show <plan-id>
|
|
137
|
+
|
|
138
|
+
# Complete a plan
|
|
139
|
+
todos plans --complete <plan-id>
|
|
140
|
+
|
|
141
|
+
# Assign tasks to a plan
|
|
142
|
+
todos add "Build API" --plan <plan-id>
|
|
43
143
|
```
|
|
44
144
|
|
|
45
145
|
## MCP Server
|
|
46
146
|
|
|
47
|
-
Register with
|
|
147
|
+
Register the MCP server with AI coding agents:
|
|
48
148
|
|
|
49
149
|
```bash
|
|
50
150
|
todos mcp --register claude # Claude Code
|
|
@@ -59,23 +159,60 @@ Or start manually via stdio:
|
|
|
59
159
|
todos-mcp
|
|
60
160
|
```
|
|
61
161
|
|
|
62
|
-
|
|
162
|
+
### MCP Tools (29)
|
|
163
|
+
|
|
164
|
+
| Category | Tools |
|
|
165
|
+
|----------|-------|
|
|
166
|
+
| **Tasks** | `create_task`, `list_tasks`, `get_task`, `update_task`, `delete_task`, `start_task`, `complete_task` |
|
|
167
|
+
| **Locking** | `lock_task`, `unlock_task` |
|
|
168
|
+
| **Dependencies** | `add_dependency`, `remove_dependency` |
|
|
169
|
+
| **Comments** | `add_comment` |
|
|
170
|
+
| **Projects** | `create_project`, `list_projects` |
|
|
171
|
+
| **Plans** | `create_plan`, `list_plans`, `get_plan`, `update_plan`, `delete_plan` |
|
|
172
|
+
| **Agents** | `register_agent`, `list_agents`, `get_agent` |
|
|
173
|
+
| **Task Lists** | `create_task_list`, `list_task_lists`, `get_task_list`, `update_task_list`, `delete_task_list` |
|
|
174
|
+
| **Search** | `search_tasks` |
|
|
175
|
+
| **Sync** | `sync` |
|
|
176
|
+
|
|
177
|
+
### MCP Resources
|
|
178
|
+
|
|
179
|
+
| URI | Description |
|
|
180
|
+
|-----|-------------|
|
|
181
|
+
| `todos://tasks` | All active tasks (pending + in_progress) |
|
|
182
|
+
| `todos://projects` | All registered projects |
|
|
183
|
+
| `todos://agents` | All registered agents |
|
|
184
|
+
| `todos://task-lists` | All task lists |
|
|
185
|
+
|
|
186
|
+
## Sync
|
|
63
187
|
|
|
64
|
-
|
|
188
|
+
Bidirectional sync with agent-specific task lists.
|
|
65
189
|
|
|
66
190
|
```bash
|
|
67
191
|
todos sync --agent claude --task-list <id>
|
|
68
192
|
todos sync --agent codex --task-list default
|
|
69
193
|
todos sync --all --task-list <id>
|
|
70
|
-
todos sync --prefer local
|
|
194
|
+
todos sync --prefer local # Resolve conflicts favoring local
|
|
195
|
+
todos sync --push # One-way: local → agent
|
|
196
|
+
todos sync --pull # One-way: agent → local
|
|
71
197
|
```
|
|
72
198
|
|
|
73
|
-
|
|
74
|
-
- `TODOS_SYNC_AGENTS` (comma-separated list for `--all`)
|
|
75
|
-
- `TODOS_TASK_LIST_ID` or `TODOS_<AGENT>_TASK_LIST`
|
|
76
|
-
- `TODOS_AGENT_TASKS_DIR` or `TODOS_<AGENT>_TASKS_DIR`
|
|
199
|
+
Claude uses native Claude Code task lists. Other agents use JSON files under `~/.todos/agents/<agent>/<task_list_id>/`.
|
|
77
200
|
|
|
78
|
-
|
|
201
|
+
### Environment Variables
|
|
202
|
+
|
|
203
|
+
| Variable | Description |
|
|
204
|
+
|----------|-------------|
|
|
205
|
+
| `TODOS_DB_PATH` | Database file path (`:memory:` for testing) |
|
|
206
|
+
| `TODOS_DB_SCOPE` | Set to `project` to force project-level DB |
|
|
207
|
+
| `TODOS_AUTO_PROJECT` | Set to `false` to disable auto-project detection |
|
|
208
|
+
| `TODOS_SYNC_AGENTS` | Comma-separated agent list for `--all` |
|
|
209
|
+
| `TODOS_TASK_LIST_ID` | Default task list ID for sync |
|
|
210
|
+
| `TODOS_<AGENT>_TASK_LIST` | Agent-specific task list ID |
|
|
211
|
+
| `TODOS_AGENT_TASKS_DIR` | Base directory for agent task files |
|
|
212
|
+
|
|
213
|
+
### Config File
|
|
214
|
+
|
|
215
|
+
`~/.todos/config.json`:
|
|
79
216
|
|
|
80
217
|
```json
|
|
81
218
|
{
|
|
@@ -84,51 +221,119 @@ Config file: `~/.todos/config.json`
|
|
|
84
221
|
"agent_tasks_dir": "/Users/you/.todos/agents",
|
|
85
222
|
"agents": {
|
|
86
223
|
"claude": { "task_list_id": "session-or-project-id" },
|
|
87
|
-
"codex": { "task_list_id": "default"
|
|
224
|
+
"codex": { "task_list_id": "default" }
|
|
88
225
|
}
|
|
89
226
|
}
|
|
90
227
|
```
|
|
91
228
|
|
|
92
229
|
## CLI Commands
|
|
93
230
|
|
|
231
|
+
### Task Operations
|
|
232
|
+
|
|
233
|
+
| Command | Description |
|
|
234
|
+
|---------|-------------|
|
|
235
|
+
| `todos add <title>` | Create a task (`-p` priority, `--tags`, `--list`, `--plan`, `--assign`, `--parent`) |
|
|
236
|
+
| `todos list` | List tasks (`-s` status, `-p` priority, `--list`, `--tags`, `-a` all) |
|
|
237
|
+
| `todos show <id>` | Show full task details with relations |
|
|
238
|
+
| `todos update <id>` | Update fields (`--title`, `-s`, `-p`, `--tags`, `--list`, `--assign`) |
|
|
239
|
+
| `todos start <id>` | Claim task, lock it, set to in_progress |
|
|
240
|
+
| `todos done <id>` | Mark task completed, release lock |
|
|
241
|
+
| `todos delete <id>` | Delete permanently |
|
|
242
|
+
| `todos lock <id>` | Acquire exclusive lock |
|
|
243
|
+
| `todos unlock <id>` | Release lock |
|
|
244
|
+
|
|
245
|
+
### Organization
|
|
246
|
+
|
|
247
|
+
| Command | Description |
|
|
248
|
+
|---------|-------------|
|
|
249
|
+
| `todos lists` | List task lists (`--add`, `--delete`, `--slug`, `-d`) |
|
|
250
|
+
| `todos plans` | List plans (`--add`, `--show`, `--delete`, `--complete`) |
|
|
251
|
+
| `todos projects` | List projects (`--add`, `--name`, `--task-list-id`) |
|
|
252
|
+
| `todos deps <id>` | Manage dependencies (`--needs`, `--remove`) |
|
|
253
|
+
| `todos comment <id> <text>` | Add a comment to a task |
|
|
254
|
+
| `todos search <query>` | Full-text search across tasks |
|
|
255
|
+
|
|
256
|
+
### Agent & System
|
|
257
|
+
|
|
94
258
|
| Command | Description |
|
|
95
259
|
|---------|-------------|
|
|
96
|
-
| `todos
|
|
97
|
-
| `todos
|
|
98
|
-
| `todos
|
|
99
|
-
| `todos
|
|
100
|
-
| `todos
|
|
101
|
-
| `todos
|
|
102
|
-
| `todos
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
| `todos mcp` | Start MCP server |
|
|
110
|
-
|
|
111
|
-
Use `--json` for JSON output on any command. Use `--agent <name>` to identify the calling agent.
|
|
260
|
+
| `todos init <name>` | Register agent, get short UUID (`-d` description) |
|
|
261
|
+
| `todos agents` | List registered agents |
|
|
262
|
+
| `todos sync` | Sync with agent task lists |
|
|
263
|
+
| `todos mcp` | MCP server (`--register`, `--unregister`) |
|
|
264
|
+
| `todos hooks install` | Install Claude Code auto-sync hooks |
|
|
265
|
+
| `todos export` | Export tasks (`-f json\|md`) |
|
|
266
|
+
| `todos upgrade` | Self-update to latest version |
|
|
267
|
+
|
|
268
|
+
### Global Options
|
|
269
|
+
|
|
270
|
+
All commands support: `--project <path>`, `--json`, `--agent <name>`, `--session <id>`.
|
|
271
|
+
|
|
272
|
+
Partial IDs work everywhere — use the first 8+ characters of any UUID.
|
|
112
273
|
|
|
113
274
|
## Library Usage
|
|
114
275
|
|
|
115
276
|
```typescript
|
|
116
|
-
import {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
277
|
+
import {
|
|
278
|
+
createTask,
|
|
279
|
+
listTasks,
|
|
280
|
+
completeTask,
|
|
281
|
+
registerAgent,
|
|
282
|
+
createTaskList,
|
|
283
|
+
createProject,
|
|
284
|
+
searchTasks,
|
|
285
|
+
} from "@hasna/todos";
|
|
286
|
+
|
|
287
|
+
// Register an agent
|
|
288
|
+
const agent = registerAgent({ name: "my-bot" });
|
|
289
|
+
|
|
290
|
+
// Create a project
|
|
291
|
+
const project = createProject({ name: "My App", path: "/app" });
|
|
292
|
+
|
|
293
|
+
// Create a task list
|
|
294
|
+
const backlog = createTaskList({ name: "Backlog", project_id: project.id });
|
|
295
|
+
|
|
296
|
+
// Create a task (gets short_id like "MYA-00001" auto-prepended)
|
|
297
|
+
const task = createTask({
|
|
298
|
+
title: "Fix login bug",
|
|
299
|
+
priority: "high",
|
|
300
|
+
project_id: project.id,
|
|
301
|
+
task_list_id: backlog.id,
|
|
302
|
+
agent_id: agent.id,
|
|
303
|
+
tags: ["bug", "auth"],
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// List and filter
|
|
307
|
+
const pending = listTasks({ status: "pending", task_list_id: backlog.id });
|
|
308
|
+
|
|
309
|
+
// Search
|
|
310
|
+
const results = searchTasks("login", project.id);
|
|
311
|
+
|
|
312
|
+
// Complete
|
|
120
313
|
completeTask(task.id);
|
|
121
314
|
```
|
|
122
315
|
|
|
123
316
|
## Database
|
|
124
317
|
|
|
125
|
-
SQLite
|
|
318
|
+
SQLite with automatic location detection:
|
|
126
319
|
|
|
127
320
|
1. `TODOS_DB_PATH` environment variable (`:memory:` for testing)
|
|
128
321
|
2. Nearest `.todos/todos.db` in current directory or any parent
|
|
129
322
|
3. `~/.todos/todos.db` global fallback
|
|
130
323
|
|
|
131
|
-
|
|
324
|
+
**Schema** (6 migrations, auto-applied):
|
|
325
|
+
|
|
326
|
+
| Table | Purpose |
|
|
327
|
+
|-------|---------|
|
|
328
|
+
| `projects` | Project registry with task prefix and counter |
|
|
329
|
+
| `tasks` | Main task table with short_id, versioning, locking |
|
|
330
|
+
| `task_lists` | Named containers for tasks |
|
|
331
|
+
| `agents` | Registered agent identities |
|
|
332
|
+
| `plans` | Execution groups |
|
|
333
|
+
| `task_dependencies` | DAG edges between tasks |
|
|
334
|
+
| `task_comments` | Notes on tasks |
|
|
335
|
+
| `task_tags` | Tag index for filtering |
|
|
336
|
+
| `sessions` | Agent session tracking |
|
|
132
337
|
|
|
133
338
|
## Development
|
|
134
339
|
|
|
@@ -136,9 +341,10 @@ Set `TODOS_DB_SCOPE=project` to force project-level DB at the git root (if found
|
|
|
136
341
|
git clone https://github.com/hasna/todos.git
|
|
137
342
|
cd todos
|
|
138
343
|
bun install
|
|
139
|
-
bun test # Run
|
|
344
|
+
bun test # Run 172 tests
|
|
140
345
|
bun run typecheck # TypeScript checking
|
|
141
346
|
bun run dev:cli # Run CLI in dev mode
|
|
347
|
+
bun run dev:mcp # Run MCP server in dev mode
|
|
142
348
|
```
|
|
143
349
|
|
|
144
350
|
## Architecture
|
|
@@ -146,10 +352,10 @@ bun run dev:cli # Run CLI in dev mode
|
|
|
146
352
|
```
|
|
147
353
|
src/
|
|
148
354
|
types/ TypeScript types, enums, custom errors
|
|
149
|
-
db/ SQLite data layer (tasks, projects, comments, sessions)
|
|
150
|
-
lib/ Business logic (search, sync)
|
|
355
|
+
db/ SQLite data layer (tasks, projects, agents, task-lists, plans, comments, sessions)
|
|
356
|
+
lib/ Business logic (search, sync, config)
|
|
151
357
|
cli/ Commander.js CLI + React/Ink TUI
|
|
152
|
-
mcp/ MCP server (stdio transport)
|
|
358
|
+
mcp/ MCP server (stdio transport, 29 tools)
|
|
153
359
|
index.ts Library re-exports
|
|
154
360
|
```
|
|
155
361
|
|
package/dist/cli/index.js
CHANGED
|
@@ -2531,8 +2531,14 @@ function nextTaskShortId(projectId, db) {
|
|
|
2531
2531
|
function ensureProject(name, path, db) {
|
|
2532
2532
|
const d = db || getDatabase();
|
|
2533
2533
|
const existing = getProjectByPath(path, d);
|
|
2534
|
-
if (existing)
|
|
2534
|
+
if (existing) {
|
|
2535
|
+
if (!existing.task_prefix) {
|
|
2536
|
+
const prefix = generatePrefix(existing.name, d);
|
|
2537
|
+
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
2538
|
+
return getProject(existing.id, d);
|
|
2539
|
+
}
|
|
2535
2540
|
return existing;
|
|
2541
|
+
}
|
|
2536
2542
|
return createProject({ name, path }, d);
|
|
2537
2543
|
}
|
|
2538
2544
|
var init_projects = __esm(() => {
|
package/dist/index.js
CHANGED
|
@@ -506,8 +506,14 @@ function nextTaskShortId(projectId, db) {
|
|
|
506
506
|
function ensureProject(name, path, db) {
|
|
507
507
|
const d = db || getDatabase();
|
|
508
508
|
const existing = getProjectByPath(path, d);
|
|
509
|
-
if (existing)
|
|
509
|
+
if (existing) {
|
|
510
|
+
if (!existing.task_prefix) {
|
|
511
|
+
const prefix = generatePrefix(existing.name, d);
|
|
512
|
+
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
513
|
+
return getProject(existing.id, d);
|
|
514
|
+
}
|
|
510
515
|
return existing;
|
|
516
|
+
}
|
|
511
517
|
return createProject({ name, path }, d);
|
|
512
518
|
}
|
|
513
519
|
|