turno 0.1.0__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 (47) hide show
  1. turno-0.1.0/LICENSE +21 -0
  2. turno-0.1.0/MANIFEST.in +3 -0
  3. turno-0.1.0/PKG-INFO +224 -0
  4. turno-0.1.0/README.md +201 -0
  5. turno-0.1.0/pyproject.toml +51 -0
  6. turno-0.1.0/setup.cfg +4 -0
  7. turno-0.1.0/turno/__init__.py +3 -0
  8. turno-0.1.0/turno/__main__.py +5 -0
  9. turno-0.1.0/turno/autopilot.py +1873 -0
  10. turno-0.1.0/turno/backlog.py +58 -0
  11. turno-0.1.0/turno/claude-agents.json +14 -0
  12. turno-0.1.0/turno/cli.py +7 -0
  13. turno-0.1.0/turno/codex-agents/implementer.toml +14 -0
  14. turno-0.1.0/turno/codex-agents/planner.toml +25 -0
  15. turno-0.1.0/turno/codex-agents/reviewer.toml +15 -0
  16. turno-0.1.0/turno/config.py +445 -0
  17. turno-0.1.0/turno/control.py +649 -0
  18. turno-0.1.0/turno/coord.py +200 -0
  19. turno-0.1.0/turno/engines.py +599 -0
  20. turno-0.1.0/turno/feed.py +82 -0
  21. turno-0.1.0/turno/history.py +71 -0
  22. turno-0.1.0/turno/messages.py +133 -0
  23. turno-0.1.0/turno/onboard.py +276 -0
  24. turno-0.1.0/turno/overrides.py +105 -0
  25. turno-0.1.0/turno/panel.py +418 -0
  26. turno-0.1.0/turno/paths.py +25 -0
  27. turno-0.1.0/turno/prompts/engine-claude.md +14 -0
  28. turno-0.1.0/turno/prompts/engine-codex.md +5 -0
  29. turno-0.1.0/turno/prompts/resume.md +14 -0
  30. turno-0.1.0/turno/prompts/setup.md +97 -0
  31. turno-0.1.0/turno/prompts/unit.md +74 -0
  32. turno-0.1.0/turno/pypi.py +114 -0
  33. turno-0.1.0/turno/repo_setup.py +199 -0
  34. turno-0.1.0/turno/schedule.py +74 -0
  35. turno-0.1.0/turno/server.py +263 -0
  36. turno-0.1.0/turno/service.py +201 -0
  37. turno-0.1.0/turno/upgrade.py +132 -0
  38. turno-0.1.0/turno/web/app.js +1168 -0
  39. turno-0.1.0/turno/web/i18n/en.js +244 -0
  40. turno-0.1.0/turno/web/i18n/pt-BR.js +244 -0
  41. turno-0.1.0/turno/web/index.html +39 -0
  42. turno-0.1.0/turno/web/style.css +129 -0
  43. turno-0.1.0/turno.egg-info/PKG-INFO +224 -0
  44. turno-0.1.0/turno.egg-info/SOURCES.txt +45 -0
  45. turno-0.1.0/turno.egg-info/dependency_links.txt +1 -0
  46. turno-0.1.0/turno.egg-info/entry_points.txt +2 -0
  47. turno-0.1.0/turno.egg-info/top_level.txt +1 -0
turno-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paulo Martins
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,3 @@
1
+ prune tests
2
+ prune scripts
3
+ prune .superpowers
turno-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: turno
3
+ Version: 0.1.0
4
+ Summary: Runs the Codex CLI and Claude Code unattended on your repositories, inside your subscription quota, with a local web panel.
5
+ Author: Paulo Martins
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/PauloFelipeM/turno
8
+ Project-URL: Issues, https://github.com/PauloFelipeM/turno/issues
9
+ Keywords: codex,claude-code,agents,scheduler,automation
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Software Development
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # turno
25
+
26
+ turno runs the Codex CLI and Claude Code unattended on several of your repositories, inside the
27
+ quota of your ChatGPT and Claude subscriptions. It works through each project's backlog one short
28
+ session at a time, following a fixed method: spec, then plan, then execution, with planner,
29
+ reviewer and implementer subagents. Decisions that matter reach your phone (ntfy) as a question
30
+ with buttons, or wait for you in the panel. A local web panel shows what is running, what happened
31
+ and how much of each backlog is left, and lets you control everything.
32
+
33
+ > **Status:** alpha. It runs one person's projects every day and is being generalized. The panel,
34
+ > the phone texts and what the agents write for you are in English or Brazilian Portuguese.
35
+ > Linux, macOS and Windows with WSL2.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pipx install turno # or: uv tool install turno
41
+ turno onboard
42
+ ```
43
+
44
+ `turno onboard` checks the machine (Python, git, tmux, Docker, the Codex and Claude CLIs), asks a
45
+ few questions (language, which engines to use, ntfy, the background service), creates `~/.turno`,
46
+ installs a service that starts the panel at boot, and opens the panel at http://localhost:8765.
47
+ Then add your projects in the panel's settings (⚙). From there on everything happens in the
48
+ browser: the service brings the panel up, the panel starts the enabled engines.
49
+
50
+ Scripted install: `turno onboard --language en --engines codex,claude --no-ntfy --service --yes`.
51
+ `turno onboard` is safe to run again: it never overwrites a file without asking.
52
+
53
+ ### Requirements
54
+
55
+ - Python 3.11 or newer (turno uses only the standard library)
56
+ - The Codex CLI and/or Claude Code, logged in with your subscription
57
+ - tmux (the loops run in it, so they outlive the terminal and the panel)
58
+ - git; Docker if your projects need it (turno brings each project's stack up before a unit; when
59
+ Docker is down it opens Docker Desktop on macOS and WSL, and waits for you on Linux)
60
+ - Optional: the [ntfy](https://ntfy.sh) app on your phone. Without it, decisions wait in the panel
61
+
62
+ ## How it works
63
+
64
+ - **Units.** A unit is one step of the cycle: apply decision answers, recover an interrupted task,
65
+ run the next task of a plan, finish an item, write a plan, write a spec or propose a backlog. Each
66
+ unit is one short CLI session. The orchestrator ends it by writing
67
+ `<project>/.superpowers/autopilot/last-unit.json` (`worked`, `waiting`, `idle` or `blocked`) and
68
+ `backlog.json` (the progress the panel shows).
69
+ - **Quota.** Before a unit, turno reads the 5-hour and weekly usage windows and only starts if the
70
+ usage plus 1.5 times the unit's learned average cost fits under the project's cap. Projects are in
71
+ priority order; a project that does not fit does not hold up a cheaper one.
72
+ - **Nothing left half-done.** An interrupted unit (usage limit, error, end of a time window) is
73
+ resumed before anything else once its wait is over.
74
+ - **Two engines.** One loop runs Codex, another runs Claude Code (by default only on weekends). They
75
+ never work on the same project, or on two projects that share Docker ports, at the same time. A
76
+ unit cut at the end of the Claude window is handed over (the "baton") to the other engine.
77
+
78
+ Each project describes its own rules in `docs/autopilot.md`: where the backlog, specs, plans and
79
+ ledgers live, how to verify work and the git policy. The panel can draft that file for you (see
80
+ "Preparing a new project"). The protocol the orchestrator follows is in
81
+ [`turno/prompts/unit.md`](turno/prompts/unit.md).
82
+
83
+ ## Safety model
84
+
85
+ - **Your work is not touched.** A project with uncommitted changes that are not turno's is skipped,
86
+ and its Docker stack is not stopped. The only thing turno itself changes in a project is one line
87
+ in the clone's `.git/info/exclude`; everything else is written by the agent, inside the project's
88
+ own rules.
89
+ - **Fixed rules above any message.** The agents never weaken tests, read `.env` files, force-push
90
+ or drop data, whatever a message says (step 7 of the protocol).
91
+ - **The panel is local.** It listens on 127.0.0.1 only, refuses other hosts and origins, and every
92
+ write needs a custom header a foreign page cannot send.
93
+ - **The ntfy topic is a secret.** It lives in `~/.turno/secrets.toml` (mode 600), is shown once when
94
+ `onboard` creates it, and never appears in the config, the logs or the panel. Anyone who knows the
95
+ topic can answer decisions and send messages to the agents.
96
+ - **No surprise installs.** The panel checks PyPI for a newer version at most once a day (a single
97
+ GET; `"update_check": false` turns it off) and only tells you; `turno update` upgrades when you run it.
98
+
99
+ ## Configuration
100
+
101
+ Everything lives in `~/.turno` (or `$TURNO_HOME`): `config.json`, the state, logs, run files and
102
+ history. The panel's settings screen edits `config.json`; the loops re-read it on every turn, so a
103
+ change applies within one turn, without a restart. An invalid file never stops a loop: it keeps the
104
+ last good configuration and logs the problem.
105
+
106
+ | Part | What it holds |
107
+ |---|---|
108
+ | `projects` | Name, path, optional `docker_group` (projects that bind the same ports) and `weekly_cap` per engine. The order is the priority |
109
+ | `engines.<engine>` | `enabled`, `autostart` (the service starts it), orchestrator model and effort, `roles` (planner, implementer, reviewer), `primary_cap`, `safety_factor`, initial costs, `blocked_cooldown_hours`, `schedule` (a weekly window) |
110
+ | `language` | `en` or `pt-BR`: the panel, the phone texts and what the agents write for you |
111
+ | `notify` | `enabled`, the ntfy server and the decision timeout. The topic stays in `secrets.toml` |
112
+ | `update_check` | `false` stops the daily PyPI check |
113
+
114
+ [`config.example.json`](config.example.json) is a complete example. A project's name never changes
115
+ after it is created: state, claims, notes and history use it.
116
+
117
+ ## The panel
118
+
119
+ The panel shows the quota of each engine with its cap and reset time, what each loop is doing,
120
+ every project with its state and backlog progress, the running unit live (commands, file changes,
121
+ which subagent was called) and the unit history with outcome, duration and cost.
122
+
123
+ | Control | What it does |
124
+ |---|---|
125
+ | Start / Stop (per engine) | Starts the loop in tmux, or stops it like a Ctrl+C: the unit is resumed on the next start |
126
+ | "Needs you" | Pending decisions to answer (with an optional comment) and blocked projects to reactivate |
127
+ | Pause, Next, Interrupt (project card) | Skip a project; give it the next new unit; stop its running unit and pause it |
128
+ | Project panel | A message to the agents, the project's caps and, while it waits to retry, "Try now" |
129
+ | ⚙ Settings | Projects (add, edit, reorder, remove), Engines (on/off, start with the panel, models, caps, window, a quota check) and General (language, ntfy) |
130
+ | Footer | The running version, a notice when PyPI has a newer one, and "Restart turno" |
131
+
132
+ "Restart turno" does what `turno restart` does: the loops stop (their units resume), the service
133
+ restarts the panel, and the panel starts the engines again.
134
+
135
+ ## Commands
136
+
137
+ | Command | What it does |
138
+ |---|---|
139
+ | `turno onboard` | Check the machine, write `~/.turno`, install the service, open the panel |
140
+ | `turno serve [--port 8765] [--start-engines]` | The panel; `--start-engines` also starts the enabled engines (the service uses it) |
141
+ | `turno service install\|uninstall\|status` | The background service: a systemd user unit on Linux and WSL, a LaunchAgent on macOS |
142
+ | `turno restart` | Stop the loops, restart the service; the panel starts them again |
143
+ | `turno update` | `pipx upgrade turno` or `uv tool upgrade turno`, then `turno restart` |
144
+ | `turno start` / `turno stop` | Start or stop the loops of the enabled engines in the tmux session `autopilot` |
145
+ | `turno status` / `turno check` | Quota and project states / what the loop would do now (reads the quota) |
146
+ | `turno decisions`, `turno answer <project> <id> <option>` | Pending decisions, from the terminal |
147
+ | `turno reset <project>` | Put an `idle`, `blocked` or `waiting` project back to `active` |
148
+ | `turno test-notify` | Send a test notification |
149
+ | `turno --engine claude <command>` | Any command for the Claude instance (the default is `codex`) |
150
+
151
+ The service runs `turno serve --start-engines`. Its unit says `KillMode=process`, so only the panel
152
+ belongs to it: the loops live in tmux and stay alive when the service restarts or stops. Without systemd (WSL without it, containers) or
153
+ launchd, keep the panel running yourself, for example in tmux. On Linux, run
154
+ `loginctl enable-linger $USER` once so the service starts at boot without a login.
155
+
156
+ ## Decisions and messages from the phone
157
+
158
+ When a decision is significant (user-facing behaviour, money, a data model that is hard to reverse,
159
+ legal matters, scope), the orchestrator writes it with two or three options and a default, and the
160
+ project waits. Answer with the notification buttons or by typing the letter in the topic (`B`, or
161
+ `my-app B` when several projects are waiting). Without an answer, the default applies at the
162
+ deadline. Without ntfy the question waits in the panel's "Needs you" strip, with the same deadline.
163
+ Any other text you type in the topic becomes a message to the agents of the projects it names (or
164
+ of all of them), read by the next unit.
165
+
166
+ ## Preparing a new project
167
+
168
+ A repository that never ran turno needs `docs/autopilot.md` and `.superpowers/` ignored by git. In
169
+ ⚙ Settings, a project without that file shows "Prepare with an agent": write what the project should
170
+ become, and a draft unit proposes `docs/autopilot.md`, an `AGENTS.md` if there is none, the initial
171
+ backlog and a short note for you. Review it in the project panel, ask for changes or approve; the
172
+ apply unit makes one commit, `docs: prepare for turno`, on the current branch (no push).
173
+
174
+ ## Updating
175
+
176
+ ```bash
177
+ turno update
178
+ ```
179
+
180
+ It upgrades with the tool that installed turno and restarts. The panel tells you when a newer
181
+ version exists; it never installs anything by itself.
182
+
183
+ ### Upgrading from a source checkout
184
+
185
+ If you ran turno from a clone (`python3 autopilot.py ...`), keep the clone and install it in
186
+ editable mode, so `git pull` keeps updating it. Order matters: a loop that is still running with
187
+ the old code reads `prompts/*.md` and `claude-agents.json` from the clone on every unit, and those
188
+ files move into `turno/` with the `git pull`. So stop everything first:
189
+
190
+ ```bash
191
+ cd ~/src/turno
192
+ python3 autopilot.py stop # still the old code: stops the loops (stop a hand-started `serve` too)
193
+ git pull
194
+ pipx install -e .
195
+ turno service install
196
+ turno restart # or: turno start
197
+ ```
198
+
199
+ Stop a hand-started `serve` before installing the service: port 8765 must be free.
200
+
201
+ `~/.turno` (config, state, history) is used as it is. `python3 autopilot.py ...` keeps working, and
202
+ `turno stop` recognises loops started either way. With an editable install `turno update` only
203
+ reminds you to `git pull`.
204
+
205
+ ## Troubleshooting
206
+
207
+ - **The loops stopped after a reboot.** Check the service with `turno service status`; on Linux,
208
+ `loginctl enable-linger $USER`. Without a service, run `turno start`; units resume where they stopped.
209
+ - **Sleep or hibernation pauses everything.** Keep the machine awake while it should work.
210
+ - **A project never runs.** Uncommitted changes that are not turno's keep it out until the tree is
211
+ clean; `turno check` says what the loop would do and why.
212
+ - **Logs.** `~/.turno/autopilot.log` and `autopilot-claude.log` (the loops), `restart.log` (restarts
213
+ from the panel), and the `.jsonl` and `.err` files of the latest runs in `runs/` and
214
+ `runs-claude/`. The panel's history links each unit to its runs. With systemd,
215
+ `journalctl --user -u turno` has the panel's output.
216
+
217
+ ## Contributing
218
+
219
+ See [CONTRIBUTING.md](CONTRIBUTING.md). The invariants a change must keep are in
220
+ [AGENTS.md](AGENTS.md).
221
+
222
+ ## License
223
+
224
+ MIT. See [LICENSE](LICENSE).
turno-0.1.0/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # turno
2
+
3
+ turno runs the Codex CLI and Claude Code unattended on several of your repositories, inside the
4
+ quota of your ChatGPT and Claude subscriptions. It works through each project's backlog one short
5
+ session at a time, following a fixed method: spec, then plan, then execution, with planner,
6
+ reviewer and implementer subagents. Decisions that matter reach your phone (ntfy) as a question
7
+ with buttons, or wait for you in the panel. A local web panel shows what is running, what happened
8
+ and how much of each backlog is left, and lets you control everything.
9
+
10
+ > **Status:** alpha. It runs one person's projects every day and is being generalized. The panel,
11
+ > the phone texts and what the agents write for you are in English or Brazilian Portuguese.
12
+ > Linux, macOS and Windows with WSL2.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pipx install turno # or: uv tool install turno
18
+ turno onboard
19
+ ```
20
+
21
+ `turno onboard` checks the machine (Python, git, tmux, Docker, the Codex and Claude CLIs), asks a
22
+ few questions (language, which engines to use, ntfy, the background service), creates `~/.turno`,
23
+ installs a service that starts the panel at boot, and opens the panel at http://localhost:8765.
24
+ Then add your projects in the panel's settings (⚙). From there on everything happens in the
25
+ browser: the service brings the panel up, the panel starts the enabled engines.
26
+
27
+ Scripted install: `turno onboard --language en --engines codex,claude --no-ntfy --service --yes`.
28
+ `turno onboard` is safe to run again: it never overwrites a file without asking.
29
+
30
+ ### Requirements
31
+
32
+ - Python 3.11 or newer (turno uses only the standard library)
33
+ - The Codex CLI and/or Claude Code, logged in with your subscription
34
+ - tmux (the loops run in it, so they outlive the terminal and the panel)
35
+ - git; Docker if your projects need it (turno brings each project's stack up before a unit; when
36
+ Docker is down it opens Docker Desktop on macOS and WSL, and waits for you on Linux)
37
+ - Optional: the [ntfy](https://ntfy.sh) app on your phone. Without it, decisions wait in the panel
38
+
39
+ ## How it works
40
+
41
+ - **Units.** A unit is one step of the cycle: apply decision answers, recover an interrupted task,
42
+ run the next task of a plan, finish an item, write a plan, write a spec or propose a backlog. Each
43
+ unit is one short CLI session. The orchestrator ends it by writing
44
+ `<project>/.superpowers/autopilot/last-unit.json` (`worked`, `waiting`, `idle` or `blocked`) and
45
+ `backlog.json` (the progress the panel shows).
46
+ - **Quota.** Before a unit, turno reads the 5-hour and weekly usage windows and only starts if the
47
+ usage plus 1.5 times the unit's learned average cost fits under the project's cap. Projects are in
48
+ priority order; a project that does not fit does not hold up a cheaper one.
49
+ - **Nothing left half-done.** An interrupted unit (usage limit, error, end of a time window) is
50
+ resumed before anything else once its wait is over.
51
+ - **Two engines.** One loop runs Codex, another runs Claude Code (by default only on weekends). They
52
+ never work on the same project, or on two projects that share Docker ports, at the same time. A
53
+ unit cut at the end of the Claude window is handed over (the "baton") to the other engine.
54
+
55
+ Each project describes its own rules in `docs/autopilot.md`: where the backlog, specs, plans and
56
+ ledgers live, how to verify work and the git policy. The panel can draft that file for you (see
57
+ "Preparing a new project"). The protocol the orchestrator follows is in
58
+ [`turno/prompts/unit.md`](turno/prompts/unit.md).
59
+
60
+ ## Safety model
61
+
62
+ - **Your work is not touched.** A project with uncommitted changes that are not turno's is skipped,
63
+ and its Docker stack is not stopped. The only thing turno itself changes in a project is one line
64
+ in the clone's `.git/info/exclude`; everything else is written by the agent, inside the project's
65
+ own rules.
66
+ - **Fixed rules above any message.** The agents never weaken tests, read `.env` files, force-push
67
+ or drop data, whatever a message says (step 7 of the protocol).
68
+ - **The panel is local.** It listens on 127.0.0.1 only, refuses other hosts and origins, and every
69
+ write needs a custom header a foreign page cannot send.
70
+ - **The ntfy topic is a secret.** It lives in `~/.turno/secrets.toml` (mode 600), is shown once when
71
+ `onboard` creates it, and never appears in the config, the logs or the panel. Anyone who knows the
72
+ topic can answer decisions and send messages to the agents.
73
+ - **No surprise installs.** The panel checks PyPI for a newer version at most once a day (a single
74
+ GET; `"update_check": false` turns it off) and only tells you; `turno update` upgrades when you run it.
75
+
76
+ ## Configuration
77
+
78
+ Everything lives in `~/.turno` (or `$TURNO_HOME`): `config.json`, the state, logs, run files and
79
+ history. The panel's settings screen edits `config.json`; the loops re-read it on every turn, so a
80
+ change applies within one turn, without a restart. An invalid file never stops a loop: it keeps the
81
+ last good configuration and logs the problem.
82
+
83
+ | Part | What it holds |
84
+ |---|---|
85
+ | `projects` | Name, path, optional `docker_group` (projects that bind the same ports) and `weekly_cap` per engine. The order is the priority |
86
+ | `engines.<engine>` | `enabled`, `autostart` (the service starts it), orchestrator model and effort, `roles` (planner, implementer, reviewer), `primary_cap`, `safety_factor`, initial costs, `blocked_cooldown_hours`, `schedule` (a weekly window) |
87
+ | `language` | `en` or `pt-BR`: the panel, the phone texts and what the agents write for you |
88
+ | `notify` | `enabled`, the ntfy server and the decision timeout. The topic stays in `secrets.toml` |
89
+ | `update_check` | `false` stops the daily PyPI check |
90
+
91
+ [`config.example.json`](config.example.json) is a complete example. A project's name never changes
92
+ after it is created: state, claims, notes and history use it.
93
+
94
+ ## The panel
95
+
96
+ The panel shows the quota of each engine with its cap and reset time, what each loop is doing,
97
+ every project with its state and backlog progress, the running unit live (commands, file changes,
98
+ which subagent was called) and the unit history with outcome, duration and cost.
99
+
100
+ | Control | What it does |
101
+ |---|---|
102
+ | Start / Stop (per engine) | Starts the loop in tmux, or stops it like a Ctrl+C: the unit is resumed on the next start |
103
+ | "Needs you" | Pending decisions to answer (with an optional comment) and blocked projects to reactivate |
104
+ | Pause, Next, Interrupt (project card) | Skip a project; give it the next new unit; stop its running unit and pause it |
105
+ | Project panel | A message to the agents, the project's caps and, while it waits to retry, "Try now" |
106
+ | ⚙ Settings | Projects (add, edit, reorder, remove), Engines (on/off, start with the panel, models, caps, window, a quota check) and General (language, ntfy) |
107
+ | Footer | The running version, a notice when PyPI has a newer one, and "Restart turno" |
108
+
109
+ "Restart turno" does what `turno restart` does: the loops stop (their units resume), the service
110
+ restarts the panel, and the panel starts the engines again.
111
+
112
+ ## Commands
113
+
114
+ | Command | What it does |
115
+ |---|---|
116
+ | `turno onboard` | Check the machine, write `~/.turno`, install the service, open the panel |
117
+ | `turno serve [--port 8765] [--start-engines]` | The panel; `--start-engines` also starts the enabled engines (the service uses it) |
118
+ | `turno service install\|uninstall\|status` | The background service: a systemd user unit on Linux and WSL, a LaunchAgent on macOS |
119
+ | `turno restart` | Stop the loops, restart the service; the panel starts them again |
120
+ | `turno update` | `pipx upgrade turno` or `uv tool upgrade turno`, then `turno restart` |
121
+ | `turno start` / `turno stop` | Start or stop the loops of the enabled engines in the tmux session `autopilot` |
122
+ | `turno status` / `turno check` | Quota and project states / what the loop would do now (reads the quota) |
123
+ | `turno decisions`, `turno answer <project> <id> <option>` | Pending decisions, from the terminal |
124
+ | `turno reset <project>` | Put an `idle`, `blocked` or `waiting` project back to `active` |
125
+ | `turno test-notify` | Send a test notification |
126
+ | `turno --engine claude <command>` | Any command for the Claude instance (the default is `codex`) |
127
+
128
+ The service runs `turno serve --start-engines`. Its unit says `KillMode=process`, so only the panel
129
+ belongs to it: the loops live in tmux and stay alive when the service restarts or stops. Without systemd (WSL without it, containers) or
130
+ launchd, keep the panel running yourself, for example in tmux. On Linux, run
131
+ `loginctl enable-linger $USER` once so the service starts at boot without a login.
132
+
133
+ ## Decisions and messages from the phone
134
+
135
+ When a decision is significant (user-facing behaviour, money, a data model that is hard to reverse,
136
+ legal matters, scope), the orchestrator writes it with two or three options and a default, and the
137
+ project waits. Answer with the notification buttons or by typing the letter in the topic (`B`, or
138
+ `my-app B` when several projects are waiting). Without an answer, the default applies at the
139
+ deadline. Without ntfy the question waits in the panel's "Needs you" strip, with the same deadline.
140
+ Any other text you type in the topic becomes a message to the agents of the projects it names (or
141
+ of all of them), read by the next unit.
142
+
143
+ ## Preparing a new project
144
+
145
+ A repository that never ran turno needs `docs/autopilot.md` and `.superpowers/` ignored by git. In
146
+ ⚙ Settings, a project without that file shows "Prepare with an agent": write what the project should
147
+ become, and a draft unit proposes `docs/autopilot.md`, an `AGENTS.md` if there is none, the initial
148
+ backlog and a short note for you. Review it in the project panel, ask for changes or approve; the
149
+ apply unit makes one commit, `docs: prepare for turno`, on the current branch (no push).
150
+
151
+ ## Updating
152
+
153
+ ```bash
154
+ turno update
155
+ ```
156
+
157
+ It upgrades with the tool that installed turno and restarts. The panel tells you when a newer
158
+ version exists; it never installs anything by itself.
159
+
160
+ ### Upgrading from a source checkout
161
+
162
+ If you ran turno from a clone (`python3 autopilot.py ...`), keep the clone and install it in
163
+ editable mode, so `git pull` keeps updating it. Order matters: a loop that is still running with
164
+ the old code reads `prompts/*.md` and `claude-agents.json` from the clone on every unit, and those
165
+ files move into `turno/` with the `git pull`. So stop everything first:
166
+
167
+ ```bash
168
+ cd ~/src/turno
169
+ python3 autopilot.py stop # still the old code: stops the loops (stop a hand-started `serve` too)
170
+ git pull
171
+ pipx install -e .
172
+ turno service install
173
+ turno restart # or: turno start
174
+ ```
175
+
176
+ Stop a hand-started `serve` before installing the service: port 8765 must be free.
177
+
178
+ `~/.turno` (config, state, history) is used as it is. `python3 autopilot.py ...` keeps working, and
179
+ `turno stop` recognises loops started either way. With an editable install `turno update` only
180
+ reminds you to `git pull`.
181
+
182
+ ## Troubleshooting
183
+
184
+ - **The loops stopped after a reboot.** Check the service with `turno service status`; on Linux,
185
+ `loginctl enable-linger $USER`. Without a service, run `turno start`; units resume where they stopped.
186
+ - **Sleep or hibernation pauses everything.** Keep the machine awake while it should work.
187
+ - **A project never runs.** Uncommitted changes that are not turno's keep it out until the tree is
188
+ clean; `turno check` says what the loop would do and why.
189
+ - **Logs.** `~/.turno/autopilot.log` and `autopilot-claude.log` (the loops), `restart.log` (restarts
190
+ from the panel), and the `.jsonl` and `.err` files of the latest runs in `runs/` and
191
+ `runs-claude/`. The panel's history links each unit to its runs. With systemd,
192
+ `journalctl --user -u turno` has the panel's output.
193
+
194
+ ## Contributing
195
+
196
+ See [CONTRIBUTING.md](CONTRIBUTING.md). The invariants a change must keep are in
197
+ [AGENTS.md](AGENTS.md).
198
+
199
+ ## License
200
+
201
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "turno"
7
+ dynamic = ["version"]
8
+ description = "Runs the Codex CLI and Claude Code unattended on your repositories, inside your subscription quota, with a local web panel."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [{ name = "Paulo Martins" }]
14
+ keywords = ["codex", "claude-code", "agents", "scheduler", "automation"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Environment :: Web Environment",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: MacOS",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Topic :: Software Development",
25
+ ]
26
+ dependencies = []
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/PauloFelipeM/turno"
30
+ Issues = "https://github.com/PauloFelipeM/turno/issues"
31
+
32
+ [project.scripts]
33
+ turno = "turno.cli:main"
34
+
35
+ [tool.setuptools]
36
+ # The data folders have no __init__.py; they are listed so setuptools does not warn that they are absent.
37
+ packages = ["turno", "turno.prompts", "turno.web", "turno.web.i18n"]
38
+
39
+ [tool.setuptools.dynamic]
40
+ version = { attr = "turno.__version__" }
41
+
42
+ [tool.setuptools.package-data]
43
+ turno = [
44
+ "prompts/*.md",
45
+ "claude-agents.json",
46
+ "codex-agents/*.toml",
47
+ "web/*.html",
48
+ "web/*.js",
49
+ "web/*.css",
50
+ "web/i18n/*.js",
51
+ ]
turno-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """turno: runs the Codex and Claude Code CLIs unattended on several projects, inside their quota."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ """`python -m turno ...`: the same as the `turno` command."""
2
+
3
+ from .cli import main
4
+
5
+ main()