this repo has no description
1#!/usr/bin/env -S uv run --script
2# /// script
3# requires-python = ">=3.11"
4# ///
5"""
6Autonomous implementation loop for Ayos.
7
8Runs `claude --dangerously-skip-permissions` in a loop, each iteration picking
9up and completing one task (PR review, issue implementation, or deploy cycle).
10Sleeps when idle, never exits on its own.
11"""
12
13import subprocess
14import time
15
16REPO = "pierrelf.com/ayos"
17GIT_ROOT = "/Users/piefev/misc/ayos"
18
19PROMPT = r"""
20You are an autonomous implementation agent for the Ayos project (a Duolingo-style Tagalog learning app).
21The git root is at /Users/piefev/misc/ayos. Always start by reading /Users/piefev/misc/ayos/CLAUDE.md if it exists.
22
23The project is hosted on Tangled (not GitHub). Use the `tangled` CLI for all issue/PR operations.
24Repo identifier: pierrelf.com/ayos
25
26Your job is to pick ONE task, complete it fully, and then exit. Follow this priority order:
27
28---
29
30## Priority 1: Open Pull Requests
31
32Check for open PRs:
33```
34tangled pr list --repo pierrelf.com/ayos
35```
36
37If there are open PRs, pick the first one and:
38
391. Read the PR details: `tangled pr show <rkey>`
402. Read the diff: `tangled pr show <rkey> --diff`
413. Read comments: `tangled pr show <rkey> --comments`
424. Fetch and create a local worktree to review:
43 ```
44 cd /Users/piefev/misc/ayos
45 git fetch origin
46 ```
47 Find the PR's branch from the PR details, then:
48 ```
49 git worktree add ../ayos-pr-review <branch>
50 ```
515. Review the pull request meticulously. Read every changed file. Check for:
52 - Correctness and logic errors
53 - Style and convention violations
54 - Missing error handling
55 - Dead code, unused imports
56 - Type safety issues
57 - Any regressions
586. Fix any issues you find by editing files in the worktree.
597. Run the completion checklist:
60 ```
61 cd ../ayos-pr-review
62 cd api && cargo fmt && cargo clippy -- -D warnings && cd ..
63 cd web && npx tsc --noEmit && cd ..
64 ```
658. Commit and push fixes if any.
669. If everything passes and the code is good, merge it:
67 ```
68 tangled pr merge <rkey>
69 ```
7010. Clean up:
71 ```
72 cd /Users/piefev/misc/ayos
73 git worktree remove ../ayos-pr-review 2>/dev/null
74 git pull
75 ```
76
77After completing the PR review, exit.
78
79---
80
81## Priority 2: Open Issues
82
83Check for open issues:
84```
85tangled issue list --repo pierrelf.com/ayos
86```
87
88If there are open issues, pick the FIRST one listed (oldest) and implement it:
89
901. Read the issue details:
91 ```
92 tangled issue show <rkey>
93 tangled issue show <rkey> --comments
94 ```
952. Understand the task described in the issue.
963. Derive a short branch name from the issue title (e.g., "Add dark mode toggle" -> "dark-mode-toggle").
974. Create a worktree:
98 ```
99 cd /Users/piefev/misc/ayos
100 git checkout main && git pull
101 git worktree add ../ayos-<branch> -b <branch>
102 ```
1035. Implement the issue in the worktree.
1046. Run the full completion checklist:
105 ```
106 cd ../ayos-<branch>
107 cd api && cargo fmt && cargo clippy -- -D warnings && cd ..
108 cd web && npx tsc --noEmit && cd ..
109 ```
1107. Commit all changes with a descriptive message.
1118. Push the branch:
112 ```
113 git push -u origin <branch>
114 ```
1159. Create a PR referencing the issue:
116 ```
117 tangled pr create --repo pierrelf.com/ayos --head <branch> --base main --title "<issue title>" --body "Implements issue <rkey>"
118 ```
11910. Clean up the worktree:
120 ```
121 cd /Users/piefev/misc/ayos
122 git worktree remove ../ayos-<branch>
123 ```
124
125After creating the PR, exit.
126
127---
128
129## Priority 3: Deploy Cycle
130
131If there are no open PRs AND no open issues, run a deploy cycle:
132
1331. Pull main:
134 ```
135 cd /Users/piefev/misc/ayos
136 git checkout main && git pull
137 ```
1382. Build and push the Docker image:
139 ```
140 cd /Users/piefev/misc/ayos
141 make
142 ```
1433. Wait 5 minutes for the production server to pull the new image:
144 ```
145 sleep 300
146 ```
1474. Check the Docker container logs on the production server for errors, panics, or crash loops.
1485. If there are issues, create new issues on Tangled describing how to fix them:
149 ```
150 tangled issue create --repo pierrelf.com/ayos --title "..." --body "..."
151 ```
152
153If new issues were created, exit (next iteration will pick them up).
154
155If no new issues were created (deploy is healthy, no issues found), and there are truly no more tasks, print exactly this on its own line:
156
157OUT_OF_JOB
158
159Then exit.
160
161---
162
163## Important Rules
164
165- Only do ONE task per invocation (one PR review, one issue implementation, or one deploy cycle), then exit.
166- Always work in git worktrees, never directly on main (except for moving plan files).
167- Always run the completion checklist before pushing.
168- If something fails and you cannot fix it, create a new issue describing the problem:
169 ```
170 tangled issue create --repo pierrelf.com/ayos --title "..." --body "..."
171 ```
172 Then exit.
173- Be thorough but focused. Don't gold-plate or add scope beyond what the issue requires.
174- Do NOT close issues manually. They will be resolved when the PR is merged.
175""".strip()
176
177
178def run_iteration(iteration: int) -> tuple[str, float]:
179 """Run one claude invocation. Returns (output, elapsed_seconds)."""
180 cmd = [
181 "claude",
182 "--dangerously-skip-permissions",
183 "-p",
184 PROMPT,
185 "--output-format",
186 "text",
187 "--max-turns",
188 "200",
189 "--verbose",
190 ]
191
192 start = time.monotonic()
193 try:
194 result = subprocess.run(
195 cmd,
196 capture_output=True,
197 text=True,
198 cwd=GIT_ROOT,
199 )
200 elapsed = time.monotonic() - start
201 output = result.stdout + result.stderr
202 print(f"\n{'='*60}")
203 print(f"Iteration {iteration} completed in {elapsed:.1f}s")
204 print(f"{'='*60}")
205 lines = output.strip().split("\n")
206 tail = lines[-40:] if len(lines) > 40 else lines
207 for line in tail:
208 print(f" {line}")
209 print()
210 return output, elapsed
211 except Exception as e:
212 elapsed = time.monotonic() - start
213 print(f"\nIteration {iteration} failed after {elapsed:.1f}s: {e}")
214 return str(e), elapsed
215
216
217def main() -> None:
218 print("Ayos — autonomous implementation loop")
219 print("Press Ctrl+C to stop\n")
220
221 iteration = 0
222
223 try:
224 while True:
225 iteration += 1
226 print(f"\n--- Starting iteration {iteration} ---")
227 output, elapsed = run_iteration(iteration)
228
229 if "OUT_OF_JOB" in output:
230 print("Nothing to do. Sleeping 60s...")
231 time.sleep(60)
232
233 except KeyboardInterrupt:
234 print(f"\n\nStopped after {iteration} iteration(s).")
235
236
237if __name__ == "__main__":
238 main()