pyworklog 0.4.0__tar.gz → 0.6.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.
- pyworklog-0.6.0/CHANGELOG.md +335 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/CONTRIBUTING.md +41 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/DESIGN.md +191 -2
- {pyworklog-0.4.0 → pyworklog-0.6.0}/DESIGN.zh.md +10 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/PKG-INFO +1 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/pyproject.toml +1 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/skills/worklog-cli/SKILL.md +10 -6
- pyworklog-0.6.0/src/worklog/cli.py +1672 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/__init__.py +34 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/bulk.py +79 -67
- pyworklog-0.6.0/src/worklog/commands/help.py +493 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/meta.py +256 -56
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/metric.py +39 -47
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/query.py +157 -128
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/state.py +407 -145
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/commands/views.py +254 -87
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/completion.py +57 -10
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/db.py +5 -2
- pyworklog-0.6.0/src/worklog/db_table.py +214 -0
- pyworklog-0.6.0/src/worklog/help/en/active.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/add.md +27 -0
- pyworklog-0.6.0/src/worklog/help/en/admin.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/agenda.md +12 -0
- pyworklog-0.6.0/src/worklog/help/en/alias.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/ancestors.md +9 -0
- pyworklog-0.6.0/src/worklog/help/en/apply.md +20 -0
- pyworklog-0.6.0/src/worklog/help/en/bulk.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/cancel.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/changes.md +14 -0
- pyworklog-0.6.0/src/worklog/help/en/checkin.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/clock.md +19 -0
- pyworklog-0.6.0/src/worklog/help/en/config.md +8 -0
- pyworklog-0.6.0/src/worklog/help/en/date.md +11 -0
- pyworklog-0.6.0/src/worklog/help/en/dateinfo.md +14 -0
- pyworklog-0.6.0/src/worklog/help/en/day.md +27 -0
- pyworklog-0.6.0/src/worklog/help/en/defer.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/delete.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/descendants.md +9 -0
- pyworklog-0.6.0/src/worklog/help/en/done.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/find.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/focus.md +13 -0
- pyworklog-0.6.0/src/worklog/help/en/goal.md +14 -0
- pyworklog-0.6.0/src/worklog/help/en/ids.md +13 -0
- pyworklog-0.6.0/src/worklog/help/en/import.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/index.md +22 -0
- pyworklog-0.6.0/src/worklog/help/en/init.md +8 -0
- pyworklog-0.6.0/src/worklog/help/en/kind.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/link.md +17 -0
- pyworklog-0.6.0/src/worklog/help/en/log.md +20 -0
- pyworklog-0.6.0/src/worklog/help/en/logs.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/ls.md +20 -0
- pyworklog-0.6.0/src/worklog/help/en/meta.md +19 -0
- pyworklog-0.6.0/src/worklog/help/en/metric.md +16 -0
- pyworklog-0.6.0/src/worklog/help/en/migrate.md +8 -0
- pyworklog-0.6.0/src/worklog/help/en/node.md +28 -0
- pyworklog-0.6.0/src/worklog/help/en/para.md +38 -0
- pyworklog-0.6.0/src/worklog/help/en/planned.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/planning.md +23 -0
- pyworklog-0.6.0/src/worklog/help/en/print-completion.md +12 -0
- pyworklog-0.6.0/src/worklog/help/en/priority.md +17 -0
- pyworklog-0.6.0/src/worklog/help/en/projects.md +13 -0
- pyworklog-0.6.0/src/worklog/help/en/prop.md +17 -0
- pyworklog-0.6.0/src/worklog/help/en/recap.md +14 -0
- pyworklog-0.6.0/src/worklog/help/en/relog.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/reopen.md +9 -0
- pyworklog-0.6.0/src/worklog/help/en/reporting.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/sched.md +25 -0
- pyworklog-0.6.0/src/worklog/help/en/set.md +11 -0
- pyworklog-0.6.0/src/worklog/help/en/shortcuts.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/show.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/spent.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/start.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/status.md +24 -0
- pyworklog-0.6.0/src/worklog/help/en/stop.md +9 -0
- pyworklog-0.6.0/src/worklog/help/en/summary.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/tag.md +20 -0
- pyworklog-0.6.0/src/worklog/help/en/themes.md +8 -0
- pyworklog-0.6.0/src/worklog/help/en/tick.md +11 -0
- pyworklog-0.6.0/src/worklog/help/en/time.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/tracking.md +15 -0
- pyworklog-0.6.0/src/worklog/help/en/tree.md +18 -0
- pyworklog-0.6.0/src/worklog/help/en/unlink.md +9 -0
- pyworklog-0.6.0/src/worklog/help/en/unlog.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/unset.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/wait.md +10 -0
- pyworklog-0.6.0/src/worklog/help/en/window.md +20 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/helpers.py +34 -18
- pyworklog-0.6.0/src/worklog/migrations/0007_utc_timestamps.sql +46 -0
- pyworklog-0.6.0/src/worklog/migrations/0008_soft_delete_tombstones.sql +40 -0
- pyworklog-0.6.0/src/worklog/migrations/0009_tombstone_node_path_view.sql +18 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/queries.py +156 -50
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/render.py +45 -3
- pyworklog-0.6.0/src/worklog/timeutil.py +159 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/conftest.py +9 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_add.py +4 -4
- pyworklog-0.6.0/tests/test_alias.py +64 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_brief.py +32 -32
- pyworklog-0.6.0/tests/test_cascade.py +152 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_checkin.py +5 -5
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_clock.py +12 -9
- pyworklog-0.6.0/tests/test_cycle_safety.py +60 -0
- pyworklog-0.6.0/tests/test_dateinfo.py +111 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_day.py +97 -18
- pyworklog-0.6.0/tests/test_db_table.py +230 -0
- pyworklog-0.6.0/tests/test_default_verb.py +386 -0
- pyworklog-0.6.0/tests/test_filters.py +186 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_find.py +16 -16
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_focus.py +3 -3
- pyworklog-0.6.0/tests/test_help.py +363 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_import_apply.py +45 -45
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_link_set.py +47 -5
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_log.py +13 -11
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_log_format.py +1 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_logs.py +1 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_meta.py +82 -11
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_metric.py +29 -26
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_migrations.py +60 -10
- pyworklog-0.6.0/tests/test_node.py +121 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_projects.py +4 -4
- pyworklog-0.6.0/tests/test_prop_clock.py +139 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_render.py +16 -16
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_sched.py +27 -27
- pyworklog-0.6.0/tests/test_show.py +125 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_state.py +38 -9
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_summary.py +1 -1
- pyworklog-0.6.0/tests/test_timeutil.py +136 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_tree.py +51 -1
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_ux.py +162 -27
- {pyworklog-0.4.0 → pyworklog-0.6.0}/uv.lock +1 -1
- pyworklog-0.4.0/CHANGELOG.md +0 -98
- pyworklog-0.4.0/src/worklog/cli.py +0 -1281
- pyworklog-0.4.0/tests/test_cascade.py +0 -27
- pyworklog-0.4.0/tests/test_dateinfo.py +0 -42
- pyworklog-0.4.0/tests/test_show.py +0 -79
- {pyworklog-0.4.0 → pyworklog-0.6.0}/.github/workflows/release.yml +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/.github/workflows/test.yml +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/.gitignore +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/AGENTS.md +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/LICENSE +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/Makefile +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/README.md +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/README.zh.md +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/pytest.ini +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/__init__.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0001_initial_schema.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0002_metric_and_log_tag.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0003_backfill_checkin_metrics.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0004_meta_props_to_typed_logs.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0005_clock_table.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/migrations/0006_rename_log_type_to_tag.sql +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/src/worklog/xdg.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/__init__.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_aliases.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_changes.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_completion.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_config.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_init.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_ls.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_sample.py +0 -0
- {pyworklog-0.4.0 → pyworklog-0.6.0}/tests/test_xdg.py +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **worklog** are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures every commit; this file is the human-curated highlight reel.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.6.0] - 2026-06-07
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`wl show` annotates a recurring schedule with its next occurrence.** The `schedule:` line
|
|
13
|
+
now reads e.g. `recur weekly:Mon,Fri (next 2026-06-08 Mon); on 2026-06-20, 2026-06-25` — the
|
|
14
|
+
`(next …)` is computed by the same `_sched_fires` predicate `wl day` / `agenda` use, so it's
|
|
15
|
+
exactly when the task will reappear (the actionable bit, the way TaskWarrior / calendars surface
|
|
16
|
+
a recurrence's next due). One-off-only schedules list their dates as before (no `(next …)`).
|
|
17
|
+
- **`wl help` — an info-style topic browser (DESIGN §25).** `wl help` shows an index of
|
|
18
|
+
topics by category; `wl help <topic>` renders a fuller explanation of a command / concept /
|
|
19
|
+
parameter / workflow than `<command> -h` gives, with a "See also" footer linking related
|
|
20
|
+
topics. Topics are **repo-managed Markdown docs** under `src/worklog/help/<lang>/<topic>.md`
|
|
21
|
+
(shipped in the wheel, **i18n-ready** — language resolves `--lang` > `$WORKLOG_LANG` >
|
|
22
|
+
`$LANG` > `en`, falling back to `en` per-topic). Seeded with index + node / status /
|
|
23
|
+
priority / para / planning / add / day; `--help` stays the quick per-command reference and
|
|
24
|
+
now points into `wl help` for the deeper read (a doc-integrity test enforces no dangling
|
|
25
|
+
see-also links). Runs without a DB (safe before `wl init`). Each command's `--help`
|
|
26
|
+
**auto-gains a `More: wl help <cmd>` pointer** when a topic doc exists for it (no per-command
|
|
27
|
+
epilog edits), `wl help <topic>` is **tab-completable** (fish/bash/zsh), and the bare-`wl`
|
|
28
|
+
splash now points to it. Topic set expanded to 18 — added log / tag / sched / done / defer /
|
|
29
|
+
tree / ls / find / link / meta / prop alongside the seed concepts/guides.
|
|
30
|
+
- **`wl node` entity group — `node add / ls / show / edit / rm / reparent`.** The start of
|
|
31
|
+
reshaping the command surface to the metric-style `<entity> <verb>` form (WL#486). The
|
|
32
|
+
high-frequency `add` / `ls` / `show` are reachable both as `wl node <verb>` and as the
|
|
33
|
+
existing top-level shortcut (`wl add` == `wl node add`), one handler each, args defined
|
|
34
|
+
once so the two forms can't drift. Three new primitives close node's CRUD to the metric
|
|
35
|
+
bar: `node edit <id>` (edit a node's own fields — title / priority / kind / body /
|
|
36
|
+
scheduled / deadline; rejects an empty title), `node reparent <id> <parent|none|0>`
|
|
37
|
+
(change the real `parent_id`, with cycle / self-parent guards — WL#485), and `node rm <id>`
|
|
38
|
+
(soft-delete a node + subtree, reversible). Every current command keeps working unchanged.
|
|
39
|
+
- **`wl prop` and `wl clock` entity groups (WL#486), filling two CRUD gaps.** `wl prop
|
|
40
|
+
set / ls / rm` — `set` keeps its top-level shortcut and `rm` gains the new shortcut
|
|
41
|
+
`wl unset <id> <key>` (you can finally *remove* a custom prop, not just overwrite it).
|
|
42
|
+
`wl clock ls / edit / rm` — list / fix (edit start-end, recomputing the duration) /
|
|
43
|
+
remove a time interval; intervals are still created by `wl start` / `stop` / `spent`.
|
|
44
|
+
- **`wl link` entity group + a default-verb mechanism for name-collision entities (WL#486).**
|
|
45
|
+
`wl link add / ls / rm` — `link add` keeps the top-level shortcut, `rm` gains the
|
|
46
|
+
`wl unlink <id> <doc>` shortcut, and `link ls` is a new primitive (list a node's links).
|
|
47
|
+
Because the entity name `link` is itself the everyday verb, the parser learned a
|
|
48
|
+
default-verb rule: when the token after `link` isn't one of its verbs it's taken as an id,
|
|
49
|
+
so `wl link 42 doc` still means `wl link add 42 doc` while `wl link ls / rm / -h` route to
|
|
50
|
+
the group. Leaf ids are always integers, so id-vs-verb is unambiguous. This unblocks the
|
|
51
|
+
same treatment for the other collision entities (`log` / `tag` / `sched`).
|
|
52
|
+
- **`wl tag` reshaped into a metric-style group (WL#486), the second default-verb entity.**
|
|
53
|
+
`wl tag add / ls / rm`. `add` is the default verb, so the everyday `wl tag <id> +work
|
|
54
|
+
-planned` keeps working unchanged (full +add / -remove / bare-add / empty-list grammar);
|
|
55
|
+
`tag ls` lists, `tag rm` removes by plain name. `tag` joins `link` in the default-verb
|
|
56
|
+
table — `wl tag 42 +work` → `wl tag add 42 +work`, while `wl tag ls / rm / -h` route to
|
|
57
|
+
the group; id-first disambiguation means `wl tag 1 ls` adds the tag `ls` whereas `wl tag
|
|
58
|
+
ls 1` lists. Tag update stays atomic (no `edit` verb).
|
|
59
|
+
- **`wl log` reshaped into a metric-style group (WL#486), the third default-verb entity.**
|
|
60
|
+
`wl log add / ls / edit / rm`. `add` is the default verb, so the everyday `wl log <id>
|
|
61
|
+
"body"` keeps working unchanged (auto TODO→DOING, `--date` / `--time` / `--metric` /
|
|
62
|
+
`--keep-status`). `log edit` and `log rm` reuse the `relog` / `unlog` handlers — both keep
|
|
63
|
+
their top-level shortcuts — and `log ls <id>` is a new node-scoped lister (the full
|
|
64
|
+
filterable / windowed stream view stays at `wl logs`). id-first disambiguation: `wl log 1
|
|
65
|
+
ls` adds a log bodied `ls`, whereas `wl log ls 1` lists.
|
|
66
|
+
- **`wl sched` reshaped into a metric-style group (WL#486), the fourth (and final collision)
|
|
67
|
+
default-verb entity.** `wl sched add / ls / rm`. `add` is the default verb, so `wl sched
|
|
68
|
+
<id> <when>` / `wl sched <id> --recur …` / `wl sched <id> --clear` all keep working
|
|
69
|
+
unchanged (full when / `--recur` / `--clear` / list-when-empty grammar). `sched ls` lists,
|
|
70
|
+
`sched rm` clears; `wl defer` (status=LATER + rough hint) stays its own command. With this,
|
|
71
|
+
every collision entity (`link` / `tag` / `log` / `sched`) is reshaped.
|
|
72
|
+
- **`date_meta` reshaped into a clean `wl date` group (WL#486) — completing the reshape.**
|
|
73
|
+
`wl date set / ls / rm / import`. Unlike the collision entities this is a *clean* group
|
|
74
|
+
(no default verb — `date` doesn't collide with any leaf); `wl dateinfo` stays as the
|
|
75
|
+
polymorphic everyday shortcut (sets when given a label, lists when not, `--clear` /
|
|
76
|
+
`--import` variants) over the same table. **With this, every entity now has a full
|
|
77
|
+
`<entity> <verb>` group.**
|
|
78
|
+
- **`wl meta` group for the history-preserving typed-log fields (WL#486).** `wl meta set / ls
|
|
79
|
+
/ rm <node> <field>` for `goal` / `summary` / `overview` / `top5` (each edit appends a typed
|
|
80
|
+
log, latest = current — distinct from props, which are static single-value). `wl set` / `wl
|
|
81
|
+
unset` are now *key-routed* shortcuts: a meta key routes to `wl meta set` / `wl meta rm`,
|
|
82
|
+
any other key to `wl prop set` / `wl prop rm` (the same way `wl add` is `wl node add`). `wl
|
|
83
|
+
goal` / `wl recap` stay as the today-auto shortcuts. Behavior of `wl set <node>
|
|
84
|
+
overview/top5/goal/summary` is unchanged — it's just documented and now has a canonical
|
|
85
|
+
group form. (Known limitation: a prop literally named goal/summary/overview/top5 can't be
|
|
86
|
+
set via `wl set` since the key routes to meta — a future `--prop`/`--meta` disambiguator.)
|
|
87
|
+
- **`wl alias add / ls / rm`** manages `~/.config/worklog/aliases.ini` (e.g. `wl alias add d
|
|
88
|
+
day` → `wl d` == `wl day`). The target must be a real command and an alias can't shadow one;
|
|
89
|
+
changes take effect on the next invocation. Alias names are now case-preserving.
|
|
90
|
+
- **Newcomer-friendlier `--help`.** Top-level `wl -h` now opens with a one-line what-it-is +
|
|
91
|
+
a `wl init → add → log → day` quickstart, replaces the unreadable 50-command brace blob in
|
|
92
|
+
the usage line with `<command>`, and ends with a **commands-grouped-by-purpose** map (track
|
|
93
|
+
work / time / see it / organize / plan-reflect / bulk-setup), a **Concepts** glossary
|
|
94
|
+
(node / log / tag / prop / meta / metric / link / sched / clock in one line each), and a
|
|
95
|
+
"good to know" tips block (id forms, fuzzy dates, planned/DOING behavior, completion,
|
|
96
|
+
shortcuts, **PARA** area▸project▸task). `wl add -h` gained help text on every option
|
|
97
|
+
(`title`, `-p` = A/B/C priority, …), a PARA organizing explainer, and leads with simple
|
|
98
|
+
relatable examples instead of work-jargon. Every user-facing arg now has help text (no more
|
|
99
|
+
blank options). `wl day -h` gained an (explicitly optional) "Time levels" section explaining
|
|
100
|
+
the year▸quarter▸month▸week▸day skeleton + a suggested planning cadence; `wl goal -h` / `wl
|
|
101
|
+
recap -h` now show the morning→evening→weekly→monthly planning rhythm — and their stale
|
|
102
|
+
pre-reshape wording ("prop 'goal'", "summary_at") was corrected (they're history-preserving
|
|
103
|
+
meta fields, not props).
|
|
104
|
+
- **Single-letter short flags for the daily-workflow args (WL#487):** `-d` for `--date`
|
|
105
|
+
(`wl log 1 "…" -d yesterday`, also on `logs` / `recap` / `unlog`) and `-n` for `--note`
|
|
106
|
+
(`wl tick 1 -n "6 pullups"`, also on `wait` / `metric`).
|
|
107
|
+
- **Help cross-references the shortcut ↔ canonical pair both ways** (now a documented
|
|
108
|
+
convention, DESIGN §1.2): e.g. `wl node -h` lists its shortcuts, `wl node add -h` names
|
|
109
|
+
the `wl add` shortcut, `wl add -h` names the canonical `wl node add` (same for set ↔
|
|
110
|
+
prop set, unset ↔ prop rm).
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
- **`wl -h` / `wl <command> -h` now colorize to match `wl help`.** argparse's `--help` output
|
|
114
|
+
is post-processed into the same 3-tier scheme: dim-grey body prose, bright-cyan references
|
|
115
|
+
(option flags, `wl <command>` invocations, subcommand-choice names, inline `code`), bold
|
|
116
|
+
bright-white section headings + the `usage:` label, and status/priority markers (`[x]`/`[/]`/
|
|
117
|
+
`[#A]`…) in their real `wl ls` colors. It's a no-op when color is off (non-TTY, `--color never`,
|
|
118
|
+
`$NO_COLOR`, mono theme), and runs *after* argparse's column math so the zero-width ANSI never
|
|
119
|
+
shifts alignment. `--help` fires before the console is built, so color/theme are re-resolved
|
|
120
|
+
from `--color`/`--theme`/env at format time. Epilogs/descriptions may use the **same restricted
|
|
121
|
+
Markdown as `wl help` topics** (`` `code` `` / `**bold**` / `*italic*` / `[text](url)`) for
|
|
122
|
+
explicit styling — markers render with color on and are *stripped* with color off (so piped
|
|
123
|
+
`-h` stays clean). The top-level `wl -h` now styles the Concepts glossary names and the
|
|
124
|
+
Commands-by-purpose lists this way (command names like `set`/`log`/`show` collide with English,
|
|
125
|
+
so explicit backticks beat any heuristic). Per-command `help=` one-line summaries stay plain
|
|
126
|
+
text — argparse line-wraps them, which would split a Markdown span across lines.
|
|
127
|
+
- **`wl help <topic>` slimming of `--help` epilogs.** With the topic browser holding the full
|
|
128
|
+
detail, verbose per-command epilogs are trimmed to a summary + key examples + a `wl help <cmd>`
|
|
129
|
+
pointer; anything cut is first verified present in (or added to) the matching topic. Covered
|
|
130
|
+
~23 commands (add / ls / day / logs / sched / tree / node / summary / log / tag / show / active /
|
|
131
|
+
link / unset / projects / changes / find / defer / set / prop / import / apply); multi-line
|
|
132
|
+
Differences / Shortcuts / format-spec blocks moved into topics, concise one-line notes kept.
|
|
133
|
+
- **`--help` now wraps the raw epilog/description with a hanging indent + caps its width.**
|
|
134
|
+
Previously the hand-formatted epilog (Concepts glossary, Commands-by-purpose) hard-wrapped at
|
|
135
|
+
the terminal edge with no indent on a narrow window. A `_WlHelpFormatter` (subclass of
|
|
136
|
+
`RawDescriptionHelpFormatter`) overrides `_fill_text` to wrap the description/epilog argparse-
|
|
137
|
+
style — a two-column `label description` row hangs its continuations under the description
|
|
138
|
+
column, bullets under their text — measured on *visible* width so a markdown span
|
|
139
|
+
(`` `wl day` ``) is never split across the boundary. Option/choice help is left to argparse's
|
|
140
|
+
own `_split_lines` (we never re-wrap it), and the formatter caps the wrap width on wide
|
|
141
|
+
terminals (`render.HELP_MAX_WIDTH`, 100 cols) so help stays readable; everything shares the one
|
|
142
|
+
`render.help_width()`, so columns line up.
|
|
143
|
+
- **Removals are now reversible — soft-delete tombstones instead of `DELETE`, foreign keys
|
|
144
|
+
off (WL#501).** Every table gained a `deleted_at` tombstone; deleting a tag / link / log /
|
|
145
|
+
node sets it instead of removing the row, and reads filter it out, so an accidental
|
|
146
|
+
`wl unlog` / `wl tag -x` / `wl apply '- #id'` is recoverable (clear `deleted_at`). FK
|
|
147
|
+
enforcement is turned off and the app maintains consistency (app-level cascade), decoupling
|
|
148
|
+
the tables — preparation for multi-end / CRDT sync (#57). Re-adding a removed tag / link
|
|
149
|
+
revives its tombstone (a new `upsert` replaces `INSERT OR IGNORE/REPLACE`). Migration 0008
|
|
150
|
+
is additive (nullable column, no data touched); 0009 makes the `v_node_path` view
|
|
151
|
+
tombstone-aware. Internally this also moved ~19 single-table reads still on raw SQL onto
|
|
152
|
+
the `db_table` helper, so they filter tombstones for free.
|
|
153
|
+
|
|
154
|
+
### Fixed
|
|
155
|
+
- **Help/timeline width handling, hardened via a GPT-5.5 / Kimi / Codex cross-model review.**
|
|
156
|
+
(1) Width is now measured in real display columns everywhere (one `east_asian_width`-based
|
|
157
|
+
`_cw`, shared by `_display_width` / `_truncate_log_body` / the help wrapper's `_vis`): CJK = 2,
|
|
158
|
+
but the narrow `·`/`→`/`…`/`—` separators = 1 (a crude non-ASCII→2 rule wrongly doubled them).
|
|
159
|
+
(2) The help wrapper hard-breaks a spaceless over-long token (CJK run / long URL) by character.
|
|
160
|
+
(3) `--theme mono` no longer leaks `\x1b[3m` — `*italic*` / links route through the palette.
|
|
161
|
+
(4) `_truncate_log_body`'s available-width floor was `max(20, …)`, which *forced* the `wl show`
|
|
162
|
+
timeline line to overflow on any terminal < ~61 cols (prefix ~39 + 20); floored at 1 now, so it
|
|
163
|
+
fits narrow terminals (body degrades to `…`). (5) The help wrapper falls back to a base-indent
|
|
164
|
+
wrap when the hanging (description) column would be wider than a very narrow terminal.
|
|
165
|
+
- **`wl show` timeline log lines no longer overflow to a second line.** The one-line truncation
|
|
166
|
+
budgeted against a hardcoded indent guess (32) while the real prefix (` <ts> #L<id> ✎ log `)
|
|
167
|
+
is ~39 cols, so a long body spilled past the terminal width by exactly the prefix gap. It now
|
|
168
|
+
measures the actual prefix via `_display_width`. (Audited the other `_truncate_log_body` callers
|
|
169
|
+
— `wl logs --by-task` / `--id` / `wl log ls` already budget correctly.)
|
|
170
|
+
- **`wl help <topic>` rendered a small Markdown subset + stopped crashing on literal brackets.**
|
|
171
|
+
A dependency-free renderer now styles ATX headings, ```fenced``` code, and inline `**bold**` /
|
|
172
|
+
`*italic*` / `` `code` `` / `[text](url)` / bare URLs; with color off the markers are stripped.
|
|
173
|
+
Crucially it escapes literal text first, so topic bodies containing `[ ]` / `[x]` / `[/]` / `#L42`
|
|
174
|
+
(e.g. `wl help status`) no longer crash rich markup parsing under `--color always`. The supported
|
|
175
|
+
subset is documented for topic authors in CONTRIBUTING.md. `wl help para` also gained a PARA
|
|
176
|
+
concept intro + a link to Tiago Forte's canonical article. The renderer now uses a 3-tier
|
|
177
|
+
contrast scheme (Claude-Code-style): dim-grey body prose, bright-cyan references (inline
|
|
178
|
+
`code`, `[links]`, See-also entries, the index's topic names, and `wl <subcommand>`
|
|
179
|
+
invocations — colored as commands only when the verb is real), and bold bright-white
|
|
180
|
+
headings/`**strong**`. Status markers in backticks (`` `[x]` `` etc.) render in their real
|
|
181
|
+
`wl ls` / `day` status colors, so the `wl help status` legend matches the rest of wl. The topic
|
|
182
|
+
title is now underlined (bold bright-white) instead of spending a whole row on a `───` rule, and
|
|
183
|
+
the index's topic-name column is padded to the longest name + 2 so a long id (`print-completion`)
|
|
184
|
+
keeps a gap before its description instead of running into it.
|
|
185
|
+
- **`wl tree --root <month>` / `wl focus <month>` now show tasks fuzzy-pinned at that time
|
|
186
|
+
node** (#436). A task pinned at `@2026-06` (via `scheduled_date`) hangs under its project,
|
|
187
|
+
not the month node, so the tree/focus parent_id walk missed it — which made a "what's
|
|
188
|
+
already scheduled this month?" check come up empty and a duplicate get created. They now
|
|
189
|
+
surface such pins (the filtered `--tag`/`--kind` path too); #413 had fixed only day nodes.
|
|
190
|
+
- **Shell completion for default-verb group leaf args.** When a top-level command became a
|
|
191
|
+
metric-style group (`log` / `sched`), its everyday args moved under the `add` subparser,
|
|
192
|
+
which the fish / bash / zsh completion walk skipped — so `wl sched <id> --recur` lost its
|
|
193
|
+
suggestions and `wl log <id> --date` its date completion. The generators now descend into
|
|
194
|
+
a default-verb group's leaf (`_default_verb_leaf`) and emit those completions under the
|
|
195
|
+
bare group name.
|
|
196
|
+
- **`parent_id` cycle safety (FK-off hardening).** With foreign-key enforcement off
|
|
197
|
+
(WL#501), `parent_id` integrity isn't DB-guaranteed and the bulk `wl apply` / `wl import`
|
|
198
|
+
update path set a parent with only an existence check (the interactive `wl reparent` had a
|
|
199
|
+
cycle guard; the bulk path didn't) — a cycle would then make the ancestor/descendant
|
|
200
|
+
walks loop forever and hang the CLI. The bulk path now refuses a self/descendant parent
|
|
201
|
+
(parity with `reparent`), and `_collect_descendants` / `_ancestors_chain` are visited-set
|
|
202
|
+
guarded so any pre-existing cycle degrades gracefully instead of hanging.
|
|
203
|
+
- **`wl link` strips an outer `[[ ]]` wrapper on input (WL#462).** `[[X]]` and `X` now store
|
|
204
|
+
identically (no more `[[[[X]]]]` double-wrap), re-linking either form dedups via the natural
|
|
205
|
+
key, and `wl unlink`/`apply -link` match regardless of wrapping. All link writes go through a
|
|
206
|
+
single normalizing chokepoint (`_upsert_link`/`_delete_link`). (Dedup + `unlink` already
|
|
207
|
+
existed; this closes the last part of #462.)
|
|
208
|
+
- **`wl day` drops the redundant per-row `«planned·not-done»` hint under the default `--by
|
|
209
|
+
plan` (WL#500).** The `▸ planned` group header + the `[ ]` status marker already say it; the
|
|
210
|
+
hint stays under `--by project`/`priority`, and the footer count is unchanged.
|
|
211
|
+
|
|
212
|
+
### Internal
|
|
213
|
+
- **Command taxonomy documented (DESIGN §1.1):** every command is a primitive entity CRUD
|
|
214
|
+
(the `metric add/ls/edit/rm` subsystem is the template), a composite helper, or a read-only
|
|
215
|
+
view; with the per-entity CRUD matrix and remaining gaps (`prop` unset, `clock` edit/rm).
|
|
216
|
+
- **Further raw-SQL reduction (DESIGN G3).** `_insert_log` returns the new log id (dropping 6
|
|
217
|
+
`SELECT last_insert_rowid()` follow-ups — also more correct than the connection-global
|
|
218
|
+
lastrowid), and 5 more single-table reads/writes moved onto `db_table`. Remaining raw SQL is
|
|
219
|
+
all genuinely complex (JOIN / time-window / aggregate / OR-LIKE / conditional / dynamic).
|
|
220
|
+
- **Raw-SQL 余债 cleared (WL#526):** `_get_or_make`'s 4 time-skeleton lookups (single-table
|
|
221
|
+
`LIKE`/`=` probes) and `_node_clock_min`'s all-time clock-SUM + log-span-DISTINCT moved onto
|
|
222
|
+
`db_table` (`cols=` carries the aggregate). Only the *day-scoped* aggregates (SUM/DISTINCT +
|
|
223
|
+
`local_day_sql` in WHERE) stay raw — genuinely complex.
|
|
224
|
+
|
|
225
|
+
## [0.5.0] — 2026-06-05
|
|
226
|
+
|
|
227
|
+
### Added
|
|
228
|
+
- **Shared `--tag` / `--kind` / `--status` filter across `wl ls` / `tree` / `day` / `logs` / `agenda`** — one filter with the same meaning everywhere (wired through a parent parser, like the existing time-window flags), instead of each command rolling its own. `-t` / `--tag` is comma-separated **AND** (the node must carry every listed tag); `--kind` / `--status` are equality. So `wl day -t work` shows only the work bucket (empty buckets/groups aren't rendered, and the summary line counts the filtered set), `wl tree -t work` prunes the tree to matching nodes plus the ancestor paths that lead to them, and `ls` / `logs` / `agenda` filter identically. An explicit `--status DONE/CANCELED` overrides the default terminal-status hide. `-t` is now the short form on all five (matching `wl add -t`).
|
|
229
|
+
- **`wl day` header states the day's nature** — every day now reads at a glance: `workday` (Mon–Fri) or `weekend` (Sat/Sun) by default, refined to `holiday` / `leave` / `workday` when a `wl dateinfo` label says so, with the label appended for context — e.g. `2026-06-01 Mon · Children's Day holiday`, `2026-06-13 Sat · makeup workday`, `2026-05-21 Thu · workday (Grain Buds solar term)`. Previously the header showed a label only when one existed, so a bare day told you nothing about its working status.
|
|
230
|
+
|
|
231
|
+
### Changed
|
|
232
|
+
- **Timestamps follow an `at`/`date` convention — precise instants stored UTC, literal dates kept local.** A column named `*_at` (or bare `at`: `created_at` / `closed_at` / `logged_at` / `metric.at` / `clock.start_at` / `end_at` / `sched.created_at`) is a precise instant, now stored as UTC and rendered in local time; a column holding a calendar *day* is named `*date` and kept verbatim as a local date. As part of this, `node.scheduled_at` → **`scheduled_date`** and `node.deadline_at` → **`deadline_date`** (they hold a planned/deadline day, plus fuzzy `@2026-06` pins — not instants). "Local" is the machine zone by default; `$WORKLOG_TZ` pins a fixed offset (e.g. `+08:00`) for reproducibility / overriding. Day-grouping (`wl day` / `summary` / `logs --date` / habit month-to-date) converts UTC→local before bucketing, so a log near local midnight lands on the right day. **Migration 0007** converts pre-existing rows from local (+08:00) to UTC (bare `YYYY-MM-DD` values left untouched) and renames the two date columns; new installs are unaffected.
|
|
233
|
+
- **Habit month-to-date label is English** — `(本月 N/M)` → `(this month N/M)` in `wl day` / `wl tree`, matching the rest of the output.
|
|
234
|
+
|
|
235
|
+
### Internal
|
|
236
|
+
- **A zero-dependency single-table CRUD layer (`db_table`).** All hand-written single-table SQL — inserts, updates, deletes, selects, dynamically-assembled `WHERE` / `SET`, and `INSERT OR IGNORE/REPLACE` — now goes through a small helper (`insert` / `update` / `delete` / `query` / `query_one` / `get` / `exists` / `count` / `clause`) with identifier validation and parameter binding throughout; SQL `datetime('now')` is gone (all timestamps flow through `timeutil`). No behavior change — a maintainability/safety refactor, codified as design goal **G3** (few deps: stdlib + `rich`; simple explicit SQL over query-builder DSLs; small helpers over an ORM). Genuinely-complex queries (display JOINs, `GROUP BY`, correlated subqueries) stay raw by design. The test fixtures were also de-Chinese'd to English (the relative-date input aliases like `明天` / `下周` remain — they're a feature).
|
|
237
|
+
|
|
238
|
+
## [0.4.0] — 2026-06-05
|
|
239
|
+
|
|
240
|
+
### Fixed
|
|
241
|
+
- **`wl sched` is now idempotent** — scheduling a task to a date/recurrence it already has no longer inserts a duplicate `sched` row; it reports `= already scheduled` instead. Previously repeated `wl sched 42 2026-06-02` accumulated duplicate rows.
|
|
242
|
+
- **`wl day` no longer tags DONE/CANCELED tasks `«planned·not-done»`** — a terminal-status task scheduled on a day with no logs is done, not pending. Both the per-row hint and the footer `planned·not-done N` count now skip terminal-status tasks (removes the contradictory `[x] … «planned·not-done»`).
|
|
243
|
+
- **`wl focus <day>` expands that day's activity** — focusing a day node now shows the nodes with a log / schedule on that date (same as `wl tree` / `wl day`), not just the handful of nodes whose `parent_id` is the day. Previously focusing a date dropped a dozen tasks that hung under their projects/habit groups; `focus` was the only path bypassing the day special-case.
|
|
244
|
+
- **Auto-created day builds its full time-ancestor chain** — `wl goal` / `wl recap` on a date whose week/month doesn't exist yet (e.g. the first of a new month) no longer dangles the new day with `parent_id NULL`; it builds the `year→quarter→month→week` skeleton on demand (lenient lookup reuses an existing `2026` or `2026 年` year). Fixes broken per-month aggregation.
|
|
245
|
+
- **`wl apply` gives an actionable error for an un-indented field op** — a flush-left `parent 6` (which must be indented under its `~ #id` lock line) now says "field op must be indented under `~ #id`" instead of the opaque "cannot parse 'parent 6'" that read as "parent isn't supported".
|
|
246
|
+
- **Shell completion no longer offers `someday` for concrete-date commands** — `wl day <tab>` (like `log` / `logs` / `unlog`) only suggests concrete dates now; `someday` is offered only for `sched` / `defer`, which actually accept fuzzy granularities. Previously picking `someday` for `wl day` just errored.
|
|
247
|
+
- **Flat `wl logs` row stays on one line with CJK titles** — the row now budgets by terminal display width (CJK = 2 columns) instead of slicing the title to 30 characters, so a wide CJK title no longer overruns the line and wraps the body.
|
|
248
|
+
- **`wl day` plan grouping collapsed to planned / unplanned** — dropped the confusing third `unplanned (untagged)` bucket; now that planned/unplanned is derived from `sched`, "has no tag" and "has the unplanned tag" both just mean "not scheduled".
|
|
249
|
+
|
|
250
|
+
### Added
|
|
251
|
+
- **`wl agenda <start> <end>`** — cross-time-range scheduling overview. Lists every node scheduled within a date range across all granularities, ordered by anchor date, reading **both** schedule sources: the `sched` table's concrete one-off days and `node.scheduled_at` fuzzy pins (`@2026-06` month / week / `someday`). A per-month tree view misses month- and someday-pinned tasks; `agenda` surfaces them in one list (the duplicate-check companion to the `wl add` warning below). `--all` includes DONE/CANCELED; `--someday` appends fuzzy pins.
|
|
252
|
+
- **`wl add` warns when a similar open task/project exists** — a best-effort title-similarity check prints a non-blocking warning listing suspected duplicates (the node is still created, so the operator can reuse via `wl sched` / `wl link` instead). Match is on normalized titles (lowercased, spaces/punctuation stripped), identical or containment with the shorter ≥4 chars; scope is task/project, DONE/CANCELED excluded.
|
|
253
|
+
- **`wl tag <id> +work -planned`** — edit a node's real tags directly (`+`/bare word adds idempotently, `-` removes, no args lists). Closes a footgun: `wl set <id> tags X` used to silently create a shadow `tags` prop while the real tag field — which bucketing/grouping reads — stayed untouched; that path is now rejected with a pointer to `wl tag` (same for import `update {tags:…}` → `add_tags`/`remove_tags`).
|
|
254
|
+
- **`wl unlink <id> "doc"`** — remove a single vault-doc link, symmetric with `wl link`; previously removing one mistaken link meant wiping them all. Multiple ids supported; a no-op prints a notice rather than failing.
|
|
255
|
+
- **`wl recap --date YYYY-MM-DD`** — read or back-fill a past day's recap (also `today`/`yesterday`/CJK aliases). Unlike the old `wl set <day> summary`, it stamps `summary_at` so the "changes after recap" staleness warning clears correctly, and creates the day node (+ its time-ancestor chain) on demand.
|
|
256
|
+
- **Habit lines show month-to-date completion** — in `wl day` / `wl tree`, a scheduled habit shows `(本月 N/M)`: N = distinct days this month (≤ the viewed day) with a check-in, M = days its schedule fires in that window. No schedule → no rate.
|
|
257
|
+
- **Metrics fold under their node/log in `wl show` / `wl day` / `wl tree`** — datapoints render indented (`↳ [glucose] 5.4 mmol/L`); in `wl show` an empty `tag='metric'` carrier log shows its datapoint directly as a `📊 metric` line (no blank log row); `wl day`/`wl tree` show a node's that-day datapoints under it (the `checkin` marker is skipped — it's reflected by `[x]`); >5 on one node/log are elided (`↳ … N more datapoints`).
|
|
258
|
+
- **`node → log → metric` data model** — a new `metric` table holds structured datapoints (check-in / number / measurement) that hang off a log; `log` gains a `tag` column for the log's role (note/goal/summary/clock/…), unifying the classification field across all three layers: node-level `tag` (a label set), `metric.tag` (a datapoint's kind), and `log.tag` (a log's role) — one word, one mental model. Foundation for querying/aggregating what was previously stuffed into log text (migrations 0002 + 0006, two-round cross-model reviewed). A metric must hang off a log; `metric.node_id` is denormalized (no FK, trigger-kept consistent); markers store `value_num=1`.
|
|
259
|
+
- **`wl metric add/ls/edit/rm`** — full CRUD for datapoints. `add` creates a carrier log (or `--on-log #L<id>`); numeric vs text autodetected (`--text` forces text); `--unit/--note/--at`; `ls` defaults to this week (`--all`, `--tag`, `--since/--until/--week/--month`); `rm` also cleans up an emptied auto-carrier log. Metric ids display/parse as `#M<id>`.
|
|
260
|
+
- **`--metric` shortcut on `wl log` / `wl add`** — attach datapoints in the same command: `wl log 42 "..." --metric 'glucose 5.4 mmol/L' --metric checkin` (repeatable; on `add`, reuses the `--log` carrier or makes one).
|
|
261
|
+
- **Habit check-in is now a structured signal** — `wl tick` / `wl checkin` attach a `checkin` metric (idempotent per day); "habit done today" (in `wl day` / `wl tree` / `wl checkin`) reads that metric instead of the old "did any log exist that day", so a stray note no longer marks a habit done. Migration 0003 backfills historical habit completions.
|
|
262
|
+
- **`wl import` supports metrics** — a log entry may carry `"metrics": [...]`, and a node may carry node-level `"metrics": [...]` attached to one carrier log (1 carrier → N datapoints, e.g. importing a day of CGM readings without 288 separate logs).
|
|
263
|
+
- **Structured time tracking (`clock` table)** — `wl start` / `stop` / `spent` / `wait` now read/write a real `clock(node_id, start_at, end_at, elapsed_sec)` interval table instead of the `CLOCK_IN` / `CLOCK_OUT` log-body convention; durations are summed from a typed column (G1), not parsed out of text. `wl active` lists open intervals; `wl show` renders intervals as `start→end (Nmin)`; `wl day` / `wl summary` totals come from the table. Migration 0005 pairs existing CLOCK_IN/OUT logs into clock rows and removes them. (`wl unlog`/`wl relog` no longer need CLOCK guards — clock is no longer a log.)
|
|
264
|
+
- **Day/week/month meta fields keep history** — `goal` / `summary` (recap) / `overview` / `top5` moved from single-value `prop`s to history-preserving typed logs (`log.tag`): each `wl goal` / `wl recap` / `wl set <node> overview|top5` write appends a new log, and the latest is the current value, so you can see how a goal or recap evolved. The recap "written at" + stale-recap warning now come from the summary log's own timestamp (the `summary_at` prop is gone). Migration 0004 converts existing props; `prop` returns to truly-static attributes.
|
|
265
|
+
- **`wl show` now displays the schedule** — a `schedule:` line lists the task's one-off dates and recurring rules (previously only visible via raw SQL on the `sched` table). First-hand info when debugging why a task shows on multiple days.
|
|
266
|
+
- **`-q`/`--brief` is accepted after the subcommand too** (`wl day -q`), not only globally before it (`wl -q day`). Implemented by injecting the flag into every subparser with `default=SUPPRESS` so it never clobbers the global value.
|
|
267
|
+
- **`wl done` warns on recurring tasks** — running `wl done` on a task with a recurring rule prints a hint that `wl done` retires the whole task (marks it done on every scheduled day) and that `wl tick <id>` is the way to record just today's occurrence. Mitigates accidentally finishing a recurring habit/meta-task.
|
|
268
|
+
|
|
269
|
+
## [0.3.0] — 2026-06-01
|
|
270
|
+
|
|
271
|
+
### Added
|
|
272
|
+
- `pip install pyworklog` now works. The project ships as a proper Python package (`src/worklog/`) with a `wl` console-script entry point; `uv build` produces a wheel + sdist with `worklog/migrations/0001_initial_schema.sql` packaged in. The PyPI **distribution** name is `pyworklog` (the short names `worklog` and `worklog-cli` were already taken on PyPI, and hyphenated names like `worklog-py` were avoided); the import name and `wl` command are unchanged.
|
|
273
|
+
- **SQL migrations runner** — schema is delivered as `src/worklog/migrations/NNNN_*.sql`; `PRAGMA user_version` tracks the highest applied. `ensure_db()` auto-applies pending migrations on every command. New `wl migrate` is the explicit form (and the one place that bypasses `ensure_db()` so the handler has work to do on a fresh DB).
|
|
274
|
+
- **Downgrade guard** — if `user_version` is ahead of the migrations shipped with the running build, the tool aborts with an upgrade prompt instead of corrupting newer schema with stale logic.
|
|
275
|
+
|
|
276
|
+
### Changed
|
|
277
|
+
- `wl.py` moved to `src/worklog/cli.py`; `schema.sql` moved to `src/worklog/migrations/0001_initial_schema.sql`. Internal imports rewritten throughout; tests, CI, and the `~/bin/wl` Makefile wrapper now go through the entry-point (`.venv/bin/wl`).
|
|
278
|
+
- `__version__` reads from `importlib.metadata.version("pyworklog")` — `pyproject.toml` is now the single in-repo source of truth for version, and `uv.lock` pins it deterministically. `release.yml` verifies tag against `pyproject.toml` only.
|
|
279
|
+
- `--cov` target switched from `wl` to `worklog`; coverage gate stays at 95%.
|
|
280
|
+
|
|
281
|
+
### Removed
|
|
282
|
+
- Hand-edited `__version__` in `wl.py`. (Use `pyproject.toml`.)
|
|
283
|
+
|
|
284
|
+
### Migration notes for existing users
|
|
285
|
+
- The DB path is unchanged (`$WORKLOG_DB` or `$XDG_DATA_HOME/worklog/worklog.db`). Existing DBs upgrade transparently on the first command after install: `_run_migrations()` no-ops on existing tables (the bootstrap migration uses `CREATE TABLE IF NOT EXISTS`) and stamps `user_version=1`. Verified on a 311 KB production-DB backup: row counts unchanged, `PRAGMA integrity_check = ok`.
|
|
286
|
+
- Re-run `make install` (or `make setup`) once so the `~/bin/wl` wrapper points at the new `.venv/bin/wl`.
|
|
287
|
+
|
|
288
|
+
## [0.2.0] — 2026-05-31
|
|
289
|
+
|
|
290
|
+
### Added
|
|
291
|
+
- **`wl config`** command — prints resolved DB path, aliases path, XDG directories, environment, runtime info; read-only (does not create the DB just to inspect paths).
|
|
292
|
+
- **`wl --db PATH`** global flag — per-invocation DB override (handy for testing or running against multiple worklogs); takes precedence over `$WORKLOG_DB` and the XDG default.
|
|
293
|
+
- **XDG Base Directory spec** — DB defaults to `$XDG_DATA_HOME/worklog/worklog.db` (`~/.local/share/worklog/worklog.db`); aliases at `$XDG_CONFIG_HOME/worklog/aliases.ini`. Paths resolved lazily at call time so `pytest`'s `monkeypatch` works without reload tricks.
|
|
294
|
+
- CI matrix on GitHub Actions (`ubuntu-latest` + `macos-latest` × Python 3.11 / 3.12 / 3.13) + Codecov coverage upload + status badges in README.
|
|
295
|
+
- **Release workflow** — push a `v*` tag → verify version anchors agree, run the test suite, publish a GitHub Release with auto-generated `Commits since vX.Y.Z` notes.
|
|
296
|
+
- **`AGENTS.md`** — vendor-neutral operating guide for AI coding agents (Claude Code, Cursor, Aider). Codifies TDD (Red-Green-Refactor) and DRY (single-source helpers) as project rules.
|
|
297
|
+
- **`CONTRIBUTING.md`** — full dev setup, TDD + DRY conventions, local Makefile overrides, release process.
|
|
298
|
+
|
|
299
|
+
### Changed
|
|
300
|
+
- Project namespace fully renamed from `wl` to `worklog` (paths and env vars): `~/.local/share/wl/wl.db` → `~/.local/share/worklog/worklog.db`; `~/.config/wl/aliases.ini` → `~/.config/worklog/aliases.ini`; `$WL_DB` / `$WL_COLOR` / `$WL_THEME` → `$WORKLOG_*`. The CLI command stays `wl`.
|
|
301
|
+
- **uv** replaces `venv` + `pip` + `requirements*.txt`. `pyproject.toml` (PEP 621) + `dependency-groups` (PEP 735) + `uv.lock` is the dependency single-source. `make setup` runs `uv sync` and installs the `~/bin/wl` wrapper.
|
|
302
|
+
- `_setup_aliases` test fixture now also clears `XDG_CONFIG_HOME` / `XDG_DATA_HOME` — CI runners preset these and otherwise leak into tests.
|
|
303
|
+
|
|
304
|
+
### Removed
|
|
305
|
+
- `requirements.txt` / `requirements-dev.txt` — replaced by `pyproject.toml` + `uv.lock`.
|
|
306
|
+
- Pre-XDG path fallbacks (`~/.worklog/wl.db`) — v0.1.0 was the only release that wrote there.
|
|
307
|
+
|
|
308
|
+
## [0.1.0] — 2026-05-31
|
|
309
|
+
|
|
310
|
+
Initial open-source release of `worklog-cli` (`wl`).
|
|
311
|
+
|
|
312
|
+
### Added
|
|
313
|
+
- **Single-table execution-system model** — one `node` table holds the whole hierarchy across 13 kinds (`lifetime` / `decade` / `year` / `quarter` / `month` / `week` / `day` / `area` / `project` / `task` / `habit` / `signal` / `meetlog`), sharing one id space and tree-linked via a `parent_id` self-reference. Six sibling tables hang off it: `log` (timestamped entries), `tag` (labels), `sched` (scheduling), `prop` (key-value attributes), `link` (vault-doc references), `date_meta`.
|
|
314
|
+
- **Seven-state task lifecycle** — `TODO` / `DOING` / `LATER` / `WAIT` / `DONE` / `DEFERRED` / `CANCELED`, a superset of the markdown `[ ]` / `[/]` / `[>]` / `[x]` four-state set that adds `LATER` (deferred to the future) vs. `WAIT` (blocked on someone). Plus `A` / `B` / `C` priority (rendered `[#A]` = P0) and auto-stamped `closed_at`.
|
|
315
|
+
- **37 subcommands** — create/edit (`add` / `set` / `link` / `init`), logging (`log` / `unlog` / `relog`), state transitions (`done` / `defer` / `start` / `stop` / `spent` / `active` / `wait` / `reopen` / `cancel` / `tick` / `checkin`), views (`show` / `ls` / `tree` / `focus` / `ancestors` / `descendants` / `projects` / `changes` / `summary` / `day`), meta fields (`goal` / `recap`), scheduling (`sched` / `dateinfo`), bulk I/O (`import` / `apply`), search (`find` / `logs`), and display (`themes` / `print-completion`). Most carry short aliases.
|
|
316
|
+
- **Scheduling with recurrence** — the `sched` table records both one-off dates (`on_date`) and recurrence rules (`rrule`: `daily`, `weekly:Mon,Wed,Fri`); a task scheduled for a given day counts as planned, derived from `sched` rather than a manual tag.
|
|
317
|
+
- **Time tracking** — `wl start` / `stop` / `spent` / `active` / `wait` record work intervals and report durations per node.
|
|
318
|
+
- **Project association via shared tags** — `wl projects` rolls a project's members up by structural children plus shared semantic tags, with a `GENERIC_TAGS` set (work / personal / planned / unplanned / P0–P2 / habit / meeting / …) excluded so generic dimension tags don't cross-link unrelated work.
|
|
319
|
+
- **Bulk loaders** — `wl import` (JSON) and `wl apply` (wl-diff format, symmetric with the rendered tree output) for scripted/AI-driven batch entry.
|
|
320
|
+
- **Compound flags on `wl add` / `done` / `cancel`** — create + log + close + timestamp + vault-link + schedule in one shot.
|
|
321
|
+
- **Date aliases** — `today` / `yesterday` / `tomorrow` / `day-before-yesterday` / … and Chinese equivalents resolve to concrete dates wherever a date is accepted.
|
|
322
|
+
- **Brief mode `-q`** on every command (skip log body / timeline / detail; ~50–90% character savings for AI consumers).
|
|
323
|
+
- **`rich`-based highlighting** with `dark` / `light` / `mono` themes, auto-detected via `$COLORFGBG` / OSC 11 terminal-background probe; manual override via `--theme` or `$WL_COLOR` / `$WL_THEME` / `$NO_COLOR`.
|
|
324
|
+
- **Shell completion** (fish / bash / zsh) generated at runtime by `wl print-completion`; loaded via init-load (the starship / direnv / zoxide pattern).
|
|
325
|
+
- **Vault integration** — `wl link` associates a node with one or more Obsidian vault docs (`[[doc]]`), kept in the `link` table for a two-way knowledge graph.
|
|
326
|
+
- Bilingual project documentation (English + Chinese for README / DESIGN / SKILL).
|
|
327
|
+
- MIT License.
|
|
328
|
+
|
|
329
|
+
[Unreleased]: https://github.com/xyb/worklog/compare/v0.5.0...HEAD
|
|
330
|
+
[0.5.0]: https://github.com/xyb/worklog/compare/v0.4.0...v0.5.0
|
|
331
|
+
[0.4.0]: https://github.com/xyb/worklog/compare/v0.3.0...v0.4.0
|
|
332
|
+
[0.3.0]: https://github.com/xyb/worklog/compare/v0.2.0...v0.3.0
|
|
333
|
+
|
|
334
|
+
[0.2.0]: https://github.com/xyb/worklog/compare/v0.1.0...v0.2.0
|
|
335
|
+
[0.1.0]: https://github.com/xyb/worklog/releases/tag/v0.1.0
|
|
@@ -48,6 +48,47 @@ wl --db /tmp/scratch.db add "experiment"
|
|
|
48
48
|
|
|
49
49
|
`AGENTS.md` is the operating guide for AI coding agents (Claude Code, Cursor, Aider). Skim it once even if you code by hand; it concentrates the hard-rules into one page.
|
|
50
50
|
|
|
51
|
+
## Writing `wl help` topics
|
|
52
|
+
|
|
53
|
+
`wl help <topic>` is an info-style browser over Markdown docs in `src/worklog/help/<lang>/<topic>.md` (architecture: DESIGN §25). `en` is the source language and the per-topic fallback; other languages mirror the same topic ids. To add or edit a topic, just add/edit a `.md` file — the loader, the index, shell completion, and each command's `--help` pointer pick it up automatically.
|
|
54
|
+
|
|
55
|
+
**File format** — minimal frontmatter between `---` fences, then a Markdown body:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
---
|
|
59
|
+
title: tag — labels on a node # "<name> — <one-line description>"; the index shows the part after the dash
|
|
60
|
+
category: command # one of: guide | concept | command | param
|
|
61
|
+
see_also: node, day, ls, add # comma/space-separated topic ids
|
|
62
|
+
---
|
|
63
|
+
<body>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- The **topic id is the filename stem** (`tag.md` → `wl help tag`). For a command topic, name it after the command so its `--help` auto-gains a `More: wl help <cmd>` pointer (commands without a same-named topic fall back to a family topic via `_HELP_FAMILY` in `cli.py`).
|
|
67
|
+
- Every `see_also` id **must resolve to a real topic** — a test (`test_all_see_also_targets_resolve`) fails on a dangling link, since a "See also" is meant to be runnable as `wl help <x>`.
|
|
68
|
+
|
|
69
|
+
**Supported Markdown is a small, fixed subset** (rendered by a dependency-free renderer in `commands/help.py` — no Markdown/`rich.markdown` engine):
|
|
70
|
+
|
|
71
|
+
| Syntax | Renders as |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `# Heading` … `###### ` | a styled heading (the `#`s are stripped) |
|
|
74
|
+
| ` ```fence ` … ` ``` ` | a code block (dimmed; the fence lines are dropped; no inline parsing inside) |
|
|
75
|
+
| `**bold**` | bold |
|
|
76
|
+
| `*italic*` | italic |
|
|
77
|
+
| `` `code` `` | inline code (cyan) |
|
|
78
|
+
| `[text](url)` | underlined text + the url |
|
|
79
|
+
| `http://…` / `https://…` (bare) | underlined link |
|
|
80
|
+
|
|
81
|
+
Everything else is printed verbatim, so **lay the body out as readable plain text** (it must read fine with color off, where the markers above are simply stripped). Notes:
|
|
82
|
+
|
|
83
|
+
- **`_italic_` is intentionally NOT supported** — bare underscores are too common in identifiers (`node_id`, `closed_at`, `$WORKLOG_LANG`). Use `*italic*`.
|
|
84
|
+
- Literal brackets (`[ ]`, `[x]`, `[#A]`, `#L42`) are safe — the renderer escapes them; don't avoid them.
|
|
85
|
+
- Preview both ways before committing: `wl help <topic>` and `wl help <topic> --color always`.
|
|
86
|
+
|
|
87
|
+
**The same subset also styles `--help`** — `argparse` epilogs and `description=` strings are colorized by `colorize_help` (in `commands/help.py`) into the identical scheme, so you may use `` `code` `` / `**bold**` / `*italic*` / `[text](url)` there too. Two rules:
|
|
88
|
+
|
|
89
|
+
- **Markdown only in `epilog=` / `description=`** (which argparse prints raw/un-wrapped). **Never in a per-command `help=` one-line summary** — argparse line-wraps those, which splits a Markdown span across lines (the marker then leaks instead of styling). Keep `help=` plain prose; `wl <command>` references in it still get colored by the heuristic.
|
|
90
|
+
- A `wl <command>` reference colors itself (the heuristic recognizes real subcommand names) — reserve backticks for the cases the heuristic can't catch: bare command names that collide with English (`` `set` `` / `` `log` `` / `` `show` ``), key=value bits, flags-in-prose.
|
|
91
|
+
|
|
51
92
|
## TDD: red → green → refactor
|
|
52
93
|
|
|
53
94
|
Every change touching behavior follows the [Red-Green-Refactor cycle](https://brennanbrown.github.io/notes/programming/python-tdd/):
|