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.
- {pyworklog-0.6.0 → pyworklog-0.7.0}/.github/workflows/release.yml +30 -7
- {pyworklog-0.6.0 → pyworklog-0.7.0}/.github/workflows/test.yml +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/AGENTS.md +1 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/CHANGELOG.md +68 -20
- {pyworklog-0.6.0 → pyworklog-0.7.0}/DESIGN.md +2 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/PKG-INFO +53 -4
- {pyworklog-0.6.0 → pyworklog-0.7.0}/README.md +48 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/README.zh.md +47 -3
- {pyworklog-0.6.0 → pyworklog-0.7.0}/pyproject.toml +5 -2
- pyworklog-0.7.0/skills/worklog-cli/SKILL.md +98 -0
- pyworklog-0.7.0/skills/worklog-cli/references/bulk.md +60 -0
- pyworklog-0.7.0/skills/worklog-cli/references/features.md +165 -0
- pyworklog-0.7.0/skills/worklog-cli/references/setup.md +81 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/cli.py +67 -18
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/__init__.py +1 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/bulk.py +4 -4
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/meta.py +9 -7
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/metric.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/query.py +5 -5
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/state.py +124 -22
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/views.py +5 -5
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/completion.py +7 -6
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/db.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/db_table.py +4 -4
- pyworklog-0.7.0/src/worklog/help/en/agent.md +72 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/day.md +1 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/defer.md +1 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/sched.md +1 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/helpers.py +77 -15
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0008_soft_delete_tombstones.sql +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0009_tombstone_node_path_view.sql +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/queries.py +49 -8
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_add.py +1 -1
- pyworklog-0.7.0/tests/test_agent.py +129 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_cascade.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_checkin.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_completion.py +11 -9
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_cycle_safety.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_dateinfo.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_day.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_default_verb.py +4 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_import_apply.py +2 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_link_set.py +11 -3
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_logs.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_meta.py +4 -4
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_migrations.py +2 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_node.py +21 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_prop_clock.py +49 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_sched.py +137 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_tree.py +2 -2
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_ux.py +1 -1
- {pyworklog-0.6.0 → pyworklog-0.7.0}/uv.lock +241 -15
- pyworklog-0.6.0/skills/worklog-cli/SKILL.md +0 -363
- {pyworklog-0.6.0 → pyworklog-0.7.0}/.gitignore +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/CONTRIBUTING.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/DESIGN.zh.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/LICENSE +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/Makefile +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/pytest.ini +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/__init__.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/commands/help.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/active.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/add.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/admin.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/agenda.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/alias.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ancestors.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/apply.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/bulk.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/cancel.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/changes.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/checkin.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/clock.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/config.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/date.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/dateinfo.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/delete.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/descendants.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/done.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/find.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/focus.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/goal.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ids.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/import.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/index.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/init.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/kind.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/link.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/log.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/logs.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/ls.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/meta.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/metric.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/migrate.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/node.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/para.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/planned.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/planning.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/print-completion.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/priority.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/projects.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/prop.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/recap.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/relog.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/reopen.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/reporting.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/set.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/shortcuts.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/show.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/spent.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/start.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/status.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/stop.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/summary.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tag.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/themes.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tick.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/time.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tracking.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/tree.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unlink.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unlog.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/unset.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/wait.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/help/en/window.md +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0001_initial_schema.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0002_metric_and_log_tag.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0003_backfill_checkin_metrics.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0004_meta_props_to_typed_logs.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0005_clock_table.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0006_rename_log_type_to_tag.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/migrations/0007_utc_timestamps.sql +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/render.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/timeutil.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/src/worklog/xdg.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/__init__.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/conftest.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_alias.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_aliases.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_brief.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_changes.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_clock.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_config.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_db_table.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_filters.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_find.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_focus.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_help.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_init.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_log.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_log_format.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_ls.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_metric.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_projects.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_render.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_sample.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_show.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_state.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_summary.py +0 -0
- {pyworklog-0.6.0 → pyworklog-0.7.0}/tests/test_timeutil.py +0 -0
- {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.
|
|
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
|
|
|
@@ -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
|
|
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 —
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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);
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
[](https://codecov.io/gh/xyb/worklog)
|
|
9
9
|
[](https://github.com/xyb/worklog/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
背景:
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
]
|