tasks-toolkit 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.
- tasks_toolkit-0.1.3/LICENSE +21 -0
- tasks_toolkit-0.1.3/PKG-INFO +121 -0
- tasks_toolkit-0.1.3/README.md +72 -0
- tasks_toolkit-0.1.3/pyproject.toml +69 -0
- tasks_toolkit-0.1.3/scripts/__init__.py +0 -0
- tasks_toolkit-0.1.3/scripts/agents_sync.py +61 -0
- tasks_toolkit-0.1.3/scripts/apply-plan.sh +51 -0
- tasks_toolkit-0.1.3/scripts/build.sh +7 -0
- tasks_toolkit-0.1.3/scripts/codex-task.sh +198 -0
- tasks_toolkit-0.1.3/scripts/install-hooks.sh +9 -0
- tasks_toolkit-0.1.3/scripts/junie-status.sh +52 -0
- tasks_toolkit-0.1.3/scripts/junie-task.sh +183 -0
- tasks_toolkit-0.1.3/scripts/kilo-task.sh +226 -0
- tasks_toolkit-0.1.3/scripts/needs-dotnet-checks.sh +24 -0
- tasks_toolkit-0.1.3/scripts/plan-sprint.sh +92 -0
- tasks_toolkit-0.1.3/scripts/run-task.sh +79 -0
- tasks_toolkit-0.1.3/scripts/run.sh +7 -0
- tasks_toolkit-0.1.3/scripts/run_stats.py +141 -0
- tasks_toolkit-0.1.3/scripts/run_summary.py +94 -0
- tasks_toolkit-0.1.3/scripts/spec-lint.sh +139 -0
- tasks_toolkit-0.1.3/scripts/sprint-auto.sh +79 -0
- tasks_toolkit-0.1.3/scripts/sprint_auto_execute.py +160 -0
- tasks_toolkit-0.1.3/scripts/sprint_auto_preflight.py +309 -0
- tasks_toolkit-0.1.3/scripts/sprint_auto_prepare.py +271 -0
- tasks_toolkit-0.1.3/scripts/sprint_auto_resume.py +153 -0
- tasks_toolkit-0.1.3/scripts/sprint_auto_state.py +191 -0
- tasks_toolkit-0.1.3/scripts/sprint_plan.py +238 -0
- tasks_toolkit-0.1.3/scripts/task-report.sh +45 -0
- tasks_toolkit-0.1.3/scripts/tasks.py +1813 -0
- tasks_toolkit-0.1.3/scripts/tasks_cli.py +14 -0
- tasks_toolkit-0.1.3/scripts/tasks_commands.py +11 -0
- tasks_toolkit-0.1.3/scripts/tasks_init.py +140 -0
- tasks_toolkit-0.1.3/scripts/tasks_mcp.py +309 -0
- tasks_toolkit-0.1.3/scripts/tasks_model.py +173 -0
- tasks_toolkit-0.1.3/scripts/tasks_plan_import.py +269 -0
- tasks_toolkit-0.1.3/scripts/tasks_render.py +148 -0
- tasks_toolkit-0.1.3/scripts/tasks_storage.py +373 -0
- tasks_toolkit-0.1.3/scripts/tasks_watch.py +159 -0
- tasks_toolkit-0.1.3/scripts/test.sh +43 -0
- tasks_toolkit-0.1.3/scripts/test_agent_config.py +97 -0
- tasks_toolkit-0.1.3/scripts/test_plan_sprint.sh +172 -0
- tasks_toolkit-0.1.3/scripts/test_python_fast.sh +26 -0
- tasks_toolkit-0.1.3/scripts/test_python_lint.sh +22 -0
- tasks_toolkit-0.1.3/scripts/test_python_slow.sh +14 -0
- tasks_toolkit-0.1.3/scripts/test_run_stats.py +64 -0
- tasks_toolkit-0.1.3/scripts/test_runners.sh +588 -0
- tasks_toolkit-0.1.3/scripts/test_spec_lint.sh +438 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto.py +50 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto_execute.py +112 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto_preflight.py +143 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto_prepare.py +150 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto_resume.py +104 -0
- tasks_toolkit-0.1.3/scripts/test_sprint_auto_state.py +157 -0
- tasks_toolkit-0.1.3/scripts/test_tasks.py +1678 -0
- tasks_toolkit-0.1.3/scripts/test_tasks_mcp.py +233 -0
- tasks_toolkit-0.1.3/scripts/test_tasks_plan_import.py +129 -0
- tasks_toolkit-0.1.3/scripts/test_tasks_storage.py +67 -0
- tasks_toolkit-0.1.3/scripts/test_tasks_watch.py +239 -0
- tasks_toolkit-0.1.3/scripts/test_worktree.sh +2112 -0
- tasks_toolkit-0.1.3/scripts/worktree.sh +992 -0
- tasks_toolkit-0.1.3/scripts/worktree_mr.py +133 -0
- tasks_toolkit-0.1.3/scripts/worktree_path.py +70 -0
- tasks_toolkit-0.1.3/scripts/worktree_sprint.py +70 -0
- tasks_toolkit-0.1.3/setup.cfg +4 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/PKG-INFO +121 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/SOURCES.txt +68 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/dependency_links.txt +1 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/entry_points.txt +4 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/requires.txt +6 -0
- tasks_toolkit-0.1.3/tasks_toolkit.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 prachwal
|
|
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.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tasks-toolkit
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Task tracker toolkit: CLI + MCP server for sprint/task/subtask management, delegation helpers.
|
|
5
|
+
Author: prachwal
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 prachwal
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/prachwal/tasks-toolkit
|
|
29
|
+
Project-URL: Repository, https://github.com/prachwal/tasks-toolkit
|
|
30
|
+
Project-URL: Issues, https://github.com/prachwal/tasks-toolkit/issues
|
|
31
|
+
Keywords: mcp,task-tracker,cli,sprints
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Requires-Python: >=3.10
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-xdist>=3; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# tasks-toolkit
|
|
51
|
+
|
|
52
|
+
A small, self-contained task tracker (sprints → tasks → subtasks) with a CLI and an MCP server,
|
|
53
|
+
plus helpers used by the VibeEmulator project.
|
|
54
|
+
|
|
55
|
+
## Contents
|
|
56
|
+
|
|
57
|
+
- `scripts/tasks.py` — CLI for the tracker (SQLite-backed).
|
|
58
|
+
- `scripts/tasks_mcp.py` — MCP (stdio) server exposing tracker operations as tools.
|
|
59
|
+
- `scripts/tasks_init.py` — create an empty database.
|
|
60
|
+
- `scripts/sprint_auto_*.py`, `scripts/sprint_plan.py`, `scripts/run_stats.py` — automation helpers.
|
|
61
|
+
- `scripts/worktree.sh`, `scripts/*-task.sh` — delegation runners.
|
|
62
|
+
- `pyproject.toml` — ruff/mypy/pytest configuration.
|
|
63
|
+
- `knowledge/`, `agents/` — design notes and tracker docs.
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
### From PyPI (recommended)
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pipx install tasks-toolkit
|
|
71
|
+
# or: uv tool install tasks-toolkit
|
|
72
|
+
tasks-init
|
|
73
|
+
tasks overview
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### From source
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/prachwal/tasks-toolkit.git
|
|
80
|
+
cd tasks-toolkit
|
|
81
|
+
python -m pip install -e .
|
|
82
|
+
tasks-init
|
|
83
|
+
tasks overview
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Docker
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
docker build -t tasks-toolkit .
|
|
90
|
+
docker run --rm -i -v tasks-data:/data tasks-toolkit
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Quick start (from source)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python3 scripts/tasks_init.py # create tasks/tasks.db
|
|
97
|
+
python3 scripts/tasks.py overview # show sprints
|
|
98
|
+
python3 scripts/tasks.py next # next ready task
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Override the data location with `VIBE_TASKS_ROOT`:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
export VIBE_TASKS_ROOT=/path/to/data
|
|
105
|
+
python3 scripts/tasks.py overview
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## MCP (OpenCode)
|
|
109
|
+
|
|
110
|
+
```jsonc
|
|
111
|
+
"tasks": {
|
|
112
|
+
"type": "local",
|
|
113
|
+
"command": ["python3", "{env:HOME}/src/tasks-toolkit/scripts/tasks_mcp.py"],
|
|
114
|
+
"cwd": "{env:HOME}/src/tasks-toolkit",
|
|
115
|
+
"environment": { "VIBE_TASKS_ROOT": "{env:HOME}/src/tasks-toolkit" }
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# tasks-toolkit
|
|
2
|
+
|
|
3
|
+
A small, self-contained task tracker (sprints → tasks → subtasks) with a CLI and an MCP server,
|
|
4
|
+
plus helpers used by the VibeEmulator project.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- `scripts/tasks.py` — CLI for the tracker (SQLite-backed).
|
|
9
|
+
- `scripts/tasks_mcp.py` — MCP (stdio) server exposing tracker operations as tools.
|
|
10
|
+
- `scripts/tasks_init.py` — create an empty database.
|
|
11
|
+
- `scripts/sprint_auto_*.py`, `scripts/sprint_plan.py`, `scripts/run_stats.py` — automation helpers.
|
|
12
|
+
- `scripts/worktree.sh`, `scripts/*-task.sh` — delegation runners.
|
|
13
|
+
- `pyproject.toml` — ruff/mypy/pytest configuration.
|
|
14
|
+
- `knowledge/`, `agents/` — design notes and tracker docs.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### From PyPI (recommended)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pipx install tasks-toolkit
|
|
22
|
+
# or: uv tool install tasks-toolkit
|
|
23
|
+
tasks-init
|
|
24
|
+
tasks overview
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### From source
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/prachwal/tasks-toolkit.git
|
|
31
|
+
cd tasks-toolkit
|
|
32
|
+
python -m pip install -e .
|
|
33
|
+
tasks-init
|
|
34
|
+
tasks overview
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Docker
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
docker build -t tasks-toolkit .
|
|
41
|
+
docker run --rm -i -v tasks-data:/data tasks-toolkit
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick start (from source)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3 scripts/tasks_init.py # create tasks/tasks.db
|
|
48
|
+
python3 scripts/tasks.py overview # show sprints
|
|
49
|
+
python3 scripts/tasks.py next # next ready task
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Override the data location with `VIBE_TASKS_ROOT`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
export VIBE_TASKS_ROOT=/path/to/data
|
|
56
|
+
python3 scripts/tasks.py overview
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## MCP (OpenCode)
|
|
60
|
+
|
|
61
|
+
```jsonc
|
|
62
|
+
"tasks": {
|
|
63
|
+
"type": "local",
|
|
64
|
+
"command": ["python3", "{env:HOME}/src/tasks-toolkit/scripts/tasks_mcp.py"],
|
|
65
|
+
"cwd": "{env:HOME}/src/tasks-toolkit",
|
|
66
|
+
"environment": { "VIBE_TASKS_ROOT": "{env:HOME}/src/tasks-toolkit" }
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tasks-toolkit"
|
|
7
|
+
version = "0.1.3"
|
|
8
|
+
description = "Task tracker toolkit: CLI + MCP server for sprint/task/subtask management, delegation helpers."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "prachwal" }]
|
|
13
|
+
keywords = ["mcp", "task-tracker", "cli", "sprints"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = ["ruff>=0.5", "mypy>=1.10", "pytest>=8", "pytest-xdist>=3"]
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/prachwal/tasks-toolkit"
|
|
30
|
+
Repository = "https://github.com/prachwal/tasks-toolkit"
|
|
31
|
+
Issues = "https://github.com/prachwal/tasks-toolkit/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
tasks = "scripts.tasks:main"
|
|
35
|
+
tasks-init = "scripts.tasks_init:main"
|
|
36
|
+
tasks-mcp = "scripts.tasks_mcp:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
packages = ["scripts"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.package-data]
|
|
42
|
+
scripts = ["*.sh"]
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
target-version = "py310"
|
|
46
|
+
line-length = 120
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "SIM"]
|
|
50
|
+
ignore = ["E501"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff.format]
|
|
53
|
+
quote-style = "double"
|
|
54
|
+
indent-style = "space"
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
python_version = "3.10"
|
|
58
|
+
warn_return_any = true
|
|
59
|
+
warn_unused_configs = true
|
|
60
|
+
ignore_missing_imports = true
|
|
61
|
+
files = ["scripts"]
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
testpaths = ["scripts"]
|
|
65
|
+
markers = [
|
|
66
|
+
"slow: marks tests as slow (subprocess-heavy)",
|
|
67
|
+
"integration: marks tests as integration tests",
|
|
68
|
+
]
|
|
69
|
+
addopts = "-n auto --ignore=scripts/test_agent_config.py --ignore=scripts/test_tasks_watch.py"
|
|
File without changes
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate the single-file guidelines for a tool that loads exactly one instruction file (Junie).
|
|
3
|
+
|
|
4
|
+
Concatenates `AGENTS.md`, the implementer subset of `agents/common/*.md` and
|
|
5
|
+
`agents/junie/INSTRUCTIONS.md` into `agents/junie/GUIDELINES.generated.md`. `--check` regenerates in
|
|
6
|
+
memory and fails when the file on disk differs, so `scripts/test.sh` catches drift. Standard library only.
|
|
7
|
+
|
|
8
|
+
Usage: scripts/agents_sync.py [--check]
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
16
|
+
TARGET = ROOT / "agents" / "junie" / "GUIDELINES.generated.md"
|
|
17
|
+
SOURCES = [
|
|
18
|
+
"AGENTS.md",
|
|
19
|
+
"agents/common/layout.md",
|
|
20
|
+
"agents/common/git-and-hooks.md",
|
|
21
|
+
"agents/junie/INSTRUCTIONS.md",
|
|
22
|
+
]
|
|
23
|
+
HEADER = (
|
|
24
|
+
"<!-- GENERATED by scripts/agents_sync.py — do not edit by hand. "
|
|
25
|
+
"Run: python3 scripts/agents_sync.py -->\n"
|
|
26
|
+
"# VibeEmulator — Junie guidelines (generated)\n\n"
|
|
27
|
+
"The shared rules and the files a Junie implementer needs, concatenated for a tool that loads\n"
|
|
28
|
+
"exactly one guidelines file. Sources: " + ", ".join(SOURCES) + ".\n"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def render() -> str:
|
|
33
|
+
"""Return the full generated guidelines text (deterministic, newline-terminated)."""
|
|
34
|
+
parts = [HEADER]
|
|
35
|
+
for rel in SOURCES:
|
|
36
|
+
text = (ROOT / rel).read_text(encoding="utf-8").rstrip("\n")
|
|
37
|
+
parts.append(f"\n---\n\n## From {rel}\n\n{text}\n")
|
|
38
|
+
return "".join(parts)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main(argv: list[str]) -> int:
|
|
42
|
+
"""Write the guidelines file, or with --check compare it and fail on drift."""
|
|
43
|
+
expected = render()
|
|
44
|
+
rel = TARGET.relative_to(ROOT)
|
|
45
|
+
if "--check" in argv:
|
|
46
|
+
if not TARGET.exists():
|
|
47
|
+
print(f"agents_sync: {rel} is missing; run: python3 scripts/agents_sync.py", file=sys.stderr)
|
|
48
|
+
return 1
|
|
49
|
+
if TARGET.read_text(encoding="utf-8") != expected:
|
|
50
|
+
print(f"agents_sync: {rel} is stale; run: python3 scripts/agents_sync.py", file=sys.stderr)
|
|
51
|
+
return 1
|
|
52
|
+
print("agents_sync: guidelines in sync")
|
|
53
|
+
return 0
|
|
54
|
+
TARGET.parent.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
TARGET.write_text(expected, encoding="utf-8")
|
|
56
|
+
print(f"wrote {rel}")
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Imports a validated sprint plan (tmp/plans/<name>.json) into the tracker through tasks.py.
|
|
3
|
+
# Previews the plan and needs a human "y" on a TTY (otherwise --yes). A plan is applied once.
|
|
4
|
+
#
|
|
5
|
+
# scripts/apply-plan.sh <name> [--yes] [--start]
|
|
6
|
+
#
|
|
7
|
+
# --start also creates the sprint branch (scripts/worktree.sh sprint start <n> <slug>).
|
|
8
|
+
# Exit: 0 applied | 1 usage/missing plan | 2 not confirmed | 4 plan invalid | 5 already applied
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
12
|
+
cd "$root"
|
|
13
|
+
|
|
14
|
+
name="${1:-}"
|
|
15
|
+
[[ "$name" =~ ^[a-z0-9][a-z0-9-]*$ ]] || { echo "usage: $0 <name> [--yes] [--start]" >&2; exit 1; }
|
|
16
|
+
shift
|
|
17
|
+
yes=0 start=0
|
|
18
|
+
while [ $# -gt 0 ]; do
|
|
19
|
+
case "$1" in
|
|
20
|
+
--yes) yes=1; shift ;;
|
|
21
|
+
--start) start=1; shift ;;
|
|
22
|
+
*) echo "unknown option '$1'" >&2; exit 1 ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
plan="tmp/plans/$name.json"
|
|
27
|
+
marker="tmp/plans/$name.applied"
|
|
28
|
+
[ -f "$plan" ] || { echo "error: no plan '$plan' (run scripts/plan-sprint.sh $name first)" >&2; exit 1; }
|
|
29
|
+
[ ! -f "$marker" ] || { echo "error: '$name' was already applied ($(cat "$marker"))" >&2; exit 5; }
|
|
30
|
+
|
|
31
|
+
python3 scripts/sprint_plan.py validate "$plan" --specs-written || exit 4
|
|
32
|
+
python3 scripts/sprint_plan.py preview "$plan"
|
|
33
|
+
echo ""
|
|
34
|
+
if [ "$yes" -ne 1 ]; then
|
|
35
|
+
if [ ! -t 0 ]; then
|
|
36
|
+
echo "error: not a TTY; re-run with --yes to apply without asking" >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
read -r -p "Apply this plan to the tracker? [y/N] " answer
|
|
40
|
+
[[ "$answer" =~ ^[yY]([eE][sS])?$ ]] || { echo "not applied" >&2; exit 2; }
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
mapping="$(python3 scripts/sprint_plan.py apply "$plan")"
|
|
44
|
+
printf '%s\n' "$mapping"
|
|
45
|
+
sprint_id="$(printf '%s\n' "$mapping" | head -1 | sed 's/.*-> \([0-9]*\)\..*/\1/')"
|
|
46
|
+
slug="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["sprint"]["slug"])' "$plan")"
|
|
47
|
+
printf 'sprint %s on %s\n' "$sprint_id" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >"$marker"
|
|
48
|
+
echo "applied: sprint $sprint_id ($slug)"
|
|
49
|
+
if [ "$start" -eq 1 ]; then
|
|
50
|
+
bash scripts/worktree.sh sprint start "$sprint_id" "$slug"
|
|
51
|
+
fi
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Delegates one task to the Codex CLI agent inside its own worktree, then verifies it.
|
|
3
|
+
# Same contract as junie-task.sh / kilo-task.sh (worktree, heartbeat, verification, patch).
|
|
4
|
+
#
|
|
5
|
+
# scripts/codex-task.sh <name> "<task text>" [--task-id <id>]
|
|
6
|
+
# scripts/codex-task.sh <name> --file tmp/tasks/<name>.md [--task-id <id>]
|
|
7
|
+
#
|
|
8
|
+
# --task-id reports progress to the task tracker (scripts/task-report.sh): the task is
|
|
9
|
+
# marked in progress at the start, and after the commit and verification it is closed
|
|
10
|
+
# automatically (done on success, blocked on failure); failures only warn.
|
|
11
|
+
#
|
|
12
|
+
# Env: CODEX_MODEL (default: Codex's own default), CODEX_SANDBOX (default workspace-write),
|
|
13
|
+
# CODEX_TIMEOUT (default 1200 s), CODEX_PING (default 30 s),
|
|
14
|
+
# CODEX_VERIFY_CMD (tests only: replaces the dotnet verification).
|
|
15
|
+
# Output: tmp/runs/<name>.{status,events.jsonl,result.txt,verify.log,summary.json},
|
|
16
|
+
# tmp/patches/<name>.patch. Every status line ends with "model=<model> runner=codex".
|
|
17
|
+
# Exit code: 0 verified, 1 verification failed, 3 Codex itself failed.
|
|
18
|
+
#
|
|
19
|
+
# Notes:
|
|
20
|
+
# - Codex runs in a workspace-write sandbox with the network off by default; `dotnet` writes to
|
|
21
|
+
# ~/.nuget and ~/.dotnet, so those are added to the sandbox (--add-dir) or restore/build fails.
|
|
22
|
+
# - The worktree is disposable; review the patch. Never bypass the sandbox or approval flags.
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
26
|
+
cd "$root"
|
|
27
|
+
|
|
28
|
+
runner="codex"
|
|
29
|
+
name="${1:?usage: codex-task.sh <name> \"<task>\" | --file <path> [--task-id <id>]}"
|
|
30
|
+
shift
|
|
31
|
+
if [ "${1:-}" = "--file" ]; then
|
|
32
|
+
task="$(cat "${2:?missing task file}")"
|
|
33
|
+
shift 2
|
|
34
|
+
else
|
|
35
|
+
task="${1:?missing task text}"
|
|
36
|
+
shift
|
|
37
|
+
fi
|
|
38
|
+
task_id=""
|
|
39
|
+
if [ "${1:-}" = "--task-id" ]; then
|
|
40
|
+
task_id="${2:?--task-id needs a value}"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
model="${CODEX_MODEL:-codex}"
|
|
44
|
+
sandbox="${CODEX_SANDBOX:-workspace-write}"
|
|
45
|
+
limit="${CODEX_TIMEOUT:-1200}"
|
|
46
|
+
ping_every="${CODEX_PING:-30}"
|
|
47
|
+
|
|
48
|
+
if [ -d "tmp/worktrees/$name" ]; then
|
|
49
|
+
wt="$root/tmp/worktrees/$name"
|
|
50
|
+
else
|
|
51
|
+
wt="$(bash scripts/worktree.sh new "$name")"
|
|
52
|
+
fi
|
|
53
|
+
mkdir -p tmp/runs
|
|
54
|
+
|
|
55
|
+
prompt="$task
|
|
56
|
+
|
|
57
|
+
Rules for this run:
|
|
58
|
+
- Work only inside this project directory (a disposable git worktree). Read AGENTS.md first and follow it.
|
|
59
|
+
- Do not commit, push or touch git. Do not add NuGet packages.
|
|
60
|
+
- You are done only when 'bash scripts/test.sh' is green AND
|
|
61
|
+
'dotnet build VibeEmulator.slnx --no-incremental -warnaserror' reports 0 warnings and 0 errors
|
|
62
|
+
(StyleCop and mandatory XML documentation are enforced on production code).
|
|
63
|
+
- Respect the sandbox; do not bypass approval or network flags.
|
|
64
|
+
- Finish with a short summary of the files you created or changed."
|
|
65
|
+
|
|
66
|
+
status_file="$root/tmp/runs/$name.status"
|
|
67
|
+
events="$root/tmp/runs/$name.events.jsonl"
|
|
68
|
+
base="$(cat "tmp/meta/$name.base")"
|
|
69
|
+
started=$SECONDS
|
|
70
|
+
started_epoch="$(date +%s)"
|
|
71
|
+
|
|
72
|
+
report() { # report <state> [detail]: one line to stdout and the status file
|
|
73
|
+
local line
|
|
74
|
+
line="$(printf '[%02d:%02d] %s' $(((SECONDS - started) / 60)) $(((SECONDS - started) % 60)) "$1${2:+ - $2}")"
|
|
75
|
+
echo "$line model=$model runner=$runner"
|
|
76
|
+
echo "$line model=$model runner=$runner" >"$status_file"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# last_activity: the last non-empty output line of the agent.
|
|
80
|
+
last_activity() {
|
|
81
|
+
grep -v '^[[:space:]]*$' "$events" 2>/dev/null | tail -n 1 | cut -c1-110 || true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
bash scripts/task-report.sh start "$task_id"
|
|
85
|
+
report "starting" "worktree=$wt${task_id:+ task=$task_id}"
|
|
86
|
+
|
|
87
|
+
codex_args=(exec -C "$wt" --skip-git-repo-check --sandbox "$sandbox" --json)
|
|
88
|
+
[ -n "${CODEX_MODEL:-}" ] && codex_args+=(-m "$model")
|
|
89
|
+
[ -d "$HOME/.nuget" ] && codex_args+=(--add-dir "$HOME/.nuget")
|
|
90
|
+
[ -d "$HOME/.dotnet" ] && codex_args+=(--add-dir "$HOME/.dotnet")
|
|
91
|
+
|
|
92
|
+
timeout "$limit" codex "${codex_args[@]}" "$prompt" \
|
|
93
|
+
>"$events" 2>"$root/tmp/runs/$name.stderr.log" &
|
|
94
|
+
codex_pid=$!
|
|
95
|
+
|
|
96
|
+
stalled=0
|
|
97
|
+
while kill -0 "$codex_pid" 2>/dev/null; do
|
|
98
|
+
sleep "$ping_every" & wait $! || true
|
|
99
|
+
kill -0 "$codex_pid" 2>/dev/null || break
|
|
100
|
+
changed="$(git -C "$wt" status --porcelain | wc -l)"
|
|
101
|
+
if [ "$changed" -eq 0 ] && [ $((SECONDS - started)) -ge "${RUNNER_STALL:-420}" ]; then
|
|
102
|
+
report "stalled" "no file changed after ${RUNNER_STALL:-420}s; killing the agent"
|
|
103
|
+
kill "$codex_pid" 2>/dev/null || true
|
|
104
|
+
stalled=1
|
|
105
|
+
break
|
|
106
|
+
fi
|
|
107
|
+
last="$(last_activity)"
|
|
108
|
+
report "codex running" "$changed file(s) changed; last: ${last:-<no output yet>}"
|
|
109
|
+
done
|
|
110
|
+
set +e
|
|
111
|
+
wait "$codex_pid"
|
|
112
|
+
codex_rc=$?
|
|
113
|
+
set -e
|
|
114
|
+
report "codex finished" "exit $codex_rc, verifying"
|
|
115
|
+
|
|
116
|
+
# With --json the last assistant text is the final message; fall back to the last output line.
|
|
117
|
+
python3 - "$events" >"$root/tmp/runs/$name.result.txt" <<'PY'
|
|
118
|
+
import json, sys
|
|
119
|
+
out = []
|
|
120
|
+
for line in open(sys.argv[1], errors="replace"):
|
|
121
|
+
line = line.strip()
|
|
122
|
+
if not line:
|
|
123
|
+
continue
|
|
124
|
+
try:
|
|
125
|
+
event = json.loads(line)
|
|
126
|
+
except ValueError:
|
|
127
|
+
out.append(line)
|
|
128
|
+
continue
|
|
129
|
+
text = event.get("text") or (event.get("message") or {}).get("content")
|
|
130
|
+
if isinstance(text, str):
|
|
131
|
+
out.append(text)
|
|
132
|
+
print(out[-1] if out else "")
|
|
133
|
+
PY
|
|
134
|
+
|
|
135
|
+
# Commit the agent's work on task/<name> (path guard first); agents never commit themselves.
|
|
136
|
+
commit_rc=0
|
|
137
|
+
if [ "$codex_rc" -eq 0 ]; then
|
|
138
|
+
title="$(printf '%s\n' "$task" | grep -m1 -v '^[[:space:]]*$' | cut -c1-72)"
|
|
139
|
+
bash scripts/worktree.sh commit "$name" --runner "$runner" --model "$model" \
|
|
140
|
+
${task_id:+--task-id "$task_id"} --title "${title:-$name}" >"tmp/runs/$name.commit.log" 2>&1 || commit_rc=$?
|
|
141
|
+
fi
|
|
142
|
+
patch="$(bash scripts/worktree.sh patch "$name")"
|
|
143
|
+
|
|
144
|
+
verify_rc=0
|
|
145
|
+
{
|
|
146
|
+
if [ -n "${CODEX_VERIFY_CMD:-}" ]; then
|
|
147
|
+
echo "== verify (CODEX_VERIFY_CMD override) =="
|
|
148
|
+
(cd "$wt" && bash -c "$CODEX_VERIFY_CMD") || verify_rc=1
|
|
149
|
+
elif ! bash "$root/scripts/needs-dotnet-checks.sh" "$wt" "$base" HEAD; then
|
|
150
|
+
echo "== conditional verification (no .NET files changed): script tests =="
|
|
151
|
+
(cd "$wt" && bash scripts/test_worktree.sh && bash scripts/test_runners.sh \
|
|
152
|
+
&& python3 -m unittest discover -s scripts -p "test_*.py") || verify_rc=1
|
|
153
|
+
else
|
|
154
|
+
echo "== format =="; (cd "$wt" && dotnet format VibeEmulator.slnx --verify-no-changes) || verify_rc=1
|
|
155
|
+
echo "== build =="; (cd "$wt" && dotnet build VibeEmulator.slnx --no-incremental -warnaserror) || verify_rc=1
|
|
156
|
+
echo "== test =="; (cd "$wt" && dotnet test VibeEmulator.slnx --no-build) || verify_rc=1
|
|
157
|
+
fi
|
|
158
|
+
} >"tmp/runs/$name.verify.log" 2>&1
|
|
159
|
+
|
|
160
|
+
if [ "$commit_rc" -ne 0 ]; then
|
|
161
|
+
verify_rc=1
|
|
162
|
+
echo "== commit failed (exit $commit_rc): see tmp/runs/$name.commit.log ==" >>"tmp/runs/$name.verify.log"
|
|
163
|
+
fi
|
|
164
|
+
finished="$(date +%s)"
|
|
165
|
+
files_changed="$(git -C "$wt" diff --cached --name-only "$base" | wc -l)"
|
|
166
|
+
if [ "$codex_rc" -ne 0 ]; then
|
|
167
|
+
verdict="agent_failed"
|
|
168
|
+
elif [ "$verify_rc" -ne 0 ]; then
|
|
169
|
+
verdict="verify_failed"
|
|
170
|
+
else
|
|
171
|
+
verdict="verified"
|
|
172
|
+
fi
|
|
173
|
+
if ! python3 scripts/run_summary.py "$root/tmp/runs/$name.summary.json" name="$name" runner="$runner" model="$model" \
|
|
174
|
+
task_id="$task_id" started="$started_epoch" finished="$finished" \
|
|
175
|
+
agent_exit="$codex_rc" verify_exit="$verify_rc" verdict="$verdict" \
|
|
176
|
+
patch="$patch" files_changed="$files_changed"; then
|
|
177
|
+
echo "warning: could not write run summary (tmp/runs/$name.summary.json)" >&2
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
report "done" "codex exit $codex_rc, verification $([ "$verify_rc" -eq 0 ] && echo PASSED || echo FAILED)"
|
|
181
|
+
echo "runner: $runner"
|
|
182
|
+
echo "model: $model"
|
|
183
|
+
echo "codex exit: $codex_rc"
|
|
184
|
+
echo "verification: $([ "$verify_rc" -eq 0 ] && echo PASSED || echo FAILED) (tmp/runs/$name.verify.log)"
|
|
185
|
+
echo "patch: $patch"
|
|
186
|
+
git -C "$wt" diff --cached --stat "$base" | tail -n 15
|
|
187
|
+
if [ "$codex_rc" -ne 0 ]; then
|
|
188
|
+
bash scripts/task-report.sh note "$task_id" "codex '$name' failed (exit 3) model=$model; see tmp/runs/$name.verify.log"
|
|
189
|
+
bash scripts/task-report.sh close "$task_id" red "agent exit $codex_rc; see tmp/runs/$name.verify.log"
|
|
190
|
+
exit 3
|
|
191
|
+
elif [ "$verify_rc" -ne 0 ]; then
|
|
192
|
+
bash scripts/task-report.sh note "$task_id" "codex '$name' failed (exit 1) model=$model; see tmp/runs/$name.verify.log"
|
|
193
|
+
bash scripts/task-report.sh close "$task_id" red "verification failed; see tmp/runs/$name.verify.log"
|
|
194
|
+
exit 1
|
|
195
|
+
else
|
|
196
|
+
bash scripts/task-report.sh note "$task_id" "codex '$name' verified model=$model; patch tmp/patches/$name.patch"
|
|
197
|
+
bash scripts/task-report.sh close "$task_id" green
|
|
198
|
+
fi
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shows the progress of a delegated Junie or Kilo run: scripts/junie-status.sh <name>
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
6
|
+
cd "$root"
|
|
7
|
+
name="${1:?usage: junie-status.sh <name>}"
|
|
8
|
+
|
|
9
|
+
[ -f "tmp/runs/$name.stdout.log" ] || [ -f "tmp/runs/$name.events.jsonl" ] || { echo "no run named '$name'" >&2; exit 1; }
|
|
10
|
+
echo "status: $(cat "tmp/runs/$name.status" 2>/dev/null || echo "(no heartbeat recorded)")"
|
|
11
|
+
# runner/model come from the run summary once the run has finished, and from the
|
|
12
|
+
# last status line while it is still running; runs that predate model recording
|
|
13
|
+
# simply have neither and print nothing here.
|
|
14
|
+
runner=""
|
|
15
|
+
model=""
|
|
16
|
+
if [ -f "tmp/runs/$name.summary.json" ]; then
|
|
17
|
+
runner="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("runner", ""))' "tmp/runs/$name.summary.json" 2>/dev/null || true)"
|
|
18
|
+
model="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("model", ""))' "tmp/runs/$name.summary.json" 2>/dev/null || true)"
|
|
19
|
+
else
|
|
20
|
+
last="$(cat "tmp/runs/$name.status" 2>/dev/null || true)"
|
|
21
|
+
runner="$(printf '%s\n' "$last" | sed -n 's/.*runner=\([^ ]*\).*/\1/p')"
|
|
22
|
+
model="$(printf '%s\n' "$last" | sed -n 's/.*model=\([^ ]*\).*/\1/p')"
|
|
23
|
+
fi
|
|
24
|
+
[ -z "$runner" ] || echo "runner: $runner"
|
|
25
|
+
[ -z "$model" ] || echo "model: $model"
|
|
26
|
+
if pgrep -f "(junie|kilo) .*$root/tmp/worktrees/$name" >/dev/null; then
|
|
27
|
+
echo "process: running"
|
|
28
|
+
else
|
|
29
|
+
echo "process: not running"
|
|
30
|
+
fi
|
|
31
|
+
if [ -d "tmp/worktrees/$name" ]; then
|
|
32
|
+
echo "changed files:"
|
|
33
|
+
git -C "tmp/worktrees/$name" status --porcelain | sed 's/^/ /'
|
|
34
|
+
fi
|
|
35
|
+
if [ -f "tmp/runs/$name.events.jsonl" ]; then
|
|
36
|
+
echo "recent activity (kilo):"
|
|
37
|
+
python3 - "tmp/runs/$name.events.jsonl" <<'PY' | tail -n 8
|
|
38
|
+
import json, sys
|
|
39
|
+
for line in open(sys.argv[1], errors="replace"):
|
|
40
|
+
try:
|
|
41
|
+
part = json.loads(line).get("part", {})
|
|
42
|
+
except ValueError:
|
|
43
|
+
continue
|
|
44
|
+
if part.get("type") == "tool":
|
|
45
|
+
print(" tool " + str(part.get("tool")))
|
|
46
|
+
elif part.get("type") == "text" and part.get("text"):
|
|
47
|
+
print(" " + " ".join(part["text"].split())[:130])
|
|
48
|
+
PY
|
|
49
|
+
fi
|
|
50
|
+
echo "recent output:"
|
|
51
|
+
tail -n 8 "tmp/runs/$name.stdout.log" 2>/dev/null | sed -E 's/\x1b\[[0-9;]*m//g' | cut -c1-140 | sed 's/^/ /'
|
|
52
|
+
[ ! -f "tmp/runs/$name.verify.log" ] || { echo "verify log tail:"; tail -n 5 "tmp/runs/$name.verify.log" | sed 's/^/ /'; }
|