taskai-cli 1.9.0__tar.gz → 1.9.25__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.
Files changed (60) hide show
  1. taskai_cli-1.9.25/.github/workflows/test.yml +43 -0
  2. taskai_cli-1.9.25/CHANGELOG.md +110 -0
  3. taskai_cli-1.9.25/PHASE_1_RELEASE.md +344 -0
  4. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/PKG-INFO +1 -1
  5. taskai_cli-1.9.25/docs/_static/demo-script.md +39 -0
  6. taskai_cli-1.9.25/docs/browser-mode.md +71 -0
  7. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/conf.py +2 -3
  8. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/index.md +4 -2
  9. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/pyproject.toml +1 -1
  10. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/browser.py +21 -1
  11. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/cli.py +6 -5
  12. taskai_cli-1.9.25/taskai/dates.py +47 -0
  13. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/filters.py +2 -4
  14. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/help_menu.py +4 -2
  15. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/models.py +5 -0
  16. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/camera.js +11 -6
  17. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/canvas.js +64 -4
  18. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/config.js +22 -5
  19. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/console.js +4 -1
  20. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/editpanel.js +4 -0
  21. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/graph.js +40 -15
  22. taskai_cli-1.9.25/taskai/static/navigation.js +83 -0
  23. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/render.js +38 -1
  24. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/shortcuts.js +59 -19
  25. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/style.css +54 -43
  26. taskai_cli-1.9.25/test/test_chains.py +311 -0
  27. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/test/test_cli.py +45 -0
  28. taskai_cli-1.9.0/docs/browser-mode.md +0 -51
  29. taskai_cli-1.9.0/taskai/static/navigation.js +0 -44
  30. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/.github/workflows/publish-to-pypi.yml +0 -0
  31. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/.gitignore +0 -0
  32. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/.readthedocs.yaml +0 -0
  33. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/DEVLOG.md +0 -0
  34. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/DEVPLAN.md +0 -0
  35. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/README.md +0 -0
  36. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/TRIAGE.md +0 -0
  37. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/.gitignore +0 -0
  38. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/_static/.gitkeep +0 -0
  39. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/ai-integration.md +0 -0
  40. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/commands.md +0 -0
  41. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/configuration.md +0 -0
  42. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/getting-started.md +0 -0
  43. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/interactive-mode.md +0 -0
  44. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/item-data.md +0 -0
  45. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/managing-the-tree.md +0 -0
  46. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/docs/requirements.txt +0 -0
  47. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/scripts/bump_version.py +0 -0
  48. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/config.py +0 -0
  49. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/errors.py +0 -0
  50. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/json_dir_database.py +0 -0
  51. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/llm_models.py +0 -0
  52. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/services/ai.py +0 -0
  53. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/services/pomodoro.py +0 -0
  54. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/services/repair_database.py +0 -0
  55. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/services/user_setup.py +0 -0
  56. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/static/index.html +0 -0
  57. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/taskai/views.py +0 -0
  58. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/test/test_execution.py +0 -0
  59. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/test/test_json_dir_database.py +0 -0
  60. {taskai_cli-1.9.0 → taskai_cli-1.9.25}/test/test_view.py +0 -0
@@ -0,0 +1,43 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ pytest:
9
+ name: pytest
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.12"
17
+ - name: Install
18
+ run: |
19
+ python -m pip install --upgrade pip
20
+ pip install -e .
21
+ pip install pytest
22
+ - name: Run tests
23
+ run: python -m pytest -q
24
+
25
+ docs:
26
+ name: docs (build + linkcheck)
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v6
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v6
32
+ with:
33
+ python-version: "3.12"
34
+ - name: Install docs dependencies
35
+ run: pip install -r docs/requirements.txt
36
+ # -W: warnings fail the build - this currently includes the known
37
+ # docs/_static/demo.gif placeholder (see PHASE_1_RELEASE.md), so this
38
+ # job is red until that's swapped for a real recording. Intentional:
39
+ # a known, tracked gap is exactly what this check should catch.
40
+ - name: Build docs (warnings as errors)
41
+ run: sphinx-build -b html -W docs docs/_build/html
42
+ - name: Check external links
43
+ run: sphinx-build -b linkcheck docs docs/_build/linkcheck
@@ -0,0 +1,110 @@
1
+ # Changelog
2
+
3
+ Notable changes, newest first. Loosely follows [Keep a
4
+ Changelog](https://keepachangelog.com/); dates are UTC, from the commit
5
+ history. Versions before 1.6.6 predate this file and aren't reconstructed
6
+ here in detail — see `DEVLOG.md` for the full engineering history back to
7
+ the project's start.
8
+
9
+ ## [Unreleased]
10
+
11
+ The v1 release polish pass — see `PHASE_1_RELEASE.md` for the full working
12
+ list this is drawn from.
13
+
14
+ ### Added
15
+ - Per-item DAG node color override (`color` field), and per-status-string
16
+ color mapping via `task config set STATUS_COLORS=...`
17
+ - `due_by` shown directly on DAG nodes
18
+ - `task browser [port]`
19
+ - Multiple accepted `due_by` formats (ISO, `MM/DD/YYYY`, `Dec 31, 2026`,
20
+ etc.) and the relative keywords `today`/`tomorrow`, both when setting a
21
+ value and in filters (`task show 'due_by=today'`)
22
+ - Configuration now reaches the browser (`GET /api/style`) — the
23
+ foundation the node-color and status-color features above build on
24
+ - Up/down arrows fall back to walking a chain when there's no real tree
25
+ relationship in that direction, instead of dead-ending
26
+ - DAG empty-state message when there are no items yet
27
+ - Test coverage for the chain feature (previously had none)
28
+ - CI: `pytest` and a docs build/linkcheck on every push and PR
29
+
30
+ ### Changed
31
+ - **DAG orientation flipped:** the tree now grows rightward with siblings
32
+ stacking downward (previously grew downward with siblings spread
33
+ right) — arrow-key directions rotated to match
34
+ - Deleting the selected node in the browser now restores the previously
35
+ selected node instead of always jumping to the whole-tree view
36
+ - `task next` now focuses the node it just created in the browser
37
+
38
+ ### Fixed
39
+ - Unquoted multi-word input across `create`/`add`/`rename` no longer
40
+ silently truncates
41
+
42
+ ## [1.9.0] - 2026-09-14
43
+
44
+ Chain data integrity and a full CLI command surface for chains
45
+ (`is_chain_head`/`prev_chain_id`/`next_chain_id` and the DAG rendering for
46
+ them shipped in the browser work below this).
47
+
48
+ ### Added
49
+ - `task chain <prev> <node>` — link two existing items into a chain,
50
+ popping `node` off its current parent first if it has one
51
+ - `task unchain <node>` — detach an item from its chain without deleting
52
+ it; it becomes a child of the chain's head's parent
53
+ - `task delete <id> -chain` — delete an entire chain in one shot, from
54
+ whichever link you target
55
+ - `task clear` now reaches items chained off the thing being cleared, not
56
+ just its direct children
57
+
58
+ ### Fixed
59
+ - Deleting a chain's head used to orphan the rest of the chain (the
60
+ parent's `child_ids` kept pointing at the deleted id)
61
+ - `task show all` no longer lists every non-head chain member as a
62
+ spurious top-level item
63
+ - Linking an item already mid-chain into a different chain no longer
64
+ corrupts both
65
+
66
+ ### Removed
67
+ - The stored `is_chain_head` field — it's derived now
68
+ (`prev_chain_id is None and next_chain_id is not None`), removing a
69
+ class of "forgot to update the flag" bugs
70
+
71
+ ## [1.8.1] - 2026-09-10
72
+
73
+ ### Fixed
74
+ - Zoom behavior in the browser DAG
75
+ - `show` filters persisting correctly through a delete
76
+ - Edit panel focus handling, including an `Esc`-related focus bug
77
+ - Assorted minor browser UX fixes
78
+
79
+ ## [1.8.0] - 2026-09-09
80
+
81
+ ### Added
82
+ - Attribute filters for `task show` (`task show 'priority>=2'
83
+ 'due_by<10-01-2026'`), in both the CLI and the browser
84
+
85
+ ## [1.7.1] - 2026-09-09
86
+
87
+ ### Added
88
+ - Per-parent "last visited child" memory for `down` navigation in the
89
+ browser DAG
90
+
91
+ ### Changed
92
+ - Left/right navigation no longer wraps within a parent's direct
93
+ children - it steps across the whole depth level instead
94
+ - `Esc` no longer moves the camera
95
+
96
+ ### Fixed
97
+ - Up/down navigation no longer wraps around at the top/bottom of the tree
98
+ - Various edit-panel interaction bugs
99
+
100
+ ## [1.7.0] - 2026-09-03
101
+
102
+ ### Added
103
+ - Keyboard shortcuts and a shortcuts reference panel in the browser
104
+
105
+ ## [1.6.6] and earlier
106
+
107
+ Everything from the initial CLI (tree-structured items, no separate
108
+ "lists" concept), through the AI layer's migration to `litellm`, to the
109
+ first browser DAG view (canvas rendering, pan/zoom, the edit panel, and
110
+ the command console). See `DEVLOG.md` for the detailed history.
@@ -0,0 +1,344 @@
1
+ # Phase 1 Release — Punch List
2
+
3
+ All the major features for v1 are done (browser DAG with chains, `task ai`
4
+ on `litellm`, filters, comments, keyboard shortcuts, the chain CLI). This is
5
+ what's left before cutting the release — pulled from the open items in
6
+ [DEVPLAN.md](DEVPLAN.md) and [TRIAGE.md](TRIAGE.md) plus new scope Alex
7
+ added directly to this list, filtered down to what's actually in scope for
8
+ "ready to ship v1," not the longer-term roadmap. Real new features
9
+ (dependency edges in the DAG, `task ai agent`, a fully expanded `task show`
10
+ view) are called out at the bottom as explicitly **not** v1 — they're
11
+ staying in DEVPLAN/TRIAGE, not duplicated here.
12
+
13
+ ---
14
+
15
+ ## Small polish
16
+
17
+ - [x] **`task browser` has no `[port]` override.** (TRIAGE / Low)
18
+ `Controller.browser_service` hardcodes uvicorn's default port.
19
+ Original DEVPLAN scoping (1.1) called for `task web [port]`; small,
20
+ contained fix. `task browser {port}`, defaults to 8000.
21
+ - [x] **DAG empty/loading state.** (DEVPLAN Phase 3) A user with no tasks
22
+ yet just sees a blank canvas — no "nothing here yet" state. Drawn in
23
+ screen space (stays centered regardless of pan/zoom) whenever
24
+ `graph.nodes.length === 0` — covers both "still loading" and
25
+ "genuinely empty" with one message rather than adding a separate
26
+ loading-flag/spinner for what's normally a near-instant local fetch.
27
+ - [x] **`style.css` spacing/typography consistency pass.** (DEVPLAN Phase 3)
28
+ Never done; low effort, worth a once-over before screenshots go in
29
+ the docs/listing. Pulled the repeated color/font literals (border,
30
+ muted/primary/label text, accent blue, input background, both font
31
+ stacks — each duplicated 2-9x) into `:root` custom properties;
32
+ normalized the one real inconsistency found, an odd `12.5px` body
33
+ text size next to `13px` used everywhere else for the same kind of
34
+ text. Deliberately left spacing/radius values (8/10/6/5px) alone —
35
+ those read as intentionally graduated by element scale, not
36
+ erroneous, and I can't visually verify a change without screenshots.
37
+ - [x] **Re-check the Phase 0 fixes are still holding.** (DEVPLAN Phase 3)
38
+ Written when the web UI was new; now that chains + the new CLI
39
+ surface exercise a lot more of `execute_commands`, worth a quick
40
+ re-pass rather than assuming. All 5 confirmed intact via source
41
+ inspection: `ai_prompt` (not the raw prompt) is what's sent to the
42
+ LLM; `_add_info`'s visited-set bug stays fixed; the parsed command
43
+ list from `task ai` is actually executed
44
+ (`services/ai.py`'s loop over `execute_commands`); `execute_commands`'s
45
+ except block has no `raise e` ahead of the friendly `throw_error`
46
+ path; `views.py` no longer calls the nonexistent `db.read`/`db.items`.
47
+ No live LLM call made (didn't want to spend API quota for this) —
48
+ everything else checked statically.
49
+ - [x] **Delete should shift focus intelligently, not just to the root.**
50
+ After deleting the selected node, restore selection to whatever was
51
+ focused *before* it (needs at least a one-deep "previously selected"
52
+ memory, since `state.selectedNode` today just gets re-resolved to
53
+ `rootNode` once the deleted id no longer exists in the fresh graph)
54
+ — fall back to the synthetic root only if there's nothing to return
55
+ to. `state.previousSelectedId` (canvas.js), updated by `selectNode`
56
+ any time the selection actually changes — so this falls out of the
57
+ same general mechanism for every navigation, not something
58
+ delete-specific.
59
+ - [x] **`next` should focus the node it just created.** Today, running
60
+ `next <prev> <name>` through the console applies the fresh tree but
61
+ doesn't select/focus anything — you have to go find it. `show`
62
+ already gets special server-side `focus`-id handling in
63
+ `POST /api/command` (`taskai/browser.py`); extending that same
64
+ `focus` response to `next` (the newly-appended item's id) is the
65
+ consistent way to do this, rather than a client-side before/after-id
66
+ diff like `addNodeAndEdit`'s shortcut does for `a`. Server-side:
67
+ diffs `db.get_item_ids()` before/after running `next` through
68
+ `execute_commands`. Found and fixed a related inconsistency in
69
+ passing: the console's *generic* command fallback (any command not
70
+ specially handled, like `next` typed directly) was already reading
71
+ `data.focus` and moving the camera, but never actually calling
72
+ `selectNode` — so it centered on the new node without selecting it
73
+ (edit panel wouldn't update, arrow nav wouldn't be relative to it).
74
+ Fixed to call both.
75
+ - [x] **Show `due_by` on the node itself.** `render.js`'s `drawNode` draws
76
+ id (top-left) and `status` (top-right) already; add `due_by`
77
+ somewhere on the card (needs a spot picked that doesn't collide with
78
+ the wrapped label or the status text). Landed bottom-right,
79
+ compact `MM/DD`. Had to add `due_by` to `graph.js`'s node object too
80
+ — it wasn't being copied from the API payload at all.
81
+
82
+ ## New features for v1
83
+
84
+ - [x] **Configuration needs to actually reach the browser.** `STYLE` in
85
+ `config.js` is hardcoded JS — the file's own header comment already
86
+ says "this will eventually be served by a backend endpoint (e.g.
87
+ `GET /api/style`)." This is the foundational piece the next two items
88
+ ride on: a config surface (new `CLIConfig` keys, most likely) that
89
+ the browser reads at load, rather than everything being a compile-time
90
+ JS constant. New `GET /api/style` (`browser.py`) returns the raw
91
+ `CLIConfig.model_dump()` unprocessed — no new endpoint needed every
92
+ time a renderer-relevant key gets added later, interpreting it is
93
+ each consumer's own job. Frontend: `canvas.js`'s new `loadConfig()`
94
+ fetches it into `STYLE.serverConfig` before the first tree load
95
+ (`loadConfig().then(loadTree)`, so config is guaranteed to be in
96
+ place before the first real render); `config.js` seeds
97
+ `STYLE.serverConfig = {}` so nothing has to null-check before that
98
+ resolves. Verified: `curl /api/style` against a live instance
99
+ returns the real `CLIConfig` JSON. No visible UI change from this
100
+ commit alone by design — nothing reads `serverConfig` yet, the next
101
+ item is the first real consumer.
102
+ - [x] **Node render color as an item attribute.** A per-item color override
103
+ (new `TodoItem` field, or reuse an existing free-form one) that
104
+ `render.js`'s `drawNode` checks before falling back to the
105
+ completed/default fill — lets a user color-code specific nodes
106
+ directly rather than only via `completed`/`status`. New `TodoItem.
107
+ color: Optional[str]` (any CSS color string), threaded through
108
+ `graph.js`'s node object and checked in `drawNode` before the
109
+ completed/default fill logic. Edit panel gets a plain text field
110
+ (deliberately not a native `<input type="color">` — that widget can
111
+ never represent "unset," always some valid hex color; an empty
112
+ string here correctly falls back to the default via JS truthiness,
113
+ no special-case handling needed anywhere). Verified end to end:
114
+ created an item with `--color`, confirmed it in the live `/api/tree`
115
+ payload, cleared it back to `""` and confirmed the model round-trips
116
+ that correctly.
117
+ - [x] **Status-string → color mapping, defined in config.** e.g.
118
+ `RUNNING=green, BLOCKED=red` as a new `CLIConfig` key (config-key
119
+ naming and exact syntax TBD — something in the spirit of the existing
120
+ `DISPLAY_STRING`/`DISPLAY_COLORS` pair). Right now every non-empty
121
+ `status` renders the same fixed orange in the DAG (`STYLE.colors.
122
+ statusText`) regardless of its value; this makes that configurable
123
+ per status string. Depends on the config-to-browser item above.
124
+ New `CLIConfig.STATUS_COLORS: str = ""`, comma-separated
125
+ `STATUS=color` pairs (e.g. `"RUNNING=green,BLOCKED=red"`) — settable
126
+ with the existing generic `task config set STATUS_COLORS ...`, no new
127
+ command needed. `render.js`'s new `parseStatusColors` parses it once
128
+ into `STYLE.statusColors` (in `canvas.js`'s `loadConfig`); `drawNode`
129
+ looks up the node's exact `status` string there before falling back
130
+ to the fixed `statusText` color. Verified: unit-tested the parser
131
+ (whitespace, multiple pairs, empty/undefined input) and round-tripped
132
+ a real value through `task config set` → `GET /api/style` against a
133
+ live instance.
134
+ - [x] **Chain-aware up/down arrows when there's no conflicting tree
135
+ relationship.** Currently arrows are pure tree nav and `f`/`b` are
136
+ the only way to walk a chain (deliberate split — see DEVPLAN 1.8,
137
+ worked out after hitting the "is c1 a chain head or a tree parent"
138
+ ambiguity). Refinement: a chain member's real children and its chain
139
+ successor/predecessor are mutually exclusive to check *per
140
+ direction*, so there's no actual ambiguity in the cases below —
141
+ - `down`: if the node has real children, behave exactly as today
142
+ (descend into them). **Only if it has none**, fall back to
143
+ `chainNext` instead of dead-ending.
144
+ - `up`: if the node has a tree parent, behave exactly as today.
145
+ **Only if it doesn't** (true for every non-head chain member,
146
+ which never has one), fall back to `chainPrev`.
147
+ This only changes behavior in the "otherwise a dead end" case — a
148
+ node with both a real child and a chain link (e.g. a chain head with
149
+ its own children) keeps today's behavior exactly, `f`/`b` still work
150
+ everywhere unchanged. Verified against 7 cases covering both
151
+ fallbacks, both non-fallback (unchanged) paths, and the true dead end
152
+ (tail, down).
153
+ - [x] **Transpose the DAG's coordinate axes: growth goes right, spread goes
154
+ down** (today: growth down, spread right). Affects `measure`/`place`
155
+ in `graph.js` — swap which recursion axis feeds `marginX +
156
+ ...*xSpacing` vs. `marginY + ...*ySpacing` in `place()`, done
157
+ consistently for both the classic tree layout and the chain layout
158
+ (a chain currently drawn "straight down, children to the right"
159
+ would become "straight right, children downward" — same relationship,
160
+ rotated). **Open question to settle when this is picked up:** should
161
+ the arrow-key mapping rotate with it (right arrow = descend, down
162
+ arrow = next sibling) so the keys still feel spatially consistent
163
+ with what's on screen? Leaning yes, but worth confirming rather than
164
+ assuming. **Went with yes.** Turned out to be a genuinely small,
165
+ contained change: `place()`'s two recursion parameters were already
166
+ abstract ("spread" and "growth" in grid units) — every call site
167
+ below the top two lines passes them straight through unchanged, so
168
+ the *entire* orientation is decided in exactly those two lines
169
+ (renamed the parameters from `x`/`y` to `spread`/`growth` so that's
170
+ obvious on read, not just true by convention). Also had to fix:
171
+ `treeGap`'s unit conversion (was dividing by `xSpacing`, needs
172
+ `ySpacing` now that it's a spread-axis gap); the synthetic
173
+ `rootNode`'s position (was "above the roots," now "before them along
174
+ growth," centered on spread); `focusOnNode`'s off-center bias
175
+ (renamed `STYLE.zoom.focusYRatio` → `focusGrowthRatio`, moved from Y
176
+ to X); and `navigation.js`'s depth-level grouping for left/right
177
+ (was "shared y sorted by x," now "shared x sorted by y"). Console
178
+ text commands (`up`/`down`/`left`/`right`) and `f`/`b` keep their
179
+ original *structural* meaning unchanged (typing "down" always means
180
+ "my child," regardless of screen orientation) — only the *physical*
181
+ arrow-key → structural-direction mapping in `shortcuts.js` rotates
182
+ (`ArrowRight`→"down", `ArrowLeft`→"up", `ArrowDown`→"right",
183
+ `ArrowUp`→"left"). `Shift`+arrow panning stays tied to the actual
184
+ screen direction, untouched. Verified: rebuilt the graph from real
185
+ chain data and confirmed growth increases along X while chain
186
+ members/side-children/siblings spread along Y; confirmed
187
+ `navigateGraph`'s rotated depth-level stepping against that same
188
+ layout; confirmed every static file still serves against a live
189
+ `task browser` instance.
190
+ - [x] **Smarter `due_by` parsing.** Two related asks:
191
+ - **Format inference.** `_parse_item_kwargs` (`cli.py`) currently
192
+ hard-requires `MM-DD-YYYY` (`datetime.strptime(v, "%m-%d-%Y")`) —
193
+ anything else throws. Accept common formats without the user
194
+ needing to know the exact one (ISO `YYYY-MM-DD`, `MM/DD/YYYY`,
195
+ `Dec 31 2026`, etc.) — likely a small ordered list of formats tried
196
+ in turn, or a light date-parsing library if one's already
197
+ available/acceptable to add as a dependency.
198
+ - **Relative keywords.** `due_by=today` / `due_by=tomorrow` (that's
199
+ filter syntax — `taskai/filters.py`'s `due_by<op>value` — so this
200
+ reads as primarily a filters ask: `task show 'due_by<today'` etc.
201
+ Worth deciding at implementation time whether the same keywords
202
+ should also work on the *setting* path — `--due_by tomorrow` on
203
+ `create`/`add`/`update`/`next` — since both go through date
204
+ parsing but currently via different code (`filters.py` vs.
205
+ `_parse_item_kwargs`) and only one was explicitly asked for.
206
+ **Resolved: did both, via one shared helper.** New
207
+ [taskai/dates.py](taskai/dates.py), `parse_date_value(raw)` — tries a
208
+ list of formats (`MM-DD-YYYY`, ISO, `MM/DD/YYYY`, `MM/DD/YY`, and
209
+ `Month D[,] YYYY` in long/short-month variants) then falls back to
210
+ `today`/`tomorrow` (case-insensitive). Used by *both*
211
+ `_parse_item_kwargs`'s `due_by` branch (`cli.py`) and
212
+ `filters.py`'s `_coerce` for `due_by`/`created_on` — so
213
+ `--due_by tomorrow` on `create`/`add`/`update`/`next` and
214
+ `task show 'due_by=today'` both work, not just one. Verified: the 7
215
+ existing tests still pass; unit-checked every format + both
216
+ keywords + a bad-input error path directly; end-to-end through the
217
+ real CLI (`create --due_by 2026-12-25`, `update --due_by tomorrow`,
218
+ `show 'due_by=tomorrow'` all resolved and matched correctly).
219
+ `help_menu.py` updated in both spots (item-fields table and the
220
+ filter example line).
221
+
222
+ ## Docs
223
+
224
+ - [~] Replace `docs/_static/demo.gif` placeholder with a real recording —
225
+ referenced from `index.md` and the README. Can't record the actual
226
+ gif (needs a real screen capture), but wrote the exact recording
227
+ script — `docs/_static/demo-script.md` (a production note, not a
228
+ published page; `_static/` is copied as-is, never parsed) — with the
229
+ precise commands to run, why each one's included, and what to skip.
230
+ Still needs someone to actually record it.
231
+ - [x] Confirm the Read the Docs project slug (`conf.py`'s `html_baseurl` +
232
+ README currently assume `taskai`). Confirmed live: fetched
233
+ `https://taskai.readthedocs.io/` and it resolves to the real project
234
+ (v1.9.0, right author, right content) — not a 404, not someone else's
235
+ docs. Removed the "needs confirmation" note in `conf.py`.
236
+ - [~] Screenshots for browser-mode docs. Can't capture real screenshots
237
+ (no browser automation available here), but added the exact shot
238
+ list inline in `docs/browser-mode.md` as HTML comments right where
239
+ each image belongs (matching `index.md`'s existing placeholder
240
+ pattern) — what each one should show and why, not just "add a
241
+ screenshot here." Updated the surrounding text in passing: it didn't
242
+ mention chains, the new per-item `color` field, or `due_by` display
243
+ at all, all real gaps now that those features exist. Still needs
244
+ someone to actually capture and drop in the images.
245
+ - [x] `help_menu.py`/README are current as of the chain work (Phase 8) —
246
+ spot-check the RTD site's generated pages didn't drift now that
247
+ `help_general` has a new "Chains:" section (`docs/commands.md`
248
+ `literalinclude`s it automatically, but worth a build-and-look). Ran
249
+ `sphinx-build -b html docs docs/_build/html`: builds clean (1
250
+ pre-existing warning, the known `demo.gif` placeholder — unrelated),
251
+ and the built `commands.html` correctly includes the full "Chains:"
252
+ section verbatim. Nothing needed fixing.
253
+ - [~] Lower priority, nice-to-have if time allows: explanation/architecture
254
+ pages, custom 404, OpenGraph cards, CI `linkcheck`, a changelog. CI
255
+ `linkcheck` is done (wired into the new CI job above). Added
256
+ [CHANGELOG.md](CHANGELOG.md) — an `Unreleased` section for
257
+ everything in this punch list plus dated entries for 1.9.0 back to
258
+ 1.6.6, built from the actual version-bump commits and what changed
259
+ between them. Left the rest (architecture pages, custom 404, OG
260
+ cards) genuinely deferred — each is either a real content-writing
261
+ effort in its own right or needs an asset I can't produce, and none
262
+ were called out as blocking.
263
+
264
+ ## Testing
265
+
266
+ - [x] **Zero test coverage for the chain feature.** The most structurally
267
+ risky thing shipping in this release (`next`/`chain`/`unchain`,
268
+ `delete -chain`, chain-aware `clear`, the `insert_node_into_chain` /
269
+ `remove_node_from_chain` / `delete_chain` DB methods) currently has no
270
+ tests at all — worth prioritizing over the general suite-broadening
271
+ below given how much subtle pointer-juggling is in that code path.
272
+ New [test/test_chains.py](test/test_chains.py), 12 DB-layer tests
273
+ covering insert (builds exactly one head; detaches a node already
274
+ mid-chain before re-splicing it elsewhere, verifying *both* chains
275
+ end up clean; pops a node off a real tree parent; inserting into the
276
+ middle of an existing chain), remove (middle-link relink; tail
277
+ shortening; head removal both standalone — the old head legitimately
278
+ stays put, matching what `unchain` needs — and via `delete_item`,
279
+ where it doesn't, which is the actual regression test for the
280
+ chain-head-deletion bug; the length-2-chain collapse edge case), and
281
+ `delete_chain`/`delete_item` interplay (full cascade + parent
282
+ detachment; a chain nested under a deleted parent takes its
283
+ real-children cascade path too; a plain mid-chain `delete` still
284
+ behaves like a linked-list splice). Also added a CLI-dispatch smoke
285
+ test to `test_cli.py` (`test_run_chain_commands`) exercising
286
+ `next`/`chain`/`unchain`/`delete -chain` end to end through the real
287
+ `execute_commands` path, matching the existing suite's
288
+ subprocess-based style. **Found one real bug writing these**, caught
289
+ by the test suite itself: my first draft of the head-removal test
290
+ called `remove_node_from_chain` directly and asserted the old head
291
+ was gone from the parent's children — wrong assertion, not a code
292
+ bug (that function alone never deletes anything, `delete_item` does,
293
+ by calling `remove_child_from_parent` *before* it) — but chasing it
294
+ down is exactly the kind of thing this coverage was for. All 20 tests
295
+ pass (7 pre-existing + 13 new).
296
+ - [ ] Broaden the general test suite (DEVPLAN Phase 6) — currently 7 tests
297
+ total across `test_cli.py`/`test_execution.py`/
298
+ `test_json_dir_database.py`/`test_view.py`. Lower priority than the
299
+ chain coverage above for a v1 cutoff specifically. **Deliberately
300
+ left for last and not started** — this is genuinely open-ended
301
+ (DEVPLAN Phase 6 scopes it as "every `execute_commands` branch,
302
+ id/name resolution, `_parse_item_kwargs`, the browser endpoints,
303
+ the AI command-execution loop with a mocked LLM"), it was already
304
+ explicitly the lowest priority item on this whole list, and
305
+ everything else got done first. A real next session, not a
306
+ last-minute partial pass tacked onto this one.
307
+ - [x] Wire CI (`pytest` on every PR, `sphinx-build -W` + `linkcheck` for
308
+ docs) — nice-to-have, fine to slip past v1 if time is tight. New
309
+ `.github/workflows/test.yml`, two jobs on `push`/`pull_request`:
310
+ `pytest` (installs the package + pytest, runs the suite) and `docs`
311
+ (`sphinx-build -b html -W` then `-b linkcheck`). **Heads up: the docs
312
+ job will start out red** — `-W` treats warnings as errors, and the
313
+ known `demo.gif` placeholder (see Docs section below) is currently
314
+ the one warning in the build. That's intentional, not a bug in the
315
+ workflow — confirmed locally that plain `sphinx-build -b html`
316
+ reports exactly that one warning and `-W` correctly turns it into a
317
+ failure. Didn't touch the existing `publish-to-pypi.yml` or any repo
318
+ branch-protection settings (whether this becomes a required check is
319
+ a repo-settings call, not something in the workflow file).
320
+
321
+ ---
322
+
323
+ ## Explicitly NOT v1 (stays in DEVPLAN/TRIAGE, not duplicated here)
324
+
325
+ - **`dependency_ids`** — Alex's call: drop this from v1 scope entirely,
326
+ revisit as a Phase 2 issue (whether that means wiring up `depend`/
327
+ `undepend` or actually removing the dead field stays an open question for
328
+ then, not now).
329
+ - **`task show` — light polish only for v1, full expansion later.**
330
+ DEVPLAN Phase 5 already flags the expanded view (depth limits, flat vs.
331
+ tree, sort order, color toggles) as needing a scoping session; Alex
332
+ confirmed: keep v1's touch light, scope the bigger version collaboratively
333
+ when picked up.
334
+ - Dependency edges in the browser DAG, and a real `depend`/`undepend` UI —
335
+ deferred to "2.0" in DEVPLAN 1.4/2.4 (same field as above).
336
+ - `task ai agent <prompt>` (real tool-calling loop, gated destructive
337
+ actions) — deferred to "2.0" in DEVPLAN 2.4.
338
+ - Advertising/launch prep (DEVPLAN Phase 7) — a separate workstream from
339
+ code readiness, not a polish/bugfix item.
340
+ - DEVPLAN Phase 1.1's originally-planned stdlib `http.server` + `task web
341
+ [port]` module is stale — the real implementation is FastAPI + uvicorn via
342
+ `taskai/browser.py`, built out completely under 1.2–1.8. Not a real to-do;
343
+ DEVPLAN should probably strike that subsection out rather than leave it
344
+ looking open.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: taskai-cli
3
- Version: 1.9.0
3
+ Version: 1.9.25
4
4
  Author-email: Alex Paskal <alexcpaskal@gmail.com>
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: fastapi>=0.141.1
@@ -0,0 +1,39 @@
1
+ # demo.gif recording script
2
+
3
+ Production note for whoever records `docs/_static/demo.gif` — not a
4
+ published page (this directory is Sphinx's static-asset folder, copied
5
+ as-is, never parsed as content). Delete this file once the real gif is in
6
+ place and this is no longer needed.
7
+
8
+ **Target:** ~20-30s, terminal only (a separate, shorter browser-mode clip
9
+ can live on its own if wanted — this one is the CLI). Readable font size,
10
+ nothing that requires the viewer to have already read the docs.
11
+
12
+ Run this in a scratch directory with a fresh `.taskai/task_db` (`task nuke`
13
+ first if reusing one, or just start somewhere new) so item ids are small
14
+ and the tree is uncluttered.
15
+
16
+ ```bash
17
+ task create "Launch blog"
18
+ task add "Launch blog" "Write outline" --priority 1 --due_by tomorrow
19
+ task next "Write outline" "Draft intro"
20
+ task show all
21
+ task done "Write outline"
22
+ task ai "add a task under Launch blog to buy a domain"
23
+ task show all
24
+ ```
25
+
26
+ What this covers, in order:
27
+ 1. `create`/`add` — the everything-is-an-item tree, with a couple of fields
28
+ set (`--priority`, `--due_by tomorrow` — also shows off the relative
29
+ date keyword)
30
+ 2. `next` — chains, the newest major feature, right after the tree basics
31
+ 3. `show all` — the Rich-rendered tree, chain included (rendered inline
32
+ with the bold down-arrow between chain links)
33
+ 4. `done` — quick, satisfying, worth the second of screen time
34
+ 5. `task ai` — the natural-language layer actually creating something,
35
+ then a final `show all` so the viewer sees its result land in the tree
36
+
37
+ Skip `task browser` here — screen-recording a pannable/zoomable canvas
38
+ doesn't compress well as a small looping gif, and it already gets its own
39
+ screenshots (see the placeholders in `docs/browser-mode.md`).
@@ -0,0 +1,71 @@
1
+ # Browser mode
2
+
3
+ ```bash
4
+ task browser
5
+ ```
6
+
7
+ This starts a local web server and serves a visual view of your task tree.
8
+
9
+ ## The canvas
10
+
11
+ The whole tree is drawn as connected cards. Drag to pan, scroll to zoom.
12
+ Double-click a card to center on it and open its edit panel.
13
+
14
+ <!-- screenshot: a tree with a few branches AND a chain (task next), wide
15
+ enough to show a plain parent/child branch, a chain rendered as its
16
+ straight bold-arrow line with real children fanned off to its side, and
17
+ at least one soft link (dashed ghost card) all in the same shot -->
18
+
19
+ - **Completed** items are tinted green.
20
+ - **`status`** text appears in the top corner of a card (or the color set by
21
+ `STATUS_COLORS` in config, if one's configured for that status).
22
+ - **`due_by`**, if set, appears in the bottom corner.
23
+ - **Soft links** (`task link`) are drawn as a dashed copy of the linked item
24
+ under its host, distinct from the solid parent/child connectors.
25
+ - **Chains** (`task next`/`task chain`) render as a straight bold arrow; a
26
+ chain link's own real children fan out to its side rather than centering
27
+ below it.
28
+
29
+ ## The edit panel
30
+
31
+ Click a card to select it; double-click it, or press `e`, to open the edit
32
+ panel on the right. It has a field for the name, description, status, color,
33
+ priority, due date, and completion, plus a comment feed. Changes are saved as
34
+ you make them — there is no save button. In a single-line field, `Enter`
35
+ commits the change and leaves the field.
36
+
37
+ <!-- screenshot: the edit panel open on a card with most fields filled in
38
+ (including a couple of comments, to show the feed) -->
39
+
40
+ ## The console
41
+
42
+ The panel at the bottom takes raw `task` commands and runs them against the
43
+ same data, printing their output and refreshing the canvas. Anything you can
44
+ type on the command line works here.
45
+
46
+ <!-- screenshot: the console expanded with a few example commands and their
47
+ output already in the scrollback -->
48
+
49
+ Filtering sticks: after `show 'priority>=2'` the canvas shows only the matches
50
+ (and their parents), and it stays scoped that way as you edit and delete —
51
+ until `show all` clears it.
52
+
53
+ ## Keyboard
54
+
55
+ Press `?` for the full list in the app. The essentials:
56
+
57
+ | Key | Action |
58
+ |---|---|
59
+ | arrows | move the selection through the tree |
60
+ | `Ctrl`+`↑` / `↓` | jump out of / into a chain member's subtree (`↑`/`↓` walk the chain itself) |
61
+ | `Shift`+arrows | pan the view |
62
+ | `+` / `-` / `0` | zoom in / out / fit |
63
+ | `t` | flip the layout between growing down and growing right |
64
+ | `e` | toggle the edit panel |
65
+ | `` ` `` | focus / close the console |
66
+ | `a` | add a child of the selected card and name it |
67
+ | `c` | create a new top-level card and name it |
68
+ | `n` | add the next card in the selected card's chain and name it |
69
+ | `d` | toggle done on the selected card |
70
+ | `Delete` | delete the selected card |
71
+ | `Esc` | leave a field, then close the panel, then (with every panel closed) show the whole tree |
@@ -66,9 +66,8 @@ exclude_patterns = ["_build", "_generated", "Thumbs.db", ".DS_Store"]
66
66
 
67
67
  html_theme = "furo"
68
68
  html_title = f"taskai {release}"
69
- # NOTE: confirm the Read the Docs project slug and adjust if it isn't "taskai"
70
- # (this also appears in the README). Read the Docs sets the per-version
71
- # canonical URL itself; this is just the site root.
69
+ # confirmed live at this slug (also referenced in the README). Read the
70
+ # Docs sets the per-version canonical URL itself; this is just the site root.
72
71
  html_baseurl = "https://taskai.readthedocs.io/"
73
72
  html_static_path = ["_static"]
74
73
 
@@ -4,8 +4,10 @@ A command-line task manager with AI features.
4
4
 
5
5
  ![taskai on the command line](_static/demo.gif)
6
6
 
7
- <!-- demo.gif: record a short terminal session (create a tree, `task show all`,
8
- complete an item, `task ai ...`) and drop it at docs/_static/demo.gif -->
7
+ <!-- demo.gif: record a short terminal session and drop it at
8
+ docs/_static/demo.gif. Full shot list + exact commands in
9
+ docs/_static/demo-script.md (not a published page - a production note
10
+ for whoever records this). -->
9
11
 
10
12
  ## What you can do
11
13
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskai-cli"
3
- version = "1.9.0"
3
+ version = "1.9.25"
4
4
  description = ""
5
5
  authors = [{name = "Alex Paskal", email = "alexcpaskal@gmail.com"}]
6
6
  readme = "README.md"