chitty 0.1.0__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.
- chitty-0.1.0/.github/workflows/release.yml +83 -0
- chitty-0.1.0/.gitignore +4 -0
- chitty-0.1.0/AGENTS.md +32 -0
- chitty-0.1.0/CLAUDE.md +1 -0
- chitty-0.1.0/LICENSE +21 -0
- chitty-0.1.0/Makefile +22 -0
- chitty-0.1.0/PKG-INFO +79 -0
- chitty-0.1.0/PLAN.md +397 -0
- chitty-0.1.0/README.md +54 -0
- chitty-0.1.0/chitty/human/HUMAN-1--decide-whether-check-should-detect-depends-on-cycles.md +9 -0
- chitty-0.1.0/chitty/human/HUMAN-2--publish-chitty-to-pypi.md +13 -0
- chitty-0.1.0/chitty/tasks/TASK-1--reject-setext-headings-in-task-bodies.md +16 -0
- chitty-0.1.0/chitty.toml +9 -0
- chitty-0.1.0/package-lock.json +28 -0
- chitty-0.1.0/package.json +7 -0
- chitty-0.1.0/pyproject.toml +70 -0
- chitty-0.1.0/src/chitty/__init__.py +0 -0
- chitty-0.1.0/src/chitty/check.py +67 -0
- chitty-0.1.0/src/chitty/cli.py +355 -0
- chitty-0.1.0/src/chitty/config.py +140 -0
- chitty-0.1.0/src/chitty/errors.py +2 -0
- chitty-0.1.0/src/chitty/git.py +29 -0
- chitty-0.1.0/src/chitty/guide.md +14 -0
- chitty-0.1.0/src/chitty/ids.py +73 -0
- chitty-0.1.0/src/chitty/mcp.py +178 -0
- chitty-0.1.0/src/chitty/model.py +576 -0
- chitty-0.1.0/src/chitty/store.py +449 -0
- chitty-0.1.0/tests/__init__.py +0 -0
- chitty-0.1.0/tests/support.py +39 -0
- chitty-0.1.0/tests/test_autocommit.py +66 -0
- chitty-0.1.0/tests/test_check.py +50 -0
- chitty-0.1.0/tests/test_cli.py +297 -0
- chitty-0.1.0/tests/test_config.py +58 -0
- chitty-0.1.0/tests/test_formatters.py +88 -0
- chitty-0.1.0/tests/test_ids.py +50 -0
- chitty-0.1.0/tests/test_mcp.py +48 -0
- chitty-0.1.0/tests/test_model.py +281 -0
- chitty-0.1.0/tests/test_store.py +110 -0
- chitty-0.1.0/uv.lock +970 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Publish release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
7
|
+
- "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
|
|
8
|
+
- "v[0-9]+.[0-9]+.[0-9]+[ab][0-9]+"
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build and verify distributions
|
|
15
|
+
runs-on: ubuntu-24.04
|
|
16
|
+
timeout-minutes: 15
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- name: Check out repository
|
|
21
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: false
|
|
29
|
+
|
|
30
|
+
- name: Install Node
|
|
31
|
+
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
|
32
|
+
|
|
33
|
+
- name: Install Python
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Verify tag matches package version
|
|
37
|
+
shell: bash
|
|
38
|
+
run: test "v$(uv version --short)" = "${GITHUB_REF_NAME}"
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: make install
|
|
42
|
+
|
|
43
|
+
- name: Check project
|
|
44
|
+
run: make check
|
|
45
|
+
|
|
46
|
+
- name: Build distributions
|
|
47
|
+
run: make build
|
|
48
|
+
|
|
49
|
+
- name: Upload verified distributions
|
|
50
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
51
|
+
with:
|
|
52
|
+
name: distributions
|
|
53
|
+
path: dist/
|
|
54
|
+
if-no-files-found: error
|
|
55
|
+
retention-days: 7
|
|
56
|
+
|
|
57
|
+
publish:
|
|
58
|
+
name: Publish verified distributions
|
|
59
|
+
needs: build
|
|
60
|
+
runs-on: ubuntu-24.04
|
|
61
|
+
timeout-minutes: 10
|
|
62
|
+
environment:
|
|
63
|
+
name: pypi
|
|
64
|
+
permissions:
|
|
65
|
+
id-token: write
|
|
66
|
+
steps:
|
|
67
|
+
- name: Install uv
|
|
68
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
69
|
+
with:
|
|
70
|
+
enable-cache: false
|
|
71
|
+
ignore-empty-workdir: true
|
|
72
|
+
|
|
73
|
+
- name: Download verified distributions
|
|
74
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
75
|
+
with:
|
|
76
|
+
name: distributions
|
|
77
|
+
path: dist/
|
|
78
|
+
|
|
79
|
+
- name: Generate PEP 740 attestations
|
|
80
|
+
uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6
|
|
81
|
+
|
|
82
|
+
- name: Publish to PyPI
|
|
83
|
+
run: uv publish
|
chitty-0.1.0/.gitignore
ADDED
chitty-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
Chitty is a markdown-native task tracker. `PLAN.md` is the design; read it
|
|
4
|
+
before changing behaviour, and update it when a decision changes.
|
|
5
|
+
|
|
6
|
+
- `make install` once (Python deps including the `mcp` extra, and the pinned
|
|
7
|
+
prettier used by the round-trip tests).
|
|
8
|
+
- `make check` (ruff, pyright, unittest) before every commit. `make format`
|
|
9
|
+
to fix formatting.
|
|
10
|
+
- Tests use `unittest` and write real files in temp dirs; no filesystem
|
|
11
|
+
mocking. The formatter tests fail, never skip, if prettier or mdformat is
|
|
12
|
+
missing.
|
|
13
|
+
- `cli.py` and `mcp.py` stay thin: behaviour lives in `store.py` and
|
|
14
|
+
`model.py` so the two front ends cannot diverge.
|
|
15
|
+
- Releases: bump `version` in `pyproject.toml`, commit, then push a tag
|
|
16
|
+
`v<version>`. `.github/workflows/release.yml` checks, builds and publishes
|
|
17
|
+
to PyPI by trusted publishing (environment `pypi`).
|
|
18
|
+
|
|
19
|
+
## Chitty
|
|
20
|
+
|
|
21
|
+
Tasks live in `chitty/`. Use the `chitty` CLI (or MCP tools); do not hand-edit
|
|
22
|
+
task files. `chitty --help` is the full command reference.
|
|
23
|
+
|
|
24
|
+
- Work on one task per session. Start with `chitty show <ID>`.
|
|
25
|
+
- Before writing code, write your approach with `chitty edit <ID> --plan` and
|
|
26
|
+
set `-s doing`. Wait for approval if the human asked for it.
|
|
27
|
+
- Tick acceptance criteria as you meet them: `chitty edit <ID> --check N`
|
|
28
|
+
(numbers are shown by `chitty show`).
|
|
29
|
+
- Record anything surprising with `--note`. Set `-s done` when every
|
|
30
|
+
criterion is ticked and the work is verified.
|
|
31
|
+
- Found unrelated work? `chitty add` it (`-c human` if it needs a person).
|
|
32
|
+
Don't do it in this session.
|
chitty-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
chitty-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ed Singleton
|
|
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.
|
chitty-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.PHONY: install check test format build
|
|
2
|
+
|
|
3
|
+
install: ## install Python and node dev dependencies
|
|
4
|
+
uv sync --all-extras
|
|
5
|
+
npm ci
|
|
6
|
+
|
|
7
|
+
check: ## lint, type-check and run the tests
|
|
8
|
+
uv run ruff format --check .
|
|
9
|
+
uv run ruff check --no-fix .
|
|
10
|
+
uv run pyright
|
|
11
|
+
uv run python -m unittest discover -s tests -t .
|
|
12
|
+
|
|
13
|
+
test: ## run the tests
|
|
14
|
+
uv run python -m unittest discover -s tests -t .
|
|
15
|
+
|
|
16
|
+
format: ## format and auto-fix Python code
|
|
17
|
+
uv run ruff check --exit-zero .
|
|
18
|
+
uv run ruff format .
|
|
19
|
+
|
|
20
|
+
build: ## build the sdist and wheel into dist/
|
|
21
|
+
rm -rf dist
|
|
22
|
+
uv build
|
chitty-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: chitty
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Markdown-native task tracker for repos worked on by humans and coding agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/Singletoned/chitty
|
|
6
|
+
Project-URL: Issues, https://github.com/Singletoned/chitty/issues
|
|
7
|
+
Author-email: Ed Singleton <ed@singletoned.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,cli,markdown,mcp,task-tracker
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Bug Tracking
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: click
|
|
20
|
+
Requires-Dist: pydantic
|
|
21
|
+
Requires-Dist: pyyaml
|
|
22
|
+
Provides-Extra: mcp
|
|
23
|
+
Requires-Dist: mcp>=2; extra == 'mcp'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Chitty
|
|
27
|
+
|
|
28
|
+
Markdown-native task tracker for repos worked on by humans and coding agents.
|
|
29
|
+
Put in a chitty; an agent (or you) picks it up; the file is the record.
|
|
30
|
+
|
|
31
|
+
Each task is one markdown file with YAML frontmatter under
|
|
32
|
+
`chitty/<context>/`, e.g. `chitty/tasks/TASK-12--add-search.md`. The files are
|
|
33
|
+
the only state: no database, no index. The tool only ever changes the lines
|
|
34
|
+
it means to, so prettier and mdformat leave its output alone and diffs stay
|
|
35
|
+
small. See `PLAN.md` for the full design.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
Not yet on PyPI. From a checkout:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
uv tool install . # CLI
|
|
43
|
+
uv tool install '.[mcp]' # CLI plus `chitty mcp`
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Use
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
chitty init # chitty.toml, chitty/tasks/, AGENTS.md block
|
|
50
|
+
chitty add "Add search" -d "Search titles and bodies." \
|
|
51
|
+
--ac "Matches title" --ac "Case-insensitive" -l cli
|
|
52
|
+
chitty list
|
|
53
|
+
chitty show 1 # bare numbers mean the default context
|
|
54
|
+
chitty edit 1 -s doing --plan "Use casefold()." --check 1
|
|
55
|
+
chitty edit 1 --note "Safari needs a polyfill."
|
|
56
|
+
chitty add "Rotate prod password" -c human -d "Needs a person."
|
|
57
|
+
chitty move 1 human # new ID there; references are rewritten
|
|
58
|
+
chitty archive HUMAN-1
|
|
59
|
+
chitty check # validate everything; non-zero on problems
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Every command takes `--json`. `chitty --help` and `chitty <command> --help`
|
|
63
|
+
are the reference.
|
|
64
|
+
|
|
65
|
+
Contexts are configured in `chitty.toml`. Contexts in other repos (for
|
|
66
|
+
posting tasks elsewhere) go in `~/.config/chitty/contexts.toml`:
|
|
67
|
+
|
|
68
|
+
```toml
|
|
69
|
+
[contexts.notes]
|
|
70
|
+
repo = "~/notes"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Develop
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
make install # uv sync --all-extras; npm ci (pinned prettier)
|
|
77
|
+
make check # ruff, pyright, unittest
|
|
78
|
+
make format
|
|
79
|
+
```
|
chitty-0.1.0/PLAN.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Chitty — plan
|
|
2
|
+
|
|
3
|
+
Markdown-native task tracker for repos worked on by humans and coding agents.
|
|
4
|
+
A clean-room reimplementation of the idea behind Backlog.md, in Python, with
|
|
5
|
+
a file format that ordinary markdown tooling leaves alone and a much smaller
|
|
6
|
+
surface area.
|
|
7
|
+
|
|
8
|
+
Put in a chitty; an agent (or you) picks it up; the file is the record.
|
|
9
|
+
|
|
10
|
+
## Principles
|
|
11
|
+
|
|
12
|
+
- The files are the truth. No database, no index, no cache. A directory scan
|
|
13
|
+
is fast enough for thousands of tasks.
|
|
14
|
+
- One task, one file, one writer at a time. No locking, no cross-branch
|
|
15
|
+
scanning, no automatic conflict repair. If you want a task from another
|
|
16
|
+
branch, merge the branch. IDs are sequential; task creation is expected to
|
|
17
|
+
be centralised (one agent or human), so we assume parallel branches don't
|
|
18
|
+
collide. `check` reports it if they do.
|
|
19
|
+
- Simple first. Add complexity when real-world use shows it is needed, not
|
|
20
|
+
before.
|
|
21
|
+
- Plain markdown. Every file must survive a round trip through the supported
|
|
22
|
+
formatters (below) without changing meaning. No HTML comments, no sentinel
|
|
23
|
+
markers, no numbering inside checklist text.
|
|
24
|
+
- Loud errors. Malformed files are rejected with a path and a reason, never
|
|
25
|
+
silently repaired. `chitty check` is the compile step.
|
|
26
|
+
- The tool carries the rules, not a prompt. Validation and `--help` are the
|
|
27
|
+
documentation; the agent instructions are ~20 lines and static.
|
|
28
|
+
- Minimal diffs. The tool never touches a line it didn't change, and never
|
|
29
|
+
writes a file whose content would be unchanged.
|
|
30
|
+
- No migrations in v1. The format is versioned (`format = 1` in config) so
|
|
31
|
+
one can be written later if ever needed.
|
|
32
|
+
|
|
33
|
+
## Non-goals
|
|
34
|
+
|
|
35
|
+
TUI. Web UI. Cross-branch task resolution. File locking. Format migrations.
|
|
36
|
+
Remote/HTTP contexts (sync a directory instead). Subtask IDs (`24.1`).
|
|
37
|
+
Milestones. Docs/decisions directories. Wizards. Shell completions (v1).
|
|
38
|
+
Configurable statuses (v1). Moving tasks between repos. ID collision
|
|
39
|
+
handling beyond reporting it.
|
|
40
|
+
|
|
41
|
+
## Layout
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
repo/
|
|
45
|
+
chitty.toml
|
|
46
|
+
chitty/
|
|
47
|
+
tasks/ default context
|
|
48
|
+
TASK-2--add-search.md
|
|
49
|
+
TASK-3--fix-login.md
|
|
50
|
+
archive/
|
|
51
|
+
TASK-1--bootstrap.md
|
|
52
|
+
human/ another context
|
|
53
|
+
HUMAN-1--rotate-prod-password.md
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`chitty/` is the backlog root; each context is a subdirectory. Archived
|
|
57
|
+
tasks move to `<context>/archive/`. Nothing else lives under `chitty/`.
|
|
58
|
+
|
|
59
|
+
A missing context directory is an empty context; it is created on first
|
|
60
|
+
write. `init` creates `chitty/tasks/` only; `chitty/human/` appears the first
|
|
61
|
+
time something is added to it. (Git doesn't track empty directories, so a
|
|
62
|
+
freshly cloned repo may have none of them.)
|
|
63
|
+
|
|
64
|
+
Root discovery (CLI and MCP alike): `CHITTY_ROOT` if set, otherwise walk up
|
|
65
|
+
from the current directory to the first directory containing `chitty.toml`.
|
|
66
|
+
Error if neither finds one.
|
|
67
|
+
|
|
68
|
+
## IDs
|
|
69
|
+
|
|
70
|
+
`<PREFIX>-<n>`, e.g. `TASK-13`.
|
|
71
|
+
|
|
72
|
+
- `PREFIX` is the context's prefix: `[A-Z]+`, unique within a repo.
|
|
73
|
+
- `n` is a positive integer, unpadded: `max(n over existing files in the
|
|
74
|
+
context, including archive) + 1`.
|
|
75
|
+
|
|
76
|
+
Filename: `<ID>--<slug>.md`, or `<ID>.md` when the slug is empty. The slug
|
|
77
|
+
is derived from the title at creation (lowercase, `[a-z0-9-]`, collapsed
|
|
78
|
+
hyphens, max 60 chars; empty if the title has no ASCII alphanumerics) and is
|
|
79
|
+
**not** updated when the title changes — it is there so `ls` is readable,
|
|
80
|
+
nothing more. Slugs never contain `--`, so the ID is everything before the
|
|
81
|
+
first `--`. The ID is not repeated in frontmatter.
|
|
82
|
+
|
|
83
|
+
Collisions: task creation is expected to be centralised, so two branches
|
|
84
|
+
creating the same ID is assumed not to happen in practice. If it does,
|
|
85
|
+
`chitty check` reports the files involved; the human renames one and fixes
|
|
86
|
+
references by hand. No tooling beyond that until real use shows it's
|
|
87
|
+
needed. Likewise, moving the highest-numbered task out of a context lets
|
|
88
|
+
its number be reused; accepted.
|
|
89
|
+
|
|
90
|
+
Referencing: commands accept `TASK-13`, or bare `13` for the default
|
|
91
|
+
context. Stored references (`depends_on`, `moved_from`) are always the full
|
|
92
|
+
`PREFIX-n` form; shorthand is expanded before writing.
|
|
93
|
+
|
|
94
|
+
## Task file
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
---
|
|
98
|
+
title: Add search to the task list
|
|
99
|
+
status: todo
|
|
100
|
+
labels:
|
|
101
|
+
- search
|
|
102
|
+
- cli
|
|
103
|
+
priority: medium
|
|
104
|
+
owner: ed
|
|
105
|
+
depends_on:
|
|
106
|
+
- TASK-7
|
|
107
|
+
- HUMAN-1
|
|
108
|
+
created: 2026-10-23
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Description
|
|
112
|
+
|
|
113
|
+
Free markdown.
|
|
114
|
+
|
|
115
|
+
## Acceptance Criteria
|
|
116
|
+
|
|
117
|
+
- [ ] `chitty list --search` matches title and body
|
|
118
|
+
- [x] Matching is case-insensitive
|
|
119
|
+
|
|
120
|
+
## Plan
|
|
121
|
+
|
|
122
|
+
Free markdown, written by the agent before implementing.
|
|
123
|
+
|
|
124
|
+
## Notes
|
|
125
|
+
|
|
126
|
+
Free markdown. Append-only by convention; not enforced.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Frontmatter schema (strict: unknown keys are an error):
|
|
130
|
+
|
|
131
|
+
| key | type | required | notes |
|
|
132
|
+
|--------------|--------------------|----------|----------------------------------------------|
|
|
133
|
+
| `title` | string | yes | non-empty, single line |
|
|
134
|
+
| `status` | `todo\|doing\|done` | yes | fixed in v1 |
|
|
135
|
+
| `labels` | list[string] | no | absent ≡ `[]` |
|
|
136
|
+
| `priority` | `low\|medium\|high` | no | absent means unset |
|
|
137
|
+
| `owner` | string | no | free text; `human`, a name, an agent |
|
|
138
|
+
| `depends_on` | list[ID] | no | any context in the same repo |
|
|
139
|
+
| `created` | date | yes | ISO 8601 date, set once |
|
|
140
|
+
| `moved_from` | ID | no | set by `move`; the ID in the source context |
|
|
141
|
+
|
|
142
|
+
There is no `updated` field. Git records when a file changed; the field would
|
|
143
|
+
add a line to every diff and conflict on every merge.
|
|
144
|
+
|
|
145
|
+
YAML is loaded with every scalar as a string (no implicit typing), so
|
|
146
|
+
`owner: no` is the string `"no"` and `status: on` fails validation as an
|
|
147
|
+
unknown status rather than becoming `True`. Pydantic parses `created` from
|
|
148
|
+
the string. When the tool *writes* a free-text field (`title`, `owner`,
|
|
149
|
+
`labels`) it quotes anything YAML 1.1 or 1.2 would read as a non-string
|
|
150
|
+
(`"no"`, `"2026-10-23"`, `"1e3"`), because other tools reading the file
|
|
151
|
+
won't be as careful. `created` is a real date and is written unquoted.
|
|
152
|
+
|
|
153
|
+
Body rules:
|
|
154
|
+
|
|
155
|
+
- Sections are `##` headings. Recognised: `Description`, `Acceptance
|
|
156
|
+
Criteria`, `Plan`, `Notes`. Unrecognised `##` headings, any `#` heading,
|
|
157
|
+
and non-blank content before the first `##` are errors. Use `###` and below
|
|
158
|
+
inside a section for structure.
|
|
159
|
+
- The parser is fence-aware: `##` inside a fenced code block is not a heading.
|
|
160
|
+
- `Description` is required and non-empty.
|
|
161
|
+
- `Acceptance Criteria` contains only a flat task list. An item starts with
|
|
162
|
+
`- [ ]`, `- [x]` or `- [X]` at column 0 and may continue on following
|
|
163
|
+
indented lines (formatters wrap long items). Blank lines between items are
|
|
164
|
+
allowed. Nested lists, unindented prose, and anything else in the section
|
|
165
|
+
are errors. Each item is one criterion, addressed by 1-based position in
|
|
166
|
+
the CLI; no numbers are stored in the text. The tool writes `- [x]`.
|
|
167
|
+
- Everything else is free markdown.
|
|
168
|
+
|
|
169
|
+
### Supported formatters
|
|
170
|
+
|
|
171
|
+
The round-trip guarantee is tested against, and only claimed for:
|
|
172
|
+
|
|
173
|
+
- `prettier` with default settings, and with `proseWrap: always`;
|
|
174
|
+
- `mdformat` with the `mdformat-gfm` and `mdformat-frontmatter` plugins
|
|
175
|
+
(plain CommonMark mdformat turns `---` into a heading and escapes `[ ]`).
|
|
176
|
+
|
|
177
|
+
Both are pinned to exact versions. If either is missing, the round-trip
|
|
178
|
+
tests fail loudly; they are never skipped.
|
|
179
|
+
|
|
180
|
+
"Survives" means the file still parses to the same task. Byte-for-byte
|
|
181
|
+
no-op is only claimed for ordinary content: `mdformat-frontmatter`
|
|
182
|
+
re-serialises YAML as 1.2 and drops the quotes the tool adds for 1.1
|
|
183
|
+
readers (`"no"` becomes `no`), and mdformat renumbers ordered lists in free
|
|
184
|
+
markdown.
|
|
185
|
+
|
|
186
|
+
### Writing
|
|
187
|
+
|
|
188
|
+
Serialisation is a patch over the original text, not parse-then-dump:
|
|
189
|
+
|
|
190
|
+
- Frontmatter is split into one span per top-level key (the key's line plus
|
|
191
|
+
any indented lines under it). Changed keys have their span replaced, new
|
|
192
|
+
keys are appended in table order, removed keys have their span deleted.
|
|
193
|
+
Every other byte — comments, quoting, blank lines — is left alone.
|
|
194
|
+
Comment and blank lines belong to the key below them, so replacing a key
|
|
195
|
+
keeps the comment above it.
|
|
196
|
+
- The body is split the same way, one span per `##` section. Only sections
|
|
197
|
+
the command changed are rewritten; new sections are appended in the
|
|
198
|
+
canonical order above.
|
|
199
|
+
- Acceptance Criteria are patched per item, so formatter wrapping survives:
|
|
200
|
+
`--check`/`--uncheck` change only the bracket character, `--rm-ac`
|
|
201
|
+
deletes that item's lines, `--ac` appends one line.
|
|
202
|
+
- Lists are written block-style (one item per line). Flow style is accepted
|
|
203
|
+
on read and left alone if the key isn't being changed.
|
|
204
|
+
- If the resulting content equals the current content, the file is not
|
|
205
|
+
written.
|
|
206
|
+
- `--note TEXT` appends `TEXT` as a new paragraph (preceded by a blank line)
|
|
207
|
+
to `## Notes`, creating the section if absent. No date prefix; include one
|
|
208
|
+
in the text if you want it.
|
|
209
|
+
- All checklist indices in a single `edit` refer to the list as it was
|
|
210
|
+
before the command ran. `show` prints the numbers next to each criterion so
|
|
211
|
+
callers don't count.
|
|
212
|
+
|
|
213
|
+
## Config
|
|
214
|
+
|
|
215
|
+
`chitty.toml` at repo root (required; `chitty init` writes it):
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
format = 1
|
|
219
|
+
default_context = "tasks"
|
|
220
|
+
autocommit = false # if true, commit each write
|
|
221
|
+
|
|
222
|
+
[contexts.tasks]
|
|
223
|
+
prefix = "TASK"
|
|
224
|
+
|
|
225
|
+
[contexts.human]
|
|
226
|
+
prefix = "HUMAN"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Context directory is `chitty/<name>/`. Not configurable.
|
|
230
|
+
|
|
231
|
+
User-level `~/.config/chitty/contexts.toml` (optional), for adding tasks to
|
|
232
|
+
other repos or to a personal backlog:
|
|
233
|
+
|
|
234
|
+
```toml
|
|
235
|
+
[contexts.hypurrcube]
|
|
236
|
+
repo = "~/src/hypurrcube" # that repo's chitty.toml applies
|
|
237
|
+
context = "tasks" # optional; default is that repo's default_context
|
|
238
|
+
|
|
239
|
+
[contexts.improvements]
|
|
240
|
+
repo = "~/notes/improvements" # any directory with a chitty.toml
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Resolution of a context name: repo `chitty.toml` first, then user file.
|
|
244
|
+
A name present in both is an error (no silent shadowing). A name in neither
|
|
245
|
+
is an error naming both paths.
|
|
246
|
+
|
|
247
|
+
## Dependencies
|
|
248
|
+
|
|
249
|
+
`depends_on` may reference any context in the same repo (`TASK-3` blocked on
|
|
250
|
+
`HUMAN-1` is the common case). Rules:
|
|
251
|
+
|
|
252
|
+
- A reference to an ID that doesn't exist in the repo is a `check` error.
|
|
253
|
+
- A reference to an archived task is fine; archived is not deleted.
|
|
254
|
+
- `move` gives the task a new ID in the target context and rewrites every
|
|
255
|
+
reference to the old ID across the repo, reporting which files it touched.
|
|
256
|
+
- User-level contexts are write targets only; nothing can depend on a task
|
|
257
|
+
in another repo. If you need to track it, write the foreign ID in your own
|
|
258
|
+
task's Notes.
|
|
259
|
+
|
|
260
|
+
## CLI
|
|
261
|
+
|
|
262
|
+
All commands take `--json` on reads (and on writes, printing the resulting
|
|
263
|
+
task). Exit non-zero with a message on stderr for every error. No prompts,
|
|
264
|
+
no wizards; every command is fully specifiable by flags.
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
chitty init [--prefix TASK] write chitty.toml, chitty/tasks/, AGENTS.md block
|
|
268
|
+
chitty add TITLE -d TEXT [-c CTX] [--ac TEXT]... [-l LABEL]... [-p PRIO]
|
|
269
|
+
[--owner X] [--depends-on ID]...
|
|
270
|
+
chitty list [-c CTX] [-s STATUS]... [-l LABEL]... [--owner X] [--search TEXT]
|
|
271
|
+
[--archived | --all]
|
|
272
|
+
chitty show ID
|
|
273
|
+
chitty edit ID [--title T] [-s STATUS] [--add-label L]... [--rm-label L]... [-p PRIO]
|
|
274
|
+
[--owner X] [--depends-on ID]... [--rm-depends-on ID]...
|
|
275
|
+
[-d TEXT | --description-file F]
|
|
276
|
+
[--ac TEXT]... [--rm-ac N]... [--check N]... [--uncheck N]...
|
|
277
|
+
[--plan TEXT | --plan-file F] [--note TEXT]
|
|
278
|
+
chitty move ID CTX CTX must be in this repo; new ID, sets moved_from
|
|
279
|
+
chitty archive ID | chitty restore ID
|
|
280
|
+
chitty check [-c CTX] [PATH]... validate; non-zero on any error
|
|
281
|
+
chitty path ID print the file path (for editors/agents)
|
|
282
|
+
chitty mcp stdio MCP server (needs the mcp extra)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
- `-c/--context` defaults to `default_context`. On `add`, a user-level
|
|
286
|
+
context name is accepted: the file is written into that repo's working
|
|
287
|
+
tree as an untracked file and nothing else happens there — no commit,
|
|
288
|
+
pull or push, and that repo's `autocommit` setting is ignored. Any
|
|
289
|
+
`--depends-on` is validated against the *target* repo. Post-and-forget.
|
|
290
|
+
On every other command, `-c` must name a context in this repo.
|
|
291
|
+
- `-d` is required on `add`, because Description is required.
|
|
292
|
+
- `ID` arguments accept `TASK-13`, or bare `13` for the default context.
|
|
293
|
+
- `list` shows non-archived tasks by default; `--archived` shows only
|
|
294
|
+
archived; `--all` shows both. Default output is one line per task:
|
|
295
|
+
`TASK-12 doing Add search to the task list [cli,search]`.
|
|
296
|
+
- `move` is within the current repo only. It gives the task the next number
|
|
297
|
+
in the target context and keeps the slug. There is no cross-repo move;
|
|
298
|
+
`add -c otherrepo` plus `archive` here is the explicit two-step version.
|
|
299
|
+
|
|
300
|
+
## MCP
|
|
301
|
+
|
|
302
|
+
`chitty mcp` runs a stdio server. Tools mirror the CLI one-to-one with the
|
|
303
|
+
same names and argument names: `add`, `list`, `show`, `edit`, `move`,
|
|
304
|
+
`archive`, `restore`, `check`, `path`. They return the same JSON
|
|
305
|
+
the CLI prints with `--json`. One resource, `chitty://guide`, returning the
|
|
306
|
+
same text as the AGENTS.md block. Tool descriptions state the rules (one
|
|
307
|
+
task per session, write Plan before coding, tick criteria as you go).
|
|
308
|
+
|
|
309
|
+
The `mcp` package pulls in starlette, uvicorn, httpx and friends, so it is an
|
|
310
|
+
optional extra: `uv tool install 'chitty[mcp]'`. Agents with a shell can use
|
|
311
|
+
the CLI with `--json` and need none of it. `chitty mcp` without the extra
|
|
312
|
+
exits with an error saying so.
|
|
313
|
+
|
|
314
|
+
## Agent instructions
|
|
315
|
+
|
|
316
|
+
`chitty init` appends this block to `AGENTS.md` (creating it if absent),
|
|
317
|
+
idempotently, keyed on the `## Chitty` heading:
|
|
318
|
+
|
|
319
|
+
```markdown
|
|
320
|
+
## Chitty
|
|
321
|
+
|
|
322
|
+
Tasks live in `chitty/`. Use the `chitty` CLI (or MCP tools); do not hand-edit
|
|
323
|
+
task files. `chitty --help` is the full command reference.
|
|
324
|
+
|
|
325
|
+
- Work on one task per session. Start with `chitty show <ID>`.
|
|
326
|
+
- Before writing code, write your approach with `chitty edit <ID> --plan` and
|
|
327
|
+
set `-s doing`. Wait for approval if the human asked for it.
|
|
328
|
+
- Tick acceptance criteria as you meet them: `chitty edit <ID> --check N`
|
|
329
|
+
(numbers are shown by `chitty show`).
|
|
330
|
+
- Record anything surprising with `--note`. Set `-s done` when every
|
|
331
|
+
criterion is ticked and the work is verified.
|
|
332
|
+
- Found unrelated work? `chitty add` it (`-c human` if it needs a person).
|
|
333
|
+
Don't do it in this session.
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
That is the whole prompt. Statuses are fixed, so the block is always correct.
|
|
337
|
+
|
|
338
|
+
## Stack
|
|
339
|
+
|
|
340
|
+
- Python ≥ 3.12, packaged with `uv`; installed as `uv tool install chitty`,
|
|
341
|
+
tried with `uvx chitty`.
|
|
342
|
+
- Runtime: `click`, `pydantic`, `pyyaml`. Frontmatter is split on `---` by
|
|
343
|
+
our own code; YAML is loaded with a loader that resolves every scalar as a
|
|
344
|
+
string. Optional extra `mcp`.
|
|
345
|
+
- Dev: `ruff`, `pyright` (strict for `src/`, standard for tests; with
|
|
346
|
+
`types-PyYAML`), `unittest` (stdlib;
|
|
347
|
+
tests write real files in a temp dir and drive the CLI through
|
|
348
|
+
`click.testing.CliRunner`; no filesystem mocking), `prettier` and `mdformat`
|
|
349
|
+
with the plugins above for the round-trip tests.
|
|
350
|
+
- `Makefile` following the mate conventions (`.PHONY` targets with `##`
|
|
351
|
+
descriptions): `make check` (ruff + pyright + tests), `make format`.
|
|
352
|
+
- Git via `subprocess.run(..., check=True)`. Only used when `autocommit` is
|
|
353
|
+
on: `git add -- <paths> && git commit --only -m "chitty: <verb> <ID> <title>" -- <paths>`,
|
|
354
|
+
with all affected paths listed (two for `move`/`archive`/`restore`, more
|
|
355
|
+
when `move` rewrites references), so nothing the user had staged is swept
|
|
356
|
+
in. Failure is a loud error.
|
|
357
|
+
|
|
358
|
+
## Module layout
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
src/chitty/
|
|
362
|
+
__init__.py
|
|
363
|
+
model.py Task, Frontmatter, Criterion — parse/validate/serialise ONE file
|
|
364
|
+
ids.py ID and filename parsing, slugs, next number, shorthand
|
|
365
|
+
config.py RepoConfig, UserContexts — load + validate TOML
|
|
366
|
+
store.py Context, find/list/read/write/move/archive
|
|
367
|
+
check.py cross-file validation: parse errors, duplicate IDs,
|
|
368
|
+
dangling or self depends_on
|
|
369
|
+
errors.py ChittyError, the base of every user-facing error
|
|
370
|
+
git.py optional autocommit
|
|
371
|
+
cli.py click commands; thin, calls store/model, prints
|
|
372
|
+
mcp.py tool definitions wrapping the same functions as cli.py
|
|
373
|
+
(mcp ≥ 2: MCPServer, not FastMCP)
|
|
374
|
+
guide.md the AGENTS.md block, as package data
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`cli.py` and `mcp.py` contain no logic; both call the same functions in
|
|
378
|
+
`store.py`/`model.py` so behaviour cannot diverge.
|
|
379
|
+
|
|
380
|
+
## Build order
|
|
381
|
+
|
|
382
|
+
1. `model.py` + `ids.py` + tests: parse, single-file validation, patch-style
|
|
383
|
+
serialisation, round-trip. Tests include: prettier (default and
|
|
384
|
+
`proseWrap: always`) and mdformat (with plugins) are no-ops on tool
|
|
385
|
+
output; a wrapped criterion is one criterion and ticking it doesn't
|
|
386
|
+
unwrap it.
|
|
387
|
+
2. `config.py`, `store.py`: contexts, read/write/move/archive,
|
|
388
|
+
missing-directory-is-empty.
|
|
389
|
+
3. `check.py`: duplicates and dangling references.
|
|
390
|
+
4. `cli.py`: `init`, `add`, `list`, `show`, `edit`, `check`, `path`.
|
|
391
|
+
5. `move`, `archive`/`restore`, user-level contexts.
|
|
392
|
+
6. `autocommit`.
|
|
393
|
+
7. `mcp.py` as the optional extra.
|
|
394
|
+
8. Dogfood: track Chitty's own work in `chitty/` from step 4 onward.
|
|
395
|
+
|
|
396
|
+
Steps 1–4 are a usable tool. Estimate for the whole list: 3–5k lines
|
|
397
|
+
including tests.
|