taskill 0.1.2__tar.gz → 0.1.3__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.
Files changed (79) hide show
  1. {taskill-0.1.2 → taskill-0.1.3}/.gitignore +2 -0
  2. taskill-0.1.3/.windsurf/workflows/daily-docs-update.md +158 -0
  3. {taskill-0.1.2 → taskill-0.1.3}/CHANGELOG.md +58 -0
  4. {taskill-0.1.2 → taskill-0.1.3}/PKG-INFO +174 -6
  5. {taskill-0.1.2 → taskill-0.1.3}/README.md +173 -5
  6. {taskill-0.1.2 → taskill-0.1.3}/ROADMAP.md +18 -10
  7. {taskill-0.1.2 → taskill-0.1.3}/SUMD.md +120 -57
  8. {taskill-0.1.2 → taskill-0.1.3}/SUMR.md +198 -119
  9. {taskill-0.1.2 → taskill-0.1.3}/TODO.md +27 -15
  10. taskill-0.1.3/VERSION +1 -0
  11. {taskill-0.1.2 → taskill-0.1.3}/app.doql.less +5 -1
  12. taskill-0.1.3/daily_update.sh +93 -0
  13. {taskill-0.1.2 → taskill-0.1.3}/planfile.yaml +65 -0
  14. {taskill-0.1.2 → taskill-0.1.3}/project/README.md +3 -3
  15. taskill-0.1.3/project/analysis.toon.yaml +85 -0
  16. {taskill-0.1.2 → taskill-0.1.3}/project/calls.mmd +58 -39
  17. taskill-0.1.3/project/calls.png +0 -0
  18. {taskill-0.1.2 → taskill-0.1.3}/project/calls.toon.yaml +57 -38
  19. {taskill-0.1.2 → taskill-0.1.3}/project/calls.yaml +397 -190
  20. taskill-0.1.3/project/compact_flow.mmd +4 -0
  21. taskill-0.1.3/project/compact_flow.png +0 -0
  22. {taskill-0.1.2 → taskill-0.1.3}/project/context.md +190 -147
  23. {taskill-0.1.2 → taskill-0.1.3}/project/duplication.toon.yaml +6 -6
  24. {taskill-0.1.2 → taskill-0.1.3}/project/evolution.toon.yaml +19 -7
  25. taskill-0.1.3/project/flow.mmd +25 -0
  26. taskill-0.1.3/project/flow.png +0 -0
  27. taskill-0.1.3/project/index.html +623 -0
  28. {taskill-0.1.2 → taskill-0.1.3}/project/map.toon.yaml +43 -9
  29. {taskill-0.1.2 → taskill-0.1.3}/project/mermaid.export +122 -53
  30. taskill-0.1.3/project/project.toon.yaml +56 -0
  31. {taskill-0.1.2 → taskill-0.1.3}/project/prompt.txt +4 -6
  32. {taskill-0.1.2 → taskill-0.1.3}/project.sh +1 -1
  33. {taskill-0.1.2 → taskill-0.1.3}/pyproject.toml +1 -1
  34. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/__init__.py +12 -2
  35. taskill-0.1.3/src/taskill/bulk.py +249 -0
  36. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/cli.py +94 -0
  37. taskill-0.1.3/tests/test_bulk.py +279 -0
  38. taskill-0.1.3/tree.txt +80 -0
  39. taskill-0.1.2/VERSION +0 -1
  40. taskill-0.1.2/project/analysis.toon.yaml +0 -60
  41. taskill-0.1.2/project/calls.png +0 -0
  42. taskill-0.1.2/project/compact_flow.mmd +0 -2
  43. taskill-0.1.2/project/compact_flow.png +0 -0
  44. taskill-0.1.2/project/flow.mmd +0 -25
  45. taskill-0.1.2/project/flow.png +0 -0
  46. taskill-0.1.2/project/index.html +0 -623
  47. taskill-0.1.2/project/project.toon.yaml +0 -53
  48. {taskill-0.1.2 → taskill-0.1.3}/LICENSE +0 -0
  49. {taskill-0.1.2 → taskill-0.1.3}/examples/ansible-playbook.yml +0 -0
  50. {taskill-0.1.2 → taskill-0.1.3}/examples/github-action.yml +0 -0
  51. {taskill-0.1.2 → taskill-0.1.3}/examples/gitlab-ci.yml +0 -0
  52. {taskill-0.1.2 → taskill-0.1.3}/examples/systemd-timer/taskill.service +0 -0
  53. {taskill-0.1.2 → taskill-0.1.3}/examples/systemd-timer/taskill.timer +0 -0
  54. {taskill-0.1.2 → taskill-0.1.3}/goal.yaml +0 -0
  55. {taskill-0.1.2 → taskill-0.1.3}/prefact.yaml +0 -0
  56. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/config.py +0 -0
  57. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/core.py +0 -0
  58. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/git_state.py +0 -0
  59. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/providers/__init__.py +0 -0
  60. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/providers/algorithmic.py +0 -0
  61. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/providers/base.py +0 -0
  62. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/providers/openrouter.py +0 -0
  63. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/providers/windsurf_mcp.py +0 -0
  64. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/state.py +0 -0
  65. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/triggers.py +0 -0
  66. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/updaters/__init__.py +0 -0
  67. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/updaters/base.py +0 -0
  68. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/updaters/changelog.py +0 -0
  69. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/updaters/readme.py +0 -0
  70. {taskill-0.1.2 → taskill-0.1.3}/src/taskill/updaters/todo.py +0 -0
  71. {taskill-0.1.2 → taskill-0.1.3}/taskill.yaml +0 -0
  72. {taskill-0.1.2 → taskill-0.1.3}/testql-scenarios/generated-cli-tests.testql.toon.yaml +0 -0
  73. {taskill-0.1.2 → taskill-0.1.3}/tests/__init__.py +0 -0
  74. {taskill-0.1.2 → taskill-0.1.3}/tests/test_algorithmic.py +0 -0
  75. {taskill-0.1.2 → taskill-0.1.3}/tests/test_config.py +0 -0
  76. {taskill-0.1.2 → taskill-0.1.3}/tests/test_providers.py +0 -0
  77. {taskill-0.1.2 → taskill-0.1.3}/tests/test_triggers.py +0 -0
  78. {taskill-0.1.2 → taskill-0.1.3}/tests/test_updaters.py +0 -0
  79. {taskill-0.1.2 → taskill-0.1.3}/tree.sh +0 -0
@@ -2,6 +2,8 @@
2
2
  examples/.env.example
3
3
  vscode-extension/.env.example
4
4
  .idea
5
+ .pyqual
6
+ .taskill
5
7
  litellm_config.yaml
6
8
  litellm-config.yaml
7
9
  .code2llm_cache
@@ -0,0 +1,158 @@
1
+ ---
2
+ description: Daily documentation update workflow across multiple projects
3
+ ---
4
+
5
+ # Daily Documentation Update Workflow
6
+
7
+ This workflow automates the routine task of keeping README.md, CHANGELOG.md, and TODO.md in sync across multiple projects.
8
+
9
+ ## Prerequisites
10
+
11
+ 1. Install taskill: `pip install taskill[mcp,schedule]`
12
+ 2. Set up OPENROUTER_API_KEY in .env files (or use Windsurf MCP)
13
+ 3. Ensure each project has a taskill.yaml config
14
+
15
+ ## Single Project
16
+
17
+ ```bash
18
+ cd /path/to/project
19
+ taskill status # Preview what would change
20
+ taskill run # Execute the update
21
+ taskill run --dry-run # Preview without writing
22
+ ```
23
+
24
+ ## Multiple Projects (Bulk Run)
25
+
26
+ ### Option 1: Using CLI command
27
+
28
+ ```bash
29
+ # Run taskill on all repos under ~/github
30
+ taskill bulk-run --root ~/github --max-depth 2
31
+
32
+ # Dry-run to preview
33
+ taskill bulk-run --root ~/github --max-depth 2 --dry-run
34
+
35
+ # Force run (ignore triggers)
36
+ taskill bulk-run --root ~/github --max-depth 2 --force
37
+
38
+ # Filter specific repos (repeatable)
39
+ taskill bulk-run --root ~/github --filter taskill --filter testql
40
+
41
+ # Limit number of projects per run (useful for rate-limited LLMs)
42
+ taskill bulk-run --root ~/github --max-projects 5
43
+
44
+ # Use a shared config + JSON output
45
+ taskill bulk-run --root ~/github \
46
+ --shared-config ~/github/taskill.yaml \
47
+ --max-projects 10 \
48
+ --json
49
+ ```
50
+
51
+ ### Option 2: Using the shell wrapper
52
+
53
+ `daily_update.sh` wraps the CLI and reads its defaults from `.env`:
54
+
55
+ ```bash
56
+ # .env in the project root
57
+ GITHUB_ROOT=$HOME/github
58
+ TASKILL_MAX_DEPTH=2
59
+ TASKILL_MAX_PROJECTS=10
60
+ TASKILL_DRY_RUN=false
61
+ TASKILL_FORCE=false
62
+ TASKILL_FILTER=oqlos,semcod # comma-separated
63
+ ```
64
+
65
+ ```bash
66
+ ./daily_update.sh # uses .env defaults
67
+ ./daily_update.sh --dry-run # preview only
68
+ ./daily_update.sh --force # ignore triggers
69
+ ./daily_update.sh --filter taskill,testql
70
+ ./daily_update.sh --root /tmp/projects # override target directory
71
+ ```
72
+
73
+ ### Option 3: Using Python script
74
+
75
+ ```python
76
+ from pathlib import Path
77
+ from taskill.bulk import bulk_run
78
+
79
+ result = bulk_run(
80
+ root=Path("/home/tom/github"),
81
+ shared_config=Path("/home/tom/github/taskill.yaml"), # optional
82
+ max_depth=2,
83
+ max_projects=10, # 0 = unlimited
84
+ force=False,
85
+ dry_run=False,
86
+ repo_filter=["taskill", "testql"], # optional substring match
87
+ )
88
+
89
+ print(result.summary())
90
+ # 3/5 ran, 2 changed, 1 skipped, 0 errors
91
+
92
+ for repo, repo_result in result.per_repo.items():
93
+ print(repo.name, "ran" if repo_result.ran else "skipped",
94
+ repo_result.files_changed)
95
+ ```
96
+
97
+ ## What taskill does
98
+
99
+ 1. **CHANGELOG.md**: Appends new entries under [Unreleased] based on git commits
100
+ 2. **TODO.md**: Moves completed items to "Done (moved to CHANGELOG)" section
101
+ 3. **README.md**: Updates status block between markers (HEAD, coverage, failing tests)
102
+ 4. **SUMD.md/SUMR.md**: Reads these files as context for the LLM provider
103
+
104
+ ## Triggers
105
+
106
+ By default, taskill only runs when:
107
+ - At least 24 hours since last run
108
+ - At least 1 new commit
109
+ - Files changed threshold met
110
+ - SUMD.md or SUMR.md modified
111
+
112
+ Use `--force` to override triggers.
113
+
114
+ ## Automation
115
+
116
+ ### Cron job (daily at 6 AM)
117
+
118
+ ```cron
119
+ 0 6 * * * cd /home/tom/github && /usr/local/bin/taskill bulk-run --root . --max-depth 2 >> ~/.taskill.log 2>&1
120
+ ```
121
+
122
+ ### Systemd timer
123
+
124
+ See `examples/systemd-timer/taskill.service` and `taskill.timer`.
125
+
126
+ ## Shared Config
127
+
128
+ Pass a shared `taskill.yaml` via `--shared-config` to provide a default config
129
+ for all discovered repos:
130
+
131
+ ```bash
132
+ # ~/github/taskill.yaml (the shared base)
133
+ triggers:
134
+ min_hours_since_last_run: 24
135
+ min_commits_since_last_run: 1
136
+ providers:
137
+ - name: openrouter
138
+ enabled: true
139
+ - name: algorithmic
140
+ enabled: true
141
+ ```
142
+
143
+ Resolution order per repo:
144
+
145
+ 1. `<repo>/taskill.yaml` (highest priority)
146
+ 2. `<repo>/.taskill.yaml`
147
+ 3. `--shared-config` rebased onto the repo's path
148
+ 4. Built-in defaults
149
+
150
+ Each repo can override only what it needs by dropping a partial `taskill.yaml`
151
+ into its own root.
152
+
153
+ ## Troubleshooting
154
+
155
+ - Use `--verbose` flag for debug output
156
+ - Check `.taskill/state.json` for last run state
157
+ - Use `--dry-run` to preview changes
158
+ - Check provider chain in taskill.yaml (windsurf_mcp → openrouter → algorithmic)
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [0.1.10] - 2026-04-25
9
9
 
10
+ ### Fixed
11
+ - Fix string-concat issues (ticket-b239ce82)
12
+ - Fix unused-imports issues (ticket-37e0cb27)
13
+ - Fix llm-generated-code issues (ticket-1c7d5f6a)
14
+ - Fix smart-return-type issues (ticket-a7a0a53e)
15
+ - Fix unused-imports issues (ticket-8dae20d0)
16
+
17
+ ## [0.1.10] - 2026-04-25
18
+
10
19
  ### Fixed
11
20
  - Fix unused-imports issues (ticket-db5e9dd3)
12
21
  - Fix magic-numbers issues (ticket-dba8c3c5)
@@ -52,6 +61,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52
61
 
53
62
  ## [Unreleased]
54
63
 
64
+ ### Added
65
+ - `taskill bulk-run` command for fleet-wide hygiene runs across multiple git repos (ROADMAP v0.5)
66
+ - `taskill.bulk` module with `find_repos`, `resolve_repo_config`, `bulk_run`, and `BulkResult` (exported from `taskill`)
67
+ - `--max-projects` CLI flag and `max_projects` parameter on `bulk_run` to cap per-run project count
68
+ - `--shared-config` flag so a parent directory's `taskill.yaml` can act as the base for all child repos
69
+ - `--filter` flag (repeatable) to limit `bulk-run` to repos whose name matches a substring
70
+ - Per-repo config resolution: `<repo>/taskill.yaml` → `<repo>/.taskill.yaml` → shared → defaults
71
+ - Plugin protocol for providers via `[project.entry-points."taskill.providers"]` and `discover_providers()` (ROADMAP v0.2.1)
72
+ - Plugin protocol for document updaters via `[project.entry-points."taskill.updaters"]` and `discover_updaters()` (ROADMAP v0.2.3)
73
+ - `DocumentUpdater` ABC and `UpdateResult` dataclass in `taskill.updaters.base`
74
+ - `daily_update.sh` shell wrapper around `bulk-run` with `.env`-driven defaults
75
+
76
+ ### Changed
77
+ - Built-in providers (`windsurf_mcp`, `openrouter`, `algorithmic`) re-registered via entry points instead of a hard-coded registry
78
+ - Built-in updaters refactored into classes (`ChangelogUpdater`, `TodoUpdater`, `ReadmeUpdater`); legacy `update_changelog` / `update_todo` / `update_readme` functions kept as backward-compatible wrappers
79
+
80
+ ### Docs
81
+ - README: new "Bulk / fleet-wide runs" and "Extending taskill" sections
82
+ - ROADMAP: marked v0.2.1, v0.2.3, and v0.5 baseline as shipped
83
+
84
+ ## [0.1.3] - 2026-04-25
85
+
86
+ ### Docs
87
+ - Update .windsurf/workflows/daily-docs-update.md
88
+ - Update CHANGELOG.md
89
+ - Update README.md
90
+ - Update ROADMAP.md
91
+ - Update SUMD.md
92
+ - Update SUMR.md
93
+ - Update TODO.md
94
+ - Update project/README.md
95
+ - Update project/context.md
96
+
97
+ ### Test
98
+ - Update tests/test_bulk.py
99
+
100
+ ### Other
101
+ - Update .env.example
102
+ - Update .gitignore
103
+ - Update app.doql.less
104
+ - Update daily_update.sh
105
+ - Update planfile.yaml
106
+ - Update project.sh
107
+ - Update project/analysis.toon.yaml
108
+ - Update project/calls.mmd
109
+ - Update project/calls.png
110
+ - Update project/calls.toon.yaml
111
+ - ... and 13 more files
112
+
55
113
  ## [0.1.2] - 2026-04-25
56
114
 
57
115
  ### Docs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskill
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Daily project hygiene: keep README / CHANGELOG / TODO in sync with reality. LLM-first, algorithmic fallback.
5
5
  Project-URL: Homepage, https://github.com/oqlos/taskill
6
6
  Project-URL: Issues, https://github.com/oqlos/taskill/issues
@@ -37,10 +37,10 @@ Description-Content-Type: text/markdown
37
37
 
38
38
  ## AI Cost Tracking
39
39
 
40
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.2-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.15-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-2.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
40
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.3-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.30-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-2.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
42
42
 
43
- - 🤖 **LLM usage:** $0.1500 (1 commits)
43
+ - 🤖 **LLM usage:** $0.3000 (2 commits)
44
44
  - 👤 **Human dev:** ~$200 (2.0h @ $100/h, 30min dedup)
45
45
 
46
46
  Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
@@ -81,7 +81,22 @@ Every run produces three (idempotent) edits:
81
81
 
82
82
  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.
83
83
  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`.
84
- 3. **`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.
84
+ 3. **`README.md`** — refreshes only the block between `<!-- taskill:status:start -->
85
+
86
+ ## Status
87
+
88
+ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 09:23 UTC_
89
+
90
+ | Metric | Value |
91
+ |---|---|
92
+ | HEAD | `4618c29` |
93
+ | Coverage | — |
94
+ | Failing tests | — |
95
+ | Commits in last cycle | 0 |
96
+
97
+ > No changes were made to the project since the last taskill run.
98
+
99
+ <!-- taskill:status:end -->` markers (HEAD, coverage, failing tests, summary). Never touches the rest of the file.
85
100
 
86
101
  ## Provider chain
87
102
 
@@ -128,6 +143,18 @@ triggers:
128
143
  0 6 * * * cd /path/to/project && /usr/local/bin/taskill run >> ~/.taskill.log 2>&1
129
144
  ```
130
145
 
146
+ ### Bulk run (multiple projects)
147
+
148
+ For fleet-wide hygiene across many repos:
149
+
150
+ ```bash
151
+ taskill bulk-run --root ~/github --max-depth 2
152
+ taskill bulk-run --root ~/github --max-depth 2 --dry-run # preview
153
+ taskill bulk-run --root ~/github --filter taskill --filter testql # filter repos
154
+ ```
155
+
156
+ See `/daily-docs-update` workflow for complete documentation.
157
+
131
158
  ### GitHub Actions
132
159
 
133
160
  See `examples/github-action.yml`. Triggers on `push` to main, runs `taskill run`, opens a PR if files changed.
@@ -149,10 +176,138 @@ taskill run # execute (respects triggers)
149
176
  taskill run --force # ignore triggers
150
177
  taskill run --dry-run # don't write files or state
151
178
  taskill run --json # machine-readable output
179
+ taskill bulk-run # run across all git repos in a directory
152
180
  taskill release X.Y.Z # promote [Unreleased] → versioned heading
153
181
  taskill clean-todo # wipe TODO.md (after a release)
154
182
  ```
155
183
 
184
+ ## Bulk / fleet-wide runs
185
+
186
+ When you maintain many small repos (a personal "github" folder, a self-hosted
187
+ GitLab group, a monorepo of independent packages), `taskill bulk-run` runs the
188
+ same hygiene job across all of them with a single command:
189
+
190
+ ```bash
191
+ # Scan ~/github (depth 2), use a shared taskill.yaml as the base config
192
+ taskill bulk-run --root ~/github --shared-config ~/github/taskill.yaml
193
+
194
+ # Same, but only run on repos whose name matches one of the filters
195
+ taskill bulk-run --root ~/github -f oqlos -f semcod
196
+
197
+ # Process at most 5 projects per run (useful for rate-limited LLMs)
198
+ taskill bulk-run --root ~/github --max-projects 5
199
+
200
+ # Preview without writing anything
201
+ taskill bulk-run --root ~/github --dry-run --force
202
+
203
+ # Machine-readable summary for CI / dashboards
204
+ taskill bulk-run --root ~/github --json
205
+ ```
206
+
207
+ ### Config resolution per repo
208
+
209
+ Per-repo `taskill.yaml` (or `.taskill.yaml`) takes precedence over the shared
210
+ config, so you can have one default policy plus per-project overrides. Repos
211
+ without any local config inherit the shared one with their own `project_root`.
212
+
213
+ Resolution order for each discovered repo:
214
+
215
+ 1. `<repo>/taskill.yaml` — local override (highest priority)
216
+ 2. `<repo>/.taskill.yaml` — alternative local override
217
+ 3. Shared config from `--shared-config`, rebased onto the repo
218
+ 4. Built-in defaults
219
+
220
+ ### Discovery rules
221
+
222
+ `bulk-run` walks the directory tree starting at `--root` up to `--max-depth`
223
+ levels deep (default 2). A directory containing a `.git` entry is treated as a
224
+ repo, and `bulk-run` does **not** descend into nested repos. Hidden directories
225
+ and common noise (`node_modules`, `__pycache__`, `.venv`, `dist`, `build`,
226
+ `target`, `.tox`, `.pytest_cache`) are skipped automatically.
227
+
228
+ ### Daily-run wrapper
229
+
230
+ A shell wrapper (`daily_update.sh`) reads its defaults from `.env`:
231
+
232
+ ```bash
233
+ # .env
234
+ GITHUB_ROOT=$HOME/github
235
+ TASKILL_MAX_DEPTH=2
236
+ TASKILL_MAX_PROJECTS=10 # cap per cron tick
237
+ TASKILL_DRY_RUN=false
238
+ TASKILL_FORCE=false
239
+ TASKILL_FILTER=oqlos,semcod # comma-separated
240
+ ```
241
+
242
+ ```bash
243
+ ./daily_update.sh # uses .env
244
+ ./daily_update.sh --dry-run # CLI override
245
+ ./daily_update.sh --root /tmp/my-projects # different root
246
+ ./daily_update.sh --filter taskill,testql # narrow scope
247
+ ```
248
+
249
+ ## Extending taskill: providers and updaters as plugins
250
+
251
+ Both providers (LLM/algorithmic backends) and document updaters (CHANGELOG /
252
+ TODO / README writers) are discovered through Python entry points, so a
253
+ third-party package can register a new provider or updater without touching
254
+ this codebase.
255
+
256
+ ### Custom provider
257
+
258
+ ```toml
259
+ # my_pkg/pyproject.toml
260
+ [project.entry-points."taskill.providers"]
261
+ my_provider = "my_pkg.provider:MyProvider"
262
+ ```
263
+
264
+ ```python
265
+ # my_pkg/provider.py
266
+ from taskill.providers.base import Provider, GeneratedDocs
267
+
268
+ class MyProvider(Provider):
269
+ name = "my_provider"
270
+
271
+ def is_available(self) -> bool:
272
+ return True
273
+
274
+ def generate(self, context) -> GeneratedDocs:
275
+ ...
276
+ ```
277
+
278
+ Then reference it from `taskill.yaml` like any built-in:
279
+
280
+ ```yaml
281
+ providers:
282
+ - name: my_provider
283
+ enabled: true
284
+ options: { ... }
285
+ - name: algorithmic
286
+ enabled: true
287
+ ```
288
+
289
+ ### Custom updater
290
+
291
+ ```toml
292
+ [project.entry-points."taskill.updaters"]
293
+ wiki = "my_pkg.wiki:WikiUpdater"
294
+ ```
295
+
296
+ ```python
297
+ from taskill.updaters.base import DocumentUpdater, UpdateResult
298
+
299
+ class WikiUpdater(DocumentUpdater):
300
+ name = "wiki"
301
+
302
+ def apply(self, path, snapshot, docs) -> UpdateResult:
303
+ ...
304
+ return UpdateResult(changed=True, path=path, updater_name=self.name)
305
+ ```
306
+
307
+ Built-in updaters (`changelog`, `todo`, `readme`) are registered the same way,
308
+ so removing or replacing them is just a matter of the entry-point taking
309
+ precedence.
310
+
156
311
  ## Configuration reference
157
312
 
158
313
  See [`taskill.yaml`](./taskill.yaml) at the repo root for the annotated default config.
@@ -188,5 +343,18 @@ Licensed under Apache-2.0.
188
343
  ## Status
189
344
 
190
345
  <!-- taskill:status:start -->
191
- _Bootstrapped — no live project status yet. Will populate after first `taskill run`._
346
+
347
+ ## Status
348
+
349
+ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 09:23 UTC_
350
+
351
+ | Metric | Value |
352
+ |---|---|
353
+ | HEAD | `4618c29` |
354
+ | Coverage | — |
355
+ | Failing tests | — |
356
+ | Commits in last cycle | 0 |
357
+
358
+ > No changes were made to the project since the last taskill run.
359
+
192
360
  <!-- taskill:status:end -->
@@ -3,10 +3,10 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.2-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.15-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-2.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.3-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.30-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-2.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $0.1500 (1 commits)
9
+ - 🤖 **LLM usage:** $0.3000 (2 commits)
10
10
  - 👤 **Human dev:** ~$200 (2.0h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
@@ -47,7 +47,22 @@ Every run produces three (idempotent) edits:
47
47
 
48
48
  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.
49
49
  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`.
50
- 3. **`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.
50
+ 3. **`README.md`** — refreshes only the block between `<!-- taskill:status:start -->
51
+
52
+ ## Status
53
+
54
+ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 09:23 UTC_
55
+
56
+ | Metric | Value |
57
+ |---|---|
58
+ | HEAD | `4618c29` |
59
+ | Coverage | — |
60
+ | Failing tests | — |
61
+ | Commits in last cycle | 0 |
62
+
63
+ > No changes were made to the project since the last taskill run.
64
+
65
+ <!-- taskill:status:end -->` markers (HEAD, coverage, failing tests, summary). Never touches the rest of the file.
51
66
 
52
67
  ## Provider chain
53
68
 
@@ -94,6 +109,18 @@ triggers:
94
109
  0 6 * * * cd /path/to/project && /usr/local/bin/taskill run >> ~/.taskill.log 2>&1
95
110
  ```
96
111
 
112
+ ### Bulk run (multiple projects)
113
+
114
+ For fleet-wide hygiene across many repos:
115
+
116
+ ```bash
117
+ taskill bulk-run --root ~/github --max-depth 2
118
+ taskill bulk-run --root ~/github --max-depth 2 --dry-run # preview
119
+ taskill bulk-run --root ~/github --filter taskill --filter testql # filter repos
120
+ ```
121
+
122
+ See `/daily-docs-update` workflow for complete documentation.
123
+
97
124
  ### GitHub Actions
98
125
 
99
126
  See `examples/github-action.yml`. Triggers on `push` to main, runs `taskill run`, opens a PR if files changed.
@@ -115,10 +142,138 @@ taskill run # execute (respects triggers)
115
142
  taskill run --force # ignore triggers
116
143
  taskill run --dry-run # don't write files or state
117
144
  taskill run --json # machine-readable output
145
+ taskill bulk-run # run across all git repos in a directory
118
146
  taskill release X.Y.Z # promote [Unreleased] → versioned heading
119
147
  taskill clean-todo # wipe TODO.md (after a release)
120
148
  ```
121
149
 
150
+ ## Bulk / fleet-wide runs
151
+
152
+ When you maintain many small repos (a personal "github" folder, a self-hosted
153
+ GitLab group, a monorepo of independent packages), `taskill bulk-run` runs the
154
+ same hygiene job across all of them with a single command:
155
+
156
+ ```bash
157
+ # Scan ~/github (depth 2), use a shared taskill.yaml as the base config
158
+ taskill bulk-run --root ~/github --shared-config ~/github/taskill.yaml
159
+
160
+ # Same, but only run on repos whose name matches one of the filters
161
+ taskill bulk-run --root ~/github -f oqlos -f semcod
162
+
163
+ # Process at most 5 projects per run (useful for rate-limited LLMs)
164
+ taskill bulk-run --root ~/github --max-projects 5
165
+
166
+ # Preview without writing anything
167
+ taskill bulk-run --root ~/github --dry-run --force
168
+
169
+ # Machine-readable summary for CI / dashboards
170
+ taskill bulk-run --root ~/github --json
171
+ ```
172
+
173
+ ### Config resolution per repo
174
+
175
+ Per-repo `taskill.yaml` (or `.taskill.yaml`) takes precedence over the shared
176
+ config, so you can have one default policy plus per-project overrides. Repos
177
+ without any local config inherit the shared one with their own `project_root`.
178
+
179
+ Resolution order for each discovered repo:
180
+
181
+ 1. `<repo>/taskill.yaml` — local override (highest priority)
182
+ 2. `<repo>/.taskill.yaml` — alternative local override
183
+ 3. Shared config from `--shared-config`, rebased onto the repo
184
+ 4. Built-in defaults
185
+
186
+ ### Discovery rules
187
+
188
+ `bulk-run` walks the directory tree starting at `--root` up to `--max-depth`
189
+ levels deep (default 2). A directory containing a `.git` entry is treated as a
190
+ repo, and `bulk-run` does **not** descend into nested repos. Hidden directories
191
+ and common noise (`node_modules`, `__pycache__`, `.venv`, `dist`, `build`,
192
+ `target`, `.tox`, `.pytest_cache`) are skipped automatically.
193
+
194
+ ### Daily-run wrapper
195
+
196
+ A shell wrapper (`daily_update.sh`) reads its defaults from `.env`:
197
+
198
+ ```bash
199
+ # .env
200
+ GITHUB_ROOT=$HOME/github
201
+ TASKILL_MAX_DEPTH=2
202
+ TASKILL_MAX_PROJECTS=10 # cap per cron tick
203
+ TASKILL_DRY_RUN=false
204
+ TASKILL_FORCE=false
205
+ TASKILL_FILTER=oqlos,semcod # comma-separated
206
+ ```
207
+
208
+ ```bash
209
+ ./daily_update.sh # uses .env
210
+ ./daily_update.sh --dry-run # CLI override
211
+ ./daily_update.sh --root /tmp/my-projects # different root
212
+ ./daily_update.sh --filter taskill,testql # narrow scope
213
+ ```
214
+
215
+ ## Extending taskill: providers and updaters as plugins
216
+
217
+ Both providers (LLM/algorithmic backends) and document updaters (CHANGELOG /
218
+ TODO / README writers) are discovered through Python entry points, so a
219
+ third-party package can register a new provider or updater without touching
220
+ this codebase.
221
+
222
+ ### Custom provider
223
+
224
+ ```toml
225
+ # my_pkg/pyproject.toml
226
+ [project.entry-points."taskill.providers"]
227
+ my_provider = "my_pkg.provider:MyProvider"
228
+ ```
229
+
230
+ ```python
231
+ # my_pkg/provider.py
232
+ from taskill.providers.base import Provider, GeneratedDocs
233
+
234
+ class MyProvider(Provider):
235
+ name = "my_provider"
236
+
237
+ def is_available(self) -> bool:
238
+ return True
239
+
240
+ def generate(self, context) -> GeneratedDocs:
241
+ ...
242
+ ```
243
+
244
+ Then reference it from `taskill.yaml` like any built-in:
245
+
246
+ ```yaml
247
+ providers:
248
+ - name: my_provider
249
+ enabled: true
250
+ options: { ... }
251
+ - name: algorithmic
252
+ enabled: true
253
+ ```
254
+
255
+ ### Custom updater
256
+
257
+ ```toml
258
+ [project.entry-points."taskill.updaters"]
259
+ wiki = "my_pkg.wiki:WikiUpdater"
260
+ ```
261
+
262
+ ```python
263
+ from taskill.updaters.base import DocumentUpdater, UpdateResult
264
+
265
+ class WikiUpdater(DocumentUpdater):
266
+ name = "wiki"
267
+
268
+ def apply(self, path, snapshot, docs) -> UpdateResult:
269
+ ...
270
+ return UpdateResult(changed=True, path=path, updater_name=self.name)
271
+ ```
272
+
273
+ Built-in updaters (`changelog`, `todo`, `readme`) are registered the same way,
274
+ so removing or replacing them is just a matter of the entry-point taking
275
+ precedence.
276
+
122
277
  ## Configuration reference
123
278
 
124
279
  See [`taskill.yaml`](./taskill.yaml) at the repo root for the annotated default config.
@@ -154,5 +309,18 @@ Licensed under Apache-2.0.
154
309
  ## Status
155
310
 
156
311
  <!-- taskill:status:start -->
157
- _Bootstrapped — no live project status yet. Will populate after first `taskill run`._
312
+
313
+ ## Status
314
+
315
+ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 09:23 UTC_
316
+
317
+ | Metric | Value |
318
+ |---|---|
319
+ | HEAD | `4618c29` |
320
+ | Coverage | — |
321
+ | Failing tests | — |
322
+ | Commits in last cycle | 0 |
323
+
324
+ > No changes were made to the project since the last taskill run.
325
+
158
326
  <!-- taskill:status:end -->