autopilot-code 0.1.0 β 0.2.1
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.
package/package.json
CHANGED
package/scripts/run_autopilot.py
CHANGED
|
@@ -792,17 +792,24 @@ def run_cycle(
|
|
|
792
792
|
continue
|
|
793
793
|
|
|
794
794
|
for issue in issues[:max_to_claim]:
|
|
795
|
-
msg = (
|
|
796
|
-
f"Autopilot claimed this issue at {time.strftime('%Y-%m-%d %H:%M:%S %Z')}.\n\n"
|
|
797
|
-
"Next: implement fix and open PR.\n\n"
|
|
798
|
-
)
|
|
799
795
|
print(f"[{cfg.repo}] next issue: #{issue['number']} {issue['title']}", flush=True)
|
|
800
796
|
if dry_run:
|
|
801
797
|
claimed_count += 1
|
|
802
798
|
continue
|
|
803
|
-
|
|
799
|
+
|
|
800
|
+
# Post initial claim comment
|
|
801
|
+
claim_msg = (
|
|
802
|
+
f"Autopilot claimed this issue at {time.strftime('%Y-%m-%d %H:%M:%S %Z')}.\n\n"
|
|
803
|
+
f"I'm now analyzing the issue and will begin implementation shortly.\n\n"
|
|
804
|
+
f"**Issue:** {issue['title']}"
|
|
805
|
+
)
|
|
806
|
+
claim_issue(cfg, issue, claim_msg)
|
|
804
807
|
claimed_count += 1
|
|
805
808
|
|
|
809
|
+
# Post comment indicating agent is about to start
|
|
810
|
+
start_msg = f"π Autopilot is now starting work on issue #{issue['number']}.\n\nI'll post regular progress updates as I work through the implementation."
|
|
811
|
+
sh(["gh", "issue", "comment", str(issue["number"]), "--repo", cfg.repo, "--body", start_msg])
|
|
812
|
+
|
|
806
813
|
# If agent==opencode, delegate to bash script
|
|
807
814
|
if cfg.agent == "opencode":
|
|
808
815
|
# Script is in the same directory as this Python file
|
|
@@ -22,6 +22,7 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
22
22
|
AUTO_FIX_CHECKS_MAX_ATTEMPTS=$(jq -r '.autoFixChecksMaxAttempts // 3' < .autopilot/autopilot.json)
|
|
23
23
|
ALLOWED_USERS=$(jq -r '.allowedMergeUsers[]' < .autopilot/autopilot.json 2>/dev/null || true)
|
|
24
24
|
AGENT_PATH=$(jq -r '.agentPath // ""' < .autopilot/autopilot.json)
|
|
25
|
+
ENABLE_PLANNING_STEP=$(jq -r '.enablePlanningStep // true' < .autopilot/autopilot.json)
|
|
25
26
|
else
|
|
26
27
|
REPO=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["repo"])')
|
|
27
28
|
AUTO_MERGE=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoMerge", True))')
|
|
@@ -32,6 +33,7 @@ else
|
|
|
32
33
|
AUTO_FIX_CHECKS_MAX_ATTEMPTS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoFixChecksMaxAttempts", 3))')
|
|
33
34
|
ALLOWED_USERS=$(python3 -c 'import json,sys; users=json.load(open(".autopilot/autopilot.json")).get("allowedMergeUsers", []); print("\n".join(users))' 2>/dev/null || true)
|
|
34
35
|
AGENT_PATH=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("agentPath", ""))' 2>/dev/null || true)
|
|
36
|
+
ENABLE_PLANNING_STEP=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("enablePlanningStep", True))' 2>/dev/null || echo "true")
|
|
35
37
|
fi
|
|
36
38
|
|
|
37
39
|
# Find opencode binary - check config, PATH, then common locations
|
|
@@ -122,30 +124,99 @@ Work rules:
|
|
|
122
124
|
- Commit with message: \"autopilot: work for issue #$ISSUE_NUMBER\".
|
|
123
125
|
- Push your changes to the remote branch $BRANCH.
|
|
124
126
|
- If the issue is a simple file-addition, just do it directly (no extra refactors)."
|
|
127
|
+
|
|
128
|
+
# 3.5. Planning step - analyze and plan before implementation
|
|
129
|
+
if [[ "$ENABLE_PLANNING_STEP" == "true" ]]; then
|
|
130
|
+
echo "[run_opencode_issue.sh] Running planning/analysis step..."
|
|
131
|
+
|
|
132
|
+
PLANNING_PROMPT="Please analyze the following GitHub issue and provide a detailed implementation plan. Do NOT make any code changes yet - only analyze and plan.
|
|
133
|
+
|
|
134
|
+
Issue #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
135
|
+
|
|
136
|
+
$ISSUE_BODY
|
|
137
|
+
|
|
138
|
+
Your task:
|
|
139
|
+
1. Analyze the issue requirements thoroughly
|
|
140
|
+
2. Explore the codebase to understand the relevant components
|
|
141
|
+
3. Identify the files that need to be modified or created
|
|
142
|
+
4. Outline the step-by-step approach for implementing this change
|
|
143
|
+
5. Note any potential issues, dependencies, or risks
|
|
144
|
+
6. List any assumptions you're making
|
|
145
|
+
|
|
146
|
+
Provide your analysis and plan in a clear, structured format. Focus on WHAT needs to be done and HOW you'll approach it."
|
|
147
|
+
|
|
148
|
+
# Run opencode to generate the plan
|
|
149
|
+
cd "$WORKTREE"
|
|
150
|
+
PLAN_OUTPUT=$("$OPENCODE_BIN" run "$PLANNING_PROMPT" 2>&1 || echo "Planning failed")
|
|
151
|
+
|
|
152
|
+
# Post the plan as a comment on the issue
|
|
153
|
+
PLAN_COMMENT="## π Implementation Plan
|
|
154
|
+
|
|
155
|
+
I've analyzed the issue and codebase. Here's my planned approach:
|
|
156
|
+
|
|
157
|
+
\`\`\`
|
|
158
|
+
$PLAN_OUTPUT
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
I will now proceed with implementation based on this plan. If you'd like me to adjust my approach, please add a comment with feedback."
|
|
162
|
+
|
|
163
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$PLAN_COMMENT" || true
|
|
164
|
+
echo "[run_opencode_issue.sh] Planning step completed and posted to issue."
|
|
165
|
+
fi
|
|
166
|
+
|
|
125
167
|
# 4. Run opencode inside worktree
|
|
126
168
|
cd "$WORKTREE"
|
|
127
169
|
"$OPENCODE_BIN" run "$PROMPT"
|
|
128
170
|
|
|
171
|
+
# 4.5. Comment that implementation is complete
|
|
172
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "β
Implementation complete.
|
|
173
|
+
|
|
174
|
+
I've finished implementing the solution. Now committing changes and creating PR..." || true
|
|
175
|
+
|
|
129
176
|
# 5. Commit any changes OpenCode made
|
|
130
177
|
if [[ -n "$(git status --porcelain)" ]]; then
|
|
131
178
|
git add -A
|
|
132
179
|
git commit -m "autopilot: work for issue #$ISSUE_NUMBER"
|
|
180
|
+
|
|
181
|
+
# Comment with commit info
|
|
182
|
+
COMMIT_SHA=$(git rev-parse --short HEAD)
|
|
183
|
+
CHANGES_COUNT=$(git diff-tree --no-commit-id --name-only -r HEAD | wc -l | xargs)
|
|
184
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "π Changes committed.
|
|
185
|
+
|
|
186
|
+
- Commit: \`$COMMIT_SHA\`
|
|
187
|
+
- Files changed: $CHANGES_COUNT
|
|
188
|
+
- Branch: \`$BRANCH\`
|
|
189
|
+
|
|
190
|
+
Next step: pushing to remote..." || true
|
|
191
|
+
else
|
|
192
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "βΉοΈ No changes were made by the AI agent. This might indicate the issue is already resolved or requires manual intervention." || true
|
|
133
193
|
fi
|
|
134
194
|
|
|
135
195
|
# 6. Ensure branch is pushed (no-op if already up to date)
|
|
136
196
|
git push -u origin "$BRANCH" || true
|
|
137
197
|
|
|
198
|
+
# 6.5. Comment after push
|
|
199
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "π€ Changes pushed to remote branch \`$BRANCH\`.
|
|
200
|
+
|
|
201
|
+
Now creating pull request..." || true
|
|
202
|
+
|
|
138
203
|
# 7. Create PR if one doesn't already exist
|
|
139
204
|
PR_URL=""
|
|
140
205
|
if gh pr view --repo "$REPO" --head "$BRANCH" --json url --jq .url >/dev/null 2>&1; then
|
|
141
206
|
PR_URL=$(gh pr view --repo "$REPO" --head "$BRANCH" --json url --jq .url)
|
|
207
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "π PR already exists: $PR_URL" || true
|
|
142
208
|
else
|
|
143
209
|
PR_URL=$(gh pr create --repo "$REPO" --title "Autopilot: Issue #$ISSUE_NUMBER" --body "Closes #$ISSUE_NUMBER" --base main --head "$BRANCH")
|
|
210
|
+
|
|
211
|
+
# Add short delay after PR creation to allow GitHub to calculate merge state
|
|
212
|
+
sleep 10
|
|
144
213
|
fi
|
|
145
214
|
|
|
146
215
|
# 8. Comment on issue with PR URL (best-effort)
|
|
147
216
|
if [[ -n "$PR_URL" ]]; then
|
|
148
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "
|
|
217
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "π PR created: $PR_URL
|
|
218
|
+
|
|
219
|
+
Autopilot has completed the initial implementation. The PR is now open and ready for review." || true
|
|
149
220
|
fi
|
|
150
221
|
|
|
151
222
|
# 8.5. Handle merge conflicts if auto-resolve is enabled
|
|
@@ -322,6 +393,11 @@ if [[ "$AUTO_MERGE" == "true" ]] && [[ -n "$PR_URL" ]]; then
|
|
|
322
393
|
if [[ "$USER_ALLOWED" == "true" ]]; then
|
|
323
394
|
echo "[run_opencode_issue.sh] Auto-merge enabled. Waiting for PR checks to pass..."
|
|
324
395
|
|
|
396
|
+
# Comment that we're waiting for checks
|
|
397
|
+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "β³ Waiting for PR checks to complete...
|
|
398
|
+
|
|
399
|
+
I'm monitoring the CI checks and will auto-merge once they pass. This may take a few minutes." || true
|
|
400
|
+
|
|
325
401
|
# Initialize auto-fix attempt counter
|
|
326
402
|
FIX_ATTEMPT=0
|
|
327
403
|
|
|
@@ -333,10 +409,45 @@ if [[ "$AUTO_MERGE" == "true" ]] && [[ -n "$PR_URL" ]]; then
|
|
|
333
409
|
while [[ $POLL_ATTEMPT -lt $MAX_POLL_ATTEMPTS ]]; do
|
|
334
410
|
POLL_ATTEMPT=$((POLL_ATTEMPT + 1))
|
|
335
411
|
|
|
336
|
-
# Get PR
|
|
337
|
-
|
|
412
|
+
# Get full PR status for debugging and decision making
|
|
413
|
+
PR_STATUS_JSON=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus,statusCheckRollup 2>/dev/null || echo '{"mergeable":"UNKNOWN","mergeStateStatus":"UNKNOWN","statusCheckRollup":null}')
|
|
414
|
+
|
|
415
|
+
# Log raw API response for debugging
|
|
416
|
+
echo "[run_opencode_issue.sh] Debug: Raw PR status = $PR_STATUS_JSON"
|
|
417
|
+
|
|
418
|
+
# Parse mergeStateStatus
|
|
419
|
+
if command -v jq >/dev/null 2>&1; then
|
|
420
|
+
MERGE_STATE_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.mergeStateStatus')
|
|
421
|
+
else
|
|
422
|
+
MERGE_STATE_STATUS=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("mergeStateStatus", "UNKNOWN"))' <<<"$PR_STATUS_JSON")
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
# Determine check status based on both statusCheckRollup and mergeStateStatus
|
|
426
|
+
# If mergeStateStatus is CLEAN or HAS_HOOKS and statusCheckRollup is null/empty β PASSED
|
|
427
|
+
# If mergeStateStatus is UNKNOWN β wait and retry
|
|
428
|
+
if [[ "$MERGE_STATE_STATUS" == "UNKNOWN" ]]; then
|
|
429
|
+
CHECK_STATUS="PENDING"
|
|
430
|
+
echo "[run_opencode_issue.sh] mergeStateStatus is UNKNOWN, waiting for GitHub to calculate..."
|
|
431
|
+
elif [[ "$MERGE_STATE_STATUS" == "CLEAN" || "$MERGE_STATE_STATUS" == "HAS_HOOKS" ]]; then
|
|
432
|
+
# Check statusCheckRollup - if null/empty, no checks configured, so PASSED
|
|
433
|
+
if command -v jq >/dev/null 2>&1; then
|
|
434
|
+
CHECK_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | if . == null or length == 0 then "PASSED" elif map(.conclusion) | any(. == "FAILURE") then "FAILED" elif map(.conclusion) | any(. == "PENDING") or any(. == "QUEUED") then "PENDING" else "PASSED" end')
|
|
435
|
+
else
|
|
436
|
+
CHECK_STATUS=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print("PASSED" if not scr or len(scr)==0 else "FAILED" if any(c.get("conclusion")=="FAILURE" for c in scr) else "PENDING" if any(c.get("conclusion") in ["PENDING","QUEUED"] for c in scr) else "PASSED")' <<<"$PR_STATUS_JSON")
|
|
437
|
+
fi
|
|
438
|
+
elif [[ "$MERGE_STATE_STATUS" == "BLOCKED" ]]; then
|
|
439
|
+
# If mergeStateStatus is BLOCKED, check if it's due to failed checks
|
|
440
|
+
if command -v jq >/dev/null 2>&1; then
|
|
441
|
+
CHECK_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | if . == null or length == 0 then "PASSED" elif map(.conclusion) | any(. == "FAILURE") then "FAILED" elif map(.conclusion) | any(. == "PENDING") or any(. == "QUEUED") then "PENDING" else "PASSED" end')
|
|
442
|
+
else
|
|
443
|
+
CHECK_STATUS=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print("PASSED" if not scr or len(scr)==0 else "FAILED" if any(c.get("conclusion")=="FAILURE" for c in scr) else "PENDING" if any(c.get("conclusion") in ["PENDING","QUEUED"] for c in scr) else "PASSED")' <<<"$PR_STATUS_JSON")
|
|
444
|
+
fi
|
|
445
|
+
else
|
|
446
|
+
# Other states (DRAFT, DIRTY, BEHIND) - mark as FAILED to exit
|
|
447
|
+
CHECK_STATUS="FAILED"
|
|
448
|
+
fi
|
|
338
449
|
|
|
339
|
-
echo "[run_opencode_issue.sh] Poll attempt $POLL_ATTEMPT/$MAX_POLL_ATTEMPTS: Check status = $CHECK_STATUS"
|
|
450
|
+
echo "[run_opencode_issue.sh] Poll attempt $POLL_ATTEMPT/$MAX_POLL_ATTEMPTS: Check status = $CHECK_STATUS, mergeStateStatus = $MERGE_STATE_STATUS"
|
|
340
451
|
|
|
341
452
|
if [[ "$CHECK_STATUS" == "PASSED" ]]; then
|
|
342
453
|
echo "[run_opencode_issue.sh] All checks passed. Proceeding with merge..."
|