pyworklog 0.3.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.3.0/.github/workflows/release.yml +94 -0
- pyworklog-0.3.0/.github/workflows/test.yml +51 -0
- pyworklog-0.3.0/.gitignore +225 -0
- pyworklog-0.3.0/AGENTS.md +72 -0
- pyworklog-0.3.0/CHANGELOG.md +66 -0
- pyworklog-0.3.0/CONTRIBUTING.md +116 -0
- pyworklog-0.3.0/DESIGN.md +409 -0
- pyworklog-0.3.0/DESIGN.zh.md +744 -0
- pyworklog-0.3.0/LICENSE +21 -0
- pyworklog-0.3.0/Makefile +125 -0
- pyworklog-0.3.0/PKG-INFO +136 -0
- pyworklog-0.3.0/README.md +109 -0
- pyworklog-0.3.0/README.zh.md +96 -0
- pyworklog-0.3.0/pyproject.toml +49 -0
- pyworklog-0.3.0/pytest.ini +20 -0
- pyworklog-0.3.0/skills/worklog-cli/SKILL.md +294 -0
- pyworklog-0.3.0/src/worklog/__init__.py +8 -0
- pyworklog-0.3.0/src/worklog/cli.py +1160 -0
- pyworklog-0.3.0/src/worklog/commands/__init__.py +89 -0
- pyworklog-0.3.0/src/worklog/commands/bulk.py +512 -0
- pyworklog-0.3.0/src/worklog/commands/meta.py +579 -0
- pyworklog-0.3.0/src/worklog/commands/query.py +854 -0
- pyworklog-0.3.0/src/worklog/commands/state.py +651 -0
- pyworklog-0.3.0/src/worklog/commands/views.py +558 -0
- pyworklog-0.3.0/src/worklog/completion.py +624 -0
- pyworklog-0.3.0/src/worklog/db.py +92 -0
- pyworklog-0.3.0/src/worklog/helpers.py +288 -0
- pyworklog-0.3.0/src/worklog/migrations/0001_initial_schema.sql +90 -0
- pyworklog-0.3.0/src/worklog/queries.py +216 -0
- pyworklog-0.3.0/src/worklog/render.py +209 -0
- pyworklog-0.3.0/src/worklog/xdg.py +42 -0
- pyworklog-0.3.0/tests/__init__.py +0 -0
- pyworklog-0.3.0/tests/conftest.py +63 -0
- pyworklog-0.3.0/tests/test_add.py +183 -0
- pyworklog-0.3.0/tests/test_aliases.py +96 -0
- pyworklog-0.3.0/tests/test_brief.py +153 -0
- pyworklog-0.3.0/tests/test_cascade.py +27 -0
- pyworklog-0.3.0/tests/test_changes.py +46 -0
- pyworklog-0.3.0/tests/test_checkin.py +315 -0
- pyworklog-0.3.0/tests/test_clock.py +191 -0
- pyworklog-0.3.0/tests/test_completion.py +183 -0
- pyworklog-0.3.0/tests/test_config.py +48 -0
- pyworklog-0.3.0/tests/test_dateinfo.py +42 -0
- pyworklog-0.3.0/tests/test_day.py +148 -0
- pyworklog-0.3.0/tests/test_find.py +99 -0
- pyworklog-0.3.0/tests/test_focus.py +95 -0
- pyworklog-0.3.0/tests/test_import_apply.py +615 -0
- pyworklog-0.3.0/tests/test_init.py +21 -0
- pyworklog-0.3.0/tests/test_link_set.py +51 -0
- pyworklog-0.3.0/tests/test_log.py +321 -0
- pyworklog-0.3.0/tests/test_log_format.py +166 -0
- pyworklog-0.3.0/tests/test_logs.py +99 -0
- pyworklog-0.3.0/tests/test_ls.py +203 -0
- pyworklog-0.3.0/tests/test_meta.py +59 -0
- pyworklog-0.3.0/tests/test_migrations.py +136 -0
- pyworklog-0.3.0/tests/test_projects.py +49 -0
- pyworklog-0.3.0/tests/test_render.py +265 -0
- pyworklog-0.3.0/tests/test_sample.py +50 -0
- pyworklog-0.3.0/tests/test_sched.py +566 -0
- pyworklog-0.3.0/tests/test_show.py +61 -0
- pyworklog-0.3.0/tests/test_state.py +151 -0
- pyworklog-0.3.0/tests/test_summary.py +88 -0
- pyworklog-0.3.0/tests/test_tree.py +265 -0
- pyworklog-0.3.0/tests/test_ux.py +727 -0
- pyworklog-0.3.0/tests/test_xdg.py +48 -0
- pyworklog-0.3.0/uv.lock +319 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Pushing a vX.Y.Z tag:
|
|
4
|
+
# 1. verifies the tag matches pyproject.toml version,
|
|
5
|
+
# 2. runs the test suite (release gate),
|
|
6
|
+
# 3. builds the wheel + sdist with uv,
|
|
7
|
+
# 4. publishes to PyPI via OIDC Trusted Publisher (no token needed),
|
|
8
|
+
# 5. creates a GitHub Release with auto-generated notes + the dist/ files attached.
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- "v*"
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write # for creating the GitHub Release
|
|
19
|
+
id-token: write # for PyPI Trusted Publisher (OIDC)
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0 # need full history for auto-generated release notes
|
|
24
|
+
|
|
25
|
+
- name: Verify tag matches pyproject version
|
|
26
|
+
run: |
|
|
27
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
28
|
+
proj=$(grep -m1 '^version = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')
|
|
29
|
+
echo "git tag=$tag · pyproject=$proj"
|
|
30
|
+
if [ "$tag" != "$proj" ]; then
|
|
31
|
+
echo "::error::version mismatch — git tag must equal pyproject.toml version (which __version__ reads via importlib.metadata)"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
uses: astral-sh/setup-uv@v6
|
|
37
|
+
with:
|
|
38
|
+
python-version: '3.12'
|
|
39
|
+
enable-cache: true
|
|
40
|
+
|
|
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
|
+
- name: Build wheel + sdist
|
|
48
|
+
run: uv build
|
|
49
|
+
|
|
50
|
+
- name: Publish to PyPI
|
|
51
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
52
|
+
# No `with: password:` — uses OIDC Trusted Publisher.
|
|
53
|
+
# One-time setup on PyPI: https://pypi.org/manage/account/publishing/
|
|
54
|
+
# PyPI Project Name: pyworklog (worklog/worklog-cli taken; hyphenated names avoided)
|
|
55
|
+
# Owner: xyb · Repository: worklog
|
|
56
|
+
# Workflow filename: release.yml · Environment name: (leave empty)
|
|
57
|
+
|
|
58
|
+
- name: Build commit list since previous tag
|
|
59
|
+
id: commits
|
|
60
|
+
run: |
|
|
61
|
+
prev=$(git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^" 2>/dev/null || echo "")
|
|
62
|
+
{
|
|
63
|
+
if [ -n "$prev" ]; then
|
|
64
|
+
echo "## Commits since $prev"
|
|
65
|
+
echo ""
|
|
66
|
+
git log --pretty=format:'- %s (%h)' "$prev..$GITHUB_REF_NAME"
|
|
67
|
+
echo ""
|
|
68
|
+
else
|
|
69
|
+
echo "## Commits"
|
|
70
|
+
echo ""
|
|
71
|
+
git log --pretty=format:'- %s (%h)' "$GITHUB_REF_NAME"
|
|
72
|
+
echo ""
|
|
73
|
+
fi
|
|
74
|
+
echo ""
|
|
75
|
+
echo "## Install"
|
|
76
|
+
echo ""
|
|
77
|
+
echo '```'
|
|
78
|
+
echo "pip install pyworklog==${GITHUB_REF_NAME#v}"
|
|
79
|
+
echo '```'
|
|
80
|
+
} > release-body.md
|
|
81
|
+
{
|
|
82
|
+
echo 'body<<EOF'
|
|
83
|
+
cat release-body.md
|
|
84
|
+
echo 'EOF'
|
|
85
|
+
} >> "$GITHUB_OUTPUT"
|
|
86
|
+
|
|
87
|
+
- name: Create GitHub Release
|
|
88
|
+
uses: softprops/action-gh-release@v2
|
|
89
|
+
with:
|
|
90
|
+
body: ${{ steps.commits.outputs.body }}
|
|
91
|
+
generate_release_notes: true # keeps the auto "Full Changelog" link and any PR list
|
|
92
|
+
files: |
|
|
93
|
+
dist/*.whl
|
|
94
|
+
dist/*.tar.gz
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
python-version: ['3.11', '3.12', '3.13']
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Sync deps
|
|
28
|
+
run: uv sync --all-groups
|
|
29
|
+
|
|
30
|
+
- name: Run tests with coverage
|
|
31
|
+
run: uv run pytest --cov-report=xml --cov-report=term-missing
|
|
32
|
+
|
|
33
|
+
- name: Upload coverage to Codecov
|
|
34
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
35
|
+
uses: codecov/codecov-action@v5
|
|
36
|
+
with:
|
|
37
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
38
|
+
files: ./coverage.xml
|
|
39
|
+
fail_ci_if_error: false
|
|
40
|
+
|
|
41
|
+
- name: Smoke test CLI
|
|
42
|
+
run: |
|
|
43
|
+
export WORKLOG_DB=$(mktemp -d)/worklog.db
|
|
44
|
+
uv run wl init
|
|
45
|
+
uv run wl --version
|
|
46
|
+
uv run wl add "ci smoke task" -k task -p A
|
|
47
|
+
uv run wl log 1 "hello from CI"
|
|
48
|
+
uv run wl done 1
|
|
49
|
+
uv run wl show 1
|
|
50
|
+
uv run wl tree
|
|
51
|
+
uv run wl migrate
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# ── worklog project specific ──
|
|
221
|
+
*.db
|
|
222
|
+
*.db-journal
|
|
223
|
+
wl.py.bak
|
|
224
|
+
TODO.local.md
|
|
225
|
+
local/
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Primary operating guide for AI coding agents (Claude Code, Cursor, Aider, etc.) working in this repository. Keep it under ~200 lines — anything longer stops getting read in full.
|
|
4
|
+
|
|
5
|
+
## Authoritative docs (read these first)
|
|
6
|
+
|
|
7
|
+
- **`DESIGN.md`** is canonical for every shared convention (command style, state machine, marker symbols, time-window flags, rendering, schema, import/apply formats, color theming, scheduled time, planned/unplanned derivation, etc.). Read the relevant section before adding a command or changing a format — keeping `src/worklog/cli.py`, the tests, the completion strings, and DESIGN in sync is the project's hardest rule.
|
|
8
|
+
- **`CONTRIBUTING.md`** holds the full dev setup, TDD + DRY conventions, local Makefile overrides, and release process. Do not duplicate that content here.
|
|
9
|
+
- **`skills/worklog-cli/SKILL.md`** is the Claude Code skill (AI-facing usage guide: when to use `wl`, scenario→command table, bulk `import` / `apply` patterns, `-q` brief mode for token savings). Update this when usage patterns change.
|
|
10
|
+
- `README.md` is the project overview + install pointer only; `tests/test_wl.py` is the de-facto contract for every command.
|
|
11
|
+
|
|
12
|
+
## Common commands
|
|
13
|
+
|
|
14
|
+
```fish
|
|
15
|
+
make setup # first-time: uv sync + install ~/bin/wl wrapper into project .venv
|
|
16
|
+
make test # pytest in parallel (-n auto) with the 95% cov gate from pytest.ini
|
|
17
|
+
make test-v # sequential, no cov — debug noisy output
|
|
18
|
+
make test-fast # parallel, no cov gate — quick dev loop
|
|
19
|
+
make cov # detailed term-missing coverage report
|
|
20
|
+
make demo # reset DB + populate sample tree (irreversible)
|
|
21
|
+
make reset # interactive: drop current DB + re-init
|
|
22
|
+
make ship # test then push (only pushes if green)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Single-test invocation:
|
|
26
|
+
|
|
27
|
+
```fish
|
|
28
|
+
uv run pytest tests/test_wl.py::TestApply::test_apply_delete_cascades_subtree -v --no-cov
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Ad-hoc DB for scratch work without touching `~/.local/share/worklog/worklog.db`:
|
|
32
|
+
|
|
33
|
+
```fish
|
|
34
|
+
wl --db /tmp/scratch.db init
|
|
35
|
+
wl --db /tmp/scratch.db add "..."
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`wl config` prints the resolved DB path + source (`--db flag` / `$WORKLOG_DB` / XDG default) plus all env vars — start there when debugging a path/env confusion.
|
|
39
|
+
|
|
40
|
+
## Architecture (the big picture)
|
|
41
|
+
|
|
42
|
+
**Single-package CLI.** `src/worklog/` holds everything: `cli.py` (~5000 lines) is the implementation, `migrations/NNNN_*.sql` is the schema (one initial file at v0.3.0, more added per schema change), `__init__.py` re-exports `__version__`. No further submodule split inside `cli.py` — kept this way intentionally; reconsider only when a feature truly cannot be co-located.
|
|
43
|
+
|
|
44
|
+
**One polymorphic `node` table.** The `kind` column distinguishes `lifetime / decade / year / quarter / month / week / day / area / project / task / meetlog / habit / signal`; `parent_id` self-reference builds the tree. Two parallel sub-trees hang under `lifetime`: a **responsibility line** (`area → project → task`, PARA-style) and a **time line** (`year → quarter → month → week → day`, time skeleton + metadata props). Day/week/month views are **log-driven** (`logged_at` + ancestor chain + tags), not via fixed parent-of-task — moving projects between areas does not break per-day views. Tables: `node / tag / log / prop / link / sched / date_meta` + recursive CTE view `v_node_path`.
|
|
45
|
+
|
|
46
|
+
**Path resolution.** Priority: `wl --db PATH` flag → `$WORKLOG_DB` env → `$XDG_DATA_HOME/worklog/worklog.db` (default `~/.local/share/worklog/worklog.db`). Config is `$XDG_CONFIG_HOME/worklog/aliases.ini`. `_resolve_db_path(args)` is the only resolver; `cmd_config` displays which branch fired.
|
|
47
|
+
|
|
48
|
+
**Subcommand dispatch.** `build_parser()` constructs the argparse tree (one `sub.add_parser(...)` block per command, with `help` + `description` + `epilog`). `HANDLERS = {"cmd": cmd_handler}` maps the parsed name to `cmd_<name>(args, con) -> None`. `main()` resolves user aliases (`~/.config/worklog/aliases.ini`), routes meta-commands (`print-completion`, `config`) that bypass `ensure_db()`, applies `--db` override, then calls the handler. Tests invoke `HANDLERS[cmd](args, con)` directly through the `cli` fixture to bypass `main()`.
|
|
49
|
+
|
|
50
|
+
**Rendering pipeline.** All output goes through `out()` (rich-aware) — never bare `print()` for user-visible rows. `_c(text, style)` is the single coloring entry: when `_CONSOLE` is set, it escapes content via `rich.markup.escape` (so `[x]`, `[[doc]]`, `[#A]` aren't eaten as rich markup) and wraps with `[style]…[/style]`; when console is plain, it returns the string. **Any fragment that may contain `[ ]` must go through `_c`** — direct insertion triggers `rich.MarkupError` or silent eating. `_node_line(con, n, ...)` is the **only node renderer**; reuse it everywhere listing nodes, including new commands, to inherit highlighting + search-hit emphasis for free. `_init_console` runs once in `main()`; tests run in plain mode (no TTY).
|
|
51
|
+
|
|
52
|
+
**Shell completion is generated, not hand-written.** `wl print-completion {fish,bash,zsh}` walks the argparse tree at runtime and emits a script tailored to that shell. Each shell template contains a `__wl_db_path[_bash|_zsh]` helper that queries SQLite directly for dynamic node-id / tag completions — **no Python startup**, so Tab is <50ms. When you add a subcommand or flag, completion is automatic; **but** if it takes a node id or tag, register it in `_FISH_POSITIONAL_NODE` / `_FISH_HELPERS` / `_BASH_DYN_HELPERS` so dynamic completion fires.
|
|
53
|
+
|
|
54
|
+
**Coverage gate is hard (95%).** `pytest.ini` enforces `--cov-fail-under=95`; the CI runs the same. The two pragma-no-cover regions are TTY/escape-sequence probes in `_detect_bg_is_dark` and the bare `main()` entrypoint (tests bypass it).
|
|
55
|
+
|
|
56
|
+
## Core principles
|
|
57
|
+
|
|
58
|
+
- **TDD (Red → Green → Refactor).** Behavioral changes start with a failing test in `tests/test_wl.py` that reproduces the bug or pins the new behavior. Confirm it fails for the right reason, then write the minimum code to make it pass, then refactor. Tests stay green at every step. A PR that adds behavior without adding the test that drives it is rejected. See CONTRIBUTING.md "TDD" for the full loop.
|
|
59
|
+
- **DRY (Don't Repeat Yourself).** The codebase has a small set of single-source helpers — `_status_marker`, `_node_line`, `out`/`_c`, `_resolve_window`, `_resolve_db_path`, `_project_members`, `_node_clock_min`, `_collect_descendants` (see DESIGN §12 for the full list). New code reuses them; re-implementing one of them in a fresh form is a review block. Same rule for docs: install / dev / release info is in CONTRIBUTING.md and only there — link to it, don't restate it.
|
|
60
|
+
|
|
61
|
+
## Hard rules
|
|
62
|
+
|
|
63
|
+
- **DESIGN.md is the source of truth.** If a convention changes (statuses, markers, time-window flag set, project↔task linkage rule, `--by` aggregation dim, theme key set, schema), update DESIGN + `src/worklog/cli.py` + tests + completion strings in the same commit. Drift between them is the failure mode this project guards against.
|
|
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
|
+
- **No bare `print()` for renderable content**; all rows go through `out()` + `_c()`.
|
|
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
|
+
- **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
|
+
- **`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
|
+
## Local overrides + release
|
|
71
|
+
|
|
72
|
+
Both processes are documented end-to-end in [CONTRIBUTING.md](CONTRIBUTING.md) — local `local/*.mk` Makefile injection and the three-anchor version-bump workflow (`__version__` in `src/worklog/cli.py` + `pyproject.toml` + git tag, all verified by `.github/workflows/release.yml`). Don't duplicate them here.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **worklog** are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
The autogenerated `Commits since vX.Y.Z` block on each GitHub Release captures every commit; this file is the human-curated highlight reel.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.3.0] — 2026-06-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `pip install pyworklog` now works. The project ships as a proper Python package (`src/worklog/`) with a `wl` console-script entry point; `uv build` produces a wheel + sdist with `worklog/migrations/0001_initial_schema.sql` packaged in. The PyPI **distribution** name is `pyworklog` (the short names `worklog` and `worklog-cli` were already taken on PyPI, and hyphenated names like `worklog-py` were avoided); the import name and `wl` command are unchanged.
|
|
13
|
+
- **SQL migrations runner** — schema is delivered as `src/worklog/migrations/NNNN_*.sql`; `PRAGMA user_version` tracks the highest applied. `ensure_db()` auto-applies pending migrations on every command. New `wl migrate` is the explicit form (and the one place that bypasses `ensure_db()` so the handler has work to do on a fresh DB).
|
|
14
|
+
- **Downgrade guard** — if `user_version` is ahead of the migrations shipped with the running build, the tool aborts with an upgrade prompt instead of corrupting newer schema with stale logic.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- `wl.py` moved to `src/worklog/cli.py`; `schema.sql` moved to `src/worklog/migrations/0001_initial_schema.sql`. Internal imports rewritten throughout; tests, CI, and the `~/bin/wl` Makefile wrapper now go through the entry-point (`.venv/bin/wl`).
|
|
18
|
+
- `__version__` reads from `importlib.metadata.version("pyworklog")` — `pyproject.toml` is now the single in-repo source of truth for version, and `uv.lock` pins it deterministically. `release.yml` verifies tag against `pyproject.toml` only.
|
|
19
|
+
- `--cov` target switched from `wl` to `worklog`; coverage gate stays at 95%.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- Hand-edited `__version__` in `wl.py`. (Use `pyproject.toml`.)
|
|
23
|
+
|
|
24
|
+
### Migration notes for existing users
|
|
25
|
+
- The DB path is unchanged (`$WORKLOG_DB` or `$XDG_DATA_HOME/worklog/worklog.db`). Existing DBs upgrade transparently on the first command after install: `_run_migrations()` no-ops on existing tables (the bootstrap migration uses `CREATE TABLE IF NOT EXISTS`) and stamps `user_version=1`. Verified on a 311 KB production-DB backup: row counts unchanged, `PRAGMA integrity_check = ok`.
|
|
26
|
+
- Re-run `make install` (or `make setup`) once so the `~/bin/wl` wrapper points at the new `.venv/bin/wl`.
|
|
27
|
+
|
|
28
|
+
## [0.2.0] — 2026-05-31
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **`wl config`** command — prints resolved DB path, aliases path, XDG directories, environment, runtime info; read-only (does not create the DB just to inspect paths).
|
|
32
|
+
- **`wl --db PATH`** global flag — per-invocation DB override (handy for testing or running against multiple worklogs); takes precedence over `$WORKLOG_DB` and the XDG default.
|
|
33
|
+
- **XDG Base Directory spec** — DB defaults to `$XDG_DATA_HOME/worklog/worklog.db` (`~/.local/share/worklog/worklog.db`); aliases at `$XDG_CONFIG_HOME/worklog/aliases.ini`. Paths resolved lazily at call time so `pytest`'s `monkeypatch` works without reload tricks.
|
|
34
|
+
- CI matrix on GitHub Actions (`ubuntu-latest` + `macos-latest` × Python 3.11 / 3.12 / 3.13) + Codecov coverage upload + status badges in README.
|
|
35
|
+
- **Release workflow** — push a `v*` tag → verify version anchors agree, run the test suite, publish a GitHub Release with auto-generated `Commits since vX.Y.Z` notes.
|
|
36
|
+
- **`AGENTS.md`** — vendor-neutral operating guide for AI coding agents (Claude Code, Cursor, Aider). Codifies TDD (Red-Green-Refactor) and DRY (single-source helpers) as project rules.
|
|
37
|
+
- **`CONTRIBUTING.md`** — full dev setup, TDD + DRY conventions, local Makefile overrides, release process.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- Project namespace fully renamed from `wl` to `worklog` (paths and env vars): `~/.local/share/wl/wl.db` → `~/.local/share/worklog/worklog.db`; `~/.config/wl/aliases.ini` → `~/.config/worklog/aliases.ini`; `$WL_DB` / `$WL_COLOR` / `$WL_THEME` → `$WORKLOG_*`. The CLI command stays `wl`.
|
|
41
|
+
- **uv** replaces `venv` + `pip` + `requirements*.txt`. `pyproject.toml` (PEP 621) + `dependency-groups` (PEP 735) + `uv.lock` is the dependency single-source. `make setup` runs `uv sync` and installs the `~/bin/wl` wrapper.
|
|
42
|
+
- `_setup_aliases` test fixture now also clears `XDG_CONFIG_HOME` / `XDG_DATA_HOME` — CI runners preset these and otherwise leak into tests.
|
|
43
|
+
|
|
44
|
+
### Removed
|
|
45
|
+
- `requirements.txt` / `requirements-dev.txt` — replaced by `pyproject.toml` + `uv.lock`.
|
|
46
|
+
- Pre-XDG path fallbacks (`~/.worklog/wl.db`) — v0.1.0 was the only release that wrote there.
|
|
47
|
+
|
|
48
|
+
## [0.1.0] — 2026-05-31
|
|
49
|
+
|
|
50
|
+
Initial open-source release of `worklog-cli` (`wl`).
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- 37 subcommands covering create / log / done / state transitions / tree / projects / day / summary / changes / find / focus / show.
|
|
54
|
+
- Bulk loaders: `wl import` (JSON) and `wl apply` (wl-diff format, symmetric with the rendered tree output).
|
|
55
|
+
- Compound flags on `wl add` / `done` / `cancel` — create + log + close + timestamp + vault-link + schedule in one shot.
|
|
56
|
+
- Brief mode `-q` on every command (skip log body / timeline / detail; ~50–90% character savings for AI consumers).
|
|
57
|
+
- `rich`-based highlighting with `dark` / `light` / `mono` themes, auto-detected via `$COLORFGBG` / OSC 11 terminal background probe.
|
|
58
|
+
- Shell completion (fish / bash / zsh) generated at runtime by `wl print-completion`; loaded via init-load (the starship / direnv / zoxide pattern).
|
|
59
|
+
- Bilingual project documentation (English + Chinese for README / DESIGN / SKILL).
|
|
60
|
+
- MIT License.
|
|
61
|
+
|
|
62
|
+
[Unreleased]: https://github.com/xyb/worklog/compare/v0.3.0...HEAD
|
|
63
|
+
[0.3.0]: https://github.com/xyb/worklog/compare/v0.2.0...v0.3.0
|
|
64
|
+
|
|
65
|
+
[0.2.0]: https://github.com/xyb/worklog/compare/v0.1.0...v0.2.0
|
|
66
|
+
[0.1.0]: https://github.com/xyb/worklog/releases/tag/v0.1.0
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Contributing to worklog
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution! This guide covers the local setup, the development loop, and the two coding principles every change is held to: **TDD** and **DRY**.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- [uv](https://docs.astral.sh/uv/) — install via `brew install uv` or `pipx install uv`.
|
|
8
|
+
- Python ≥ 3.11. `uv` handles the interpreter — no system Python juggling.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```fish
|
|
13
|
+
git clone https://github.com/xyb/worklog.git ~/projects/worklog
|
|
14
|
+
cd ~/projects/worklog
|
|
15
|
+
make setup # uv sync (creates .venv) + installs ~/bin/wl wrapper
|
|
16
|
+
make test # run the full suite (parallel, 95% cov gate)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`make setup` does two things: `uv sync --all-groups` materializes `.venv/` from `pyproject.toml` + `uv.lock`, and `make install` writes `~/bin/wl` pointing into that venv. From there, every command (`uv run pytest …`, `make test`, etc.) uses the same locked deps.
|
|
20
|
+
|
|
21
|
+
## Day-to-day development loop
|
|
22
|
+
|
|
23
|
+
```fish
|
|
24
|
+
make test # parallel, cov, 95% gate — pre-commit baseline
|
|
25
|
+
make test-fast # parallel, no cov, no gate — quick iteration
|
|
26
|
+
make test-v # sequential, verbose, no cov — debug a single failure
|
|
27
|
+
|
|
28
|
+
# single test by node id
|
|
29
|
+
uv run pytest tests/test_wl.py::TestApply::test_apply_delete_cascades_subtree -v --no-cov
|
|
30
|
+
|
|
31
|
+
# coverage gate already lives in pytest.ini (--cov-fail-under=95); `make cov`
|
|
32
|
+
# just re-runs `make test` with term-missing output for inspection.
|
|
33
|
+
make cov
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run `wl` against a throwaway DB without touching `~/.local/share/worklog/worklog.db`:
|
|
37
|
+
|
|
38
|
+
```fish
|
|
39
|
+
wl --db /tmp/scratch.db init
|
|
40
|
+
wl --db /tmp/scratch.db add "experiment"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`wl config` shows which DB is currently resolved (helpful when an env / flag mix is confusing).
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
`DESIGN.md` is canonical for every shared convention — command style, state machine, marker symbols, time-window flags, render pipeline, schema, `import` / `apply` formats, theme keys, scheduled-time resolution, planned/unplanned derivation. Read the relevant section before adding a command or changing a format. If your change touches a convention, the same commit must update **DESIGN.md + `src/worklog/cli.py` + `tests/test_wl.py` + completion strings together** — drift between them is the failure mode this project guards against.
|
|
48
|
+
|
|
49
|
+
`AGENTS.md` is the operating guide for AI coding agents (Claude Code, Cursor, Aider). Skim it once even if you code by hand; it concentrates the hard-rules into one page.
|
|
50
|
+
|
|
51
|
+
## TDD: red → green → refactor
|
|
52
|
+
|
|
53
|
+
Every change touching behavior follows the [Red-Green-Refactor cycle](https://brennanbrown.github.io/notes/programming/python-tdd/):
|
|
54
|
+
|
|
55
|
+
1. **Red** — add a failing test in `tests/test_wl.py` first. Run it; confirm it fails for the reason you expect (not an import error). A test that never fails is not a test.
|
|
56
|
+
2. **Green** — write the minimum production code to make that test pass. Resist the urge to also fix neighboring code; one cycle, one concern.
|
|
57
|
+
3. **Refactor** — clean up. Extract helpers, remove duplication, rename. Tests still green at every step.
|
|
58
|
+
|
|
59
|
+
Bug fixes follow the same path: write a regression test that reproduces the bug first, then fix.
|
|
60
|
+
|
|
61
|
+
Tests don't ship without docstrings explaining what behavior they pin down (see existing `TestXDGPaths`, `TestConfig` for the style). One `Test<Command>` class per command. Cover happy path + boundaries (missing id, empty DB, illegal flag values) — the boundaries are where DESIGN drift surfaces first.
|
|
62
|
+
|
|
63
|
+
## DRY: there is exactly one of each
|
|
64
|
+
|
|
65
|
+
The codebase has a small set of single-source helpers; new code must reuse them, never re-implement. The full list lives in DESIGN §12, but the load-bearing ones:
|
|
66
|
+
|
|
67
|
+
- `_status_marker(status)` — status → `[ ]/[x]/[/]/...` marker. Never hard-code the marker elsewhere.
|
|
68
|
+
- `_node_line(con, n, ...)` — the **only** node renderer. Any place listing nodes reuses it and inherits highlighting + search emphasis for free. Hand-rolling a node line is rejected in review.
|
|
69
|
+
- `out()` / `_c(text, style)` — the single output pipe. Bare `print()` for renderable content is rejected; any fragment that might contain `[ ]` must go through `_c` so `rich.markup` doesn't eat it.
|
|
70
|
+
- `_resolve_window(args)` — time-window flag resolution. Commands must not parse `--since` / `--until` / `--week` / `--month` themselves.
|
|
71
|
+
- `_resolve_db_path(args)` — DB path resolution (`--db` flag > `$WORKLOG_DB` > XDG default). Mirrored by `__wl_db_path[_bash|_zsh]` helpers in the generated shell completion — keep them in sync.
|
|
72
|
+
|
|
73
|
+
Same rule for documentation: install / dev / release info lives here, not duplicated in README. Sections in README that need this content link to it instead of restating it.
|
|
74
|
+
|
|
75
|
+
## Local Makefile overrides
|
|
76
|
+
|
|
77
|
+
The Makefile loads any `local/*.mk` files at the end via `-include local/*.mk`. The `local/` directory is gitignored, so site-specific variables, private remotes, or extra targets go there without touching the shipped Makefile. Missing is fine — make won't complain.
|
|
78
|
+
|
|
79
|
+
Example `local/private.mk`:
|
|
80
|
+
|
|
81
|
+
```makefile
|
|
82
|
+
GITEA_REMOTE := git@your-private-host:user/worklog.git
|
|
83
|
+
|
|
84
|
+
push-gitea: ## push current branch to private remote
|
|
85
|
+
@$(GIT) -c commit.gpgsign=false push $(GITEA_REMOTE) $$($(GIT) branch --show-current)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
After saving, `make help` lists `push-gitea` alongside the built-in targets.
|
|
89
|
+
|
|
90
|
+
## Release process
|
|
91
|
+
|
|
92
|
+
Version has a **single** in-repo source: `version = "X.Y.Z"` in `pyproject.toml`. `__version__` in `src/worklog/cli.py` reads it via `importlib.metadata.version("worklog")` — never edit it by hand. The git tag `vX.Y.Z` must match the pyproject version; the release workflow enforces that.
|
|
93
|
+
|
|
94
|
+
The `Release` workflow (`.github/workflows/release.yml`) is triggered by pushing a `v*` tag. It verifies tag == pyproject version, re-runs the test suite, then calls `softprops/action-gh-release@v2` to publish a GitHub Release with auto-generated notes (commits since the previous tag).
|
|
95
|
+
|
|
96
|
+
To cut a release:
|
|
97
|
+
|
|
98
|
+
```fish
|
|
99
|
+
# 1. bump version in pyproject.toml only
|
|
100
|
+
# 2. refresh the lock entry so uv.lock pins the new version
|
|
101
|
+
uv lock --upgrade-package worklog
|
|
102
|
+
# 3. commit + tag + push
|
|
103
|
+
git commit -am "chore: bump version to X.Y.Z"
|
|
104
|
+
git tag vX.Y.Z
|
|
105
|
+
git push origin main
|
|
106
|
+
git push origin vX.Y.Z
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The workflow fails the release if any of the three version anchors disagree, so a typo can't ship.
|
|
110
|
+
|
|
111
|
+
## Pull requests
|
|
112
|
+
|
|
113
|
+
- Branch from `main`. PRs target `main`.
|
|
114
|
+
- Each PR moves the test count up: a refactor adds no tests but keeps coverage ≥95%; a feature/fix adds the test that exercises it.
|
|
115
|
+
- Run `make test` locally before pushing. CI runs the same parallel pytest + 95% gate across ubuntu + macos × Python 3.11/3.12/3.13.
|
|
116
|
+
- DESIGN.md / AGENTS.md drift is the most common review block — when in doubt, update them in the same PR.
|