pyworklog 0.5.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.5.0 → pyworklog-0.6.0}/CHANGELOG.md +216 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/CONTRIBUTING.md +41 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/DESIGN.md +181 -1
- {pyworklog-0.5.0 → pyworklog-0.6.0}/PKG-INFO +1 -1
- {pyworklog-0.5.0 → pyworklog-0.6.0}/pyproject.toml +1 -1
- {pyworklog-0.5.0 → pyworklog-0.6.0}/skills/worklog-cli/SKILL.md +8 -4
- pyworklog-0.6.0/src/worklog/cli.py +1672 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/__init__.py +34 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/bulk.py +42 -16
- pyworklog-0.6.0/src/worklog/commands/help.py +493 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/meta.py +233 -31
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/metric.py +1 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/query.py +60 -37
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/state.py +329 -60
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/commands/views.py +71 -29
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/completion.py +57 -10
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/db.py +5 -2
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/db_table.py +66 -26
- 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.5.0 → pyworklog-0.6.0}/src/worklog/helpers.py +17 -7
- 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.5.0 → pyworklog-0.6.0}/src/worklog/queries.py +78 -20
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/render.py +43 -1
- pyworklog-0.6.0/tests/test_alias.py +64 -0
- pyworklog-0.6.0/tests/test_cascade.py +152 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_clock.py +7 -5
- pyworklog-0.6.0/tests/test_cycle_safety.py +60 -0
- pyworklog-0.6.0/tests/test_dateinfo.py +111 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_day.py +12 -1
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_db_table.py +35 -2
- pyworklog-0.6.0/tests/test_default_verb.py +386 -0
- pyworklog-0.6.0/tests/test_help.py +363 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_import_apply.py +7 -7
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_link_set.py +47 -5
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_meta.py +71 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_metric.py +11 -11
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_migrations.py +16 -10
- pyworklog-0.6.0/tests/test_node.py +121 -0
- pyworklog-0.6.0/tests/test_prop_clock.py +139 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_show.py +46 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_tree.py +48 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_ux.py +135 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/uv.lock +1 -1
- pyworklog-0.5.0/src/worklog/cli.py +0 -1287
- pyworklog-0.5.0/tests/test_cascade.py +0 -27
- pyworklog-0.5.0/tests/test_dateinfo.py +0 -42
- {pyworklog-0.5.0 → pyworklog-0.6.0}/.github/workflows/release.yml +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/.github/workflows/test.yml +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/.gitignore +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/AGENTS.md +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/DESIGN.zh.md +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/LICENSE +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/Makefile +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/README.md +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/README.zh.md +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/pytest.ini +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/__init__.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0001_initial_schema.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0002_metric_and_log_tag.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0003_backfill_checkin_metrics.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0004_meta_props_to_typed_logs.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0005_clock_table.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0006_rename_log_type_to_tag.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/migrations/0007_utc_timestamps.sql +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/timeutil.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/src/worklog/xdg.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/__init__.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/conftest.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_add.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_aliases.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_brief.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_changes.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_checkin.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_completion.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_config.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_filters.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_find.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_focus.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_init.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_log.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_log_format.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_logs.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_ls.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_projects.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_render.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_sample.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_sched.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_state.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_summary.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_timeutil.py +0 -0
- {pyworklog-0.5.0 → pyworklog-0.6.0}/tests/test_xdg.py +0 -0
|
@@ -6,6 +6,222 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
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
|
+
|
|
9
225
|
## [0.5.0] — 2026-06-05
|
|
10
226
|
|
|
11
227
|
### Added
|
|
@@ -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/):
|
|
@@ -41,6 +41,117 @@ worklog deliberately stays **dependency-light and low-abstraction**: runtime is
|
|
|
41
41
|
- Long flags `--xxx`; short flags only for the highest-frequency ones (`-k` kind / `-p` priority / `-t` tag)
|
|
42
42
|
- Missing node: always `sys.exit(f"✗ ... #{id} not found")` with non-zero exit code
|
|
43
43
|
|
|
44
|
+
### 1.1 Command taxonomy (primitive CRUD / composite helper / view)
|
|
45
|
+
|
|
46
|
+
Every command falls into exactly one of three buckets. Knowing which keeps the
|
|
47
|
+
surface coherent and tells you where a new command belongs.
|
|
48
|
+
|
|
49
|
+
**A. Primitive entity CRUD** — directly create/read/update/delete one underlying
|
|
50
|
+
table row. The **`metric` subsystem is the template**: `metric add / ls / edit / rm`,
|
|
51
|
+
a complete, consistently-named CRUD set. Every entity should reach that completeness;
|
|
52
|
+
naming/behaviour stays uniform across entities. Current state:
|
|
53
|
+
|
|
54
|
+
| entity | create | read | update | delete |
|
|
55
|
+
|---|---|---|---|---|
|
|
56
|
+
| **metric** | `metric add` | `metric ls` | `metric edit` | `metric rm` |
|
|
57
|
+
| **node** | `node add` | `node ls` / `node show` | `node edit` / `reparent` | `node rm` |
|
|
58
|
+
| **prop** | `prop set` (= `set` w/ a non-meta key) | `prop ls` / `show` | `prop set` | `prop rm` (= `unset`) |
|
|
59
|
+
| **meta** (typed-log fields) | `meta set` (= `set` w/ a meta key; `goal`/`recap` for today) | `meta ls` / `show` | `meta set` (appends; latest = current) | `meta rm` (= `unset` w/ a meta key) |
|
|
60
|
+
| **clock** | `start` / `spent` | `clock ls` / `active` | `clock edit` | `clock rm` |
|
|
61
|
+
| **link** | `link add` (= `link`) | `link ls` / `show` | — (atomic) | `link rm` (= `unlink`) |
|
|
62
|
+
| **log** | `log add` (= `log`) | `log ls` / `logs` / `show` | `log edit` (= `relog`) | `log rm` (= `unlog`) |
|
|
63
|
+
| **sched** | `sched add` (= `sched`) | `sched ls` / `show` | `sched add` / `defer` | `sched rm` (= `sched --clear`) |
|
|
64
|
+
| **tag** | `tag add` (= `tag +x`) | `tag ls` / `show` | — (atomic) | `tag rm` (= `tag -x`) |
|
|
65
|
+
| **date_meta** | `date set` (= `dateinfo`) | `date ls` / `dateinfo` | `date set` | `date rm` (= `dateinfo --clear`); `date import` |
|
|
66
|
+
|
|
67
|
+
Every entity now has a full metric-style `<entity> <verb>` group. Entities reshaped: **metric**
|
|
68
|
+
(template), **node**, **prop**, **meta**, **clock**, **link**, **tag**, **log**, **sched**, **date** (the `date_meta` table). High-frequency verbs keep a top-level
|
|
69
|
+
shortcut onto the same handler (`wl add` == `wl node add`, `wl unlink` == `wl link rm`,
|
|
70
|
+
`wl relog` == `wl log edit`, `wl unlog` == `wl log rm`, `wl dateinfo` == polymorphic over
|
|
71
|
+
`wl date set/ls/rm/import`), args defined once via a shared adder so the two forms can't
|
|
72
|
+
drift. `clock` intervals are CREATED by the composite helpers (`start`/`stop`/`spent`).
|
|
73
|
+
Removal is soft-delete (reversible tombstone, § soft-delete).
|
|
74
|
+
|
|
75
|
+
**`wl set` / `wl unset` are *key-routed* shortcuts** (the same way `wl add` is `node add`):
|
|
76
|
+
a meta key (`goal`/`summary`/`overview`/`top5`) routes to `wl meta set` / `wl meta rm`
|
|
77
|
+
(history-preserving typed log); any other key routes to `wl prop set` / `wl prop rm`
|
|
78
|
+
(static single-value UDA prop). So one `set`/`unset` surface fronts two group-set verbs by
|
|
79
|
+
key — `prop` and `meta` are distinct stores (prop = overwrite single-value; meta = append
|
|
80
|
+
typed log, latest = current), but share the everyday `set`/`unset` entry. `wl goal` / `wl
|
|
81
|
+
recap` additionally auto-target today's day node for the goal / summary fields.
|
|
82
|
+
|
|
83
|
+
> Known limitation: the four meta keys are reserved, so a *prop* literally named
|
|
84
|
+
> `goal`/`summary`/`overview`/`top5` can't be set through `wl set` (the key routes to meta).
|
|
85
|
+
> Acceptable for now (nobody needs a prop with those names); a future fix would be a
|
|
86
|
+
> `--prop`/`--meta` disambiguator flag on `set`, or `wl prop set` (which is unambiguous) as
|
|
87
|
+
> the escape hatch.
|
|
88
|
+
|
|
89
|
+
**`wl alias add/ls/rm`** manages `~/.config/worklog/aliases.ini` (maps a short name to a
|
|
90
|
+
command, e.g. `wl d` == `wl day`); aliases are wired into the parser at startup, so a change
|
|
91
|
+
takes effect on the next invocation. The target must be a real command and an alias may not
|
|
92
|
+
shadow one.
|
|
93
|
+
|
|
94
|
+
**Default-verb dispatch (collision entities).** When the group name equals the old leaf
|
|
95
|
+
command (`link` / `tag` / `log` / `sched`), a custom parser (`_WlParser` /
|
|
96
|
+
`_expand_default_verb`) inserts the group's *default verb* when the token after the entity
|
|
97
|
+
isn't a known verb — so the legacy leaf keeps working: `wl link 42 doc` → `wl link add 42
|
|
98
|
+
doc`, `wl tag 42 +work` → `wl tag add 42 +work`, `wl log 42 "body"` → `wl log add 42
|
|
99
|
+
"body"`, `wl sched 42 2026-06-15` → `wl sched add 42 2026-06-15`, while `wl sched ls 42` /
|
|
100
|
+
`wl sched -h` are left alone. The leaf's first positional is always an int id, never a verb
|
|
101
|
+
word, so the test is unambiguous. For `tag` the `add` default verb keeps the full `+add` /
|
|
102
|
+
`-remove` / bare-add / empty-list grammar (`cmd_tag`); `tag ls` / `tag rm` are
|
|
103
|
+
single-purpose convenience verbs. For `log`, `log edit` / `log rm` reuse the `relog` /
|
|
104
|
+
`unlog` handlers (which keep their shortcuts), and `log ls` is a node-scoped lister (the
|
|
105
|
+
full filterable stream view stays at `wl logs`). For `sched`, the `add` default verb keeps
|
|
106
|
+
the full when / `--recur` / `--clear` / list-when-empty grammar (`cmd_sched`); `sched ls`
|
|
107
|
+
lists and `sched rm` clears, while `wl defer` (status=LATER + rough hint) stays its own
|
|
108
|
+
composite command.
|
|
109
|
+
|
|
110
|
+
A note for the completion generators: a default-verb group's leaf args (`sched`'s
|
|
111
|
+
`--recur` / `when`, `log`'s `--date`) live under the nested `add` subparser, so the
|
|
112
|
+
per-subcommand walk (which skips `_SubParsersAction`) must also descend into the
|
|
113
|
+
default-verb leaf (`_default_verb_leaf`) and emit those completions under the bare group
|
|
114
|
+
condition — otherwise `wl sched <id> --recur<TAB>` silently loses its suggestions.
|
|
115
|
+
|
|
116
|
+
The one **clean group** (no collision, so no default verb) is **`date`** (the `date_meta`
|
|
117
|
+
table): `date set / ls / rm / import`, with `wl dateinfo` as the polymorphic everyday
|
|
118
|
+
shortcut (sets when given a label, lists when not, `--clear` / `--import` variants).
|
|
119
|
+
|
|
120
|
+
The `<entity> <verb>` reshape (WL#486) is now **complete** for every entity.
|
|
121
|
+
|
|
122
|
+
**B. Composite helper** — a one-step shortcut that wraps one or more primitives for
|
|
123
|
+
the common path; never the *only* way to do something (the primitive stays). Status
|
|
124
|
+
verbs `done` / `cancel` / `reopen` / `wait` (set `node.status` + side effects),
|
|
125
|
+
`defer` (sched + LATER), `start` / `stop` (clock + DOING), `tick` (habit check-in
|
|
126
|
+
metric + done), and the compound `add --log/--done/--at/--link/--sched/--metric`
|
|
127
|
+
(add + several primitives in one transaction).
|
|
128
|
+
|
|
129
|
+
**C. View** — read-only cross-entity rendering, no single-row CRUD: `ls` / `tree` /
|
|
130
|
+
`day` / `agenda` / `projects` / `changes` / `summary` / `focus` / `ancestors` /
|
|
131
|
+
`descendants` / `find` / `logs` / `show`. These share the unified filter (§ shared
|
|
132
|
+
`--tag/--kind/--status`), time-window (§8) and `--by` (§9) conventions.
|
|
133
|
+
|
|
134
|
+
System commands (`migrate` / `config` / `init` / `import` / `apply` / `themes` /
|
|
135
|
+
`print-completion`) sit outside the three buckets.
|
|
136
|
+
|
|
137
|
+
### 1.2 Help system (battery-included + shortcut ↔ canonical cross-reference)
|
|
138
|
+
|
|
139
|
+
Every command's `--help` is self-sufficient (§35 "battery-included"): a one-line intro,
|
|
140
|
+
a few scenario examples, and a "Differences from related commands" block. On top of that,
|
|
141
|
+
**wherever a shortcut and its canonical entity-verb both exist, both helps must name the
|
|
142
|
+
relationship — this is mandatory, never omitted**, so a reader landing on either form
|
|
143
|
+
learns the other:
|
|
144
|
+
|
|
145
|
+
- The **canonical entity-verb** help (e.g. `wl node add -h`) states it's also reachable as
|
|
146
|
+
the shortcut (`also: wl add`).
|
|
147
|
+
- The **shortcut** help (e.g. `wl add -h`) states it's the shortcut for the canonical form
|
|
148
|
+
(`canonical: wl node add`).
|
|
149
|
+
- The **entity group** help (e.g. `wl node -h`) carries a dedicated **Shortcuts** section
|
|
150
|
+
mapping each shortcut-able verb to its top-level name (`add → wl add`, `ls → wl ls`, …).
|
|
151
|
+
|
|
152
|
+
This applies to every entity as the `<entity> <verb>` + shortcut rollout proceeds, so the
|
|
153
|
+
two ways to invoke the same operation never drift in discoverability, only in keystrokes.
|
|
154
|
+
|
|
44
155
|
## 2. Data model (`src/worklog/migrations/`)
|
|
45
156
|
|
|
46
157
|
A single `node` table carries everything; the `kind` field discriminates type; `parent_id` self-reference builds the tree. The schema is delivered as numbered SQL migrations under `src/worklog/migrations/NNNN_*.sql`; `PRAGMA user_version` tracks the highest applied migration. `ensure_db()` auto-applies pending migrations on every command; `wl migrate` is the explicit form. See `src/worklog/migrations/0001_initial_schema.sql` for the initial layout and `README.md` for the high-level picture.
|
|
@@ -51,7 +162,7 @@ A single `node` table carries everything; the `kind` field discriminates type; `
|
|
|
51
162
|
- **status only applies to task / habit / meetlog**; time-hierarchy kinds (year/month/...) and project kind leave status NULL
|
|
52
163
|
- Tables: `node / tag / log / metric / clock / prop / link / sched / date_meta` + derived view `v_node_path`
|
|
53
164
|
- **`node → log → metric` spine** (the log-centric core): a `node` has many `log`s; a `log` (carrying a `tag` — `note`/`goal`/`summary`/`overview`/`top5`/`clock`(carrier)/… , NULL = plain note) has 0..N `metric`s. A `metric` is a structured datapoint (`tag` = what it is e.g. `glucose`/`pullups`/`checkin`; `value_num`/`value_text`/`unit`/`note`/`at`). **`tag` is the uniform classification field across all three** — node (a multi-value label set), log (its role, single-value), metric (its kind, single-value); same word, different scopes, SQL-unambiguous. A metric **must hang off a log** (`metric.log_id` NOT NULL) — so every datapoint has a log carrier; a `CHECK` requires a value (pure markers store `value_num=1`). `metric.node_id` is denormalized for join-free per-node queries (no FK; triggers keep it equal to the carrier log's node). CRUD surface: `wl metric add/ls/edit/rm` (`add` without `--on-log` creates a carrier log; a value-less marker is stored as `value_num=1`); `--metric` on `wl log`/`wl add` and `metrics` in `wl import` attach datapoints inline. Habit "done today" = a `tag=checkin` metric that day (written by `wl tick`/`wl checkin`), not "any log exists".
|
|
54
|
-
- **Meta fields are history-preserving typed logs**: day `goal`/`summary`, week `overview`, month `top5` are `log.tag` logs (latest = current; each edit appends), written by `wl goal`/`wl recap
|
|
165
|
+
- **Meta fields are history-preserving typed logs**: day `goal`/`summary`, week `overview`, month `top5` are `log.tag` logs (latest = current; each edit appends), with their own `wl meta set/ls/rm` CRUD group; also written by `wl goal`/`wl recap` (today-auto) and the key-routed `wl set <node> <key>` shortcut. (`prop` is back to truly-static single-value attributes — a separate store.)
|
|
55
166
|
- **`clock` is structured time tracking**: `clock(node_id, start_at, end_at, elapsed_sec)`, written by `wl start`/`stop`/`spent`/`wait` — replaces the old `CLOCK_IN`/`CLOCK_OUT` log-body convention. Durations are summed from `elapsed_sec`, not parsed from text.
|
|
56
167
|
- **Two parallel trees, both hung under lifetime**:
|
|
57
168
|
- **Responsibility line**: `lifetime → area → project → task` (PARA model: area is a cross-time responsibility domain, projects belong to areas, tasks belong to projects)
|
|
@@ -431,6 +542,75 @@ The decision model: **schedule (forward planning, calendar-like) and log (retros
|
|
|
431
542
|
|
|
432
543
|
When changing sched / day derivation, keep `_sched_fires` / `_scheduled_node_ids` / `_node_plan` / `_sec_group` / `_render_day_group` / `cmd_day` / `cmd_logs(--group)` / `cmd_sched` in sync.
|
|
433
544
|
|
|
545
|
+
## 25. Help topic system (`wl help` — info-style)
|
|
546
|
+
|
|
547
|
+
Beyond per-command `--help` (§1.2), `wl help` is a standalone, **info-style topic browser**:
|
|
548
|
+
a repo-managed set of docs — one per command / concept / common-param / guide — navigable by
|
|
549
|
+
"see also" links. This keeps `--help` short (summary + a pointer) while giving each topic room
|
|
550
|
+
for a full explanation, and makes the help content **i18n-able and reviewable as plain files**.
|
|
551
|
+
|
|
552
|
+
### Docs live in the repo (packaged, i18n-ready)
|
|
553
|
+
- Location: `src/worklog/help/<lang>/<topic>.md`, shipped in the wheel like `migrations/`.
|
|
554
|
+
`en` is the source language and the fallback; other langs mirror the same topic ids.
|
|
555
|
+
- One Markdown file per topic; topic id = filename stem (`node.md` → `wl help node`).
|
|
556
|
+
- Minimal frontmatter (no YAML dep), `key: value` lines between `---` fences:
|
|
557
|
+
```
|
|
558
|
+
---
|
|
559
|
+
title: node — the single unit of everything
|
|
560
|
+
category: concept # concept | command | param | guide
|
|
561
|
+
see_also: log, tag, status, add
|
|
562
|
+
---
|
|
563
|
+
<markdown body>
|
|
564
|
+
```
|
|
565
|
+
- `index.md` is the root (`wl help` with no topic): the overview + topic list grouped by category.
|
|
566
|
+
|
|
567
|
+
### `wl help` behavior
|
|
568
|
+
- `wl help` → render `index.md` + list topics by category.
|
|
569
|
+
- `wl help <topic>` → title, body, then a "See also: …" footer resolved from `see_also`.
|
|
570
|
+
- Unknown topic → closest-match suggestions (never a stack trace).
|
|
571
|
+
- Language: `--lang` > `$WORKLOG_LANG` > `$LANG` prefix > `en`; a missing translation falls back
|
|
572
|
+
to `en` per-topic, never a hard error.
|
|
573
|
+
- Rendering: a **dependency-free renderer** (`commands/help.py`) handles a small fixed Markdown
|
|
574
|
+
subset — ATX headings, ```fenced``` code blocks, and inline `**bold**` / `*italic*` /
|
|
575
|
+
`` `code` `` / `[text](url)` / bare URLs (no `_italic_` — underscores collide with identifiers;
|
|
576
|
+
no Markdown engine, no `rich.markdown`). It **escapes literal text** before injecting style
|
|
577
|
+
markup, so bodies with `[ ]` / `[x]` / `[/]` / `#L42` render safely (a raw `[/]` previously
|
|
578
|
+
crashed rich); with color off the markers are simply stripped. The exact subset + frontmatter
|
|
579
|
+
spec for topic authors lives in **CONTRIBUTING.md** ("Writing `wl help` topics").
|
|
580
|
+
|
|
581
|
+
### Relationship to `--help` (the slimming policy)
|
|
582
|
+
- `--help` stays the *quick reference at the point of use*: usage, one-line intro, a couple of
|
|
583
|
+
examples, and — when a fuller topic exists — a closing `More: wl help <topic>`.
|
|
584
|
+
- `wl help <topic>` is the *teaching* layer: concepts, rationale, cross-links, the planning
|
|
585
|
+
rhythm, PARA, etc. Content that would bloat `--help` lives here, keeping `--help` scannable.
|
|
586
|
+
- The top-level `wl -h` Concepts glossary stays a one-line teaser per concept, pointing to
|
|
587
|
+
`wl help <concept>` for the full entry.
|
|
588
|
+
|
|
589
|
+
### `--help` rendering: color, Markdown, wrapping (shared with `wl help`)
|
|
590
|
+
- **Color is post-processed, not native.** argparse has no color before Py 3.14 (and 3.14's uses
|
|
591
|
+
its own palette), so `colorize_help` injects ANSI into argparse's *already-formatted* output.
|
|
592
|
+
The ANSI is zero-width, so argparse's column math is untouched. Color/theme are re-resolved from
|
|
593
|
+
`--color`/`--theme`/env at format time, because `--help` fires inside `parse_args`, before
|
|
594
|
+
`main()` builds the console. It's the same 3-tier scheme as `wl help` (dim body / bright-cyan
|
|
595
|
+
references / bold-white headings).
|
|
596
|
+
- **`--help` may use the topic Markdown subset — but only in `epilog`/`description`.** Markers
|
|
597
|
+
render with color on and are stripped with color off. `help=` one-line summaries stay **plain
|
|
598
|
+
text**: argparse line-wraps them, which would split a Markdown span across lines. The point of
|
|
599
|
+
Markdown here is explicit styling where a heuristic can't help — a command name like `set` /
|
|
600
|
+
`log` / `show` collides with the English word, so it's backticked to mark it as code.
|
|
601
|
+
- **Wrapping lives in the formatter, not the post-processor.** A `RawDescriptionHelpFormatter`
|
|
602
|
+
subclass overrides `_fill_text` (the hook argparse calls *only* for epilog/description) to wrap
|
|
603
|
+
the raw epilog with a hanging indent; option/choice help is still wrapped by argparse's own
|
|
604
|
+
`_split_lines`. Re-wrapping argparse's whole formatted output as a post-process was the wrong
|
|
605
|
+
layer — it re-wraps already-wrapped rows and misaligns them. The formatter caps the width on
|
|
606
|
+
wide terminals (`HELP_MAX_WIDTH`) and shares one `render.help_width()` with the `wl help`
|
|
607
|
+
topic-body wrapper, so every help surface lines up to a single width.
|
|
608
|
+
|
|
609
|
+
### Rollout (incremental)
|
|
610
|
+
Land the loader + `wl help` command + a handful of seed topics first; then author one topic per
|
|
611
|
+
command / concept / param and trim each `--help` to summary + pointer. Topics land piecemeal —
|
|
612
|
+
a missing topic simply isn't offered; nothing breaks.
|
|
613
|
+
|
|
434
614
|
## 24+. Additional sections (24–37)
|
|
435
615
|
|
|
436
616
|
Sections 24 onward cover finer-grained extensions: metadata at the top of `wl day` (goal / summary / top5 / overview blockquote), date context (`date_meta` for holidays / makeups), `--by-task` per-task aggregation in `wl logs`, `wl active` (active CLOCKs with elapsed), the `compound flag` semantics on `add` / `done` / `cancel` (`--log` / `--at` / `--link` / `--sched` / `--done` in one shot), `unlog` / `relog` (log editing through `#L<id>` references), time backfill (`start --at` / `stop --at` / `spent`), the multi-habit interactive `wl checkin`, recurrence rules including `-1` for "last day of period", the `wl ls` multi-dimension query model, default-tail-N to prevent screen-blast, shell completion via `print-completion` init-load (matching the starship / direnv / zoxide pattern), and the **battery-included command help design philosophy** (every `--help` carries a one-line intro + scenario + diff-from-neighbors).
|
|
@@ -30,7 +30,8 @@ Likewise, before adding a dev todo under a project, check that project's existin
|
|
|
30
30
|
| Record a number / measurement / check-in | `wl metric add <id> glucose 5.4 --unit mmol/L` / `wl metric ls <id>` / `wl metric edit #M7` / `wl metric rm #M7`; inline: `wl log <id> "..." --metric 'pullups 8'`. Habit done-today = a `checkin` metric (`wl tick`/`wl checkin`) |
|
|
31
31
|
| Mark done / defer (fuzzy time ok) / start clock | `wl done <id>` / `wl defer <id> next-month` (also accepts `2026-Q3` / `someday` / precise date) / `wl start <id>` `wl stop <id>` |
|
|
32
32
|
| Schedule task to a date / repeat (drives "planned") | `wl sched <id> 2026-06-15` (also accepts `tomorrow` / `day-after-tomorrow`) / `--clear`. `--recur` supports period start / end: `daily` / `weekly:Mon,Fri` (also 1-7 / -1..-7) / `monthly:1` (month start) · `monthly:-1` (month end) / `quarterly:1-1` (quarter start) · `quarterly:-1` (quarter end) / `yearly:01-01` (year start) · `yearly:-1` (year end); `-1` always means period end. A task scheduled to a day shows up in `wl day` as "planned · not yet logged" even with no log |
|
|
33
|
-
| Meta info (end-of-day summary / Top5 / today's goal) | History-preserving typed logs (`log.tag`),
|
|
33
|
+
| Meta info (end-of-day summary / Top5 / today's goal) | History-preserving typed logs (`log.tag`), **a distinct store from props** — own group `wl meta set/ls/rm <node> <field>` (`field` ∈ goal/summary/overview/top5; each write appends, latest = current). Shortcuts onto it: `wl goal "..."` / `wl recap "..."` (auto-target today's day; preferred — read-back + stale-warning), and the key-routed `wl set <node> <field>` (= `wl meta set`; `wl unset <node> <field>` = `wl meta rm`). e.g. `wl meta set <week> overview "..."` / `wl meta set <month> top5 "..."`. `wl day` shows them at the top as a blockquote |
|
|
34
|
+
| Command aliases | `wl alias add d day` (→ `wl d` == `wl day`) / `wl alias ls` / `wl alias rm d`. Stored in `~/.config/worklog/aliases.ini`; target must be a real command, can't shadow one; takes effect next run |
|
|
34
35
|
| Date context (holidays / vacation / makeup days) | `wl dateinfo 2026-05-01 Labor-Day-holiday` / `wl dateinfo --import holidays.json` (`{"YYYY-MM-DD":"label"}`) / `wl dateinfo <date> --clear`. Weekday auto-computed; `wl day` header shows "date weekday · label" |
|
|
35
36
|
| Reproduce a day's progress (like markdown worklog) | `wl day [YYYY-MM-DD]` (log-date based: work/personal split → secondary group → task → indented logs + stats). **Default `--by plan`** (planned / unplanned). **Planned = has a `sched`/recur entry firing that day (source of truth, set via `wl sched`); everything else = unplanned.** The legacy `:planned:` tag is honored only as a transitional fallback — do NOT add it to new tasks, use `wl sched`. Switch dimensions with `--by project` / `--by priority` (P0/P1/P2) |
|
|
36
37
|
| List all active projects | `wl projects` |
|
|
@@ -154,9 +155,12 @@ wl checkin --all-kinds # not limited to habit kind
|
|
|
154
155
|
|
|
155
156
|
### Meta fields keep history (typed logs, not props)
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
stored as `log.tag` logs — each write appends, the latest is
|
|
159
|
-
history is kept (`prop` is only for truly-static
|
|
158
|
+
The four meta fields (goal/summary/overview/top5) have their own group `wl meta
|
|
159
|
+
set/ls/rm <node> <field>`, stored as `log.tag` logs — each write appends, the latest is
|
|
160
|
+
current, so edit history is kept (`prop` is a separate store, only for truly-static
|
|
161
|
+
single-value attributes). `wl goal` / `wl recap` are today-auto shortcuts; `wl set <node>
|
|
162
|
+
<field>` / `wl unset <node> <field>` are key-routed shortcuts onto `wl meta set` / `wl meta
|
|
163
|
+
rm` (so `wl set` fronts both `prop set` and `meta set` by key, like `wl add` is `node add`).
|
|
160
164
|
|
|
161
165
|
### Recurrence rules (`--recur`), every variant supports `-1` for "last day of period"
|
|
162
166
|
|