Daily project hygiene: keep README / CHANGELOG / TODO in sync with reality. LLM-first, algorithmic fallback.
Project description
taskill
AI Cost Tracking
- 🤖 LLM usage: $0.9000 (6 commits)
- 👤 Human dev: ~$487 (4.9h @ $100/h, 30min dedup)
Generated on 2026-04-25 using openrouter/qwen/qwen3-coder-next
Daily project hygiene: keep
README.md/CHANGELOG.md/TODO.mdin sync with reality. LLM-first, algorithmic fallback. Works standalone, in CI, or via Ansible.
taskill is the small daemon you stop forgetting to run. Once a day (or whenever metrics drift), it reads your git log, your SUMD.md / SUMR.md, your coverage report, and updates the three documentation files everyone tells themselves they'll keep current and never do.
It uses an LLM where one is available — Windsurf MCP first (because you're probably already running it in JetBrains), OpenRouter second — and falls back to a deterministic Conventional Commits parser when no LLM is reachable. The fallback is always available and always runs offline.
taskill deliberately doesn't replace pyqual, llx, or prefact. It calls them as subprocesses when configured, picks up their reports, and stays out of the way otherwise.
Install
pip install taskill # core
pip install "taskill[mcp]" # with Windsurf MCP support
pip install "taskill[mcp,schedule]" # with built-in scheduler
Quickstart
cd your-project/
taskill init # writes taskill.yaml + .env.example
cp .env.example .env # add OPENROUTER_API_KEY
taskill status # preview without running
taskill run --dry-run # see what would change
taskill run # do it
What it does
Every run produces three (idempotent) edits:
CHANGELOG.md— appends new entries under## [Unreleased], grouped by Conventional Commit type (### Added,### Fixed,### Performance, etc.). Uses Keep a Changelog layout. Existing entries are deduplicated.TODO.md— moves completed items to a## Done (moved to CHANGELOG)section, and appendsTODO:/FIXME:markers found in new commit bodies under## Discovered.README.md— refreshes only the block between<!-- taskill:status:start -->and<!-- taskill:status:end -->markers (HEAD, coverage, failing tests, summary). Never touches the rest of the file.
Provider chain
The chain runs top-to-bottom. First provider that's available and succeeds wins.
| Order | Provider | Used when |
|---|---|---|
| 1 | windsurf_mcp |
mcp package installed and a Windsurf endpoint resolves |
| 2 | openrouter |
OPENROUTER_API_KEY is set |
| 3 | algorithmic |
always — pure git-log + Conventional Commits parser |
You can reorder, disable, or pass options via taskill.yaml:
providers:
- name: openrouter # skip windsurf, go straight to OpenRouter
enabled: true
- name: algorithmic
enabled: true
Triggers
taskill run is a no-op unless one of the configured thresholds is crossed. State lives in .taskill/state.json so cron, GitHub Actions, and Ansible all share the same delta logic.
triggers:
min_hours_since_last_run: 24
min_commits_since_last_run: 1
changed_files_threshold: 1
coverage_change_pct: 2.0 # absolute pp; null to disable
failed_tests_changed: true
watch_files: [SUMD.md, SUMR.md]
require_all: false # OR by default; set true for AND
taskill run --force ignores triggers entirely.
Running it
Cron / systemd timer
0 6 * * * cd /path/to/project && /usr/local/bin/taskill run >> ~/.taskill.log 2>&1
Bulk run (multiple projects)
For fleet-wide hygiene across many repos:
taskill bulk-run --root ~/github --max-depth 2
taskill bulk-run --root ~/github --max-depth 2 --dry-run # preview
taskill bulk-run --root ~/github --filter taskill --filter testql # filter repos
See /daily-docs-update workflow for complete documentation.
GitHub Actions
See examples/github-action.yml. Triggers on push to main, runs taskill run, opens a PR if files changed.
GitLab CI
See examples/gitlab-ci.yml. Same idea, with merge-request creation via the GitLab API.
Ansible
See examples/ansible-playbook.yml. Useful for fleet-wide hygiene across many self-hosted repos.
CLI
taskill init # generate taskill.yaml + .env.example
taskill status # show what would happen, no writes
taskill run # execute (respects triggers)
taskill run --force # ignore triggers
taskill run --dry-run # don't write files or state
taskill run --json # machine-readable output
taskill bulk-run # run across all git repos in a directory
taskill release X.Y.Z # promote [Unreleased] → versioned heading
taskill clean-todo # wipe TODO.md (after a release)
Bulk / fleet-wide runs
When you maintain many small repos (a personal "github" folder, a self-hosted
GitLab group, a monorepo of independent packages), taskill bulk-run runs the
same hygiene job across all of them with a single command:
# Scan ~/github (depth 2), use a shared taskill.yaml as the base config
taskill bulk-run --root ~/github --shared-config ~/github/taskill.yaml
# Same, but only run on repos whose name matches one of the filters
taskill bulk-run --root ~/github -f oqlos -f semcod
# Process at most 5 projects per run (useful for rate-limited LLMs)
taskill bulk-run --root ~/github --max-projects 5
# Preview without writing anything
taskill bulk-run --root ~/github --dry-run --force
# Machine-readable summary for CI / dashboards
taskill bulk-run --root ~/github --json
Config resolution per repo
Per-repo taskill.yaml (or .taskill.yaml) takes precedence over the shared
config, so you can have one default policy plus per-project overrides. Repos
without any local config inherit the shared one with their own project_root.
Resolution order for each discovered repo:
<repo>/taskill.yaml— local override (highest priority)<repo>/.taskill.yaml— alternative local override- Shared config from
--shared-config, rebased onto the repo - Built-in defaults
Discovery rules
bulk-run walks the directory tree starting at --root up to --max-depth
levels deep (default 2). A directory containing a .git entry is treated as a
repo, and bulk-run does not descend into nested repos. Hidden directories
and common noise (node_modules, __pycache__, .venv, dist, build,
target, .tox, .pytest_cache) are skipped automatically.
Daily-run wrapper
A shell wrapper (daily_update.sh) reads its defaults from .env:
# .env
GITHUB_ROOT=$HOME/github
TASKILL_MAX_DEPTH=2
TASKILL_MAX_PROJECTS=10 # cap per cron tick
TASKILL_DRY_RUN=false
TASKILL_FORCE=false
TASKILL_FILTER=oqlos,semcod # comma-separated
./daily_update.sh # uses .env
./daily_update.sh --dry-run # CLI override
./daily_update.sh --root /tmp/my-projects # different root
./daily_update.sh --filter taskill,testql # narrow scope
Extending taskill: providers and updaters as plugins
Both providers (LLM/algorithmic backends) and document updaters (CHANGELOG / TODO / README writers) are discovered through Python entry points, so a third-party package can register a new provider or updater without touching this codebase.
Custom provider
# my_pkg/pyproject.toml
[project.entry-points."taskill.providers"]
my_provider = "my_pkg.provider:MyProvider"
# my_pkg/provider.py
from taskill.providers.base import Provider, GeneratedDocs
class MyProvider(Provider):
name = "my_provider"
def is_available(self) -> bool:
return True
def generate(self, context) -> GeneratedDocs:
...
Then reference it from taskill.yaml like any built-in:
providers:
- name: my_provider
enabled: true
options: { ... }
- name: algorithmic
enabled: true
Custom updater
[project.entry-points."taskill.updaters"]
wiki = "my_pkg.wiki:WikiUpdater"
from taskill.updaters.base import DocumentUpdater, UpdateResult
class WikiUpdater(DocumentUpdater):
name = "wiki"
def apply(self, path, snapshot, docs) -> UpdateResult:
...
return UpdateResult(changed=True, path=path, updater_name=self.name)
Built-in updaters (changelog, todo, readme) are registered the same way,
so removing or replacing them is just a matter of the entry-point taking
precedence.
Configuration reference
See taskill.yaml at the repo root for the annotated default config.
Reusing existing tools
taskill doesn't try to absorb pyqual / llx / prefact. It calls them by subprocess when toggled in reuse: and feeds their JSON output to the LLM as extra context:
reuse:
pyqual: true # taskill will run `pyqual report --json`
llx: false # ...add llx context (planned for v0.2)
prefact: false # ...add prefact suggestions (planned for v0.2)
If a tool isn't on PATH, taskill skips it silently — no hard dependency.
How it relates to the wider stack
SUMD (description) ─┐
├─→ taskill ──→ README.md
git log ────────────┤ CHANGELOG.md
pyqual report ──────┤ TODO.md
SUMR (state) ───────┘
taskill reads, never generates code. That's what prefact / llx / pyqual are for.
License
Licensed under Apache-2.0.
Status
Last updated by taskill at 2026-04-25 09:23 UTC
| Metric | Value |
|---|---|
| HEAD | 4618c29 |
| Coverage | — |
| Failing tests | — |
| Commits in last cycle | 0 |
No changes were made to the project since the last taskill run.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file taskill-0.1.7.tar.gz.
File metadata
- Download URL: taskill-0.1.7.tar.gz
- Upload date:
- Size: 459.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1ec447d6dfd9989d58535b4dbc45307a8fc7fa5f3b969b8a47953f57a38b8bf
|
|
| MD5 |
d4cb27742b9cad20dcfc2930599e0fa1
|
|
| BLAKE2b-256 |
edd153bedd9412b12cfb825d6835948c3df3fefb098cddfb391102a53635f373
|
File details
Details for the file taskill-0.1.7-py3-none-any.whl.
File metadata
- Download URL: taskill-0.1.7-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c816134482a8942b2888f276001eda0ed81bb3c1dc8387f9418dadb07a4b6ef
|
|
| MD5 |
c70a6970409586002f85b1f85851109d
|
|
| BLAKE2b-256 |
54e9052e74798d77f69abe5fe9b42e687002bf3446bc775b9be5274395aae024
|