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.
Files changed (70) hide show
  1. tasks_toolkit-0.1.3/LICENSE +21 -0
  2. tasks_toolkit-0.1.3/PKG-INFO +121 -0
  3. tasks_toolkit-0.1.3/README.md +72 -0
  4. tasks_toolkit-0.1.3/pyproject.toml +69 -0
  5. tasks_toolkit-0.1.3/scripts/__init__.py +0 -0
  6. tasks_toolkit-0.1.3/scripts/agents_sync.py +61 -0
  7. tasks_toolkit-0.1.3/scripts/apply-plan.sh +51 -0
  8. tasks_toolkit-0.1.3/scripts/build.sh +7 -0
  9. tasks_toolkit-0.1.3/scripts/codex-task.sh +198 -0
  10. tasks_toolkit-0.1.3/scripts/install-hooks.sh +9 -0
  11. tasks_toolkit-0.1.3/scripts/junie-status.sh +52 -0
  12. tasks_toolkit-0.1.3/scripts/junie-task.sh +183 -0
  13. tasks_toolkit-0.1.3/scripts/kilo-task.sh +226 -0
  14. tasks_toolkit-0.1.3/scripts/needs-dotnet-checks.sh +24 -0
  15. tasks_toolkit-0.1.3/scripts/plan-sprint.sh +92 -0
  16. tasks_toolkit-0.1.3/scripts/run-task.sh +79 -0
  17. tasks_toolkit-0.1.3/scripts/run.sh +7 -0
  18. tasks_toolkit-0.1.3/scripts/run_stats.py +141 -0
  19. tasks_toolkit-0.1.3/scripts/run_summary.py +94 -0
  20. tasks_toolkit-0.1.3/scripts/spec-lint.sh +139 -0
  21. tasks_toolkit-0.1.3/scripts/sprint-auto.sh +79 -0
  22. tasks_toolkit-0.1.3/scripts/sprint_auto_execute.py +160 -0
  23. tasks_toolkit-0.1.3/scripts/sprint_auto_preflight.py +309 -0
  24. tasks_toolkit-0.1.3/scripts/sprint_auto_prepare.py +271 -0
  25. tasks_toolkit-0.1.3/scripts/sprint_auto_resume.py +153 -0
  26. tasks_toolkit-0.1.3/scripts/sprint_auto_state.py +191 -0
  27. tasks_toolkit-0.1.3/scripts/sprint_plan.py +238 -0
  28. tasks_toolkit-0.1.3/scripts/task-report.sh +45 -0
  29. tasks_toolkit-0.1.3/scripts/tasks.py +1813 -0
  30. tasks_toolkit-0.1.3/scripts/tasks_cli.py +14 -0
  31. tasks_toolkit-0.1.3/scripts/tasks_commands.py +11 -0
  32. tasks_toolkit-0.1.3/scripts/tasks_init.py +140 -0
  33. tasks_toolkit-0.1.3/scripts/tasks_mcp.py +309 -0
  34. tasks_toolkit-0.1.3/scripts/tasks_model.py +173 -0
  35. tasks_toolkit-0.1.3/scripts/tasks_plan_import.py +269 -0
  36. tasks_toolkit-0.1.3/scripts/tasks_render.py +148 -0
  37. tasks_toolkit-0.1.3/scripts/tasks_storage.py +373 -0
  38. tasks_toolkit-0.1.3/scripts/tasks_watch.py +159 -0
  39. tasks_toolkit-0.1.3/scripts/test.sh +43 -0
  40. tasks_toolkit-0.1.3/scripts/test_agent_config.py +97 -0
  41. tasks_toolkit-0.1.3/scripts/test_plan_sprint.sh +172 -0
  42. tasks_toolkit-0.1.3/scripts/test_python_fast.sh +26 -0
  43. tasks_toolkit-0.1.3/scripts/test_python_lint.sh +22 -0
  44. tasks_toolkit-0.1.3/scripts/test_python_slow.sh +14 -0
  45. tasks_toolkit-0.1.3/scripts/test_run_stats.py +64 -0
  46. tasks_toolkit-0.1.3/scripts/test_runners.sh +588 -0
  47. tasks_toolkit-0.1.3/scripts/test_spec_lint.sh +438 -0
  48. tasks_toolkit-0.1.3/scripts/test_sprint_auto.py +50 -0
  49. tasks_toolkit-0.1.3/scripts/test_sprint_auto_execute.py +112 -0
  50. tasks_toolkit-0.1.3/scripts/test_sprint_auto_preflight.py +143 -0
  51. tasks_toolkit-0.1.3/scripts/test_sprint_auto_prepare.py +150 -0
  52. tasks_toolkit-0.1.3/scripts/test_sprint_auto_resume.py +104 -0
  53. tasks_toolkit-0.1.3/scripts/test_sprint_auto_state.py +157 -0
  54. tasks_toolkit-0.1.3/scripts/test_tasks.py +1678 -0
  55. tasks_toolkit-0.1.3/scripts/test_tasks_mcp.py +233 -0
  56. tasks_toolkit-0.1.3/scripts/test_tasks_plan_import.py +129 -0
  57. tasks_toolkit-0.1.3/scripts/test_tasks_storage.py +67 -0
  58. tasks_toolkit-0.1.3/scripts/test_tasks_watch.py +239 -0
  59. tasks_toolkit-0.1.3/scripts/test_worktree.sh +2112 -0
  60. tasks_toolkit-0.1.3/scripts/worktree.sh +992 -0
  61. tasks_toolkit-0.1.3/scripts/worktree_mr.py +133 -0
  62. tasks_toolkit-0.1.3/scripts/worktree_path.py +70 -0
  63. tasks_toolkit-0.1.3/scripts/worktree_sprint.py +70 -0
  64. tasks_toolkit-0.1.3/setup.cfg +4 -0
  65. tasks_toolkit-0.1.3/tasks_toolkit.egg-info/PKG-INFO +121 -0
  66. tasks_toolkit-0.1.3/tasks_toolkit.egg-info/SOURCES.txt +68 -0
  67. tasks_toolkit-0.1.3/tasks_toolkit.egg-info/dependency_links.txt +1 -0
  68. tasks_toolkit-0.1.3/tasks_toolkit.egg-info/entry_points.txt +4 -0
  69. tasks_toolkit-0.1.3/tasks_toolkit.egg-info/requires.txt +6 -0
  70. 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,7 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$root"
6
+
7
+ exec dotnet build VibeEmulator.slnx "$@"
@@ -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,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$root"
6
+
7
+ chmod +x .githooks/*
8
+ git config core.hooksPath .githooks
9
+ echo "Git hooks installed (core.hooksPath=.githooks)."
@@ -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/^/ /'; }