bot-todo 0.2.1__tar.gz

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.
@@ -0,0 +1,318 @@
1
+ Metadata-Version: 2.3
2
+ Name: bot-todo
3
+ Version: 0.2.1
4
+ Summary: Manage canonical repository task files.
5
+ Classifier: Development Status :: 4 - Beta
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Operating System :: OS Independent
8
+ Classifier: Programming Language :: Python
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Natural Language :: English
13
+ Classifier: Topic :: Utilities
14
+ Requires-Dist: portalocker>=2.7
15
+ Requires-Python: >=3.11
16
+ Project-URL: Homepage, https://github.com/caltechads/bot-todo
17
+ Project-URL: Repository, https://github.com/caltechads/bot-todo
18
+ Description-Content-Type: text/markdown
19
+
20
+ # bot-todo
21
+
22
+ `bot-todo` is a command-line tool for a canonical, Git-friendly task backlog.
23
+ Humans and coding agents share one `TODO.md` per repository and mutate it only
24
+ through this CLI.
25
+
26
+ The package also ships a thin `todo` skill that teaches Codex, Claude, Cursor,
27
+ and Grok to call `bot-todo` instead of editing the task files by hand.
28
+
29
+ ## Requirements
30
+
31
+ - Python 3.11 or newer
32
+ - [uv](https://docs.astral.sh/uv/) for installation (recommended)
33
+
34
+ The only runtime dependency is `portalocker`, used for per-repository locking.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ uv tool bot-todo
40
+ ```
41
+
42
+ That puts `bot-todo` on your `PATH` in an isolated environment. While hacking
43
+ on the CLI itself, keep the checkout live:
44
+
45
+ ```bash
46
+ uv tool install --editable .
47
+ ```
48
+
49
+ One-shot use without a persistent install:
50
+
51
+ ```bash
52
+ uvx --from . bot-todo --help
53
+ ```
54
+
55
+ Confirm the install:
56
+
57
+ ```bash
58
+ bot-todo --version
59
+ ```
60
+
61
+ ## Quick start
62
+
63
+ From a project directory:
64
+
65
+ ```bash
66
+ bot-todo init
67
+ bot-todo add "Write the README" --type docs --priority P1 --acceptance "README covers install and usage"
68
+ bot-todo list
69
+ bot-todo claim T001 --actor glenn
70
+ bot-todo complete T001
71
+ bot-todo validate
72
+ ```
73
+
74
+ `init` always targets the current directory (or `--root`). Omit `--name` to use
75
+ the repository directory basename as the project heading. Successful human
76
+ `init` prints a copy-paste Task Management section for `AGENTS.md` or
77
+ `CLAUDE.md`; JSON `init` includes that text as `data.snippet`. `bot-todo`
78
+ never writes those instruction files. Other commands without a selector walk
79
+ from the current directory toward the filesystem root and use the nearest
80
+ `TODO.md`.
81
+
82
+ ## Usage
83
+
84
+ Global options precede the command:
85
+
86
+ ```text
87
+ bot-todo [--json] [--config PATH] [--root PATH | --repo NAME | --all] COMMAND ...
88
+ ```
89
+
90
+ `--help` and `--version` always print human text and succeed, including when
91
+ `--json` is present. Commands never prompt, page, or colorize output.
92
+
93
+ ### Commands
94
+
95
+ | Command | Purpose |
96
+ | --- | --- |
97
+ | `init [--name NAME]` | Create `TODO.md` and `TODO.archive.md` |
98
+ | `validate` | Check the canonical files |
99
+ | `list` | List open and review tasks |
100
+ | `show TASK_ID` | Print one task |
101
+ | `critical` | Highest-priority open task (even if blocked or claimed) |
102
+ | `actionable` | First unclaimed open task whose blockers are completed |
103
+ | `add TITLE --type TYPE` | Create an open task |
104
+ | `edit TASK_ID ...` | Change an open or review task |
105
+ | `claim TASK_ID --actor NAME` | Take an advisory claim on an open task |
106
+ | `release TASK_ID` | Drop a claim |
107
+ | `review TASK_ID` | Move an open task into review |
108
+ | `reopen TASK_ID` | Return a review task to open |
109
+ | `complete TASK_ID` | Mark an open or review task completed |
110
+ | `cancel TASK_ID --reason TEXT` | Mark an open or review task cancelled |
111
+ | `archive` | Move older Done tasks into the archive |
112
+ | `migrate` | Upgrade the task data format to 2 |
113
+ | `repos path` | Show the active configuration path |
114
+ | `repos list` | List configured repositories |
115
+ | `repos add [PATH]` | Add a repository entry; PATH defaults to `.` |
116
+ | `repos remove TARGET` | Remove a repository entry by name or path |
117
+ | `install-skill --target TARGET` | Install the bundled `todo` skill |
118
+ | `snippet` | Print the Task Management section for `AGENTS.md` or `CLAUDE.md` |
119
+
120
+ Task IDs look like `T001` and are never reused. Types are `bug`, `chore`,
121
+ `docs`, `feature`, and `ops`. Priorities are `P0`, `P1`, and `P2` (default
122
+ `P2`).
123
+
124
+ `add` requires either `--acceptance` or `--simple`. Repeatable options are
125
+ `--tag` and `--blocked-by`. `claim` records the actor, today's date, and the
126
+ current Git branch unless `--branch` is given. `review` clears that claim and
127
+ records today's date; `reopen` returns the task to open. `complete` and
128
+ `cancel` accept open or review tasks.
129
+
130
+ `init` writes Task Data Format 2. Format 1 files still load for queries;
131
+ mutations require `bot-todo migrate` first.
132
+
133
+ ```bash
134
+ bot-todo add "Fix the lock timeout" --type bug --priority P0 \
135
+ --acceptance "Conflict errors after five seconds, no partial writes" \
136
+ --tag locking --blocked-by T002
137
+
138
+ bot-todo edit T003 --title "Clarify lock timeout" --priority P1 --clear-blockers
139
+ bot-todo cancel T004 --reason "Superseded by T003"
140
+ ```
141
+
142
+ `edit` is a usage error if it requests no change. It also accepts `--simple`,
143
+ `--clear-context`, `--clear-related`, and `--clear-blockers`.
144
+
145
+ ### Selecting a repository
146
+
147
+ | Selector | Meaning | Allowed commands |
148
+ | --- | --- | --- |
149
+ | *(none)* | Nearest ancestor `TODO.md`; `init` uses the current directory | Task commands |
150
+ | `--root PATH` | Exact directory | Task commands |
151
+ | `--repo NAME` | One named entry from configuration | Task commands |
152
+ | `--all` | Every configured repository | `list`, `critical`, `actionable` only |
153
+
154
+ `--root`, `--repo`, and `--all` are mutually exclusive. `install-skill` and
155
+ `snippet` accept none of them. `repos` accepts `--config` and rejects the other
156
+ selectors.
157
+
158
+ ```bash
159
+ bot-todo --root ~/Programming/bot_todo list
160
+ bot-todo --repo bot-todo show T001
161
+ bot-todo --all critical
162
+ ```
163
+
164
+ ### Configuration
165
+
166
+ `--repo`, `--all`, and `repos` read a TOML file. `--config PATH` overrides
167
+ `BOT_TODO_CONFIG`, which overrides the platform default:
168
+
169
+ - Unix: `${XDG_CONFIG_HOME:-~/.config}/bot-todo/config.toml`
170
+ - Windows: `%APPDATA%\bot-todo\config.toml`
171
+
172
+ ```toml
173
+ schema_version = 1
174
+
175
+ [[repositories]]
176
+ name = "bot-todo"
177
+ path = "~/Programming/bot_todo"
178
+
179
+ [[repositories]]
180
+ name = "ledger"
181
+ path = "~/Programming/ledger"
182
+ ```
183
+
184
+ Names are unique lowercase slugs matching `[a-z0-9][a-z0-9._-]*`. Paths may be
185
+ absolute, start with `~`, or be relative to the configuration file. A missing
186
+ path is valid so `init --repo NAME` can create it.
187
+
188
+ `--all` orders JSON results, `critical`, and `actionable` by priority, then
189
+ configuration order, then file order. Human `--all list` groups tasks by
190
+ Repository Name in collection order, omits repositories with no open tasks,
191
+ and omits the name from task lines.
192
+
193
+ If any configured repository cannot be read, the command prints no task data
194
+ and exits `3`.
195
+
196
+ A missing default config is an empty collection. Local discovery and `--root`
197
+ never load configuration.
198
+
199
+ Manage the collection without editing the file by hand:
200
+
201
+ ```bash
202
+ bot-todo repos path
203
+ bot-todo repos list
204
+ cd ~/Programming/new-repo
205
+ bot-todo repos add
206
+ bot-todo repos add --name ledger
207
+ bot-todo repos remove ledger
208
+ ```
209
+
210
+ `repos add` stores `~/...` when the path is under home, otherwise an absolute
211
+ path. A missing default file is created on the first add. A missing `--config`
212
+ path is an error. Duplicate names and resolved paths are rejected.
213
+
214
+ ## JSON output
215
+
216
+ `--json` is the stable automation interface. Agents should pass it on every
217
+ command that returns data. Humans typing in a terminal can omit it.
218
+
219
+ Success writes one JSON document to stdout:
220
+
221
+ ```json
222
+ {
223
+ "schema_version": 2,
224
+ "command": "list",
225
+ "data": {
226
+ "tasks": []
227
+ }
228
+ }
229
+ ```
230
+
231
+ JSON task objects include `state` (`open`, `review`, `completed`, or
232
+ `cancelled`), `reviewed_on` (an ISO date while in Review, otherwise `null`),
233
+ and `closed_on`.
234
+
235
+ Expected failure writes nothing to stdout and one error document to stderr:
236
+
237
+ ```json
238
+ {
239
+ "schema_version": 2,
240
+ "error": {
241
+ "code": "unknown_task",
242
+ "message": "unknown task ID T999"
243
+ }
244
+ }
245
+ ```
246
+
247
+ Exit statuses: `0` success (including empty queries), `1` operational or data
248
+ failure, `2` usage error, `3` aggregate partial failure.
249
+
250
+ ## Agent skill
251
+
252
+ Install the bundled `todo` skill for one agent at a time:
253
+
254
+ ```bash
255
+ bot-todo install-skill --target cursor
256
+ bot-todo install-skill --target claude
257
+ bot-todo install-skill --target grok
258
+ bot-todo install-skill --target codex
259
+ ```
260
+
261
+ Default skill roots:
262
+
263
+ | Target | Skill root | Installed path |
264
+ | --- | --- | --- |
265
+ | `cursor` | `~/.cursor/skills` | `~/.cursor/skills/todo` |
266
+ | `claude` | `~/.claude/skills` | `~/.claude/skills/todo` |
267
+ | `grok` | `~/.grok/skills` | `~/.grok/skills/todo` |
268
+ | `codex` | `~/.agents/skills` | `~/.agents/skills/todo` |
269
+
270
+ `--destination PATH` replaces the skill root, not the final `todo` directory.
271
+ `--dry-run` classifies the action without writing. `--force` replaces a
272
+ conflicting tree after moving it to a `todo.backup-*` sibling.
273
+
274
+ Codex receives `SKILL.md` plus `agents/openai.yaml`. The other targets receive
275
+ `SKILL.md` only. A managed install is marked with `.bot-todo-install.json`.
276
+ Unknown or modified files are a conflict unless `--force` is given.
277
+
278
+ The installer only writes files. It does not reload the agent.
279
+
280
+ ## Task files
281
+
282
+ Each Task Repository is a directory with `TODO.md` and `TODO.archive.md`. Treat
283
+ `TODO.md` as the human-readable source of truth, but do not rewrite it by hand
284
+ except to resolve a sequential-ID merge collision.
285
+
286
+ Open tasks live under `P0`, `P1`, and `P2`. Completed and cancelled tasks move
287
+ to Done; the newest 20 stay there, and older closed tasks are appended to the
288
+ archive. A closed task that still blocks an open task stays in Done until
289
+ nothing depends on it. Cancellation does not satisfy dependents.
290
+
291
+ Every mutation takes an exclusive lock (`.bot-todo.lock`), validates before and
292
+ after the write, and replaces the canonical files atomically. Reads take a
293
+ shared lock. Lock acquisition waits up to five seconds, then fails with
294
+ `conflict`.
295
+
296
+ ## Development
297
+
298
+ ```bash
299
+ uv sync
300
+ source .venv/bin/activate
301
+ make pytest
302
+ ```
303
+
304
+ Quality gates used in this repository:
305
+
306
+ ```bash
307
+ uv run ruff check src tests
308
+ uv run mypy src
309
+ make napoleon-gate
310
+ ```
311
+
312
+ Pass extra pytest arguments with `make pytest ARGS="tests/test_cli.py -q"`.
313
+
314
+ ## Further reading
315
+
316
+ - [`CONTEXT.md`](CONTEXT.md) — domain vocabulary
317
+ - [`.scratch/installable-bot-todo/spec.md`](.scratch/installable-bot-todo/spec.md) — architecture and public contract
318
+ - [`docs/adr/`](docs/adr/) — architecture decisions
@@ -0,0 +1,299 @@
1
+ # bot-todo
2
+
3
+ `bot-todo` is a command-line tool for a canonical, Git-friendly task backlog.
4
+ Humans and coding agents share one `TODO.md` per repository and mutate it only
5
+ through this CLI.
6
+
7
+ The package also ships a thin `todo` skill that teaches Codex, Claude, Cursor,
8
+ and Grok to call `bot-todo` instead of editing the task files by hand.
9
+
10
+ ## Requirements
11
+
12
+ - Python 3.11 or newer
13
+ - [uv](https://docs.astral.sh/uv/) for installation (recommended)
14
+
15
+ The only runtime dependency is `portalocker`, used for per-repository locking.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ uv tool bot-todo
21
+ ```
22
+
23
+ That puts `bot-todo` on your `PATH` in an isolated environment. While hacking
24
+ on the CLI itself, keep the checkout live:
25
+
26
+ ```bash
27
+ uv tool install --editable .
28
+ ```
29
+
30
+ One-shot use without a persistent install:
31
+
32
+ ```bash
33
+ uvx --from . bot-todo --help
34
+ ```
35
+
36
+ Confirm the install:
37
+
38
+ ```bash
39
+ bot-todo --version
40
+ ```
41
+
42
+ ## Quick start
43
+
44
+ From a project directory:
45
+
46
+ ```bash
47
+ bot-todo init
48
+ bot-todo add "Write the README" --type docs --priority P1 --acceptance "README covers install and usage"
49
+ bot-todo list
50
+ bot-todo claim T001 --actor glenn
51
+ bot-todo complete T001
52
+ bot-todo validate
53
+ ```
54
+
55
+ `init` always targets the current directory (or `--root`). Omit `--name` to use
56
+ the repository directory basename as the project heading. Successful human
57
+ `init` prints a copy-paste Task Management section for `AGENTS.md` or
58
+ `CLAUDE.md`; JSON `init` includes that text as `data.snippet`. `bot-todo`
59
+ never writes those instruction files. Other commands without a selector walk
60
+ from the current directory toward the filesystem root and use the nearest
61
+ `TODO.md`.
62
+
63
+ ## Usage
64
+
65
+ Global options precede the command:
66
+
67
+ ```text
68
+ bot-todo [--json] [--config PATH] [--root PATH | --repo NAME | --all] COMMAND ...
69
+ ```
70
+
71
+ `--help` and `--version` always print human text and succeed, including when
72
+ `--json` is present. Commands never prompt, page, or colorize output.
73
+
74
+ ### Commands
75
+
76
+ | Command | Purpose |
77
+ | --- | --- |
78
+ | `init [--name NAME]` | Create `TODO.md` and `TODO.archive.md` |
79
+ | `validate` | Check the canonical files |
80
+ | `list` | List open and review tasks |
81
+ | `show TASK_ID` | Print one task |
82
+ | `critical` | Highest-priority open task (even if blocked or claimed) |
83
+ | `actionable` | First unclaimed open task whose blockers are completed |
84
+ | `add TITLE --type TYPE` | Create an open task |
85
+ | `edit TASK_ID ...` | Change an open or review task |
86
+ | `claim TASK_ID --actor NAME` | Take an advisory claim on an open task |
87
+ | `release TASK_ID` | Drop a claim |
88
+ | `review TASK_ID` | Move an open task into review |
89
+ | `reopen TASK_ID` | Return a review task to open |
90
+ | `complete TASK_ID` | Mark an open or review task completed |
91
+ | `cancel TASK_ID --reason TEXT` | Mark an open or review task cancelled |
92
+ | `archive` | Move older Done tasks into the archive |
93
+ | `migrate` | Upgrade the task data format to 2 |
94
+ | `repos path` | Show the active configuration path |
95
+ | `repos list` | List configured repositories |
96
+ | `repos add [PATH]` | Add a repository entry; PATH defaults to `.` |
97
+ | `repos remove TARGET` | Remove a repository entry by name or path |
98
+ | `install-skill --target TARGET` | Install the bundled `todo` skill |
99
+ | `snippet` | Print the Task Management section for `AGENTS.md` or `CLAUDE.md` |
100
+
101
+ Task IDs look like `T001` and are never reused. Types are `bug`, `chore`,
102
+ `docs`, `feature`, and `ops`. Priorities are `P0`, `P1`, and `P2` (default
103
+ `P2`).
104
+
105
+ `add` requires either `--acceptance` or `--simple`. Repeatable options are
106
+ `--tag` and `--blocked-by`. `claim` records the actor, today's date, and the
107
+ current Git branch unless `--branch` is given. `review` clears that claim and
108
+ records today's date; `reopen` returns the task to open. `complete` and
109
+ `cancel` accept open or review tasks.
110
+
111
+ `init` writes Task Data Format 2. Format 1 files still load for queries;
112
+ mutations require `bot-todo migrate` first.
113
+
114
+ ```bash
115
+ bot-todo add "Fix the lock timeout" --type bug --priority P0 \
116
+ --acceptance "Conflict errors after five seconds, no partial writes" \
117
+ --tag locking --blocked-by T002
118
+
119
+ bot-todo edit T003 --title "Clarify lock timeout" --priority P1 --clear-blockers
120
+ bot-todo cancel T004 --reason "Superseded by T003"
121
+ ```
122
+
123
+ `edit` is a usage error if it requests no change. It also accepts `--simple`,
124
+ `--clear-context`, `--clear-related`, and `--clear-blockers`.
125
+
126
+ ### Selecting a repository
127
+
128
+ | Selector | Meaning | Allowed commands |
129
+ | --- | --- | --- |
130
+ | *(none)* | Nearest ancestor `TODO.md`; `init` uses the current directory | Task commands |
131
+ | `--root PATH` | Exact directory | Task commands |
132
+ | `--repo NAME` | One named entry from configuration | Task commands |
133
+ | `--all` | Every configured repository | `list`, `critical`, `actionable` only |
134
+
135
+ `--root`, `--repo`, and `--all` are mutually exclusive. `install-skill` and
136
+ `snippet` accept none of them. `repos` accepts `--config` and rejects the other
137
+ selectors.
138
+
139
+ ```bash
140
+ bot-todo --root ~/Programming/bot_todo list
141
+ bot-todo --repo bot-todo show T001
142
+ bot-todo --all critical
143
+ ```
144
+
145
+ ### Configuration
146
+
147
+ `--repo`, `--all`, and `repos` read a TOML file. `--config PATH` overrides
148
+ `BOT_TODO_CONFIG`, which overrides the platform default:
149
+
150
+ - Unix: `${XDG_CONFIG_HOME:-~/.config}/bot-todo/config.toml`
151
+ - Windows: `%APPDATA%\bot-todo\config.toml`
152
+
153
+ ```toml
154
+ schema_version = 1
155
+
156
+ [[repositories]]
157
+ name = "bot-todo"
158
+ path = "~/Programming/bot_todo"
159
+
160
+ [[repositories]]
161
+ name = "ledger"
162
+ path = "~/Programming/ledger"
163
+ ```
164
+
165
+ Names are unique lowercase slugs matching `[a-z0-9][a-z0-9._-]*`. Paths may be
166
+ absolute, start with `~`, or be relative to the configuration file. A missing
167
+ path is valid so `init --repo NAME` can create it.
168
+
169
+ `--all` orders JSON results, `critical`, and `actionable` by priority, then
170
+ configuration order, then file order. Human `--all list` groups tasks by
171
+ Repository Name in collection order, omits repositories with no open tasks,
172
+ and omits the name from task lines.
173
+
174
+ If any configured repository cannot be read, the command prints no task data
175
+ and exits `3`.
176
+
177
+ A missing default config is an empty collection. Local discovery and `--root`
178
+ never load configuration.
179
+
180
+ Manage the collection without editing the file by hand:
181
+
182
+ ```bash
183
+ bot-todo repos path
184
+ bot-todo repos list
185
+ cd ~/Programming/new-repo
186
+ bot-todo repos add
187
+ bot-todo repos add --name ledger
188
+ bot-todo repos remove ledger
189
+ ```
190
+
191
+ `repos add` stores `~/...` when the path is under home, otherwise an absolute
192
+ path. A missing default file is created on the first add. A missing `--config`
193
+ path is an error. Duplicate names and resolved paths are rejected.
194
+
195
+ ## JSON output
196
+
197
+ `--json` is the stable automation interface. Agents should pass it on every
198
+ command that returns data. Humans typing in a terminal can omit it.
199
+
200
+ Success writes one JSON document to stdout:
201
+
202
+ ```json
203
+ {
204
+ "schema_version": 2,
205
+ "command": "list",
206
+ "data": {
207
+ "tasks": []
208
+ }
209
+ }
210
+ ```
211
+
212
+ JSON task objects include `state` (`open`, `review`, `completed`, or
213
+ `cancelled`), `reviewed_on` (an ISO date while in Review, otherwise `null`),
214
+ and `closed_on`.
215
+
216
+ Expected failure writes nothing to stdout and one error document to stderr:
217
+
218
+ ```json
219
+ {
220
+ "schema_version": 2,
221
+ "error": {
222
+ "code": "unknown_task",
223
+ "message": "unknown task ID T999"
224
+ }
225
+ }
226
+ ```
227
+
228
+ Exit statuses: `0` success (including empty queries), `1` operational or data
229
+ failure, `2` usage error, `3` aggregate partial failure.
230
+
231
+ ## Agent skill
232
+
233
+ Install the bundled `todo` skill for one agent at a time:
234
+
235
+ ```bash
236
+ bot-todo install-skill --target cursor
237
+ bot-todo install-skill --target claude
238
+ bot-todo install-skill --target grok
239
+ bot-todo install-skill --target codex
240
+ ```
241
+
242
+ Default skill roots:
243
+
244
+ | Target | Skill root | Installed path |
245
+ | --- | --- | --- |
246
+ | `cursor` | `~/.cursor/skills` | `~/.cursor/skills/todo` |
247
+ | `claude` | `~/.claude/skills` | `~/.claude/skills/todo` |
248
+ | `grok` | `~/.grok/skills` | `~/.grok/skills/todo` |
249
+ | `codex` | `~/.agents/skills` | `~/.agents/skills/todo` |
250
+
251
+ `--destination PATH` replaces the skill root, not the final `todo` directory.
252
+ `--dry-run` classifies the action without writing. `--force` replaces a
253
+ conflicting tree after moving it to a `todo.backup-*` sibling.
254
+
255
+ Codex receives `SKILL.md` plus `agents/openai.yaml`. The other targets receive
256
+ `SKILL.md` only. A managed install is marked with `.bot-todo-install.json`.
257
+ Unknown or modified files are a conflict unless `--force` is given.
258
+
259
+ The installer only writes files. It does not reload the agent.
260
+
261
+ ## Task files
262
+
263
+ Each Task Repository is a directory with `TODO.md` and `TODO.archive.md`. Treat
264
+ `TODO.md` as the human-readable source of truth, but do not rewrite it by hand
265
+ except to resolve a sequential-ID merge collision.
266
+
267
+ Open tasks live under `P0`, `P1`, and `P2`. Completed and cancelled tasks move
268
+ to Done; the newest 20 stay there, and older closed tasks are appended to the
269
+ archive. A closed task that still blocks an open task stays in Done until
270
+ nothing depends on it. Cancellation does not satisfy dependents.
271
+
272
+ Every mutation takes an exclusive lock (`.bot-todo.lock`), validates before and
273
+ after the write, and replaces the canonical files atomically. Reads take a
274
+ shared lock. Lock acquisition waits up to five seconds, then fails with
275
+ `conflict`.
276
+
277
+ ## Development
278
+
279
+ ```bash
280
+ uv sync
281
+ source .venv/bin/activate
282
+ make pytest
283
+ ```
284
+
285
+ Quality gates used in this repository:
286
+
287
+ ```bash
288
+ uv run ruff check src tests
289
+ uv run mypy src
290
+ make napoleon-gate
291
+ ```
292
+
293
+ Pass extra pytest arguments with `make pytest ARGS="tests/test_cli.py -q"`.
294
+
295
+ ## Further reading
296
+
297
+ - [`CONTEXT.md`](CONTEXT.md) — domain vocabulary
298
+ - [`.scratch/installable-bot-todo/spec.md`](.scratch/installable-bot-todo/spec.md) — architecture and public contract
299
+ - [`docs/adr/`](docs/adr/) — architecture decisions
@@ -0,0 +1,84 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.9,<0.13"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "bot-todo"
7
+ version = "0.2.1"
8
+ description = "Manage canonical repository task files."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = ["portalocker>=2.7"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Natural Language :: English",
21
+ "Topic :: Utilities",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/caltechads/bot-todo"
26
+ Repository = "https://github.com/caltechads/bot-todo"
27
+
28
+ [project.scripts]
29
+ bot-todo = "bot_todo.cli:main"
30
+
31
+ [tool.setuptools]
32
+ include-package-data = true
33
+
34
+ [tool.setuptools.packages.find]
35
+ exclude = [
36
+ "*.tests",
37
+ "*.tests.*",
38
+ "tests.*",
39
+ "tests",
40
+ "htmlcov",
41
+ "bin",
42
+ ]
43
+ where = ["."]
44
+
45
+ [tool.pytest.ini_options]
46
+ testpaths = ["tests"]
47
+
48
+ [tool.mypy]
49
+ python_version = "3.11"
50
+ strict = true
51
+
52
+ [[tool.mypy.overrides]]
53
+ module = "tests.*"
54
+ disallow_untyped_defs = false
55
+
56
+ [tool.ruff]
57
+ target-version = "py311"
58
+
59
+ [tool.bumpversion]
60
+ current_version = "0.2.1"
61
+ commit = true
62
+ tag = true
63
+ tag_name = "{new_version}"
64
+ parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-dev(?P<dev>\d+))?'
65
+ serialize = [
66
+ "{major}.{minor}.{patch}-dev{dev}",
67
+ "{major}.{minor}.{patch}",
68
+ ]
69
+
70
+ [[tool.bumpversion.files]]
71
+ filename = "Makefile"
72
+
73
+ [[tool.bumpversion.files]]
74
+ filename = "pyproject.toml"
75
+ search = 'version = "{current_version}"'
76
+ replace = 'version = "{new_version}"'
77
+
78
+ [dependency-groups]
79
+ dev = [
80
+ "mypy>=1.11",
81
+ "pytest>=8",
82
+ "ruff>=0.6",
83
+ "build>=1.2.2.post1",
84
+ ]