pyworklog 0.6.0__tar.gz → 0.7.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.
Files changed (161) hide show
  1. {pyworklog-0.6.0 → pyworklog-0.7.0}/.github/workflows/release.yml +30 -7
  2. {pyworklog-0.6.0 → pyworklog-0.7.0}/.github/workflows/test.yml +1 -1
  3. {pyworklog-0.6.0 → pyworklog-0.7.0}/AGENTS.md +1 -0
  4. {pyworklog-0.6.0 → pyworklog-0.7.0}/CHANGELOG.md +68 -20
  5. {pyworklog-0.6.0 → pyworklog-0.7.0}/DESIGN.md +2 -1
  6. {pyworklog-0.6.0 → pyworklog-0.7.0}/PKG-INFO +53 -4
  7. {pyworklog-0.6.0 → pyworklog-0.7.0}/README.md +48 -2
  8. {pyworklog-0.6.0 → pyworklog-0.7.0}/README.zh.md +47 -3
  9. {pyworklog-0.6.0 → pyworklog-0.7.0}/pyproject.toml +5 -2
  10. pyworklog-0.7.0/skills/worklog-cli/SKILL.md +98 -0
  11. pyworklog-0.7.0/skills/worklog-cli/references/bulk.md +60 -0
  12. pyworklog-0.7.0/skills/worklog-cli/references/features.md +165 -0
  13. pyworklog-0.7.0/skills/worklog-cli/references/setup.md +81 -0
  14. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/cli.py +67 -18
  15. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/__init__.py +1 -0
  16. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/bulk.py +4 -4
  17. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/meta.py +9 -7
  18. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/metric.py +1 -1
  19. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/query.py +5 -5
  20. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/state.py +124 -22
  21. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/views.py +5 -5
  22. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/completion.py +7 -6
  23. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/db.py +1 -1
  24. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/db_table.py +4 -4
  25. pyworklog-0.7.0/src/worklog/help/en/agent.md +72 -0
  26. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/day.md +1 -0
  27. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/defer.md +1 -0
  28. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/sched.md +1 -0
  29. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/helpers.py +77 -15
  30. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0008_soft_delete_tombstones.sql +1 -1
  31. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0009_tombstone_node_path_view.sql +1 -1
  32. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/queries.py +49 -8
  33. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_add.py +1 -1
  34. pyworklog-0.7.0/tests/test_agent.py +129 -0
  35. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_cascade.py +1 -1
  36. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_checkin.py +1 -1
  37. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_completion.py +11 -9
  38. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_cycle_safety.py +1 -1
  39. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_dateinfo.py +1 -1
  40. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_day.py +1 -1
  41. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_default_verb.py +4 -2
  42. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_import_apply.py +2 -2
  43. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_link_set.py +11 -3
  44. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_logs.py +1 -1
  45. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_meta.py +4 -4
  46. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_migrations.py +2 -2
  47. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_node.py +21 -1
  48. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_prop_clock.py +49 -2
  49. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_sched.py +137 -2
  50. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_tree.py +2 -2
  51. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_ux.py +1 -1
  52. {pyworklog-0.6.0 → pyworklog-0.7.0}/uv.lock +241 -15
  53. pyworklog-0.6.0/skills/worklog-cli/SKILL.md +0 -363
  54. {pyworklog-0.6.0 → pyworklog-0.7.0}/.gitignore +0 -0
  55. {pyworklog-0.6.0 → pyworklog-0.7.0}/CONTRIBUTING.md +0 -0
  56. {pyworklog-0.6.0 → pyworklog-0.7.0}/DESIGN.zh.md +0 -0
  57. {pyworklog-0.6.0 → pyworklog-0.7.0}/LICENSE +0 -0
  58. {pyworklog-0.6.0 → pyworklog-0.7.0}/Makefile +0 -0
  59. {pyworklog-0.6.0 → pyworklog-0.7.0}/pytest.ini +0 -0
  60. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/__init__.py +0 -0
  61. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/help.py +0 -0
  62. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/active.md +0 -0
  63. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/add.md +0 -0
  64. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/admin.md +0 -0
  65. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/agenda.md +0 -0
  66. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/alias.md +0 -0
  67. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ancestors.md +0 -0
  68. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/apply.md +0 -0
  69. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/bulk.md +0 -0
  70. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/cancel.md +0 -0
  71. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/changes.md +0 -0
  72. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/checkin.md +0 -0
  73. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/clock.md +0 -0
  74. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/config.md +0 -0
  75. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/date.md +0 -0
  76. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/dateinfo.md +0 -0
  77. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/delete.md +0 -0
  78. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/descendants.md +0 -0
  79. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/done.md +0 -0
  80. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/find.md +0 -0
  81. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/focus.md +0 -0
  82. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/goal.md +0 -0
  83. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ids.md +0 -0
  84. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/import.md +0 -0
  85. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/index.md +0 -0
  86. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/init.md +0 -0
  87. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/kind.md +0 -0
  88. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/link.md +0 -0
  89. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/log.md +0 -0
  90. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/logs.md +0 -0
  91. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ls.md +0 -0
  92. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/meta.md +0 -0
  93. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/metric.md +0 -0
  94. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/migrate.md +0 -0
  95. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/node.md +0 -0
  96. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/para.md +0 -0
  97. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/planned.md +0 -0
  98. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/planning.md +0 -0
  99. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/print-completion.md +0 -0
  100. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/priority.md +0 -0
  101. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/projects.md +0 -0
  102. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/prop.md +0 -0
  103. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/recap.md +0 -0
  104. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/relog.md +0 -0
  105. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/reopen.md +0 -0
  106. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/reporting.md +0 -0
  107. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/set.md +0 -0
  108. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/shortcuts.md +0 -0
  109. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/show.md +0 -0
  110. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/spent.md +0 -0
  111. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/start.md +0 -0
  112. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/status.md +0 -0
  113. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/stop.md +0 -0
  114. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/summary.md +0 -0
  115. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tag.md +0 -0
  116. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/themes.md +0 -0
  117. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tick.md +0 -0
  118. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/time.md +0 -0
  119. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tracking.md +0 -0
  120. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tree.md +0 -0
  121. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unlink.md +0 -0
  122. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unlog.md +0 -0
  123. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unset.md +0 -0
  124. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/wait.md +0 -0
  125. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/window.md +0 -0
  126. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0001_initial_schema.sql +0 -0
  127. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0002_metric_and_log_tag.sql +0 -0
  128. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0003_backfill_checkin_metrics.sql +0 -0
  129. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0004_meta_props_to_typed_logs.sql +0 -0
  130. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0005_clock_table.sql +0 -0
  131. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0006_rename_log_type_to_tag.sql +0 -0
  132. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0007_utc_timestamps.sql +0 -0
  133. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/render.py +0 -0
  134. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/timeutil.py +0 -0
  135. {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/xdg.py +0 -0
  136. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/__init__.py +0 -0
  137. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/conftest.py +0 -0
  138. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_alias.py +0 -0
  139. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_aliases.py +0 -0
  140. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_brief.py +0 -0
  141. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_changes.py +0 -0
  142. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_clock.py +0 -0
  143. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_config.py +0 -0
  144. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_db_table.py +0 -0
  145. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_filters.py +0 -0
  146. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_find.py +0 -0
  147. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_focus.py +0 -0
  148. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_help.py +0 -0
  149. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_init.py +0 -0
  150. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_log.py +0 -0
  151. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_log_format.py +0 -0
  152. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_ls.py +0 -0
  153. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_metric.py +0 -0
  154. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_projects.py +0 -0
  155. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_render.py +0 -0
  156. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_sample.py +0 -0
  157. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_show.py +0 -0
  158. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_state.py +0 -0
  159. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_summary.py +0 -0
  160. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_timeutil.py +0 -0
  161. {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_xdg.py +0 -0
@@ -2,7 +2,8 @@ name: Release
2
2
 
3
3
  # Pushing a vX.Y.Z tag:
4
4
  # 1. verifies the tag matches pyproject.toml version,
5
- # 2. runs the test suite (release gate),
5
+ # 2. requires the full Test matrix (test.yml: 3.11/3.12/3.13 × ubuntu/macos) to be green for
6
+ # this commit — depends on that run, does not re-run a single-version gate,
6
7
  # 3. builds the wheel + sdist with uv,
7
8
  # 4. publishes to PyPI via OIDC Trusted Publisher (no token needed),
8
9
  # 5. creates a GitHub Release with auto-generated notes + the dist/ files attached.
@@ -17,6 +18,7 @@ jobs:
17
18
  permissions:
18
19
  contents: write # for creating the GitHub Release
19
20
  id-token: write # for PyPI Trusted Publisher (OIDC)
21
+ actions: read # to read the Test workflow's run status for this commit (release gate)
20
22
  steps:
21
23
  - uses: actions/checkout@v4
22
24
  with:
@@ -32,18 +34,39 @@ jobs:
32
34
  exit 1
33
35
  fi
34
36
 
37
+ # Release gate: require the FULL Test matrix (test.yml: 3.11/3.12/3.13 × ubuntu/macos) to
38
+ # have passed for this exact commit. The tag points at a commit already pushed to main, so
39
+ # test.yml has run (or is running) for this SHA — we depend on that result instead of
40
+ # re-running a single 3.12 gate here (which missed e.g. a 3.13-only argparse difference).
41
+ - name: Require the Test matrix green for this commit
42
+ env:
43
+ GH_TOKEN: ${{ github.token }}
44
+ run: |
45
+ sha="${{ github.sha }}"
46
+ echo "Waiting for the Test matrix (test.yml) to pass on $sha ..."
47
+ for i in $(seq 1 80); do # up to ~20 min (matrix takes a couple minutes)
48
+ run=$(gh run list --workflow=test.yml --commit "$sha" -L 1 \
49
+ --json status,conclusion,url 2>/dev/null || echo '[]')
50
+ status=$(echo "$run" | jq -r '.[0].status // "missing"')
51
+ concl=$(echo "$run" | jq -r '.[0].conclusion // ""')
52
+ url=$(echo "$run" | jq -r '.[0].url // ""')
53
+ echo " test.yml: status=$status conclusion=$concl $url"
54
+ if [ "$status" = "completed" ]; then
55
+ [ "$concl" = "success" ] && { echo "Test matrix green — publishing."; exit 0; }
56
+ echo "::error::Test matrix for $sha did not pass (conclusion=$concl). Refusing to publish. $url"
57
+ exit 1
58
+ fi
59
+ sleep 15
60
+ done
61
+ echo "::error::Timed out (~20 min) waiting for the Test matrix on $sha."
62
+ exit 1
63
+
35
64
  - name: Install uv
36
65
  uses: astral-sh/setup-uv@v6
37
66
  with:
38
67
  python-version: '3.12'
39
68
  enable-cache: true
40
69
 
41
- - name: Sync deps
42
- run: uv sync --all-groups
43
-
44
- - name: Run tests (release gate)
45
- run: uv run pytest -q --no-cov
46
-
47
70
  - name: Build wheel + sdist
48
71
  run: uv build
49
72
 
@@ -13,7 +13,7 @@ jobs:
13
13
  fail-fast: false
14
14
  matrix:
15
15
  os: [ubuntu-latest, macos-latest]
16
- python-version: ['3.11', '3.12', '3.13']
16
+ python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
17
17
 
18
18
  steps:
19
19
  - uses: actions/checkout@v4
@@ -64,6 +64,7 @@ wl --db /tmp/scratch.db add "..."
64
64
  - **Status / priority / theme key names** are enumerated in DESIGN §3/§5/§19; add to `_THEME_KEYS` when introducing a new palette entry — `test_themes_have_same_keys` catches misses.
65
65
  - **No bare `print()` for renderable content**; all rows go through `out()` + `_c()`.
66
66
  - **Bulk writes default to `--dry-run`.** `wl apply` and `wl import` validate first, then execute as a single transaction (`_collect_descendants` recurses for the `-` delete prefix because the FK is `ON DELETE SET NULL`, not cascade). Update flags strictly: only fields that appear in the diff are touched — see DESIGN §18.2 "anti-wipe" rule.
67
+ - **No internal task-tracker ids in the repo.** This is a public repo; the maintainer tracks work in a private system whose ids are meaningless to outside readers. Never write a `WL#NNN` or a bareword `#NNN` design backref in source comments, docstrings, tests, the changelog, or commit / PR text — a bare `#NNN` also renders as a GitHub issue/PR link and misleads. Describe the change in prose instead ("the dedup-warning feature", "the focus-on-day-node fix"); if you must label it, write `task NNN` without the `#`. **Preserved** (these are not backrefs): illustrative example ids in usage snippets (`wl show 42`), the `#L<id>` / `node #123` format documentation, and the deliberately-nonexistent ids in apply/import test fixtures (`#999` / `#888`).
67
68
  - **i18n layout.** README + DESIGN are bilingual (`*.zh.md`), `src/worklog/cli.py` strings + tests + SKILL.md are English. CJK fixtures in tests are intentional (they exercise unicode width / sort / title handling).
68
69
  - **`tmp_path_home` test fixture pattern.** When a test changes `$HOME` to assert path resolution, it must also `monkeypatch.delenv("XDG_CONFIG_HOME")` + `delenv("XDG_DATA_HOME")` — CI runners preset XDG vars and otherwise leak through. See `TestUserAliasesIni._setup_aliases` for the working pattern.
69
70
 
@@ -6,6 +6,55 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.7.0] - 2026-06-09
10
+
11
+ ### Added
12
+ - **Signed relative-delta dates for quick entry** — `+1` / `-2` / `+1d` / `-2day` / `+3w` /
13
+ `-2week` / `+1m` / `-1y`: a signed number plus an optional unit (`d`/`day[s]` default, `w`/`week[s]`,
14
+ `m`/`month[s]`, `y`/`year[s]`), resolved from today. Added in the one central resolver
15
+ (`_resolve_concrete_date`), so every date-accepting command gets it — `wl day -2`, `wl sched 42 +3w`,
16
+ `wl log 42 "..." --date -1d`, `wl defer 42 +2w`, `wl agenda +0 +1m`, … Month/year deltas clamp the day
17
+ (Jan 31 `+1m` → Feb 28); `wl defer` accepts a delta as a precise day hint alongside its fuzzy forms.
18
+ A negative delta with a unit (`-2week`, `-2d`) works as a positional on every supported Python —
19
+ `_WlParser` widens argparse's negative-number matcher (3.9–3.13 would otherwise reject it as an
20
+ unknown option; 3.14 already accepts it).
21
+ - **Relative date words resolve everywhere, connector- and case-insensitive.** `next-week` /
22
+ `next-month` / `next-quarter` now resolve to that period's first day, so `wl sched` / `wl day`
23
+ accept them (alongside `wl defer`, which keeps the coarser week/month bucket). All relative words
24
+ match ignoring case and connector chars — `NEXT_WEEK`, `next week`, `Day After Tomorrow` all parse
25
+ — and plural units (`+2weeks` == `+2week` == `+2w`) work. `someday` stays `defer`-only; `wl sched`'s
26
+ error message now points there for fuzzy values.
27
+ - **`wl agent` — bind the current AI agent session to a task.** `wl agent <id>` ties the current
28
+ Claude Code session (read from `$WL_SESSION_ID` / `$CLAUDE_CODE_SESSION_ID`, failing closed if
29
+ neither is set) to a node, so the agent knows which task it's on and a status line / hook can
30
+ surface it. `wl agent` shows the current binding, `wl agent ls` lists all session→task bindings,
31
+ `wl agent rm` unbinds. Stored as the `agent_session.claude` prop on the node (prefix
32
+ `agent_session.` shared across apps; no new table); rebinding moves the prop off the old node so
33
+ the reverse lookup stays unique.
34
+ - **`wl agent <id> --record` — keep a node's session history.** The `agent_session.claude` prop is
35
+ the *live pointer* (one session → one node, moves on rebind) and stays log-free by default;
36
+ `--record` additionally appends a one-off log + an `agent_session` metric carrying the *full*
37
+ session id, which stays on the node permanently. Recover every session a node was worked under
38
+ with `wl metric ls <id> --tag agent_session --all`. Only the bind event is recorded (not every
39
+ later write), so it costs one row per association. Follows the new design rule (DESIGN §2): extra
40
+ per-log data goes on a metric, never a new `log` column — the log body stays thin.
41
+
42
+ ### Changed
43
+ - **`wl set` / `wl prop set` and import `props` refuse reserved node-field names** (`status`,
44
+ `priority`, `title`, `parent`, `tags`, `kind`, `scheduled`, `deadline`, …) so a prop can't
45
+ silently shadow a real column — that had produced two conflicting `status` values in `wl show`.
46
+ Each rejection points to the right editor (`wl done`/`defer`, `wl node edit`, `wl node reparent`).
47
+ Generalizes the earlier `tags`-only guard.
48
+ - **Wider Python support: 3.9 – 3.14** (was 3.11+). No code used a 3.10/3.11-only feature, so the
49
+ floor dropped to 3.9 (matching the lone dependency, `rich`); the CI matrix now tests 3.9 / 3.10 /
50
+ 3.11 / 3.12 / 3.13 / 3.14 on Ubuntu + macOS, and the classifiers list the full range.
51
+
52
+ ### Docs
53
+ - **README reframed around the AI-first / local-first thesis** — short scannable Why / Features
54
+ bullets, a Quickstart, and the Python requirement in prose (EN + 中文 in sync).
55
+ - **Internal task-tracker ids dropped from the public repo** (comments, docstrings, tests,
56
+ changelog) and a hard rule added to `AGENTS.md`.
57
+
9
58
  ## [0.6.0] - 2026-06-07
10
59
 
11
60
  ### Added
@@ -28,20 +77,20 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
28
77
  splash now points to it. Topic set expanded to 18 — added log / tag / sched / done / defer /
29
78
  tree / ls / find / link / meta / prop alongside the seed concepts/guides.
30
79
  - **`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
80
+ reshaping the command surface to the metric-style `<entity> <verb>` form. The
32
81
  high-frequency `add` / `ls` / `show` are reachable both as `wl node <verb>` and as the
33
82
  existing top-level shortcut (`wl add` == `wl node add`), one handler each, args defined
34
83
  once so the two forms can't drift. Three new primitives close node's CRUD to the metric
35
84
  bar: `node edit <id>` (edit a node's own fields — title / priority / kind / body /
36
85
  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>`
86
+ (change the real `parent_id`, with cycle / self-parent guards —), and `node rm <id>`
38
87
  (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
88
+ - **`wl prop` and `wl clock` entity groups, filling two CRUD gaps.** `wl prop
40
89
  set / ls / rm` — `set` keeps its top-level shortcut and `rm` gains the new shortcut
41
90
  `wl unset <id> <key>` (you can finally *remove* a custom prop, not just overwrite it).
42
91
  `wl clock ls / edit / rm` — list / fix (edit start-end, recomputing the duration) /
43
92
  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).**
93
+ - **`wl link` entity group + a default-verb mechanism for name-collision entities.**
45
94
  `wl link add / ls / rm` — `link add` keeps the top-level shortcut, `rm` gains the
46
95
  `wl unlink <id> <doc>` shortcut, and `link ls` is a new primitive (list a node's links).
47
96
  Because the entity name `link` is itself the everyday verb, the parser learned a
@@ -49,33 +98,33 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
49
98
  so `wl link 42 doc` still means `wl link add 42 doc` while `wl link ls / rm / -h` route to
50
99
  the group. Leaf ids are always integers, so id-vs-verb is unambiguous. This unblocks the
51
100
  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.**
101
+ - **`wl tag` reshaped into a metric-style group, the second default-verb entity.**
53
102
  `wl tag add / ls / rm`. `add` is the default verb, so the everyday `wl tag <id> +work
54
103
  -planned` keeps working unchanged (full +add / -remove / bare-add / empty-list grammar);
55
104
  `tag ls` lists, `tag rm` removes by plain name. `tag` joins `link` in the default-verb
56
105
  table — `wl tag 42 +work` → `wl tag add 42 +work`, while `wl tag ls / rm / -h` route to
57
106
  the group; id-first disambiguation means `wl tag 1 ls` adds the tag `ls` whereas `wl tag
58
107
  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.**
108
+ - **`wl log` reshaped into a metric-style group, the third default-verb entity.**
60
109
  `wl log add / ls / edit / rm`. `add` is the default verb, so the everyday `wl log <id>
61
110
  "body"` keeps working unchanged (auto TODO→DOING, `--date` / `--time` / `--metric` /
62
111
  `--keep-status`). `log edit` and `log rm` reuse the `relog` / `unlog` handlers — both keep
63
112
  their top-level shortcuts — and `log ls <id>` is a new node-scoped lister (the full
64
113
  filterable / windowed stream view stays at `wl logs`). id-first disambiguation: `wl log 1
65
114
  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)
115
+ - **`wl sched` reshaped into a metric-style group, the fourth (and final collision)
67
116
  default-verb entity.** `wl sched add / ls / rm`. `add` is the default verb, so `wl sched
68
117
  <id> <when>` / `wl sched <id> --recur …` / `wl sched <id> --clear` all keep working
69
118
  unchanged (full when / `--recur` / `--clear` / list-when-empty grammar). `sched ls` lists,
70
119
  `sched rm` clears; `wl defer` (status=LATER + rough hint) stays its own command. With this,
71
120
  every collision entity (`link` / `tag` / `log` / `sched`) is reshaped.
72
- - **`date_meta` reshaped into a clean `wl date` group (WL#486) — completing the reshape.**
121
+ - **`date_meta` reshaped into a clean `wl date` group — completing the reshape.**
73
122
  `wl date set / ls / rm / import`. Unlike the collision entities this is a *clean* group
74
123
  (no default verb — `date` doesn't collide with any leaf); `wl dateinfo` stays as the
75
124
  polymorphic everyday shortcut (sets when given a label, lists when not, `--clear` /
76
125
  `--import` variants) over the same table. **With this, every entity now has a full
77
126
  `<entity> <verb>` group.**
78
- - **`wl meta` group for the history-preserving typed-log fields (WL#486).** `wl meta set / ls
127
+ - **`wl meta` group for the history-preserving typed-log fields.** `wl meta set / ls
79
128
  / rm <node> <field>` for `goal` / `summary` / `overview` / `top5` (each edit appends a typed
80
129
  log, latest = current — distinct from props, which are static single-value). `wl set` / `wl
81
130
  unset` are now *key-routed* shortcuts: a meta key routes to `wl meta set` / `wl meta rm`,
@@ -101,7 +150,7 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
101
150
  recap -h` now show the morning→evening→weekly→monthly planning rhythm — and their stale
102
151
  pre-reshape wording ("prop 'goal'", "summary_at") was corrected (they're history-preserving
103
152
  meta fields, not props).
104
- - **Single-letter short flags for the daily-workflow args (WL#487):** `-d` for `--date`
153
+ - **Single-letter short flags for the daily-workflow args:** `-d` for `--date`
105
154
  (`wl log 1 "…" -d yesterday`, also on `logs` / `recap` / `unlog`) and `-n` for `--note`
106
155
  (`wl tick 1 -n "6 pullups"`, also on `wait` / `metric`).
107
156
  - **Help cross-references the shortcut ↔ canonical pair both ways** (now a documented
@@ -141,11 +190,11 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
141
190
  terminals (`render.HELP_MAX_WIDTH`, 100 cols) so help stays readable; everything shares the one
142
191
  `render.help_width()`, so columns line up.
143
192
  - **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 /
193
+ off.** Every table gained a `deleted_at` tombstone; deleting a tag / link / log /
145
194
  node sets it instead of removing the row, and reads filter it out, so an accidental
146
195
  `wl unlog` / `wl tag -x` / `wl apply '- #id'` is recoverable (clear `deleted_at`). FK
147
196
  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
197
+ the tables — preparation for multi-end / CRDT sync. Re-adding a removed tag / link
149
198
  revives its tombstone (a new `upsert` replaces `INSERT OR IGNORE/REPLACE`). Migration 0008
150
199
  is additive (nullable column, no data touched); 0009 makes the `v_node_path` view
151
200
  tombstone-aware. Internally this also moved ~19 single-table reads still on raw SQL onto
@@ -183,30 +232,29 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
183
232
  the index's topic-name column is padded to the longest name + 2 so a long id (`print-completion`)
184
233
  keeps a gap before its description instead of running into it.
185
234
  - **`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,
235
+ node**. A task pinned at `@2026-06` (via `scheduled_date`) hangs under its project,
187
236
  not the month node, so the tree/focus parent_id walk missed it — which made a "what's
188
237
  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.
238
+ surface such pins (the filtered `--tag`/`--kind` path too); an earlier fix covered only day nodes.
190
239
  - **Shell completion for default-verb group leaf args.** When a top-level command became a
191
240
  metric-style group (`log` / `sched`), its everyday args moved under the `add` subparser,
192
241
  which the fish / bash / zsh completion walk skipped — so `wl sched <id> --recur` lost its
193
242
  suggestions and `wl log <id> --date` its date completion. The generators now descend into
194
243
  a default-verb group's leaf (`_default_verb_leaf`) and emit those completions under the
195
244
  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`
245
+ - **`parent_id` cycle safety (FK-off hardening).** With foreign-key enforcement off, `parent_id` integrity isn't DB-guaranteed and the bulk `wl apply` / `wl import`
198
246
  update path set a parent with only an existence check (the interactive `wl reparent` had a
199
247
  cycle guard; the bulk path didn't) — a cycle would then make the ancestor/descendant
200
248
  walks loop forever and hang the CLI. The bulk path now refuses a self/descendant parent
201
249
  (parity with `reparent`), and `_collect_descendants` / `_ancestors_chain` are visited-set
202
250
  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
251
+ - **`wl link` strips an outer `[[ ]]` wrapper on input.** `[[X]]` and `X` now store
204
252
  identically (no more `[[[[X]]]]` double-wrap), re-linking either form dedups via the natural
205
253
  key, and `wl unlink`/`apply -link` match regardless of wrapping. All link writes go through a
206
254
  single normalizing chokepoint (`_upsert_link`/`_delete_link`). (Dedup + `unlink` already
207
- existed; this closes the last part of #462.)
255
+ existed; this closes the last part of that work.)
208
256
  - **`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
257
+ plan`.** The `▸ planned` group header + the `[ ]` status marker already say it; the
210
258
  hint stays under `--by project`/`priority`, and the footer count is unchanged.
211
259
 
212
260
  ### Internal
@@ -217,7 +265,7 @@ The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures e
217
265
  `SELECT last_insert_rowid()` follow-ups — also more correct than the connection-global
218
266
  lastrowid), and 5 more single-table reads/writes moved onto `db_table`. Remaining raw SQL is
219
267
  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
268
+ - **Raw-SQL 余债 cleared:** `_get_or_make`'s 4 time-skeleton lookups (single-table
221
269
  `LIKE`/`=` probes) and `_node_clock_min`'s all-time clock-SUM + log-span-DISTINCT moved onto
222
270
  `db_table` (`cols=` carries the aggregate). Only the *day-scoped* aggregates (SUM/DISTINCT +
223
271
  `local_day_sql` in WHERE) stay raw — genuinely complex.
@@ -117,7 +117,7 @@ The one **clean group** (no collision, so no default verb) is **`date`** (the `d
117
117
  table): `date set / ls / rm / import`, with `wl dateinfo` as the polymorphic everyday
118
118
  shortcut (sets when given a label, lists when not, `--clear` / `--import` variants).
119
119
 
120
- The `<entity> <verb>` reshape (WL#486) is now **complete** for every entity.
120
+ The `<entity> <verb>` reshape is now **complete** for every entity.
121
121
 
122
122
  **B. Composite helper** — a one-step shortcut that wraps one or more primitives for
123
123
  the common path; never the *only* way to do something (the primitive stays). Status
@@ -162,6 +162,7 @@ A single `node` table carries everything; the `kind` field discriminates type; `
162
162
  - **status only applies to task / habit / meetlog**; time-hierarchy kinds (year/month/...) and project kind leave status NULL
163
163
  - Tables: `node / tag / log / metric / clock / prop / link / sched / date_meta` + derived view `v_node_path`
164
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".
165
+ - **Design rule — keep `log` thin; new per-log data goes on a `metric`, not a new log column.** The `log` table stays a small fixed shape (`node_id`/`logged_at`/`body`/`tag`). When some feature wants to attach structured data to a log event (a measurement, an id, a category, a flag), the answer is a `metric` hanging off that log — never a widened `log` row. A metric is already typed (`tag` + `value_num`/`value_text`/`unit`/`note`/`at`), queryable (`wl metric ls --tag …`), and append-only, so it carries arbitrary per-event fields without bloating every log. Example: `wl agent --record` stores the bound session id as an `agent_session` metric, not as a `log.session_id` column. This keeps the log body human-readable and the schema stable (G3).
165
166
  - **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.)
166
167
  - **`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.
167
168
  - **Two parallel trees, both hung under lifetime**:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyworklog
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: SQLite-backed worklog tool with a todo.sh-style CLI
5
5
  Project-URL: Homepage, https://github.com/xyb/worklog
6
6
  Project-URL: Repository, https://github.com/xyb/worklog
@@ -16,12 +16,15 @@ Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Operating System :: MacOS
17
17
  Classifier: Operating System :: POSIX :: Linux
18
18
  Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
19
21
  Classifier: Programming Language :: Python :: 3.11
20
22
  Classifier: Programming Language :: Python :: 3.12
21
23
  Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
22
25
  Classifier: Topic :: Office/Business
23
26
  Classifier: Topic :: Utilities
24
- Requires-Python: >=3.11
27
+ Requires-Python: >=3.9
25
28
  Requires-Dist: rich>=13
26
29
  Description-Content-Type: text/markdown
27
30
 
@@ -37,16 +40,48 @@ Description-Content-Type: text/markdown
37
40
 
38
41
  > **Changelog**: see [CHANGELOG.md](CHANGELOG.md) for a curated highlight reel of every release.
39
42
 
40
- SQLite-backed worklog tool with a `todo.sh`-style CLI. Models the full execution-system hierarchy in a single `node` table — lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog — all sharing one id space, tree-linked via `parent_id` self-reference.
43
+ **worklog (`wl`) is an AI-first, local-first execution-system CLI** a structured replacement for a Markdown worklog. It models the full execution hierarchy in a single SQLite `node` table — lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog — all sharing one id space, tree-linked via `parent_id` self-reference, behind a `todo.sh`-style command surface.
44
+
45
+ ## Why worklog?
46
+
47
+ The origin: Markdown worklogs an AI kept for me grew ~50× and stopped scaling — concurrent writes clobbered, wikilinks drifted, summaries meant re-reading huge files. So I moved the structured part into a database built for an AI to drive.
48
+
49
+ **AI-first** — the AI is the real user; you just glance at the terminal to confirm:
50
+
51
+ - One-line commands, no interactive prompts — reliable to call from a shell.
52
+ - `-q` brief mode + width-clipped rows → token-cheap output.
53
+ - Plain-text output an AI reads directly; bundled [Claude Code skill](skills/worklog-cli/SKILL.md).
54
+
55
+ **Local-first** — one SQLite file, transparent schema, no daemon / GUI / lock-in:
56
+
57
+ - You and the AI read and write the same file — one source of truth.
58
+ - Concurrent-write-safe → parallel AI sessions don't clobber (Markdown can't).
59
+ - Pairs with your vault via `wl link` — structured execution in `wl`, long-form notes in Obsidian.
41
60
 
42
61
  **Design conventions: see [DESIGN.md](DESIGN.md)** — required reading before adding commands, to keep everything consistent.
43
62
  **AI collaboration: see [skills/worklog-cli/SKILL.md](skills/worklog-cli/SKILL.md)** — Claude Code skill (when / how to use `wl`, plus bulk import / apply).
44
- Background: structured worklog tool, built as a self-built alternative after surveying 12 candidate products (Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear etc.) and finding no off-the-shelf tool that fits all three dimensions (time hierarchy, project hierarchy, vault wikilink) without compromise.
63
+ Background: built after surveying 12 candidate products (Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear etc.) and finding no off-the-shelf tool that fits all three dimensions (time hierarchy, project hierarchy, vault wikilink) without compromise.
64
+
65
+ ## Features
66
+
67
+ - **One `node` table for everything** — time line (year → day) + project line (area → task) + habit / meetlog, tree-linked.
68
+ - **Logs** — timestamped progress on any node, history-preserving.
69
+ - **Metrics** — structured datapoints (reps, glucose, check-ins) that trend.
70
+ - **Habits & recurrence** — check-ins + `--recur` (daily / weekly / monthly / …).
71
+ - **Scheduling** — sched a task to a day; fuzzy words (`tomorrow`, `next-week`, `+3w`).
72
+ - **Status machine** — TODO / DOING / LATER / WAIT / DONE / DEFERRED / CANCELED.
73
+ - **Day / week / month views** — `wl day` / `tree` / `summary` rebuild the picture + stats.
74
+ - **Full-text search** — `wl find`, hits highlighted.
75
+ - **Vault link** — `wl link` to Obsidian docs (`[[wikilink]]`).
76
+ - **Bulk import / apply** — load a whole day in one JSON or wl-diff.
77
+ - **AI-friendly output** — `-q` brief, plain-text on capture, colors on a TTY, shell completion.
45
78
 
46
79
  ## Install
47
80
 
48
81
  ### From PyPI (recommended for users)
49
82
 
83
+ Requires Python ≥ 3.9 (tested on 3.9–3.14).
84
+
50
85
  ```fish
51
86
  pipx install pyworklog # or: uv tool install pyworklog
52
87
  wl init
@@ -76,8 +111,22 @@ Behind the scenes `make setup` runs `uv sync` to create `.venv/` from `pyproject
76
111
 
77
112
  DB location follows the [XDG Base Directory spec](https://specifications.freedesktop.org/basedir-spec/): default `$XDG_DATA_HOME/worklog/worklog.db` (i.e. `~/.local/share/worklog/worklog.db`). Override per-invocation with `wl --db PATH ...`, or globally with the `$WORKLOG_DB` env var. User config (aliases.ini) lives at `$XDG_CONFIG_HOME/worklog/aliases.ini` (default `~/.config/worklog/aliases.ini`).
78
113
 
114
+ ## Quickstart
115
+
116
+ The first 30 seconds — add a task, log progress, close it, replay the day:
117
+
118
+ ```fish
119
+ wl init # create the DB (once)
120
+ wl add "write the README" -k task -p A # → prints the new id, e.g. #1
121
+ wl log 1 "drafted the Features section" # append progress
122
+ wl done 1 # close it
123
+ wl day # today's work, regrouped + stats
124
+ ```
125
+
79
126
  ## Commands
80
127
 
128
+ The fuller surface — every command also has `wl <cmd> --help`, and `wl help` browses topic docs:
129
+
81
130
  ```fish
82
131
  wl add "research X" -k task -p A -t work,P0 --proj dev_tooling --parent 42
83
132
  wl add "Dev tooling" -k project -p A --parent 4 # project hangs under month
@@ -10,16 +10,48 @@
10
10
 
11
11
  > **Changelog**: see [CHANGELOG.md](CHANGELOG.md) for a curated highlight reel of every release.
12
12
 
13
- SQLite-backed worklog tool with a `todo.sh`-style CLI. Models the full execution-system hierarchy in a single `node` table — lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog — all sharing one id space, tree-linked via `parent_id` self-reference.
13
+ **worklog (`wl`) is an AI-first, local-first execution-system CLI** a structured replacement for a Markdown worklog. It models the full execution hierarchy in a single SQLite `node` table — lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog — all sharing one id space, tree-linked via `parent_id` self-reference, behind a `todo.sh`-style command surface.
14
+
15
+ ## Why worklog?
16
+
17
+ The origin: Markdown worklogs an AI kept for me grew ~50× and stopped scaling — concurrent writes clobbered, wikilinks drifted, summaries meant re-reading huge files. So I moved the structured part into a database built for an AI to drive.
18
+
19
+ **AI-first** — the AI is the real user; you just glance at the terminal to confirm:
20
+
21
+ - One-line commands, no interactive prompts — reliable to call from a shell.
22
+ - `-q` brief mode + width-clipped rows → token-cheap output.
23
+ - Plain-text output an AI reads directly; bundled [Claude Code skill](skills/worklog-cli/SKILL.md).
24
+
25
+ **Local-first** — one SQLite file, transparent schema, no daemon / GUI / lock-in:
26
+
27
+ - You and the AI read and write the same file — one source of truth.
28
+ - Concurrent-write-safe → parallel AI sessions don't clobber (Markdown can't).
29
+ - Pairs with your vault via `wl link` — structured execution in `wl`, long-form notes in Obsidian.
14
30
 
15
31
  **Design conventions: see [DESIGN.md](DESIGN.md)** — required reading before adding commands, to keep everything consistent.
16
32
  **AI collaboration: see [skills/worklog-cli/SKILL.md](skills/worklog-cli/SKILL.md)** — Claude Code skill (when / how to use `wl`, plus bulk import / apply).
17
- Background: structured worklog tool, built as a self-built alternative after surveying 12 candidate products (Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear etc.) and finding no off-the-shelf tool that fits all three dimensions (time hierarchy, project hierarchy, vault wikilink) without compromise.
33
+ Background: built after surveying 12 candidate products (Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear etc.) and finding no off-the-shelf tool that fits all three dimensions (time hierarchy, project hierarchy, vault wikilink) without compromise.
34
+
35
+ ## Features
36
+
37
+ - **One `node` table for everything** — time line (year → day) + project line (area → task) + habit / meetlog, tree-linked.
38
+ - **Logs** — timestamped progress on any node, history-preserving.
39
+ - **Metrics** — structured datapoints (reps, glucose, check-ins) that trend.
40
+ - **Habits & recurrence** — check-ins + `--recur` (daily / weekly / monthly / …).
41
+ - **Scheduling** — sched a task to a day; fuzzy words (`tomorrow`, `next-week`, `+3w`).
42
+ - **Status machine** — TODO / DOING / LATER / WAIT / DONE / DEFERRED / CANCELED.
43
+ - **Day / week / month views** — `wl day` / `tree` / `summary` rebuild the picture + stats.
44
+ - **Full-text search** — `wl find`, hits highlighted.
45
+ - **Vault link** — `wl link` to Obsidian docs (`[[wikilink]]`).
46
+ - **Bulk import / apply** — load a whole day in one JSON or wl-diff.
47
+ - **AI-friendly output** — `-q` brief, plain-text on capture, colors on a TTY, shell completion.
18
48
 
19
49
  ## Install
20
50
 
21
51
  ### From PyPI (recommended for users)
22
52
 
53
+ Requires Python ≥ 3.9 (tested on 3.9–3.14).
54
+
23
55
  ```fish
24
56
  pipx install pyworklog # or: uv tool install pyworklog
25
57
  wl init
@@ -49,8 +81,22 @@ Behind the scenes `make setup` runs `uv sync` to create `.venv/` from `pyproject
49
81
 
50
82
  DB location follows the [XDG Base Directory spec](https://specifications.freedesktop.org/basedir-spec/): default `$XDG_DATA_HOME/worklog/worklog.db` (i.e. `~/.local/share/worklog/worklog.db`). Override per-invocation with `wl --db PATH ...`, or globally with the `$WORKLOG_DB` env var. User config (aliases.ini) lives at `$XDG_CONFIG_HOME/worklog/aliases.ini` (default `~/.config/worklog/aliases.ini`).
51
83
 
84
+ ## Quickstart
85
+
86
+ The first 30 seconds — add a task, log progress, close it, replay the day:
87
+
88
+ ```fish
89
+ wl init # create the DB (once)
90
+ wl add "write the README" -k task -p A # → prints the new id, e.g. #1
91
+ wl log 1 "drafted the Features section" # append progress
92
+ wl done 1 # close it
93
+ wl day # today's work, regrouped + stats
94
+ ```
95
+
52
96
  ## Commands
53
97
 
98
+ The fuller surface — every command also has `wl <cmd> --help`, and `wl help` browses topic docs:
99
+
54
100
  ```fish
55
101
  wl add "research X" -k task -p A -t work,P0 --proj dev_tooling --parent 42
56
102
  wl add "Dev tooling" -k project -p A --parent 4 # project hangs under month
@@ -8,15 +8,45 @@
8
8
  [![codecov](https://codecov.io/gh/xyb/worklog/branch/main/graph/badge.svg)](https://codecov.io/gh/xyb/worklog)
9
9
  [![License: MIT](https://img.shields.io/pypi/l/pyworklog.svg)](https://github.com/xyb/worklog/blob/main/LICENSE)
10
10
 
11
- SQLite 后端的 worklog 工具,`todo.sh` 风格 CLI。完整执行体系层级建模在单个 `node` 表里 —— lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog —— 共享同一个 id 空间,通过 `parent_id` 自引用形成树状结构。
11
+ **worklog(`wl`)是一个 AI-first、local-first 的执行体系 CLI** —— 用来替代 Markdown 工作记录。完整执行体系层级建模在单个 SQLite `node` 表里 —— lifetime / decade / year / quarter / month / week / day / project / task / habit / signal / meetlog —— 共享同一个 id 空间,通过 `parent_id` 自引用形成树,命令风格沿用 `todo.sh`。
12
+
13
+ ## 为什么用 worklog?
14
+
15
+ 起源:AI 帮我记的 Markdown 工作记录涨了大约 50 倍后撑不住——并发写互相覆盖、wikilink 重命名就断、总结要重读大文件。于是把结构化的部分挪进一个为 AI 驱动而设计的数据库。
16
+
17
+ **AI-first** —— AI 才是真正的使用者,人只在终端看一眼确认:
18
+
19
+ - 命令单行、无交互问答 —— shell 里调用最不易出错。
20
+ - `-q` brief 模式 + 按宽度截行 → 输出省 token。
21
+ - 输出是 AI 能直接读的纯文本;自带 [Claude Code skill](skills/worklog-cli/SKILL.md)。
22
+
23
+ **local-first** —— 一个 SQLite 文件、schema 透明、无常驻进程 / GUI / 锁定:
24
+
25
+ - 人和 AI 读写同一个文件 —— 同一份事实。
26
+ - 并发写安全 → 多个 AI session 并行记录不互相覆盖(Markdown 做不到)。
27
+ - 通过 `wl link` 跟 vault 配合 —— 结构化执行进 `wl`,长文笔记留在 Obsidian。
12
28
 
13
29
  **设计约定见 [DESIGN.md](DESIGN.md)** —— 加命令前必读,保持各处一致。
14
30
  **AI 协作见 [skills/worklog-cli/SKILL.md](skills/worklog-cli/SKILL.md)** —— Claude Code skill(何时 / 如何用 `wl` + 批量 import / apply)。
15
- 背景: 结构化 worklog 工具,在调研了 12 个候选产品(Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear 等)后没找到能同时覆盖三个维度(时间层级 / 项目层级 / vault wikilink)又无折中的现成方案,所以自建。
31
+ 背景: 在调研了 12 个候选产品(Logseq / Tana / TaskWarrior / org-mode / Anytype / Capacities / Linear 等)后没找到能同时覆盖三个维度(时间层级 / 项目层级 / vault wikilink)又无折中的现成方案,所以自建。
32
+
33
+ ## 特性
34
+
35
+ - **一张 `node` 表装下一切** —— 时间线(year → day)+ 项目线(area → task)+ 习惯 / 会议记录,连成树。
36
+ - **Log** —— 任意 node 上带时间戳的进展,保留历史。
37
+ - **Metric** —— 结构化数据点(次数、血糖、打卡),能做趋势。
38
+ - **习惯 & 循环** —— 打卡 + `--recur`(daily / weekly / monthly / …)。
39
+ - **排期** —— 把任务排到某天;模糊时间词(`tomorrow` / `next-week` / `+3w`)。
40
+ - **状态机** —— TODO / DOING / LATER / WAIT / DONE / DEFERRED / CANCELED。
41
+ - **日 / 周 / 月视图** —— `wl day` / `tree` / `summary` 重建画面 + 统计。
42
+ - **全文搜索** —— `wl find`,命中高亮。
43
+ - **Vault 关联** —— `wl link` 到 Obsidian 文档(`[[wikilink]]`)。
44
+ - **批量 import / apply** —— 一个 JSON 或 wl-diff 导入一整天。
45
+ - **AI 友好输出** —— `-q` brief、捕获时纯文本、TTY 上配色、shell 补全。
16
46
 
17
47
  ## 安装
18
48
 
19
- 需要先装 [uv](https://docs.astral.sh/uv/)(`brew install uv` 或 `pipx install uv`)。
49
+ 需要 Python ≥ 3.9(在 3.9–3.14 上测过)。先装 [uv](https://docs.astral.sh/uv/)(`brew install uv` 或 `pipx install uv`)。
20
50
 
21
51
  ```fish
22
52
  git clone https://github.com/xyb/worklog.git ~/projects/worklog
@@ -36,8 +66,22 @@ wl init
36
66
 
37
67
  数据库位置遵循 [XDG Base Directory 规范](https://specifications.freedesktop.org/basedir-spec/): 默认 `$XDG_DATA_HOME/worklog/worklog.db`(即 `~/.local/share/worklog/worklog.db`)。可以用 `wl --db PATH ...` 单次覆盖,也可以用 `$WORKLOG_DB` 环境变量全局覆盖。用户配置(aliases.ini)走 `$XDG_CONFIG_HOME/worklog/aliases.ini`(默认 `~/.config/worklog/aliases.ini`)。
38
68
 
69
+ ## 快速开始
70
+
71
+ 头 30 秒 —— 加任务、记进展、完成、复现当天:
72
+
73
+ ```fish
74
+ wl init # 建库(一次)
75
+ wl add "写 README" -k task -p A # → 打印新 id,如 #1
76
+ wl log 1 "起草了 Features 段" # 追加进展
77
+ wl done 1 # 完成
78
+ wl day # 今天的工作, 重新分组 + 统计
79
+ ```
80
+
39
81
  ## 命令
40
82
 
83
+ 更全的命令面 —— 每条命令也有 `wl <cmd> --help`,`wl help` 浏览主题文档:
84
+
41
85
  ```fish
42
86
  wl add "调研 X" -k task -p A -t work,P0 --proj dev_tooling --parent 42
43
87
  wl add "Dev tooling" -k project -p A --parent 4 # 项目挂在月份下
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "pyworklog"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  description = "SQLite-backed worklog tool with a todo.sh-style CLI"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  authors = [{ name = "Xie Yanbo" }]
8
- requires-python = ">=3.11"
8
+ requires-python = ">=3.9"
9
9
  keywords = ["worklog", "cli", "sqlite", "todo", "productivity"]
10
10
  classifiers = [
11
11
  "Development Status :: 4 - Beta",
@@ -15,9 +15,12 @@ classifiers = [
15
15
  "Operating System :: MacOS",
16
16
  "Operating System :: POSIX :: Linux",
17
17
  "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
18
20
  "Programming Language :: Python :: 3.11",
19
21
  "Programming Language :: Python :: 3.12",
20
22
  "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
21
24
  "Topic :: Office/Business",
22
25
  "Topic :: Utilities",
23
26
  ]