autopilot-code 0.2.4 → 0.3.0

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/dist/cli.js CHANGED
@@ -300,6 +300,15 @@ function logsSystemdService() {
300
300
  (0, node_child_process_1.spawnSync)("journalctl", args, { stdio: "inherit" });
301
301
  }
302
302
  async function initCommand() {
303
+ const logo = `
304
+ _ _ _ _
305
+ (_) |_ _ ___ ___| |_ __ _| |
306
+ | | __| |/ _ \\/ __| __/ _\` | |
307
+ | | |_| | (_) \\__ \\ || (_| | |
308
+ |_|\\__|_|\\___/|___/\\__\\__,_|_|
309
+ `;
310
+ console.log(logo);
311
+ console.log();
303
312
  const cwd = process.cwd();
304
313
  const validation = validatePath(cwd);
305
314
  // 1. Ensure global config exists
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autopilot-code",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Repo-issue–driven autopilot runner",
6
6
  "license": "MIT",
@@ -0,0 +1,3 @@
1
+ from .base import BaseAgent, AgentResult
2
+
3
+ __all__ = ["BaseAgent", "AgentResult"]
@@ -0,0 +1,130 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import Optional
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass
8
+ class AgentResult:
9
+ success: bool
10
+ session_id: Optional[str]
11
+ output: str
12
+ error: Optional[str] = None
13
+
14
+
15
+ class BaseAgent(ABC):
16
+ def __init__(self, config: dict):
17
+ self.config = config
18
+ self._binary_path: Optional[str] = None
19
+
20
+ @property
21
+ @abstractmethod
22
+ def name(self) -> str:
23
+ pass
24
+
25
+ @property
26
+ @abstractmethod
27
+ def supports_sessions(self) -> bool:
28
+ pass
29
+
30
+ @abstractmethod
31
+ def find_binary(self) -> str:
32
+ pass
33
+
34
+ @property
35
+ def binary_path(self) -> str:
36
+ if self._binary_path is None:
37
+ self._binary_path = self.find_binary()
38
+ return self._binary_path
39
+
40
+ @abstractmethod
41
+ def run(
42
+ self, worktree: Path, prompt: str, session_id: Optional[str] = None
43
+ ) -> AgentResult:
44
+ pass
45
+
46
+ def run_planning(
47
+ self,
48
+ worktree: Path,
49
+ issue_number: int,
50
+ issue_title: str,
51
+ issue_body: str,
52
+ session_id: Optional[str] = None,
53
+ ) -> AgentResult:
54
+ prompt = f"""Please analyze the following GitHub issue and provide a detailed implementation plan. Do NOT make any code changes yet - only analyze and plan.
55
+
56
+ Issue #{issue_number}: {issue_title}
57
+
58
+ {issue_body}
59
+
60
+ Your task:
61
+ 1. Analyze the issue requirements thoroughly
62
+ 2. Explore the codebase to understand the relevant components
63
+ 3. Identify the files that need to be modified or created
64
+ 4. Outline the step-by-step approach for implementing this change
65
+ 5. Note any potential issues, dependencies, or risks
66
+ 6. List any assumptions you're making
67
+
68
+ Provide your analysis and plan in a clear, structured format."""
69
+
70
+ return self.run(worktree, prompt, session_id)
71
+
72
+ def run_implementation(
73
+ self,
74
+ worktree: Path,
75
+ issue_number: int,
76
+ issue_title: str,
77
+ issue_body: str,
78
+ branch: str,
79
+ session_id: Optional[str] = None,
80
+ ) -> AgentResult:
81
+ prompt = f"""Please implement the following GitHub issue.
82
+
83
+ Issue #{issue_number}: {issue_title}
84
+
85
+ {issue_body}
86
+
87
+ Work rules:
88
+ - Make the necessary code changes.
89
+ - Commit with message: "autopilot: work for issue #{issue_number}".
90
+ - Push your changes to the remote branch {branch}.
91
+ - If the issue is a simple file-addition, just do it directly (no extra refactors)."""
92
+
93
+ return self.run(worktree, prompt, session_id)
94
+
95
+ def run_conflict_resolution(
96
+ self, worktree: Path, session_id: Optional[str] = None
97
+ ) -> AgentResult:
98
+ prompt = """This PR has merge conflicts. Please resolve them:
99
+
100
+ 1. Run 'git status' to see all conflicted files
101
+ 2. For each conflicted file, examine the conflict markers (<<<<<, =======, >>>>>)
102
+ 3. Resolve the conflicts by choosing the appropriate code
103
+ 4. Stage the resolved files with 'git add <file>'
104
+ 5. The goal is to make the branch mergeable with main
105
+
106
+ After resolving all conflicts, report the files that were resolved."""
107
+
108
+ return self.run(worktree, prompt, session_id)
109
+
110
+ def run_ci_fix(
111
+ self,
112
+ worktree: Path,
113
+ failure_context: str,
114
+ attempt: int,
115
+ max_attempts: int,
116
+ branch: str,
117
+ session_id: Optional[str] = None,
118
+ ) -> AgentResult:
119
+ prompt = f"""The PR checks have failed. Please analyze the CI failures and fix the issues.
120
+
121
+ {failure_context}
122
+
123
+ Work rules:
124
+ - Examine the failed checks and identify the root cause
125
+ - Make the necessary code changes to fix the failures
126
+ - Commit with message: "autopilot: fix CI check failures (attempt {attempt}/{max_attempts})"
127
+ - Push your changes to the branch {branch}
128
+ - Focus only on fixing the CI failures, do not make unrelated changes"""
129
+
130
+ return self.run(worktree, prompt, session_id)
@@ -440,8 +440,31 @@ I'm monitoring the CI checks and will auto-merge once they pass. This may take a
440
440
  # If mergeStateStatus is CLEAN or HAS_HOOKS and statusCheckRollup is null/empty → PASSED
441
441
  # If mergeStateStatus is UNKNOWN → wait and retry
442
442
  if [[ "$MERGE_STATE_STATUS" == "UNKNOWN" ]]; then
443
- CHECK_STATUS="PENDING"
444
- echo "[run_opencode_issue.sh] mergeStateStatus is UNKNOWN, waiting for GitHub to calculate..."
443
+ # Check if there are any CI checks configured
444
+ if command -v jq >/dev/null 2>&1; then
445
+ SCR_IS_NULL=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup == null')
446
+ SCR_LENGTH=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | length // 0')
447
+ else
448
+ SCR_VALUE=$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(data.get("statusCheckRollup", "null"))' <<<"$PR_STATUS_JSON")
449
+ SCR_IS_NULL=$([[ "$SCR_VALUE" == "null" ]] && echo "true" || echo "false")
450
+ SCR_LENGTH=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print(len(scr) if scr else 0)' <<<"$PR_STATUS_JSON")
451
+ fi
452
+
453
+ # If no checks configured, wait at most 20 seconds for them to appear
454
+ if [[ "$SCR_IS_NULL" == "true" ]] || [[ "$SCR_LENGTH" == "0" ]]; then
455
+ if [[ $POLL_ATTEMPT -lt 1 ]]; then
456
+ CHECK_STATUS="PENDING"
457
+ echo "[run_opencode_issue.sh] No CI checks configured, waiting up to 20 seconds for checks to appear..."
458
+ sleep 20
459
+ continue
460
+ else
461
+ CHECK_STATUS="PASSED"
462
+ echo "[run_opencode_issue.sh] No checks found after waiting 20 seconds, continuing to merge..."
463
+ fi
464
+ else
465
+ CHECK_STATUS="PENDING"
466
+ echo "[run_opencode_issue.sh] mergeStateStatus is UNKNOWN, waiting for GitHub to calculate..."
467
+ fi
445
468
  elif [[ "$MERGE_STATE_STATUS" == "CLEAN" || "$MERGE_STATE_STATUS" == "HAS_HOOKS" ]]; then
446
469
  # Check statusCheckRollup - if null/empty, no checks configured, so PASSED
447
470
  if command -v jq >/dev/null 2>&1; then