https://github.com/semcod/taskill
https://github.com/semcod/taskill
Last synced: 8 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/semcod/taskill
- Owner: semcod
- License: apache-2.0
- Created: 2026-04-25T09:03:56.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-13T15:10:40.000Z (27 days ago)
- Last Synced: 2026-05-13T17:18:09.621Z (26 days ago)
- Language: Python
- Size: 1.65 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
# taskill
## AI Cost Tracking
   
  
- 🤖 **LLM usage:** $1.9500 (13 commits)
- 👤 **Human dev:** ~$789 (7.9h @ $100/h, 30min dedup)
Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
---
> Daily project hygiene: keep `README.md` / `CHANGELOG.md` / `TODO.md` in 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
```bash
pip install taskill # core
pip install "taskill[mcp]" # with Windsurf MCP support
pip install "taskill[mcp,schedule]" # with built-in scheduler
```
## Quickstart
```bash
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:
1. **`CHANGELOG.md`** — appends new entries under `## [Unreleased]`, grouped by Conventional Commit type (`### Added`, `### Fixed`, `### Performance`, etc.). Uses [Keep a Changelog](https://keepachangelog.com/) layout. Existing entries are deduplicated.
2. **`TODO.md`** — moves completed items to a `## Done (moved to CHANGELOG)` section, and appends `TODO:` / `FIXME:` markers found in new commit bodies under `## Discovered`.
3. **`README.md`** — refreshes only the block between `
## Status
_Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 14:10 UTC_
| Metric | Value |
|---|---|
| HEAD | `11740ba` |
| Coverage | — |
| Failing tests | — |
| Commits in last cycle | 1 |
> Refactored the CLI documentation and interface, with corresponding updates to README and CHANGELOG. Minor packaging/version metadata files were touched alongside various source modules.
` 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`:
```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.
```yaml
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
```cron
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:
```bash
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:
```bash
# 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:
1. `/taskill.yaml` — local override (highest priority)
2. `/.taskill.yaml` — alternative local override
3. Shared config from `--shared-config`, rebased onto the repo
4. 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`:
```bash
# .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
```
```bash
./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
```toml
# my_pkg/pyproject.toml
[project.entry-points."taskill.providers"]
my_provider = "my_pkg.provider:MyProvider"
```
```python
# 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:
```yaml
providers:
- name: my_provider
enabled: true
options: { ... }
- name: algorithmic
enabled: true
```
### Custom updater
```toml
[project.entry-points."taskill.updaters"]
wiki = "my_pkg.wiki:WikiUpdater"
```
```python
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`](./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:
```yaml
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](https://github.com/oqlos/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.