weektag 0.0.1__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.
- weektag-0.0.1/.github/workflows/ci.yml +28 -0
- weektag-0.0.1/.github/workflows/release.yml +31 -0
- weektag-0.0.1/.gitignore +218 -0
- weektag-0.0.1/CLAUDE.md +63 -0
- weektag-0.0.1/LICENSE +21 -0
- weektag-0.0.1/PKG-INFO +130 -0
- weektag-0.0.1/README.ja.md +111 -0
- weektag-0.0.1/README.md +112 -0
- weektag-0.0.1/docs/adr/README.md +26 -0
- weektag-0.0.1/docs/adr/latest/0001-cli-first-positioning.md +18 -0
- weektag-0.0.1/docs/adr/latest/0002-recording-model.md +18 -0
- weektag-0.0.1/docs/adr/latest/0003-weekly-jsonl-storage.md +22 -0
- weektag-0.0.1/docs/adr/latest/0004-record-schema.md +24 -0
- weektag-0.0.1/docs/adr/latest/0005-v1-command-set.md +31 -0
- weektag-0.0.1/docs/adr/latest/0006-report-export-split.md +21 -0
- weektag-0.0.1/docs/adr/latest/0007-daily-export-preset.md +20 -0
- weektag-0.0.1/docs/adr/latest/0008-naming-weektag-tt.md +24 -0
- weektag-0.0.1/docs/adr/latest/0009-stack-typer-py311.md +22 -0
- weektag-0.0.1/docs/adr/latest/0010-repo-ci-publishing.md +20 -0
- weektag-0.0.1/pyproject.toml +45 -0
- weektag-0.0.1/src/weektag/__init__.py +0 -0
- weektag-0.0.1/src/weektag/cli.py +262 -0
- weektag-0.0.1/src/weektag/export.py +95 -0
- weektag-0.0.1/src/weektag/ops.py +203 -0
- weektag-0.0.1/src/weektag/report.py +46 -0
- weektag-0.0.1/src/weektag/storage.py +116 -0
- weektag-0.0.1/src/weektag/timeutil.py +117 -0
- weektag-0.0.1/src/weektag/ulid.py +33 -0
- weektag-0.0.1/tests/conftest.py +23 -0
- weektag-0.0.1/tests/test_cli.py +247 -0
- weektag-0.0.1/tests/test_export.py +133 -0
- weektag-0.0.1/tests/test_frozen_time.py +29 -0
- weektag-0.0.1/tests/test_ops.py +226 -0
- weektag-0.0.1/tests/test_report.py +49 -0
- weektag-0.0.1/tests/test_storage.py +121 -0
- weektag-0.0.1/tests/test_timeutil.py +115 -0
- weektag-0.0.1/tests/test_ulid.py +40 -0
- weektag-0.0.1/uv.lock +214 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install
|
|
22
|
+
run: uv sync
|
|
23
|
+
- name: Lint
|
|
24
|
+
run: |
|
|
25
|
+
uv run ruff check .
|
|
26
|
+
uv run ruff format --check .
|
|
27
|
+
- name: Test
|
|
28
|
+
run: uv run pytest -q
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
- name: Build
|
|
14
|
+
run: uv build
|
|
15
|
+
- uses: actions/upload-artifact@v4
|
|
16
|
+
with:
|
|
17
|
+
name: dist
|
|
18
|
+
path: dist/
|
|
19
|
+
|
|
20
|
+
publish:
|
|
21
|
+
needs: build
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
environment: pypi
|
|
24
|
+
permissions:
|
|
25
|
+
id-token: write # PyPI Trusted Publishing (OIDC), no API tokens (ADR 0010)
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/download-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: dist
|
|
30
|
+
path: dist/
|
|
31
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
weektag-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
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
|
weektag-0.0.1/CLAUDE.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
**weektag** — a CLI-first, tag-based time tracker (command name: `tt`) distributed on PyPI as `weektag`. Its differentiator is agent-readability: the data files must be directly readable by humans, `jq`, and coding agents with no preprocessing.
|
|
8
|
+
|
|
9
|
+
Implementation lives in `src/weektag/` (`cli.py` typer commands → `ops.py` operations → `storage.py` JSONL I/O, plus `timeutil.py`, `ulid.py`, `report.py`, `export.py`). Tests in `tests/` pin the current time by monkeypatching `timeutil.now_local` (see `conftest.py` fixtures `data_dir` / `freeze_now`) so results don't depend on the machine timezone.
|
|
10
|
+
|
|
11
|
+
## Design source of truth: docs/adr/
|
|
12
|
+
|
|
13
|
+
`docs/adr/latest/` (Japanese) is the **single canonical source** for all design decisions. Read the relevant ADRs before implementing or reviewing anything, and resolve design questions against them.
|
|
14
|
+
|
|
15
|
+
ADR rules: new decisions get a new sequential number in `latest/`; numbers are never reused or renumbered. To change a decision, move the old ADR to `archive/{NNNN}/` and write a new one under a new number. Each ADR has four sections: ステータス / コンテキスト / 決定 / 結果.
|
|
16
|
+
|
|
17
|
+
## Architecture (decided in ADRs 0001–0010)
|
|
18
|
+
|
|
19
|
+
**Recording model (0002):** start/stop interval records only. At most **one running task** — `start` while another runs auto-stops it. A running task is simply a record with no `stop` key; there is no separate state file. `add`/`edit`/`rm`/`cancel` provide after-the-fact correction.
|
|
20
|
+
|
|
21
|
+
**Storage (0003):** weekly JSONL files are the **only source of truth** — no index, no DB, no hidden state. Location: `~/.local/share/weektag/events/` (XDG), overridable via `WEEKTAG_DATA_DIR`. Files are named by ISO 8601 week (`2026-W27.jsonl`), weeks start **Monday (unchangeable)**. A record belongs to the week of its **start time**; week-spanning records are not split. Writes go through temp file + atomic `os.replace`. Hand-editing by users/agents is officially supported. The running task is found by scanning recent week files for a record lacking `stop`.
|
|
22
|
+
|
|
23
|
+
**Record schema (0004):** one JSON object per line:
|
|
24
|
+
```json
|
|
25
|
+
{"id":"01JZK3AB","start":"2026-07-06T09:00:00+09:00","stop":"2026-07-06T10:30:00+09:00","tags":["writing","client-a"],"note":"ブログ下書き"}
|
|
26
|
+
```
|
|
27
|
+
- Times are **local time + UTC offset** (ISO 8601) — never normalize to UTC. Week membership is judged by local date.
|
|
28
|
+
- `id` is a self-implemented sortable **mini-ULID** (timestamp + randomness); commands accept prefix matches.
|
|
29
|
+
- Tags are positional args (`tt start writing client-a`); a quoted `'#tag'` is accepted with `#` stripped. Note goes via `-m`.
|
|
30
|
+
|
|
31
|
+
**v1 command set (0005):** `start`, `stop`, `status`, `resume`, `add`, `edit`, `rm`, `cancel`, `log`, `report`, `export`. Explicitly **out of scope for v1**: goals/targets, focus timer, timeline UI, plan.
|
|
32
|
+
|
|
33
|
+
**report vs export (0006):** `report` is a per-tag summary table for the terminal (multi-tag events count their full duration under *each* tag, so the tag column can exceed the total row, which is computed from real event time). `export` emits row-level data, default **TSV**, columns `date/start/stop/hours/tags/note`. Hours are **decimal** (1.50), unrounded unless `--round`. All output is **plain text — no rich formatting anywhere**. No clipboard integration (pipe to clip.exe/pbcopy instead).
|
|
34
|
+
|
|
35
|
+
**Daily preset (0007):** `tt export --daily [--date 7/6]` emits exactly 3 TSV columns (summary / AM hours / PM hours), **no header by default**, aggregated per (tag-set + note) per day, split mechanically at noon (`--noon` to change), pro-rated across the boundary. Summary = note if present, else space-joined tags.
|
|
36
|
+
|
|
37
|
+
**Naming (0008):** PyPI/import/repo name `weektag`, console command `tt` via `[project.scripts] tt = "weektag.cli:main"`.
|
|
38
|
+
|
|
39
|
+
## Stack & constraints (0009)
|
|
40
|
+
|
|
41
|
+
- **typer** is the only runtime dependency (bundled rich/shellingham included, but rich decoration is unused).
|
|
42
|
+
- Python **3.11+**.
|
|
43
|
+
- Shell completion is a v1 requirement, including **dynamic tag completion** (candidates gathered from recent week files via typer autocompletion callbacks).
|
|
44
|
+
- Current time: `datetime.now().astimezone()` (works on Windows without tzdata).
|
|
45
|
+
- No config file in v1 — env var + flags only.
|
|
46
|
+
|
|
47
|
+
## Development (0010)
|
|
48
|
+
|
|
49
|
+
- **src layout** (`src/weektag/`), **hatchling** backend, everything in `pyproject.toml`.
|
|
50
|
+
- Tooling: **uv** (env/lock), **ruff** (lint + format), **pytest + freezegun** (deterministic time-based tests). Dev deps separated from runtime deps.
|
|
51
|
+
- This checkout lives under OneDrive, which breaks uv's default hardlinking — run uv with `UV_LINK_MODE=copy` (e.g. `UV_LINK_MODE=copy uv sync`).
|
|
52
|
+
- Commands:
|
|
53
|
+
- `uv sync` — set up environment
|
|
54
|
+
- `uv run pytest` — run tests; single test: `uv run pytest tests/test_x.py::test_name`
|
|
55
|
+
- `uv run ruff check .` / `uv run ruff format .`
|
|
56
|
+
- `uv run tt ...` — run the CLI locally
|
|
57
|
+
- CI: GitHub Actions matrix ubuntu/macos/windows × Python 3.11–3.13 on every push/PR.
|
|
58
|
+
- Publishing: PyPI **Trusted Publishing** (OIDC) triggered by pushing a git tag. No API tokens.
|
|
59
|
+
- License MIT. README in English with a `README.ja.md` alongside.
|
|
60
|
+
|
|
61
|
+
## Testing focus
|
|
62
|
+
|
|
63
|
+
Bugs are expected to concentrate in time handling — write freezegun-pinned tests for: ISO week boundaries, the W53 ISO-week-year mismatch (e.g. 2027-01-01 belongs to `2026-W53.jsonl`), noon splitting/pro-rating, timezone offsets, and midnight-spanning events. Windows behavior (paths, `os.replace` atomicity, console output) is verified in CI, not locally.
|
weektag-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Takehiro M.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
weektag-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weektag
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Tag-based CLI time tracker with agent-readable weekly JSONL storage
|
|
5
|
+
Project-URL: Repository, https://github.com/takeknock/weektag
|
|
6
|
+
Author: takeknock
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cli,jsonl,tags,time-tracking
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: typer>=0.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# weektag
|
|
20
|
+
|
|
21
|
+
Tag-based CLI time tracker with agent-readable weekly JSONL storage.
|
|
22
|
+
|
|
23
|
+
[日本語版 README](README.ja.md)
|
|
24
|
+
|
|
25
|
+
`weektag` records what you work on as start/stop intervals with flat tags, stores them
|
|
26
|
+
as plain JSONL files split by ISO week, and turns them into weekly reports or
|
|
27
|
+
TSV/CSV you can paste straight into Excel. The files are the whole database:
|
|
28
|
+
`cat`, `grep`, `jq`, and coding agents (Claude Code etc.) can read them with no
|
|
29
|
+
preprocessing — and hand-editing them is officially supported.
|
|
30
|
+
|
|
31
|
+
```console
|
|
32
|
+
$ tt start writing client-a -m "blog draft"
|
|
33
|
+
started writing client-a "blog draft" at 09:00 [KZ3M2A7Q]
|
|
34
|
+
|
|
35
|
+
$ tt status
|
|
36
|
+
running: writing client-a "blog draft" (started 09:00, 1.50 h elapsed) [KZ3M2A7Q]
|
|
37
|
+
|
|
38
|
+
$ tt stop
|
|
39
|
+
stopped writing client-a "blog draft" (1.50 h)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```console
|
|
45
|
+
pipx install weektag # or: uv tool install weektag
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The installed command is `tt` (the package name `tt` was taken on PyPI).
|
|
49
|
+
Requires Python 3.11+.
|
|
50
|
+
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
tt start <tags...> [-m NOTE] [--at 9:00] # start (auto-stops a running task)
|
|
55
|
+
tt stop [--at 10:30] # stop
|
|
56
|
+
tt status # running task + elapsed time
|
|
57
|
+
tt resume # restart previous task (same tags/note)
|
|
58
|
+
tt add 9:00-10:30 <tags...> [-m NOTE] # add a past interval
|
|
59
|
+
tt edit <id-prefix> [--start] [--stop] [--tags] [-m]
|
|
60
|
+
tt rm <id-prefix> # delete a record
|
|
61
|
+
tt cancel # discard the running task
|
|
62
|
+
tt log [--week 2026-W27] # raw log with ids (entry point for edit)
|
|
63
|
+
tt report [--week 2026-W27 | --last] # per-tag weekly summary
|
|
64
|
+
tt export [--format csv] [-o FILE] [--no-header]
|
|
65
|
+
tt export --daily [--date 7/6] [--noon 13:00] [--round 0.25] [--header]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Only one task runs at a time: `tt start` while another task is running stops it
|
|
69
|
+
first. Forgot to start or stop? `add` / `edit` / `rm` fix the record afterwards.
|
|
70
|
+
|
|
71
|
+
Shell completion (bash / zsh / fish), including dynamic tag completion from your
|
|
72
|
+
recent records:
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
tt --install-completion
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Data format
|
|
79
|
+
|
|
80
|
+
One JSON object per line, one file per ISO week (Monday start), in
|
|
81
|
+
`~/.local/share/weektag/events/` (override with `WEEKTAG_DATA_DIR`;
|
|
82
|
+
`XDG_DATA_HOME` is honored):
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{"id":"KZ3M2A7Q","start":"2026-07-06T09:00:00+09:00","stop":"2026-07-06T10:30:00+09:00","tags":["writing","client-a"],"note":"blog draft"}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- A running task is simply a record without a `stop` key.
|
|
89
|
+
- Times are local time with UTC offset; week membership follows the local date
|
|
90
|
+
of `start`. Week-spanning records are not split.
|
|
91
|
+
- The week files are the only source of truth — no hidden state, no index, no
|
|
92
|
+
database. Edit them by hand or with an agent whenever you like.
|
|
93
|
+
|
|
94
|
+
### Note on ISO week years
|
|
95
|
+
|
|
96
|
+
Files use ISO 8601 week numbering, which can differ from the calendar year at
|
|
97
|
+
year boundaries: a record on 2027-01-01 lives in `2026-W53.jsonl`. Commands
|
|
98
|
+
resolve this correctly; it only matters when you browse the files by hand.
|
|
99
|
+
|
|
100
|
+
## Excel workflow
|
|
101
|
+
|
|
102
|
+
`tt export` prints TSV rows (`date start stop hours tags note`) with decimal
|
|
103
|
+
hours (`1.50`), so a paste into Excel splits into columns and `SUM` just works.
|
|
104
|
+
|
|
105
|
+
`tt export --daily` is a preset for daily-report sheets: exactly three columns
|
|
106
|
+
(summary / AM hours / PM hours), aggregated per (tag set + note), split at noon
|
|
107
|
+
(`--noon` to change), no header by default so you can append to an existing
|
|
108
|
+
table day after day. There is no clipboard integration — pipe instead:
|
|
109
|
+
|
|
110
|
+
```console
|
|
111
|
+
tt export --daily | clip # Windows
|
|
112
|
+
tt export --daily | pbcopy # macOS
|
|
113
|
+
tt export --daily | xclip -sel c
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Command name collision
|
|
117
|
+
|
|
118
|
+
The `tt-time-tracker` package also installs a `tt` command. pipx / uv isolate
|
|
119
|
+
the environments, so only the PATH entry can collide. If you use both, rename
|
|
120
|
+
one with a shell alias, e.g. `alias wt=tt`.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```console
|
|
125
|
+
uv sync
|
|
126
|
+
uv run pytest
|
|
127
|
+
uv run ruff check . && uv run ruff format --check .
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
MIT license.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# weektag
|
|
2
|
+
|
|
3
|
+
タグ式のCLIタイムトラッカー。記録はエージェント可読な週別JSONLに保存。
|
|
4
|
+
|
|
5
|
+
[English README](README.md)
|
|
6
|
+
|
|
7
|
+
`weektag` は作業を start/stop の区間としてフラットなタグ付きで記録し、ISO週ごとの
|
|
8
|
+
プレーンな JSONL ファイルに保存します。週次レポートや、Excel にそのまま貼れる
|
|
9
|
+
TSV/CSV に変換できます。ファイルがデータベースのすべてであり、`cat` / `grep` /
|
|
10
|
+
`jq`、そして Claude Code などのコーディングエージェントが前処理なしで読めます。
|
|
11
|
+
手編集も正式サポートです。
|
|
12
|
+
|
|
13
|
+
```console
|
|
14
|
+
$ tt start writing client-a -m "ブログ下書き"
|
|
15
|
+
started writing client-a "ブログ下書き" at 09:00 [KZ3M2A7Q]
|
|
16
|
+
|
|
17
|
+
$ tt status
|
|
18
|
+
running: writing client-a "ブログ下書き" (started 09:00, 1.50 h elapsed) [KZ3M2A7Q]
|
|
19
|
+
|
|
20
|
+
$ tt stop
|
|
21
|
+
stopped writing client-a "ブログ下書き" (1.50 h)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## インストール
|
|
25
|
+
|
|
26
|
+
```console
|
|
27
|
+
pipx install weektag # または: uv tool install weektag
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
インストールされるコマンドは `tt` です(PyPI の `tt` は取得済みのため配布名は
|
|
31
|
+
`weektag`)。Python 3.11 以上が必要です。
|
|
32
|
+
|
|
33
|
+
## コマンド
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
tt start <tags...> [-m メモ] [--at 9:00] # 開始(実行中があれば自動stop)
|
|
37
|
+
tt stop [--at 10:30] # 停止
|
|
38
|
+
tt status # 実行中タスクと経過時間
|
|
39
|
+
tt resume # 直前タスクを同タグ・同メモで再開
|
|
40
|
+
tt add 9:00-10:30 <tags...> [-m メモ] # 後追いの区間追加
|
|
41
|
+
tt edit <id前方一致> [--start] [--stop] [--tags] [-m]
|
|
42
|
+
tt rm <id前方一致> # 削除
|
|
43
|
+
tt cancel # 実行中を記録せず破棄
|
|
44
|
+
tt log [--week 2026-W27] # id付き生ログ(editの入口)
|
|
45
|
+
tt report [--week 2026-W27 | --last] # タグ別の週次集計
|
|
46
|
+
tt export [--format csv] [-o FILE] [--no-header]
|
|
47
|
+
tt export --daily [--date 7/6] [--noon 13:00] [--round 0.25] [--header]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
同時に実行できるタスクは常に1本だけです。実行中に `tt start` すると前のタスクを
|
|
51
|
+
止めてから開始します。打ち忘れ・止め忘れは `add` / `edit` / `rm` で後から直せます。
|
|
52
|
+
|
|
53
|
+
シェル補完(bash / zsh / fish)。直近の記録からのタグ動的補完つき:
|
|
54
|
+
|
|
55
|
+
```console
|
|
56
|
+
tt --install-completion
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## データ形式
|
|
60
|
+
|
|
61
|
+
1行=1 JSONオブジェクト、1ファイル=1 ISO週(月曜始まり)。保存先は
|
|
62
|
+
`~/.local/share/weektag/events/`(`WEEKTAG_DATA_DIR` で変更可、
|
|
63
|
+
`XDG_DATA_HOME` にも対応):
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"id":"KZ3M2A7Q","start":"2026-07-06T09:00:00+09:00","stop":"2026-07-06T10:30:00+09:00","tags":["writing","client-a"],"note":"ブログ下書き"}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- 実行中タスクは `stop` キーを持たないレコードです。
|
|
70
|
+
- 時刻はローカル時刻+UTCオフセット。週の帰属は `start` のローカル日付で決まり、
|
|
71
|
+
週を跨ぐレコードも分割しません。
|
|
72
|
+
- 週ファイルが唯一の真実です。隠れた状態ファイル・インデックス・DBはありません。
|
|
73
|
+
手やエージェントでいつでも編集できます。
|
|
74
|
+
|
|
75
|
+
### ISO週年についての注意
|
|
76
|
+
|
|
77
|
+
ファイル名は ISO 8601 週番号なので、年末年始で暦年とズレることがあります。
|
|
78
|
+
例:2027-01-01 のレコードは `2026-W53.jsonl` に入ります。コマンドは正しく
|
|
79
|
+
解決するので、手でファイルを探すときだけ気にしてください。
|
|
80
|
+
|
|
81
|
+
## Excel との連携
|
|
82
|
+
|
|
83
|
+
`tt export` は十進時間(`1.50`)の TSV(`date start stop hours tags note`)を
|
|
84
|
+
出力するので、Excel に貼ると列が自動で分かれ、SUM がそのまま効きます。
|
|
85
|
+
|
|
86
|
+
`tt export --daily` は日報転記用プリセットです:3列だけ(概要 / 午前実績 /
|
|
87
|
+
午後実績)、その日の「タグ集合+メモ」ごとに集約、正午で機械分割(`--noon` で
|
|
88
|
+
変更可)、既存の表に日々貼るためヘッダーは既定で出しません。予定列は出力しない
|
|
89
|
+
ので、右端の予定列を上書きしません。クリップボード連携は内蔵せず、パイプで:
|
|
90
|
+
|
|
91
|
+
```console
|
|
92
|
+
tt export --daily | clip # Windows
|
|
93
|
+
tt export --daily | pbcopy # macOS
|
|
94
|
+
tt export --daily | xclip -sel c
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## コマンド名の衝突
|
|
98
|
+
|
|
99
|
+
`tt-time-tracker` パッケージも `tt` コマンドをインストールします。pipx / uv では
|
|
100
|
+
環境自体は隔離され、PATH 上でのみ衝突します。両方使う場合はシェルエイリアスで
|
|
101
|
+
どちらかを改名してください(例:`alias wt=tt`)。
|
|
102
|
+
|
|
103
|
+
## 開発
|
|
104
|
+
|
|
105
|
+
```console
|
|
106
|
+
uv sync
|
|
107
|
+
uv run pytest
|
|
108
|
+
uv run ruff check . && uv run ruff format --check .
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
MIT ライセンス。
|