opentasks 0.1.2 → 0.2.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 +218 -49
- package/dist/cli.d.ts +20 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +451 -15
- package/dist/cli.js.map +1 -1
- package/dist/client/client.d.ts +54 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +148 -4
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/core/discover.d.ts.map +1 -1
- package/dist/core/discover.js +7 -2
- package/dist/core/discover.js.map +1 -1
- package/dist/core/hash.d.ts.map +1 -1
- package/dist/core/hash.js +7 -0
- package/dist/core/hash.js.map +1 -1
- package/dist/core/id.d.ts +1 -1
- package/dist/core/id.d.ts.map +1 -1
- package/dist/core/id.js +2 -0
- package/dist/core/id.js.map +1 -1
- package/dist/core/worktree.js +4 -4
- package/dist/core/worktree.js.map +1 -1
- package/dist/daemon/events.d.ts +82 -0
- package/dist/daemon/events.d.ts.map +1 -0
- package/dist/daemon/events.js +71 -0
- package/dist/daemon/events.js.map +1 -0
- package/dist/daemon/flush.d.ts.map +1 -1
- package/dist/daemon/flush.js +12 -2
- package/dist/daemon/flush.js.map +1 -1
- package/dist/daemon/health-counters.d.ts +38 -0
- package/dist/daemon/health-counters.d.ts.map +1 -0
- package/dist/daemon/health-counters.js +42 -0
- package/dist/daemon/health-counters.js.map +1 -0
- package/dist/daemon/idempotency.d.ts +50 -0
- package/dist/daemon/idempotency.d.ts.map +1 -0
- package/dist/daemon/idempotency.js +104 -0
- package/dist/daemon/idempotency.js.map +1 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +1 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/ipc.d.ts +32 -2
- package/dist/daemon/ipc.d.ts.map +1 -1
- package/dist/daemon/ipc.js +54 -3
- package/dist/daemon/ipc.js.map +1 -1
- package/dist/daemon/lifecycle.d.ts.map +1 -1
- package/dist/daemon/lifecycle.js +302 -8
- package/dist/daemon/lifecycle.js.map +1 -1
- package/dist/daemon/location-state.d.ts +60 -1
- package/dist/daemon/location-state.d.ts.map +1 -1
- package/dist/daemon/location-state.js +136 -31
- package/dist/daemon/location-state.js.map +1 -1
- package/dist/daemon/methods/context-files.d.ts.map +1 -1
- package/dist/daemon/methods/context-files.js +4 -3
- package/dist/daemon/methods/context-files.js.map +1 -1
- package/dist/daemon/methods/events.d.ts +22 -0
- package/dist/daemon/methods/events.d.ts.map +1 -0
- package/dist/daemon/methods/events.js +30 -0
- package/dist/daemon/methods/events.js.map +1 -0
- package/dist/daemon/methods/graph.d.ts +7 -0
- package/dist/daemon/methods/graph.d.ts.map +1 -1
- package/dist/daemon/methods/graph.js +24 -14
- package/dist/daemon/methods/graph.js.map +1 -1
- package/dist/daemon/methods/lifecycle.d.ts +16 -0
- package/dist/daemon/methods/lifecycle.d.ts.map +1 -1
- package/dist/daemon/methods/lifecycle.js +3 -1
- package/dist/daemon/methods/lifecycle.js.map +1 -1
- package/dist/daemon/methods/location.js.map +1 -1
- package/dist/daemon/methods/sync.d.ts +113 -0
- package/dist/daemon/methods/sync.d.ts.map +1 -0
- package/dist/daemon/methods/sync.js +158 -0
- package/dist/daemon/methods/sync.js.map +1 -0
- package/dist/daemon/methods/tools.d.ts.map +1 -1
- package/dist/daemon/methods/tools.js +42 -8
- package/dist/daemon/methods/tools.js.map +1 -1
- package/dist/daemon/methods/watch.d.ts +24 -0
- package/dist/daemon/methods/watch.d.ts.map +1 -1
- package/dist/daemon/methods/watch.js +178 -88
- package/dist/daemon/methods/watch.js.map +1 -1
- package/dist/daemon/sessionlog-watcher.d.ts.map +1 -1
- package/dist/daemon/sessionlog-watcher.js +4 -0
- package/dist/daemon/sessionlog-watcher.js.map +1 -1
- package/dist/graph/EdgeTypeRegistry.d.ts.map +1 -1
- package/dist/graph/EdgeTypeRegistry.js +32 -0
- package/dist/graph/EdgeTypeRegistry.js.map +1 -1
- package/dist/graph/FederatedGraph.d.ts.map +1 -1
- package/dist/graph/GraphologyAdapter.d.ts.map +1 -1
- package/dist/graph/GraphologyAdapter.js.map +1 -1
- package/dist/graph/HydratingFederatedGraph.d.ts.map +1 -1
- package/dist/graph/HydratingFederatedGraph.js +1 -1
- package/dist/graph/HydratingFederatedGraph.js.map +1 -1
- package/dist/graph/attempts.d.ts +50 -0
- package/dist/graph/attempts.d.ts.map +1 -0
- package/dist/graph/attempts.js +51 -0
- package/dist/graph/attempts.js.map +1 -0
- package/dist/graph/coordination.d.ts +4 -2
- package/dist/graph/coordination.d.ts.map +1 -1
- package/dist/graph/coordination.js +77 -85
- package/dist/graph/coordination.js.map +1 -1
- package/dist/graph/expansion.d.ts.map +1 -1
- package/dist/graph/expansion.js +1 -1
- package/dist/graph/expansion.js.map +1 -1
- package/dist/graph/git-graph-syncer.d.ts +24 -0
- package/dist/graph/git-graph-syncer.d.ts.map +1 -1
- package/dist/graph/git-graph-syncer.js +111 -26
- package/dist/graph/git-graph-syncer.js.map +1 -1
- package/dist/graph/provider-store.d.ts +12 -1
- package/dist/graph/provider-store.d.ts.map +1 -1
- package/dist/graph/provider-store.js +35 -8
- package/dist/graph/provider-store.js.map +1 -1
- package/dist/graph/query.d.ts.map +1 -1
- package/dist/graph/query.js +18 -2
- package/dist/graph/query.js.map +1 -1
- package/dist/graph/store.d.ts +18 -2
- package/dist/graph/store.d.ts.map +1 -1
- package/dist/graph/store.js +43 -0
- package/dist/graph/store.js.map +1 -1
- package/dist/graph/sync.js +1 -1
- package/dist/graph/sync.js.map +1 -1
- package/dist/graph/types.d.ts +9 -1
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/types.js.map +1 -1
- package/dist/graph/validation.d.ts.map +1 -1
- package/dist/graph/validation.js +20 -2
- package/dist/graph/validation.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/materialization/archiver.d.ts.map +1 -1
- package/dist/materialization/archiver.js.map +1 -1
- package/dist/materialization/snapshot.d.ts.map +1 -1
- package/dist/materialization/snapshot.js +1 -1
- package/dist/materialization/snapshot.js.map +1 -1
- package/dist/materialization/types.d.ts +7 -5
- package/dist/materialization/types.d.ts.map +1 -1
- package/dist/materialization/types.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +308 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/providers/beads.d.ts.map +1 -1
- package/dist/providers/beads.js +2 -0
- package/dist/providers/beads.js.map +1 -1
- package/dist/providers/claude-tasks.d.ts.map +1 -1
- package/dist/providers/claude-tasks.js.map +1 -1
- package/dist/providers/global.js +1 -1
- package/dist/providers/global.js.map +1 -1
- package/dist/providers/location.d.ts.map +1 -1
- package/dist/providers/location.js.map +1 -1
- package/dist/providers/map-client-factory.d.ts.map +1 -1
- package/dist/providers/map-client-factory.js +1 -1
- package/dist/providers/map-client-factory.js.map +1 -1
- package/dist/providers/map-connector.js +1 -1
- package/dist/providers/map-connector.js.map +1 -1
- package/dist/providers/map-event-bridge.d.ts.map +1 -1
- package/dist/providers/map-event-bridge.js +1 -0
- package/dist/providers/map-event-bridge.js.map +1 -1
- package/dist/providers/map.d.ts.map +1 -1
- package/dist/providers/map.js +5 -2
- package/dist/providers/map.js.map +1 -1
- package/dist/providers/native.d.ts.map +1 -1
- package/dist/providers/native.js +21 -8
- package/dist/providers/native.js.map +1 -1
- package/dist/providers/registry.js +1 -1
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/sudocode.d.ts.map +1 -1
- package/dist/providers/sudocode.js +5 -11
- package/dist/providers/sudocode.js.map +1 -1
- package/dist/providers/traits/Reconcilable.d.ts.map +1 -1
- package/dist/providers/traits/TaskManageable.d.ts +15 -1
- package/dist/providers/traits/TaskManageable.d.ts.map +1 -1
- package/dist/providers/traits/TaskManageable.js +26 -0
- package/dist/providers/traits/TaskManageable.js.map +1 -1
- package/dist/providers/traits/Watchable.d.ts +6 -0
- package/dist/providers/traits/Watchable.d.ts.map +1 -1
- package/dist/providers/traits/Watchable.js.map +1 -1
- package/dist/providers/types.d.ts +1 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/schema/edges.d.ts +5 -1
- package/dist/schema/edges.d.ts.map +1 -1
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/nodes.d.ts +55 -2
- package/dist/schema/nodes.d.ts.map +1 -1
- package/dist/schema/storage.d.ts +2 -0
- package/dist/schema/storage.d.ts.map +1 -1
- package/dist/schema/validation.d.ts +6 -2
- package/dist/schema/validation.d.ts.map +1 -1
- package/dist/schema/validation.js +18 -1
- package/dist/schema/validation.js.map +1 -1
- package/dist/sessionlog/agent/registry.js.map +1 -1
- package/dist/sessionlog/agent/session-types.d.ts.map +1 -1
- package/dist/sessionlog/agent/types.d.ts.map +1 -1
- package/dist/sessionlog/commands/clean.js +1 -1
- package/dist/sessionlog/commands/clean.js.map +1 -1
- package/dist/sessionlog/commands/disable.d.ts.map +1 -1
- package/dist/sessionlog/commands/disable.js.map +1 -1
- package/dist/sessionlog/commands/doctor.js +1 -1
- package/dist/sessionlog/commands/doctor.js.map +1 -1
- package/dist/sessionlog/commands/enable.js +1 -1
- package/dist/sessionlog/commands/enable.js.map +1 -1
- package/dist/sessionlog/commands/resume.js +1 -1
- package/dist/sessionlog/commands/resume.js.map +1 -1
- package/dist/sessionlog/commands/rewind.d.ts.map +1 -1
- package/dist/sessionlog/commands/rewind.js +1 -1
- package/dist/sessionlog/commands/rewind.js.map +1 -1
- package/dist/sessionlog/commands/status.d.ts.map +1 -1
- package/dist/sessionlog/commands/status.js.map +1 -1
- package/dist/sessionlog/hooks/lifecycle.d.ts.map +1 -1
- package/dist/sessionlog/hooks/lifecycle.js +3 -3
- package/dist/sessionlog/hooks/lifecycle.js.map +1 -1
- package/dist/sessionlog/security/redaction.js +1 -1
- package/dist/sessionlog/security/redaction.js.map +1 -1
- package/dist/sessionlog/store/checkpoint-store.d.ts.map +1 -1
- package/dist/sessionlog/store/checkpoint-store.js +2 -2
- package/dist/sessionlog/store/checkpoint-store.js.map +1 -1
- package/dist/sessionlog/store/native-store.d.ts.map +1 -1
- package/dist/sessionlog/store/native-store.js.map +1 -1
- package/dist/sessionlog/store/session-store.d.ts.map +1 -1
- package/dist/sessionlog/store/session-store.js +1 -1
- package/dist/sessionlog/store/session-store.js.map +1 -1
- package/dist/sessionlog/strategy/attribution.js +2 -2
- package/dist/sessionlog/strategy/attribution.js.map +1 -1
- package/dist/sessionlog/strategy/common.d.ts +1 -1
- package/dist/sessionlog/strategy/common.d.ts.map +1 -1
- package/dist/sessionlog/strategy/common.js +3 -3
- package/dist/sessionlog/strategy/common.js.map +1 -1
- package/dist/sessionlog/strategy/content-overlap.d.ts.map +1 -1
- package/dist/sessionlog/strategy/content-overlap.js +1 -1
- package/dist/sessionlog/strategy/content-overlap.js.map +1 -1
- package/dist/sessionlog/strategy/manual-commit.d.ts.map +1 -1
- package/dist/sessionlog/strategy/manual-commit.js +6 -16
- package/dist/sessionlog/strategy/manual-commit.js.map +1 -1
- package/dist/sessionlog/utils/preview-rewind.d.ts.map +1 -1
- package/dist/sessionlog/utils/preview-rewind.js +1 -1
- package/dist/sessionlog/utils/preview-rewind.js.map +1 -1
- package/dist/sessionlog/utils/trailers.d.ts.map +1 -1
- package/dist/sessionlog/utils/trailers.js +0 -1
- package/dist/sessionlog/utils/trailers.js.map +1 -1
- package/dist/storage/interface.d.ts +64 -0
- package/dist/storage/interface.d.ts.map +1 -1
- package/dist/storage/interface.js.map +1 -1
- package/dist/storage/jsonl.js +2 -2
- package/dist/storage/jsonl.js.map +1 -1
- package/dist/storage/sqlite-schema.d.ts +8 -2
- package/dist/storage/sqlite-schema.d.ts.map +1 -1
- package/dist/storage/sqlite-schema.js +10 -1
- package/dist/storage/sqlite-schema.js.map +1 -1
- package/dist/storage/sqlite.d.ts +16 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +134 -2
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/link.js +1 -1
- package/dist/tools/link.js.map +1 -1
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +8 -29
- package/dist/tools/query.js.map +1 -1
- package/dist/tools/task.d.ts.map +1 -1
- package/dist/tools/task.js +115 -5
- package/dist/tools/task.js.map +1 -1
- package/dist/tools/types.d.ts +84 -3
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/version.d.ts +14 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +44 -0
- package/dist/version.js.map +1 -0
- package/package.json +32 -7
- package/skills/opentasks/SKILL.md +226 -0
- package/skills/opentasks/dependency-management.md +119 -0
- package/skills/opentasks/feedback-and-review.md +100 -0
- package/skills/opentasks/linking-external-data.md +103 -0
- package/skills/opentasks/spec-to-implementation.md +171 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Ngai
|
|
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,42 +1,120 @@
|
|
|
1
1
|
# OpenTasks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/alexngai/opentasks/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/opentasks)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
|
|
8
|
+
**One dependency graph across every task system you already use.** OpenTasks is a
|
|
9
|
+
local-first graph layer that links Claude Tasks, Beads, and native tasks — with
|
|
10
|
+
Jira, Linear, and other trackers on the roadmap ([docs/STATUS.md](docs/STATUS.md)) —
|
|
11
|
+
so you can ask *what's blocked* and *what's ready* across all of them at once.
|
|
4
12
|
|
|
5
13
|
```
|
|
6
14
|
npm install opentasks
|
|
7
15
|
```
|
|
8
16
|
|
|
17
|
+
## Why OpenTasks
|
|
18
|
+
|
|
19
|
+
- **It's the relationship layer, not another tracker.** Keep using Beads, Jira, or
|
|
20
|
+
Claude Tasks. OpenTasks adds typed edges (`blocks`, `depends-on`, `implements`,
|
|
21
|
+
`verifies`, …) *between* items that live in different systems — the layer none of
|
|
22
|
+
them have.
|
|
23
|
+
- **Cross-system `ready` / `blocked` in one query.** Surface unblocked work, or the
|
|
24
|
+
chain holding something up, spanning every connected provider at once.
|
|
25
|
+
- **Local-first, no server to run.** A tiny Unix-socket daemon auto-starts on first
|
|
26
|
+
use. `graph.jsonl` is the git-tracked source of truth; SQLite is a rebuildable
|
|
27
|
+
cache. Nothing to host, nothing to sign up for.
|
|
28
|
+
- **Agent-native.** Ships an MCP server (22 tools across 5 scopes) and is built for
|
|
29
|
+
multi-agent work: collision-resistant IDs, task leases/claims, idempotent writes,
|
|
30
|
+
change events, and a git merge driver so concurrent worktrees don't clobber the graph.
|
|
31
|
+
- **Federated and offline-tolerant.** Providers own their content; remote items
|
|
32
|
+
resolve on demand and disappear cleanly when a backend is unreachable — no stale data.
|
|
33
|
+
|
|
34
|
+
### How it compares
|
|
35
|
+
|
|
36
|
+
| | Task trackers (Beads / Jira / Linear / Claude Tasks) | Orchestration engines | **OpenTasks** |
|
|
37
|
+
|--|:--:|:--:|:--:|
|
|
38
|
+
| Stores task content | yes | sometimes | no — delegates to providers |
|
|
39
|
+
| Typed edges *across* systems | no | no | **yes** |
|
|
40
|
+
| `ready` / `blocked` across systems | no | no | **yes** |
|
|
41
|
+
| Local-first, no server | varies | no | **yes** |
|
|
42
|
+
| Built for agents (MCP, leases, idempotency) | no | varies | **yes** |
|
|
43
|
+
|
|
44
|
+
OpenTasks doesn't replace your tracker — it connects them. See
|
|
45
|
+
[What This Is Not](#what-this-is-not).
|
|
46
|
+
|
|
9
47
|
## Quick Start
|
|
10
48
|
|
|
49
|
+
OpenTasks runs as a small daemon that the CLI, the MCP server, and the client all
|
|
50
|
+
talk to over a Unix socket. **You don't manage it** — the first command that needs
|
|
51
|
+
it starts one automatically (opt out with `--no-autostart`). If it ever misbehaves,
|
|
52
|
+
see [docs/TROUBLESHOOTING.md](./docs/TROUBLESHOOTING.md).
|
|
53
|
+
|
|
54
|
+
### CLI
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install -g opentasks # or run any command below via `npx opentasks …`
|
|
58
|
+
|
|
59
|
+
opentasks init # create .opentasks/ in your repo
|
|
60
|
+
opentasks create --type task --title "Wire up auth" --status open
|
|
61
|
+
opentasks create --type task --title "Add OAuth provider" --status open
|
|
62
|
+
|
|
63
|
+
opentasks ready # unblocked tasks, ready to work on
|
|
64
|
+
opentasks list # open tasks (closed hidden)
|
|
65
|
+
opentasks tree t-xxxx # a task and its blocker tree
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`ready` / `list` / `blocked` / `tree` are compact, token-light views. Linking
|
|
69
|
+
edges, claiming tasks with a lease, and arbitrary graph queries are each one more
|
|
70
|
+
subcommand — run `opentasks help`.
|
|
71
|
+
|
|
72
|
+
### MCP (Claude Code / agents)
|
|
73
|
+
|
|
74
|
+
Register the server once; the daemon auto-starts on first use:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
claude mcp add opentasks -- npx opentasks mcp --scope all
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
…or in `.mcp.json`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"opentasks": { "command": "npx", "args": ["opentasks", "mcp", "--scope", "all"] }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Scopes are `tasks` (default), `graph`, `annotate`, `context`, `attempts`; `--scope all`
|
|
91
|
+
enables everything. The agent gets `create_task`, `claim_task`, `query`, `events_since`,
|
|
92
|
+
and the rest.
|
|
93
|
+
|
|
94
|
+
### Programmatic (embedders)
|
|
95
|
+
|
|
11
96
|
```typescript
|
|
12
|
-
import {
|
|
97
|
+
import { createClient } from 'opentasks'
|
|
13
98
|
|
|
14
|
-
|
|
15
|
-
await link({
|
|
16
|
-
fromId: 't-x7k9',
|
|
17
|
-
toId: 'jira://PROJ-123',
|
|
18
|
-
type: 'blocks',
|
|
19
|
-
})
|
|
99
|
+
const client = createClient({ autoConnect: true })
|
|
20
100
|
|
|
21
101
|
// What's ready to work on?
|
|
22
|
-
const ready = await query({ ready: {} })
|
|
102
|
+
const ready = await client.query({ ready: {} })
|
|
23
103
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
blockers: { nodeId: 't-x7k9', transitive: true },
|
|
27
|
-
})
|
|
104
|
+
// Connect a task to an external reference
|
|
105
|
+
await client.link({ fromId: 't-x7k9', toId: 'beads://./bd-xyz', type: 'blocks' })
|
|
28
106
|
|
|
29
|
-
//
|
|
30
|
-
await
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
content: 'Needs error handling for token refresh',
|
|
34
|
-
type: 'suggestion',
|
|
35
|
-
anchor: { text: 'OAuth2 with PKCE' },
|
|
36
|
-
},
|
|
37
|
-
})
|
|
107
|
+
// What blocks this task (full chain)?
|
|
108
|
+
const blockers = await client.query({ blockers: { nodeId: 't-x7k9', transitive: true } })
|
|
109
|
+
|
|
110
|
+
await client.disconnect()
|
|
38
111
|
```
|
|
39
112
|
|
|
113
|
+
> The top-level `link`/`query`/`annotate` exports are the lower-level forms that
|
|
114
|
+
> take a `GraphStore` as their first argument — see [Programmatic API](#programmatic-api)
|
|
115
|
+
> for daemon-free usage. The `client.*` methods above take params only and route
|
|
116
|
+
> through the daemon.
|
|
117
|
+
|
|
40
118
|
## The Problem
|
|
41
119
|
|
|
42
120
|
Claude Tasks, Beads, Jira, Linear, Taskmaster each manage their own content. None of them can express cross-system relationships. You cannot say "this Claude subtask implements that Beads issue" or "this Beads issue is blocked by that Jira ticket."
|
|
@@ -78,6 +156,10 @@ You keep using each system's native tools. OpenTasks owns the graph.
|
|
|
78
156
|
|
|
79
157
|
## Three Tools
|
|
80
158
|
|
|
159
|
+
*Examples below use the bare tool form for brevity. Through a daemon, call them
|
|
160
|
+
as `client.link(...)` / `client.query(...)` / `client.annotate(...)` (params only);
|
|
161
|
+
the top-level `link`/`query`/`annotate` functions take a `GraphStore` first arg.*
|
|
162
|
+
|
|
81
163
|
### link()
|
|
82
164
|
|
|
83
165
|
Create or remove edges between any nodes.
|
|
@@ -88,7 +170,7 @@ await link({ fromId: 't-setup', toId: 't-impl', type: 'blocks' })
|
|
|
88
170
|
await link({ fromId: 't-setup', toId: 't-impl', type: 'blocks', remove: true })
|
|
89
171
|
```
|
|
90
172
|
|
|
91
|
-
Edge types: `blocks` (cycle-checked), `implements`, `references`, `related`, `parent-of`, `depends-on`, `discovered-from`, `duplicates`, `supersedes`. Add custom types as strings.
|
|
173
|
+
Edge types: `blocks` (cycle-checked), `implements`, `references`, `related`, `parent-of`, `child-of`, `depends-on`, `discovered-from`, `duplicates`, `supersedes`, `verifies`, `reproduces`. Add custom types as strings.
|
|
92
174
|
|
|
93
175
|
### query()
|
|
94
176
|
|
|
@@ -125,14 +207,17 @@ Types: `comment`, `suggestion`, `request`. Each can be resolved, dismissed, or r
|
|
|
125
207
|
|
|
126
208
|
## Nodes
|
|
127
209
|
|
|
128
|
-
|
|
210
|
+
Five types, all stored in `.opentasks/graph.jsonl`:
|
|
129
211
|
|
|
130
212
|
| Type | Prefix | Purpose |
|
|
131
213
|
|------|--------|---------|
|
|
132
|
-
|
|
|
133
|
-
| Task |
|
|
214
|
+
| Context | c- | Requirements, specs, user intent (inline or file-backed) |
|
|
215
|
+
| Task | t- | Actionable work with status (open / in_progress / blocked / closed) |
|
|
134
216
|
| Feedback | f- | Anchored comments on nodes, with threading |
|
|
135
|
-
| ExternalNode | e- | References to Jira,
|
|
217
|
+
| ExternalNode | e- | References to Beads — and (planned) Jira, Linear, GitHub |
|
|
218
|
+
| Attempt | a- | Records of work attempts, with `verifies` / `reproduces` edges |
|
|
219
|
+
|
|
220
|
+
Edges carry an `x-` prefix.
|
|
136
221
|
|
|
137
222
|
A typical feature graph looks like this:
|
|
138
223
|
|
|
@@ -140,7 +225,7 @@ A typical feature graph looks like this:
|
|
|
140
225
|
graph LR
|
|
141
226
|
S["c-a2b3<br/>Auth Spec"]
|
|
142
227
|
I1["t-x7k9<br/>Implement OAuth"]
|
|
143
|
-
I2["
|
|
228
|
+
I2["t-m4n5<br/>Add rate limiting"]
|
|
144
229
|
F["f-p8q9<br/>suggestion"]
|
|
145
230
|
EXT["e-jira<br/>PROJ-123"]
|
|
146
231
|
|
|
@@ -208,41 +293,50 @@ File-backed contexts never duplicate file content into the graph store. They rec
|
|
|
208
293
|
Expose the full tool interface via [Model Context Protocol](https://modelcontextprotocol.io):
|
|
209
294
|
|
|
210
295
|
```bash
|
|
211
|
-
opentasks mcp --scope tasks,graph,annotate,context
|
|
296
|
+
opentasks mcp --scope tasks,graph,annotate,context,attempts
|
|
212
297
|
```
|
|
213
298
|
|
|
214
|
-
|
|
299
|
+
Register with Claude Code (the MCP server auto-starts the daemon on first use):
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
claude mcp add opentasks -- npx opentasks mcp --scope all
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
22 tools across 5 scopes: `tasks` (CRUD + lifecycle + atomic claiming: `claim_task`, `claim_next`, `release_task`, `renew_claim`), `graph` (edges, queries, context summary, `events_since` change polling), `annotate` (feedback), `context` (context CRUD with file/snippet/inline sources), `attempts` (`record_attempt`, `list_attempts` with `verifies` edges).
|
|
215
306
|
|
|
216
307
|
## Programmatic API
|
|
217
308
|
|
|
218
|
-
For direct graph manipulation without the daemon
|
|
309
|
+
For direct graph manipulation without the daemon. `createStoreForLocation` wires
|
|
310
|
+
the SQLite cache and JSONL persister for a `.opentasks/` directory and initializes
|
|
311
|
+
the store:
|
|
219
312
|
|
|
220
313
|
```typescript
|
|
221
|
-
import {
|
|
314
|
+
import { createStoreForLocation } from 'opentasks'
|
|
222
315
|
|
|
223
|
-
const
|
|
224
|
-
const store = createGraphStore({ storage: persister })
|
|
316
|
+
const store = await createStoreForLocation('.opentasks')
|
|
225
317
|
|
|
226
|
-
const spec = await store.
|
|
318
|
+
const spec = await store.createNode({
|
|
227
319
|
type: 'context',
|
|
228
320
|
title: 'OAuth2 authentication',
|
|
229
321
|
content: 'Users authenticate via OAuth2 with PKCE...',
|
|
230
322
|
})
|
|
231
323
|
|
|
232
|
-
const
|
|
324
|
+
const task = await store.createNode({
|
|
233
325
|
type: 'task',
|
|
234
326
|
title: 'Implement OAuth2 flow',
|
|
235
327
|
status: 'open',
|
|
236
328
|
})
|
|
237
329
|
|
|
238
330
|
await store.createEdge({
|
|
239
|
-
from_id:
|
|
331
|
+
from_id: task.id,
|
|
240
332
|
to_id: spec.id,
|
|
241
333
|
type: 'implements',
|
|
242
334
|
})
|
|
243
335
|
|
|
244
|
-
const ready = await store.ready()
|
|
245
|
-
const blockers = await store.blockers(
|
|
336
|
+
const ready = await store.query.ready()
|
|
337
|
+
const blockers = await store.query.blockers(task.id)
|
|
338
|
+
|
|
339
|
+
await store.close() // flush pending changes to graph.jsonl
|
|
246
340
|
```
|
|
247
341
|
|
|
248
342
|
## Client Library
|
|
@@ -262,7 +356,7 @@ await client.disconnect()
|
|
|
262
356
|
|
|
263
357
|
```
|
|
264
358
|
.opentasks/
|
|
265
|
-
├── graph.jsonl #
|
|
359
|
+
├── graph.jsonl # Git-tracked source of truth (rewritten on flush)
|
|
266
360
|
├── tombstones.jsonl # Soft deletes with configurable TTL
|
|
267
361
|
├── cache.db # SQLite for fast queries (gitignored, rebuilt from JSONL)
|
|
268
362
|
├── config.json # Location config, providers, retention
|
|
@@ -286,15 +380,25 @@ graph TB
|
|
|
286
380
|
style MD fill:#f0f0f0,stroke:#999,stroke-dasharray: 5 5
|
|
287
381
|
```
|
|
288
382
|
|
|
289
|
-
JSONL is the source of truth (git-tracked
|
|
383
|
+
JSONL is the source of truth (git-tracked). The daemon writes it as a full-file snapshot on a debounced flush (not literally append-only). SQLite is the query cache (gitignored, rebuilt on startup). Markdown is optional human-readable expansion.
|
|
384
|
+
|
|
385
|
+
## Configuration
|
|
386
|
+
|
|
387
|
+
Everything lives in `.opentasks/config.json` (created by `opentasks init`); every
|
|
388
|
+
field is optional. Precedence is **defaults < `config.json` < `OPENTASKS_*` env
|
|
389
|
+
vars**. See the full reference — storage, daemon, providers, sync, reconciliation,
|
|
390
|
+
logging, and env-var overrides — in [docs/CONFIGURATION.md](./docs/CONFIGURATION.md).
|
|
391
|
+
|
|
392
|
+
Daemon acting up (socket not found, hangs, stale lock, `NODE_MODULE_VERSION`)?
|
|
393
|
+
See [docs/TROUBLESHOOTING.md](./docs/TROUBLESHOOTING.md).
|
|
290
394
|
|
|
291
395
|
## Providers
|
|
292
396
|
|
|
293
|
-
OpenTasks owns the graph. Providers own node content.
|
|
397
|
+
OpenTasks owns the graph. Providers own node content. Four patterns:
|
|
294
398
|
|
|
295
399
|
| Pattern | Use | Example |
|
|
296
400
|
|---------|-----|---------|
|
|
297
|
-
| Provider | Resolve URIs on demand | Jira, Linear, GitHub |
|
|
401
|
+
| Provider | Resolve URIs on demand | Jira, Linear, GitHub *(planned)* |
|
|
298
402
|
| Adapter | Delegate all CRUD to backend | Beads (`bd` CLI) |
|
|
299
403
|
| SyncTarget | Two-way sync | Sudocode |
|
|
300
404
|
| IPC Bridge | Federate across daemons | Global store (`~/.opentasks`) |
|
|
@@ -350,19 +454,25 @@ opentasks://../other-repo/c-c4d5 # Relative path
|
|
|
350
454
|
|
|
351
455
|
### Global Store
|
|
352
456
|
|
|
353
|
-
|
|
457
|
+
A shared store at `~/.opentasks/` you can use from any directory — handy for a
|
|
458
|
+
personal todo list that isn't tied to one repo. Commands are project-local by
|
|
459
|
+
default; pass `--global` (or set `OPENTASKS_GLOBAL=1`) to target the global store
|
|
460
|
+
instead.
|
|
354
461
|
|
|
355
462
|
```bash
|
|
356
463
|
# One-time setup
|
|
357
464
|
opentasks init --global
|
|
358
465
|
|
|
359
|
-
#
|
|
466
|
+
# Use from anywhere with --global (no per-project init needed)
|
|
360
467
|
cd /any/directory
|
|
361
|
-
opentasks create --type task --title "Read paper on transformers"
|
|
362
|
-
opentasks
|
|
468
|
+
opentasks create --type task --title "Read paper on transformers" --status open --global
|
|
469
|
+
opentasks ready --global
|
|
363
470
|
```
|
|
364
471
|
|
|
365
|
-
|
|
472
|
+
`--global` routes both the auto-started daemon and the client at `~/.opentasks/`,
|
|
473
|
+
so a task created from one directory is visible from any other. Without it, a
|
|
474
|
+
command operates on the project store it discovers: project `.opentasks/` > git
|
|
475
|
+
worktree > (fallback) global `~/.opentasks/`.
|
|
366
476
|
|
|
367
477
|
### Federation
|
|
368
478
|
|
|
@@ -429,7 +539,7 @@ graph TB
|
|
|
429
539
|
style W3 fill:#fff3cd,stroke:#ffc107
|
|
430
540
|
```
|
|
431
541
|
|
|
432
|
-
One daemon serves all worktrees. Workers redirect reads and writes to the manager. Hash-based IDs prevent collisions across concurrent agents.
|
|
542
|
+
One daemon serves all worktrees. Workers redirect reads and writes to the manager. Hash-based IDs prevent collisions across concurrent agents. A custom git merge driver handles branch merges of `graph.jsonl`.
|
|
433
543
|
|
|
434
544
|
```gitattributes
|
|
435
545
|
.opentasks/graph.jsonl merge=opentasks
|
|
@@ -445,6 +555,52 @@ Hash-based, collision-resistant. Generated from UUID v4 through SHA256 and base3
|
|
|
445
555
|
| < 6,000 | 5 chars | t-x7k9p |
|
|
446
556
|
| < 35,000 | 6 chars | t-x7k9pm |
|
|
447
557
|
|
|
558
|
+
## Git Sync
|
|
559
|
+
|
|
560
|
+
> For the full consistency model — durability tiers, flush↔pull serialization, graceful shutdown, and the multi-machine last-writer-wins caveat — see [docs/SYNC.md](docs/SYNC.md).
|
|
561
|
+
|
|
562
|
+
OpenTasks can auto-commit and auto-push `graph.jsonl` to a git remote. When enabled in `.opentasks/config.json`:
|
|
563
|
+
|
|
564
|
+
```json
|
|
565
|
+
{
|
|
566
|
+
"sync": {
|
|
567
|
+
"git": {
|
|
568
|
+
"enabled": true,
|
|
569
|
+
"autoCommit": true,
|
|
570
|
+
"autoPush": true,
|
|
571
|
+
"pushDebounceMs": 5000,
|
|
572
|
+
"pullOnStartup": false
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
The daemon:
|
|
579
|
+
- **On startup** — installs the custom merge driver, optionally pulls from remote, starts auto-sync timer
|
|
580
|
+
- **On shutdown** — final commit+push if both flags are set
|
|
581
|
+
- **Continuously** — commits changes per `autoCommit`, pulls before push, respects debounce window
|
|
582
|
+
|
|
583
|
+
Four IPC methods are exposed for external control:
|
|
584
|
+
|
|
585
|
+
| Method | Purpose |
|
|
586
|
+
|--------|---------|
|
|
587
|
+
| `sync.now` | Runs the full cycle (commitIfDirty → pull → push). Returns `{ ran: false, reason }` if disabled. |
|
|
588
|
+
| `sync.pull` | Pulls only (used by external signal-driven convergence, e.g. OpenHive's MAP bridge). |
|
|
589
|
+
| `sync.status` | Returns `{ enabled, remote, autoCommit, autoPush, pullOnStartup, autoSyncRunning }`. |
|
|
590
|
+
| `sync.reload` | Re-reads `.opentasks/config.json` and hot-swaps the syncer. Lets external writers flip the flag without restart. |
|
|
591
|
+
|
|
592
|
+
Example usage:
|
|
593
|
+
|
|
594
|
+
```typescript
|
|
595
|
+
const status = await client.call('sync.status', {})
|
|
596
|
+
console.log(status) // { enabled: true, remote: 'origin', autoCommit: true, autoPush: true, ... }
|
|
597
|
+
|
|
598
|
+
await client.call('sync.now', {}) // Manual sync
|
|
599
|
+
await client.call('sync.reload', {}) // Re-read config after external update
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
448
604
|
## MAP Integration
|
|
449
605
|
|
|
450
606
|
OpenTasks integrates with the [Multi-Agent Protocol (MAP)](https://github.com/multi-agent-protocol/multi-agent-protocol) for multi-agent coordination and observability. Two independent components:
|
|
@@ -491,6 +647,19 @@ OpenTasks is not a replacement for Claude Tasks, Beads, Jira, or any existing to
|
|
|
491
647
|
|
|
492
648
|
It adds the relationship layer these tools lack.
|
|
493
649
|
|
|
650
|
+
## Documentation
|
|
651
|
+
|
|
652
|
+
| Doc | What's in it |
|
|
653
|
+
|-----|--------------|
|
|
654
|
+
| [CONFIGURATION.md](./docs/CONFIGURATION.md) | Full `config.json` reference + env overrides |
|
|
655
|
+
| [TROUBLESHOOTING.md](./docs/TROUBLESHOOTING.md) | Daemon issues, socket resolution, recovery |
|
|
656
|
+
| [ARCHITECTURE.md](./docs/ARCHITECTURE.md) | System design and layering |
|
|
657
|
+
| [PROVIDERS.md](./docs/PROVIDERS.md) | Provider patterns, MAP, reconciliation |
|
|
658
|
+
| [SYNC.md](./docs/SYNC.md) | Git sync model and consistency |
|
|
659
|
+
| [PERSISTENCE.md](./docs/PERSISTENCE.md) | Storage, JSONL/SQLite, archiving |
|
|
660
|
+
| [SCHEMA.md](./docs/SCHEMA.md) | Node and edge schema |
|
|
661
|
+
| [STATUS.md](./docs/STATUS.md) | As-built implementation status |
|
|
662
|
+
|
|
494
663
|
## Development
|
|
495
664
|
|
|
496
665
|
```bash
|
package/dist/cli.d.ts
CHANGED
|
@@ -5,14 +5,34 @@
|
|
|
5
5
|
* Command-line interface for OpenTasks.
|
|
6
6
|
* Phase 2 additions: init, connect, disconnect, connections
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the directory the daemon actually uses for its lock + socket.
|
|
10
|
+
*
|
|
11
|
+
* In a git repo the daemon runs in multi-location mode with its home at
|
|
12
|
+
* `<git-common-dir>/opentasks` (shared across worktrees); otherwise it's the
|
|
13
|
+
* location's own `.opentasks`. This must match `cmdDaemonStart`'s single-vs-multi
|
|
14
|
+
* decision, or the CLI's `checkExistingDaemon` probes look in the wrong place and
|
|
15
|
+
* never detect a running git-repo daemon (auto-start "times out", `daemon status`
|
|
16
|
+
* reports not_running, `daemon stop` no-ops — even though the daemon is up).
|
|
17
|
+
*/
|
|
18
|
+
export declare function daemonHomeFor(opentasksDir: string): string;
|
|
8
19
|
/**
|
|
9
20
|
* Extract a flag value from args. Returns undefined if not present.
|
|
10
21
|
*/
|
|
11
22
|
export declare function getFlag(args: string[], flag: string): string | undefined;
|
|
12
23
|
export declare function hasFlag(args: string[], flag: string): boolean;
|
|
24
|
+
export declare function cmdReady(args: string[]): Promise<void>;
|
|
25
|
+
export declare function cmdList(args: string[]): Promise<void>;
|
|
26
|
+
export declare function cmdBlocked(args: string[]): Promise<void>;
|
|
27
|
+
export declare function cmdCleanup(args: string[]): Promise<void>;
|
|
28
|
+
export declare function cmdTree(args: string[]): Promise<void>;
|
|
13
29
|
export declare function cmdLink(args: string[]): Promise<void>;
|
|
14
30
|
export declare function cmdQuery(args: string[]): Promise<void>;
|
|
15
31
|
export declare function cmdAnnotate(args: string[]): Promise<void>;
|
|
32
|
+
export declare function cmdClaim(args: string[]): Promise<void>;
|
|
33
|
+
export declare function cmdClaimNext(args: string[]): Promise<void>;
|
|
34
|
+
export declare function cmdRelease(args: string[]): Promise<void>;
|
|
35
|
+
export declare function cmdRenew(args: string[]): Promise<void>;
|
|
16
36
|
export declare function cmdCreate(args: string[]): Promise<void>;
|
|
17
37
|
export declare function cmdGet(args: string[]): Promise<void>;
|
|
18
38
|
export declare function cmdUpdate(args: string[]): Promise<void>;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAsiBH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG1D;AAiND;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAmED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB5D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC9D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC3D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3D;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5D;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/D;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5D;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9D;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC7D;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAa1D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC7D;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBrE;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAe7D;AAMD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgF9D;AAMD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC7D"}
|