dev-autopilot 0.4.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.
- dev_autopilot-0.4.0/LICENSE +21 -0
- dev_autopilot-0.4.0/PKG-INFO +148 -0
- dev_autopilot-0.4.0/README.md +121 -0
- dev_autopilot-0.4.0/autopilot/__init__.py +4 -0
- dev_autopilot-0.4.0/autopilot/__main__.py +6 -0
- dev_autopilot-0.4.0/autopilot/backends/__init__.py +159 -0
- dev_autopilot-0.4.0/autopilot/backends/claude_cli.py +368 -0
- dev_autopilot-0.4.0/autopilot/backends/command.py +90 -0
- dev_autopilot-0.4.0/autopilot/cli.py +1614 -0
- dev_autopilot-0.4.0/autopilot/commands/__init__.py +0 -0
- dev_autopilot-0.4.0/autopilot/commands/preflight.py +131 -0
- dev_autopilot-0.4.0/autopilot/config.py +155 -0
- dev_autopilot-0.4.0/autopilot/context.py +414 -0
- dev_autopilot-0.4.0/autopilot/dash_term.py +493 -0
- dev_autopilot-0.4.0/autopilot/dash_web.py +347 -0
- dev_autopilot-0.4.0/autopilot/dashboard.py +410 -0
- dev_autopilot-0.4.0/autopilot/decisions.py +85 -0
- dev_autopilot-0.4.0/autopilot/deploy.py +67 -0
- dev_autopilot-0.4.0/autopilot/docs.py +301 -0
- dev_autopilot-0.4.0/autopilot/doctor.py +478 -0
- dev_autopilot-0.4.0/autopilot/gate.py +383 -0
- dev_autopilot-0.4.0/autopilot/github_util.py +42 -0
- dev_autopilot-0.4.0/autopilot/gitops.py +251 -0
- dev_autopilot-0.4.0/autopilot/growth.py +85 -0
- dev_autopilot-0.4.0/autopilot/handoff.py +27 -0
- dev_autopilot-0.4.0/autopilot/layers.py +134 -0
- dev_autopilot-0.4.0/autopilot/log_scrub.py +11 -0
- dev_autopilot-0.4.0/autopilot/mcp.py +104 -0
- dev_autopilot-0.4.0/autopilot/notify.py +98 -0
- dev_autopilot-0.4.0/autopilot/orchestrator.py +2214 -0
- dev_autopilot-0.4.0/autopilot/plain.py +238 -0
- dev_autopilot-0.4.0/autopilot/plan.py +320 -0
- dev_autopilot-0.4.0/autopilot/proc.py +176 -0
- dev_autopilot-0.4.0/autopilot/prompts/auditor.md +39 -0
- dev_autopilot-0.4.0/autopilot/prompts/decide.md +38 -0
- dev_autopilot-0.4.0/autopilot/prompts/docs.md +48 -0
- dev_autopilot-0.4.0/autopilot/prompts/fixer.md +26 -0
- dev_autopilot-0.4.0/autopilot/prompts/onboard.md +43 -0
- dev_autopilot-0.4.0/autopilot/prompts/plan_writer.md +40 -0
- dev_autopilot-0.4.0/autopilot/prompts/replanner.md +42 -0
- dev_autopilot-0.4.0/autopilot/prompts/research.md +25 -0
- dev_autopilot-0.4.0/autopilot/prompts/resume.md +10 -0
- dev_autopilot-0.4.0/autopilot/prompts/retry.md +8 -0
- dev_autopilot-0.4.0/autopilot/prompts/review.md +40 -0
- dev_autopilot-0.4.0/autopilot/prompts/system.md +16 -0
- dev_autopilot-0.4.0/autopilot/prompts/task.md +78 -0
- dev_autopilot-0.4.0/autopilot/prompts/triage.md +30 -0
- dev_autopilot-0.4.0/autopilot/prompts/unstick.md +37 -0
- dev_autopilot-0.4.0/autopilot/prompts/verify.md +32 -0
- dev_autopilot-0.4.0/autopilot/repomap.py +71 -0
- dev_autopilot-0.4.0/autopilot/report.py +633 -0
- dev_autopilot-0.4.0/autopilot/rights.py +184 -0
- dev_autopilot-0.4.0/autopilot/schemas.py +42 -0
- dev_autopilot-0.4.0/autopilot/state.py +298 -0
- dev_autopilot-0.4.0/autopilot/templates/__pycache__/cloudflare_apply.cpython-313.pyc +0 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/BRAIN.md +21 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/CLAUDE.md.snippet +10 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/DECISIONS.md +5 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/HANDOFF.md +3 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/gitignore +10 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/ide_rules.md +8 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/plan.yaml +48 -0
- dev_autopilot-0.4.0/autopilot/templates/agent/project.yaml +259 -0
- dev_autopilot-0.4.0/autopilot/templates/cloudflare_apply.py +211 -0
- dev_autopilot-0.4.0/autopilot/templates/deploy.sh +72 -0
- dev_autopilot-0.4.0/autopilot/templates/email_verify.py +144 -0
- dev_autopilot-0.4.0/autopilot/templates/infra/cloudflare.yaml +11 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/docker.yaml +4 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/generic.yaml +3 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/go.yaml +6 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/java.yaml +4 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/node.yaml +7 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/python.yaml +6 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/rust.yaml +5 -0
- dev_autopilot-0.4.0/autopilot/templates/presets/static.yaml +3 -0
- dev_autopilot-0.4.0/autopilot/upgrade.py +192 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/PKG-INFO +148 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/SOURCES.txt +144 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/dependency_links.txt +1 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/entry_points.txt +3 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/requires.txt +7 -0
- dev_autopilot-0.4.0/dev_autopilot.egg-info/top_level.txt +1 -0
- dev_autopilot-0.4.0/pyproject.toml +45 -0
- dev_autopilot-0.4.0/setup.cfg +4 -0
- dev_autopilot-0.4.0/tests/test_adopt.py +221 -0
- dev_autopilot-0.4.0/tests/test_autofix.py +119 -0
- dev_autopilot-0.4.0/tests/test_autopilot.py +353 -0
- dev_autopilot-0.4.0/tests/test_backend.py +169 -0
- dev_autopilot-0.4.0/tests/test_cache.py +48 -0
- dev_autopilot-0.4.0/tests/test_chat.py +167 -0
- dev_autopilot-0.4.0/tests/test_conflicts.py +42 -0
- dev_autopilot-0.4.0/tests/test_consolidate.py +53 -0
- dev_autopilot-0.4.0/tests/test_dash_term.py +564 -0
- dev_autopilot-0.4.0/tests/test_dash_web.py +175 -0
- dev_autopilot-0.4.0/tests/test_dashboard.py +124 -0
- dev_autopilot-0.4.0/tests/test_dashboard_rows.py +82 -0
- dev_autopilot-0.4.0/tests/test_dashboard_time.py +58 -0
- dev_autopilot-0.4.0/tests/test_decide.py +103 -0
- dev_autopilot-0.4.0/tests/test_deploy.py +77 -0
- dev_autopilot-0.4.0/tests/test_deps.py +187 -0
- dev_autopilot-0.4.0/tests/test_docs_session.py +59 -0
- dev_autopilot-0.4.0/tests/test_doctor.py +134 -0
- dev_autopilot-0.4.0/tests/test_functional.py +158 -0
- dev_autopilot-0.4.0/tests/test_gate.py +349 -0
- dev_autopilot-0.4.0/tests/test_github_identity.py +43 -0
- dev_autopilot-0.4.0/tests/test_handoff.py +81 -0
- dev_autopilot-0.4.0/tests/test_heal.py +299 -0
- dev_autopilot-0.4.0/tests/test_ide_rules.py +88 -0
- dev_autopilot-0.4.0/tests/test_infra_templates.py +212 -0
- dev_autopilot-0.4.0/tests/test_intake.py +182 -0
- dev_autopilot-0.4.0/tests/test_killed_usage.py +65 -0
- dev_autopilot-0.4.0/tests/test_layers.py +44 -0
- dev_autopilot-0.4.0/tests/test_lean.py +95 -0
- dev_autopilot-0.4.0/tests/test_lifecycle.py +231 -0
- dev_autopilot-0.4.0/tests/test_mcp.py +40 -0
- dev_autopilot-0.4.0/tests/test_needs_you.py +290 -0
- dev_autopilot-0.4.0/tests/test_new_file_gate.py +301 -0
- dev_autopilot-0.4.0/tests/test_opening.py +51 -0
- dev_autopilot-0.4.0/tests/test_orchestrator.py +269 -0
- dev_autopilot-0.4.0/tests/test_parallel.py +253 -0
- dev_autopilot-0.4.0/tests/test_plan_source.py +55 -0
- dev_autopilot-0.4.0/tests/test_preflight.py +113 -0
- dev_autopilot-0.4.0/tests/test_presets.py +181 -0
- dev_autopilot-0.4.0/tests/test_proc.py +60 -0
- dev_autopilot-0.4.0/tests/test_prompt_contract_task.py +8 -0
- dev_autopilot-0.4.0/tests/test_quality.py +50 -0
- dev_autopilot-0.4.0/tests/test_quickstart.py +447 -0
- dev_autopilot-0.4.0/tests/test_reliability.py +283 -0
- dev_autopilot-0.4.0/tests/test_repomap.py +46 -0
- dev_autopilot-0.4.0/tests/test_research.py +80 -0
- dev_autopilot-0.4.0/tests/test_review.py +133 -0
- dev_autopilot-0.4.0/tests/test_rights.py +121 -0
- dev_autopilot-0.4.0/tests/test_roadmap.py +104 -0
- dev_autopilot-0.4.0/tests/test_routing.py +179 -0
- dev_autopilot-0.4.0/tests/test_runner_detect.py +309 -0
- dev_autopilot-0.4.0/tests/test_sandbox.py +60 -0
- dev_autopilot-0.4.0/tests/test_simple_view.py +331 -0
- dev_autopilot-0.4.0/tests/test_stats.py +144 -0
- dev_autopilot-0.4.0/tests/test_status_line.py +230 -0
- dev_autopilot-0.4.0/tests/test_status_page.py +153 -0
- dev_autopilot-0.4.0/tests/test_sync.py +176 -0
- dev_autopilot-0.4.0/tests/test_three_actions.py +116 -0
- dev_autopilot-0.4.0/tests/test_upgrade.py +163 -0
- dev_autopilot-0.4.0/tests/test_usage.py +114 -0
- dev_autopilot-0.4.0/tests/test_version.py +42 -0
- dev_autopilot-0.4.0/tests/test_window.py +117 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Manish Kumar
|
|
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,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dev-autopilot
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Autopilot builds your software project while you are away. You write what you want in plain words and start it once. You get working software, tested, with a record of every step.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/manishjnv/AutoPilot
|
|
7
|
+
Project-URL: Documentation, https://github.com/manishjnv/AutoPilot#readme
|
|
8
|
+
Project-URL: Changelog, https://github.com/manishjnv/AutoPilot/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/manishjnv/AutoPilot/issues
|
|
10
|
+
Keywords: autonomous,coding-agent,claude-code,developer-tools
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: PyYAML>=6.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Provides-Extra: aws
|
|
25
|
+
Requires-Dist: boto3>=1.26; extra == "aws"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Autopilot
|
|
29
|
+
|
|
30
|
+
Autopilot builds your software project while you are away. You write what you want in plain words and start it once. You get working software, tested, with a record of every step.
|
|
31
|
+
It is for one developer, or for a person with an idea and a developer friend.
|
|
32
|
+
|
|
33
|
+
**Give it a plan tonight. Wake up to a working app, and proof that it works.** Claude Code (the coding agent) writes the code; Python code decides when a task is complete.
|
|
34
|
+
|
|
35
|
+
[](https://github.com/manishjnv/AutoPilot/actions/workflows/ci.yml)
|
|
36
|
+

|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
## Claude Code alone and with Autopilot
|
|
40
|
+
|
|
41
|
+
| | Claude Code alone | With Autopilot |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Direction | Claude can choose the design as it works. | Your plan sets the design. The gate refuses plan changes. |
|
|
44
|
+
| Checks | The model can say that work is done. | Autopilot runs your build, lint and test commands. A task merges only if they pass. |
|
|
45
|
+
| Failures | You find and repair a problem. | A stronger model tries again. Autopilot repairs a missing tool or a red main branch. |
|
|
46
|
+
| Questions | The chat waits for you. | That task waits. Other tasks continue. |
|
|
47
|
+
| Status | You scroll the chat. | `ap watch` shows tasks, output, progress and usage. |
|
|
48
|
+
|
|
49
|
+
The [full comparison](ARCHITECTURE.md#13-claude-code-alone-and-with-autopilot) has ten rows. Use Claude Code alone
|
|
50
|
+
for a small change, a question, or an open design.
|
|
51
|
+
|
|
52
|
+
## How it works
|
|
53
|
+
|
|
54
|
+

|
|
55
|
+
|
|
56
|
+
```mermaid
|
|
57
|
+
flowchart LR
|
|
58
|
+
accTitle: How Autopilot builds a plan
|
|
59
|
+
accDescr: Each task runs in a new Claude Code session. Python runs the checks. A pass merges to main, a fail retries on a stronger model, and a blocked task becomes a question while the other tasks continue. At the end, a completion audit adds tasks for gaps.
|
|
60
|
+
plan["Plan"] --> task["Next task"]
|
|
61
|
+
task --> session["New Claude Code<br/>session"]
|
|
62
|
+
session --> gate{"Python runs<br/>the checks"}
|
|
63
|
+
gate -->|pass| merge["Merge to main"]
|
|
64
|
+
gate -->|fail| retry["Retry on a<br/>stronger model"]
|
|
65
|
+
retry --> session
|
|
66
|
+
retry -->|blocked| park["Question in NEEDS-YOU.md,<br/>build the other tasks"]
|
|
67
|
+
park --> task
|
|
68
|
+
merge --> task
|
|
69
|
+
merge -->|plan done| audit{"Completion<br/>audit"}
|
|
70
|
+
audit -->|gaps| task
|
|
71
|
+
audit -->|complete| app["Working app"]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Follow the build
|
|
75
|
+
|
|
76
|
+
`ap watch` shows the build in the terminal; the status page shows the same data in your browser (sample data).
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
## Real results
|
|
83
|
+
|
|
84
|
+
Two real projects built from a plan:
|
|
85
|
+
|
|
86
|
+
- **SecretScan** finds leaked secrets in a repository.
|
|
87
|
+
- **envguard** checks `.env` files against a schema and finds secrets in the git history.
|
|
88
|
+
|
|
89
|
+
| Measure | SecretScan | envguard |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| Tasks merged | **27 of 27**: 13 from the plan, 14 from the audits | **21 of 21**: 11 from the plan, 10 from the audits |
|
|
92
|
+
| Blocked tasks | **0** | **0** |
|
|
93
|
+
| Passed on the first attempt | 25 of 27 | 17 of 21 |
|
|
94
|
+
| Decisions that needed a person | **0** | **0** |
|
|
95
|
+
| Agent time | 2.2 hours, 38 sessions | 1.4 hours, 42 sessions |
|
|
96
|
+
| Cost at API prices | $17.13 | $19.01 |
|
|
97
|
+
|
|
98
|
+
Both runs used a subscription. The envguard audits found 8 real bugs; Autopilot fixed all. envguard is private; numbers come from the run log. A lean rerun of envguard used 39.5 percent fewer tokens per task.
|
|
99
|
+
|
|
100
|
+
## Install
|
|
101
|
+
|
|
102
|
+
One line. It installs what is missing for your user, with no administrator prompt.
|
|
103
|
+
Windows (PowerShell):
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
irm https://raw.githubusercontent.com/manishjnv/AutoPilot/main/install.ps1 | iex
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
macOS, Linux or WSL:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
curl -fsSL https://raw.githubusercontent.com/manishjnv/AutoPilot/main/install.sh | bash
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
On Windows pick Windows or WSL, not both; WSL only for the sandbox. With [uv](https://docs.astral.sh/uv/) already
|
|
116
|
+
installed, `uv tool install dev-autopilot` is the same.
|
|
117
|
+
Open a project folder and type `ap`. The first run logs you in to Claude (a paid plan, Pro or Max), then starts.
|
|
118
|
+
See [What `ap` does](HOWTO.md#what-ap-does-in-a-folder). After a release, run `ap upgrade`. For a repair or a
|
|
119
|
+
second machine, run `ap install`. Checksums and each step: [Install with one script](HOWTO.md#install-with-one-script).
|
|
120
|
+
|
|
121
|
+
## Example output
|
|
122
|
+
|
|
123
|
+
Shortened real output:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
task P02-T01 attempt 1 failed: PROBLEM: skip/focus marker added in tests/test_walker.py
|
|
127
|
+
session #10 task P02-T01 model=sonnet attempt=2
|
|
128
|
+
task P02-T01 done (sonnet, $0.39)
|
|
129
|
+
audit: completion audit: 92% complete, 8 corrective tasks (FIX001)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Documentation
|
|
133
|
+
|
|
134
|
+
| To | Read |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Build your first project | [Tutorial](GUIDE.md) |
|
|
137
|
+
| Do one job: a server, phone alerts, an existing project | [How-to guides](HOWTO.md) |
|
|
138
|
+
| Look up a command, setting, file or term | [Reference](REFERENCE.md) |
|
|
139
|
+
| How Autopilot works and why | [Architecture](ARCHITECTURE.md) |
|
|
140
|
+
|
|
141
|
+
## Good to know
|
|
142
|
+
|
|
143
|
+
> [!WARNING]
|
|
144
|
+
> Use a sandbox on a server: sessions run without permission prompts. See
|
|
145
|
+
> [Run Autopilot on a server](HOWTO.md#run-autopilot-on-a-server).
|
|
146
|
+
|
|
147
|
+
- Version 0.3.0, an early release. [MIT license](LICENSE).
|
|
148
|
+
- One CLI agent per session. See [limits and runners](HOWTO.md#limits-and-supported-agent-clis).
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Autopilot
|
|
2
|
+
|
|
3
|
+
Autopilot builds your software project while you are away. You write what you want in plain words and start it once. You get working software, tested, with a record of every step.
|
|
4
|
+
It is for one developer, or for a person with an idea and a developer friend.
|
|
5
|
+
|
|
6
|
+
**Give it a plan tonight. Wake up to a working app, and proof that it works.** Claude Code (the coding agent) writes the code; Python code decides when a task is complete.
|
|
7
|
+
|
|
8
|
+
[](https://github.com/manishjnv/AutoPilot/actions/workflows/ci.yml)
|
|
9
|
+

|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
## Claude Code alone and with Autopilot
|
|
13
|
+
|
|
14
|
+
| | Claude Code alone | With Autopilot |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Direction | Claude can choose the design as it works. | Your plan sets the design. The gate refuses plan changes. |
|
|
17
|
+
| Checks | The model can say that work is done. | Autopilot runs your build, lint and test commands. A task merges only if they pass. |
|
|
18
|
+
| Failures | You find and repair a problem. | A stronger model tries again. Autopilot repairs a missing tool or a red main branch. |
|
|
19
|
+
| Questions | The chat waits for you. | That task waits. Other tasks continue. |
|
|
20
|
+
| Status | You scroll the chat. | `ap watch` shows tasks, output, progress and usage. |
|
|
21
|
+
|
|
22
|
+
The [full comparison](ARCHITECTURE.md#13-claude-code-alone-and-with-autopilot) has ten rows. Use Claude Code alone
|
|
23
|
+
for a small change, a question, or an open design.
|
|
24
|
+
|
|
25
|
+
## How it works
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart LR
|
|
31
|
+
accTitle: How Autopilot builds a plan
|
|
32
|
+
accDescr: Each task runs in a new Claude Code session. Python runs the checks. A pass merges to main, a fail retries on a stronger model, and a blocked task becomes a question while the other tasks continue. At the end, a completion audit adds tasks for gaps.
|
|
33
|
+
plan["Plan"] --> task["Next task"]
|
|
34
|
+
task --> session["New Claude Code<br/>session"]
|
|
35
|
+
session --> gate{"Python runs<br/>the checks"}
|
|
36
|
+
gate -->|pass| merge["Merge to main"]
|
|
37
|
+
gate -->|fail| retry["Retry on a<br/>stronger model"]
|
|
38
|
+
retry --> session
|
|
39
|
+
retry -->|blocked| park["Question in NEEDS-YOU.md,<br/>build the other tasks"]
|
|
40
|
+
park --> task
|
|
41
|
+
merge --> task
|
|
42
|
+
merge -->|plan done| audit{"Completion<br/>audit"}
|
|
43
|
+
audit -->|gaps| task
|
|
44
|
+
audit -->|complete| app["Working app"]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Follow the build
|
|
48
|
+
|
|
49
|
+
`ap watch` shows the build in the terminal; the status page shows the same data in your browser (sample data).
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
## Real results
|
|
56
|
+
|
|
57
|
+
Two real projects built from a plan:
|
|
58
|
+
|
|
59
|
+
- **SecretScan** finds leaked secrets in a repository.
|
|
60
|
+
- **envguard** checks `.env` files against a schema and finds secrets in the git history.
|
|
61
|
+
|
|
62
|
+
| Measure | SecretScan | envguard |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| Tasks merged | **27 of 27**: 13 from the plan, 14 from the audits | **21 of 21**: 11 from the plan, 10 from the audits |
|
|
65
|
+
| Blocked tasks | **0** | **0** |
|
|
66
|
+
| Passed on the first attempt | 25 of 27 | 17 of 21 |
|
|
67
|
+
| Decisions that needed a person | **0** | **0** |
|
|
68
|
+
| Agent time | 2.2 hours, 38 sessions | 1.4 hours, 42 sessions |
|
|
69
|
+
| Cost at API prices | $17.13 | $19.01 |
|
|
70
|
+
|
|
71
|
+
Both runs used a subscription. The envguard audits found 8 real bugs; Autopilot fixed all. envguard is private; numbers come from the run log. A lean rerun of envguard used 39.5 percent fewer tokens per task.
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
One line. It installs what is missing for your user, with no administrator prompt.
|
|
76
|
+
Windows (PowerShell):
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
irm https://raw.githubusercontent.com/manishjnv/AutoPilot/main/install.ps1 | iex
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
macOS, Linux or WSL:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
curl -fsSL https://raw.githubusercontent.com/manishjnv/AutoPilot/main/install.sh | bash
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
On Windows pick Windows or WSL, not both; WSL only for the sandbox. With [uv](https://docs.astral.sh/uv/) already
|
|
89
|
+
installed, `uv tool install dev-autopilot` is the same.
|
|
90
|
+
Open a project folder and type `ap`. The first run logs you in to Claude (a paid plan, Pro or Max), then starts.
|
|
91
|
+
See [What `ap` does](HOWTO.md#what-ap-does-in-a-folder). After a release, run `ap upgrade`. For a repair or a
|
|
92
|
+
second machine, run `ap install`. Checksums and each step: [Install with one script](HOWTO.md#install-with-one-script).
|
|
93
|
+
|
|
94
|
+
## Example output
|
|
95
|
+
|
|
96
|
+
Shortened real output:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
task P02-T01 attempt 1 failed: PROBLEM: skip/focus marker added in tests/test_walker.py
|
|
100
|
+
session #10 task P02-T01 model=sonnet attempt=2
|
|
101
|
+
task P02-T01 done (sonnet, $0.39)
|
|
102
|
+
audit: completion audit: 92% complete, 8 corrective tasks (FIX001)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
| To | Read |
|
|
108
|
+
|---|---|
|
|
109
|
+
| Build your first project | [Tutorial](GUIDE.md) |
|
|
110
|
+
| Do one job: a server, phone alerts, an existing project | [How-to guides](HOWTO.md) |
|
|
111
|
+
| Look up a command, setting, file or term | [Reference](REFERENCE.md) |
|
|
112
|
+
| How Autopilot works and why | [Architecture](ARCHITECTURE.md) |
|
|
113
|
+
|
|
114
|
+
## Good to know
|
|
115
|
+
|
|
116
|
+
> [!WARNING]
|
|
117
|
+
> Use a sandbox on a server: sessions run without permission prompts. See
|
|
118
|
+
> [Run Autopilot on a server](HOWTO.md#run-autopilot-on-a-server).
|
|
119
|
+
|
|
120
|
+
- Version 0.3.0, an early release. [MIT license](LICENSE).
|
|
121
|
+
- One CLI agent per session. See [limits and runners](HOWTO.md#limits-and-supported-agent-clis).
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Agent backends. Each runs ONE fresh session and returns a SessionResult."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
|
|
9
|
+
# Anchored to a line start: the CLI prints these as its own message, so prose inside a failed session doesn't count.
|
|
10
|
+
LIMIT_RX = re.compile(r"^\s*(?:you've|you have) hit your (?:[\w-]+ ){0,3}limit"
|
|
11
|
+
r"(?:\s*[·•|\-–—,.]*\s*resets?\s+(?P<reset>[^\n]+))?", re.I | re.M)
|
|
12
|
+
API_LIMIT_RX = re.compile(r"^\s*API Error: (?:429|529)\b|^\s*API Error:.*(?:rate_limit_error|overloaded_error)", re.I | re.M)
|
|
13
|
+
_RESET_RX = re.compile(r"^(?:(?P<mon>[a-z]{3,9})\.?\s+(?P<day>\d{1,2})\s*(?:,|at)?\s*)?(?P<h>\d{1,2})(?::(?P<m>\d{2}))?"
|
|
14
|
+
r"\s*(?P<ap>am|pm)?\s*(?:\((?P<tz>[^)]+)\))?$", re.I)
|
|
15
|
+
_MONTHS = {m: i + 1 for i, m in enumerate("jan feb mar apr may jun jul aug sep oct nov dec".split())}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def parse_reset(s: str, now: datetime) -> float | None:
|
|
19
|
+
"""Next future occurrence of a CLI reset time like `3:45pm`, `Oct 3, 9am (Asia/Calcutta)`; None if unparseable."""
|
|
20
|
+
try:
|
|
21
|
+
m = _RESET_RX.match((s or "").strip().rstrip(".").strip())
|
|
22
|
+
if not m or not (m["ap"] or m["m"]):
|
|
23
|
+
return None
|
|
24
|
+
tz = now.tzinfo
|
|
25
|
+
if m["tz"]:
|
|
26
|
+
try:
|
|
27
|
+
from zoneinfo import ZoneInfo
|
|
28
|
+
tz = ZoneInfo(m["tz"].strip())
|
|
29
|
+
except Exception: # noqa: BLE001 no tzdata / unknown zone: fall back to local time
|
|
30
|
+
pass
|
|
31
|
+
if now.tzinfo:
|
|
32
|
+
now = now.astimezone(tz)
|
|
33
|
+
h, mi = int(m["h"]), int(m["m"] or 0)
|
|
34
|
+
if m["ap"]:
|
|
35
|
+
if not 1 <= h <= 12:
|
|
36
|
+
return None
|
|
37
|
+
h = h % 12 + (12 if m["ap"].lower() == "pm" else 0)
|
|
38
|
+
if m["mon"]:
|
|
39
|
+
mon = _MONTHS.get(m["mon"][:3].lower())
|
|
40
|
+
if not mon:
|
|
41
|
+
return None
|
|
42
|
+
t = now.replace(month=mon, day=int(m["day"]), hour=h, minute=mi, second=0, microsecond=0)
|
|
43
|
+
if t <= now:
|
|
44
|
+
t = t.replace(year=t.year + 1)
|
|
45
|
+
else:
|
|
46
|
+
t = now.replace(hour=h, minute=mi, second=0, microsecond=0)
|
|
47
|
+
if t <= now:
|
|
48
|
+
t += timedelta(days=1)
|
|
49
|
+
return t.timestamp()
|
|
50
|
+
except Exception: # noqa: BLE001
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def detect_limit(text: str, now: datetime | None = None) -> tuple[bool, float | None]:
|
|
55
|
+
"""(limited, reset_at epoch). Only the CLI's subscription message or API 429/529 errors count, never agent prose."""
|
|
56
|
+
text = text or ""
|
|
57
|
+
m = LIMIT_RX.search(text)
|
|
58
|
+
if m:
|
|
59
|
+
return True, (parse_reset(m["reset"], now or datetime.now().astimezone()) if m["reset"] else None)
|
|
60
|
+
return bool(API_LIMIT_RX.search(text)), None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Failures of the machinery, not of the work: retried by the orchestrator, never counted as a task attempt.
|
|
64
|
+
# Anchored to a line start like LIMIT_RX, so the agent's own prose about these words never counts.
|
|
65
|
+
_AUTH_RX = re.compile(r"^\s*(?:Invalid API key|Please run /login|OAuth token (?:has )?expired|API Error: 401\b)",
|
|
66
|
+
re.I | re.M)
|
|
67
|
+
# Node's own error shapes ("reason: getaddrinfo ENOTFOUND", "Error: connect ECONNREFUSED"), not a bare code in prose
|
|
68
|
+
_NET_RX = re.compile(r"^\s*API Error: (?:Connection error|Request timed out|50[0234]\b)|"
|
|
69
|
+
r"(?:reason:|Error:)\s+(?:getaddrinfo |connect |read |write )?"
|
|
70
|
+
r"(?:ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN)\b", re.I | re.M)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def detect_infra(text: str) -> str:
|
|
74
|
+
"""'auth' (not logged in / key rejected), 'network' (connection, timeout, server 5xx) or ''."""
|
|
75
|
+
text = text or ""
|
|
76
|
+
return "auth" if _AUTH_RX.search(text) else "network" if _NET_RX.search(text) else ""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class SessionRequest:
|
|
81
|
+
prompt: str
|
|
82
|
+
model: str
|
|
83
|
+
cwd: str
|
|
84
|
+
timeout_sec: int = 3600
|
|
85
|
+
budget_usd: float | None = None
|
|
86
|
+
system_append: str = ""
|
|
87
|
+
read_only: bool = False
|
|
88
|
+
log_path: str = ""
|
|
89
|
+
resume: str = "" # continue this CLI session instead of starting fresh
|
|
90
|
+
resume_totals: dict = field(default_factory=dict) # that session's totals so far (see SessionResult.totals)
|
|
91
|
+
schema: dict | None = None # JSON Schema of the final report (schemas.REPORTS)
|
|
92
|
+
effort: str = "" # overrides agent.effort for this session
|
|
93
|
+
web_only: bool = False # research: only web search/fetch and reading files
|
|
94
|
+
mcp_config: str = "" # MCP servers for this session only (e.g. Playwright)
|
|
95
|
+
no_tools: bool = False # triage of untrusted text: no file, shell, web or MCP tools
|
|
96
|
+
label: str = "" # live progress lines: "P01-T01 Project skeleton [haiku]"
|
|
97
|
+
on_context: object = None # J5: called with the session's context size (tokens)
|
|
98
|
+
on_tokens: object = None # G8: called with the session's running token total
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class SessionResult:
|
|
103
|
+
ok: bool
|
|
104
|
+
text: str = ""
|
|
105
|
+
cost: float = 0.0
|
|
106
|
+
session_id: str = ""
|
|
107
|
+
report: dict = field(default_factory=dict)
|
|
108
|
+
error: str = ""
|
|
109
|
+
rate_limited: bool = False
|
|
110
|
+
reset_at: float | None = None
|
|
111
|
+
timed_out: bool = False
|
|
112
|
+
stuck: bool = False # killed for repeating the same action (stream-json watch)
|
|
113
|
+
usage: dict = field(default_factory=dict) # model -> {input, output, cache_read, cache_write, cost}
|
|
114
|
+
num_turns: int = 0
|
|
115
|
+
duration_ms: int = 0
|
|
116
|
+
totals: dict = field(default_factory=dict) # whole-session {cost, usage, num_turns, duration_ms}, for a later resume
|
|
117
|
+
infra: str = "" # 'cli' | 'auth' | 'network': the machinery failed, not the work
|
|
118
|
+
window: dict = field(default_factory=dict) # G1: the CLI's own usage figure {five_hour: {pct, reset}, seven_day, status}
|
|
119
|
+
config: dict = field(default_factory=dict) # I1: what the session loaded from the user's Claude config (names, counts)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def since(totals: dict, base: dict) -> dict:
|
|
123
|
+
"""A resumed CLI session reports whole-session totals; keep only what this run added (never below zero)."""
|
|
124
|
+
def sub(a, b):
|
|
125
|
+
return max(0, (a or 0) - (b or 0))
|
|
126
|
+
bu = base.get("usage") or {}
|
|
127
|
+
usage = {m: {k: sub(v, (bu.get(m) or {}).get(k)) for k, v in u.items()} for m, u in (totals.get("usage") or {}).items()}
|
|
128
|
+
return {"cost": sub(totals.get("cost"), base.get("cost")), "usage": usage,
|
|
129
|
+
"num_turns": sub(totals.get("num_turns"), base.get("num_turns")),
|
|
130
|
+
"duration_ms": sub(totals.get("duration_ms"), base.get("duration_ms"))}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def parse_report(text: str) -> dict:
|
|
134
|
+
"""Extract the last ```json block (or bare JSON object) from the agent's final message."""
|
|
135
|
+
blocks = re.findall(r"```(?:json)?\s*(\{.*?\})\s*```", text or "", re.S)
|
|
136
|
+
candidates = list(reversed(blocks))
|
|
137
|
+
if not candidates:
|
|
138
|
+
m = re.search(r"(\{[^{}]*\"status\".*\})\s*$", text or "", re.S)
|
|
139
|
+
if m:
|
|
140
|
+
candidates = [m.group(1)]
|
|
141
|
+
for c in candidates:
|
|
142
|
+
try:
|
|
143
|
+
data = json.loads(c)
|
|
144
|
+
if isinstance(data, dict):
|
|
145
|
+
return data
|
|
146
|
+
except json.JSONDecodeError:
|
|
147
|
+
continue
|
|
148
|
+
return {}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_backend(cfg):
|
|
152
|
+
kind = cfg.get("agent.backend", "claude_cli")
|
|
153
|
+
if kind == "claude_cli":
|
|
154
|
+
from .claude_cli import ClaudeCLIBackend
|
|
155
|
+
return ClaudeCLIBackend(cfg)
|
|
156
|
+
if kind == "command":
|
|
157
|
+
from .command import CommandBackend
|
|
158
|
+
return CommandBackend(cfg)
|
|
159
|
+
raise ValueError(f"unknown backend {kind}")
|