@astroanywhere/agent 0.1.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/LICENSE +76 -0
- package/README.md +178 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +401 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +9 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/mcp.d.ts +16 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +19 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/setup.d.ts +20 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +585 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/start.d.ts +16 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +638 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +63 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +5 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +85 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/execution/direct-strategy.d.ts +18 -0
- package/dist/execution/direct-strategy.d.ts.map +1 -0
- package/dist/execution/direct-strategy.js +156 -0
- package/dist/execution/direct-strategy.js.map +1 -0
- package/dist/execution/docker-strategy.d.ts +26 -0
- package/dist/execution/docker-strategy.d.ts.map +1 -0
- package/dist/execution/docker-strategy.js +222 -0
- package/dist/execution/docker-strategy.js.map +1 -0
- package/dist/execution/index.d.ts +14 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +13 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/kubernetes-exec-strategy.d.ts +23 -0
- package/dist/execution/kubernetes-exec-strategy.d.ts.map +1 -0
- package/dist/execution/kubernetes-exec-strategy.js +232 -0
- package/dist/execution/kubernetes-exec-strategy.js.map +1 -0
- package/dist/execution/registry.d.ts +41 -0
- package/dist/execution/registry.d.ts.map +1 -0
- package/dist/execution/registry.js +84 -0
- package/dist/execution/registry.js.map +1 -0
- package/dist/execution/slurm-strategy.d.ts +22 -0
- package/dist/execution/slurm-strategy.d.ts.map +1 -0
- package/dist/execution/slurm-strategy.js +219 -0
- package/dist/execution/slurm-strategy.js.map +1 -0
- package/dist/execution/types.d.ts +72 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/execution/types.js +10 -0
- package/dist/execution/types.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +35 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +126 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/config.d.ts +174 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +399 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/copy-worktree.d.ts +73 -0
- package/dist/lib/copy-worktree.d.ts.map +1 -0
- package/dist/lib/copy-worktree.js +374 -0
- package/dist/lib/copy-worktree.js.map +1 -0
- package/dist/lib/git-pr.d.ts +63 -0
- package/dist/lib/git-pr.d.ts.map +1 -0
- package/dist/lib/git-pr.js +224 -0
- package/dist/lib/git-pr.js.map +1 -0
- package/dist/lib/hardware-id.d.ts +25 -0
- package/dist/lib/hardware-id.d.ts.map +1 -0
- package/dist/lib/hardware-id.js +186 -0
- package/dist/lib/hardware-id.js.map +1 -0
- package/dist/lib/hpc-context.d.ts +35 -0
- package/dist/lib/hpc-context.d.ts.map +1 -0
- package/dist/lib/hpc-context.js +167 -0
- package/dist/lib/hpc-context.js.map +1 -0
- package/dist/lib/prompt-templates.d.ts +195 -0
- package/dist/lib/prompt-templates.d.ts.map +1 -0
- package/dist/lib/prompt-templates.js +353 -0
- package/dist/lib/prompt-templates.js.map +1 -0
- package/dist/lib/providers.d.ts +27 -0
- package/dist/lib/providers.d.ts.map +1 -0
- package/dist/lib/providers.js +372 -0
- package/dist/lib/providers.js.map +1 -0
- package/dist/lib/repo-context.d.ts +18 -0
- package/dist/lib/repo-context.d.ts.map +1 -0
- package/dist/lib/repo-context.js +61 -0
- package/dist/lib/repo-context.js.map +1 -0
- package/dist/lib/repo-utils.d.ts +35 -0
- package/dist/lib/repo-utils.d.ts.map +1 -0
- package/dist/lib/repo-utils.js +222 -0
- package/dist/lib/repo-utils.js.map +1 -0
- package/dist/lib/resources.d.ts +17 -0
- package/dist/lib/resources.d.ts.map +1 -0
- package/dist/lib/resources.js +227 -0
- package/dist/lib/resources.js.map +1 -0
- package/dist/lib/slurm-detect.d.ts +15 -0
- package/dist/lib/slurm-detect.d.ts.map +1 -0
- package/dist/lib/slurm-detect.js +148 -0
- package/dist/lib/slurm-detect.js.map +1 -0
- package/dist/lib/slurm-executor.d.ts +70 -0
- package/dist/lib/slurm-executor.d.ts.map +1 -0
- package/dist/lib/slurm-executor.js +402 -0
- package/dist/lib/slurm-executor.js.map +1 -0
- package/dist/lib/slurm-job-monitor.d.ts +52 -0
- package/dist/lib/slurm-job-monitor.d.ts.map +1 -0
- package/dist/lib/slurm-job-monitor.js +212 -0
- package/dist/lib/slurm-job-monitor.js.map +1 -0
- package/dist/lib/ssh-discovery.d.ts +17 -0
- package/dist/lib/ssh-discovery.d.ts.map +1 -0
- package/dist/lib/ssh-discovery.js +287 -0
- package/dist/lib/ssh-discovery.js.map +1 -0
- package/dist/lib/ssh-installer.d.ts +69 -0
- package/dist/lib/ssh-installer.d.ts.map +1 -0
- package/dist/lib/ssh-installer.js +230 -0
- package/dist/lib/ssh-installer.js.map +1 -0
- package/dist/lib/streaming-prompt.d.ts +48 -0
- package/dist/lib/streaming-prompt.d.ts.map +1 -0
- package/dist/lib/streaming-prompt.js +91 -0
- package/dist/lib/streaming-prompt.js.map +1 -0
- package/dist/lib/task-executor.d.ts +114 -0
- package/dist/lib/task-executor.d.ts.map +1 -0
- package/dist/lib/task-executor.js +753 -0
- package/dist/lib/task-executor.js.map +1 -0
- package/dist/lib/websocket-client.d.ts +200 -0
- package/dist/lib/websocket-client.d.ts.map +1 -0
- package/dist/lib/websocket-client.js +781 -0
- package/dist/lib/websocket-client.js.map +1 -0
- package/dist/lib/workdir-safety.d.ts +63 -0
- package/dist/lib/workdir-safety.d.ts.map +1 -0
- package/dist/lib/workdir-safety.js +247 -0
- package/dist/lib/workdir-safety.js.map +1 -0
- package/dist/lib/worktree-include.d.ts +14 -0
- package/dist/lib/worktree-include.d.ts.map +1 -0
- package/dist/lib/worktree-include.js +68 -0
- package/dist/lib/worktree-include.js.map +1 -0
- package/dist/lib/worktree-setup.d.ts +18 -0
- package/dist/lib/worktree-setup.d.ts.map +1 -0
- package/dist/lib/worktree-setup.js +60 -0
- package/dist/lib/worktree-setup.js.map +1 -0
- package/dist/lib/worktree.d.ts +37 -0
- package/dist/lib/worktree.d.ts.map +1 -0
- package/dist/lib/worktree.js +411 -0
- package/dist/lib/worktree.js.map +1 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +45 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +153 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session-bridge.d.ts +87 -0
- package/dist/mcp/session-bridge.d.ts.map +1 -0
- package/dist/mcp/session-bridge.js +317 -0
- package/dist/mcp/session-bridge.js.map +1 -0
- package/dist/mcp/tools.d.ts +70 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +234 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +197 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +16 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/providers/base-adapter.d.ts +56 -0
- package/dist/providers/base-adapter.d.ts.map +1 -0
- package/dist/providers/base-adapter.js +5 -0
- package/dist/providers/base-adapter.js.map +1 -0
- package/dist/providers/claude-code-adapter.d.ts +27 -0
- package/dist/providers/claude-code-adapter.d.ts.map +1 -0
- package/dist/providers/claude-code-adapter.js +298 -0
- package/dist/providers/claude-code-adapter.js.map +1 -0
- package/dist/providers/claude-sdk-adapter.d.ts +60 -0
- package/dist/providers/claude-sdk-adapter.d.ts.map +1 -0
- package/dist/providers/claude-sdk-adapter.js +632 -0
- package/dist/providers/claude-sdk-adapter.js.map +1 -0
- package/dist/providers/codex-adapter.d.ts +21 -0
- package/dist/providers/codex-adapter.d.ts.map +1 -0
- package/dist/providers/codex-adapter.js +197 -0
- package/dist/providers/codex-adapter.js.map +1 -0
- package/dist/providers/index.d.ts +26 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +58 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/slurm-adapter.d.ts +26 -0
- package/dist/providers/slurm-adapter.d.ts.map +1 -0
- package/dist/providers/slurm-adapter.js +146 -0
- package/dist/providers/slurm-adapter.js.map +1 -0
- package/dist/types.d.ts +592 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { mkdir, cp, rm, readdir, stat, readFile, copyFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join, relative, dirname, sep } from 'node:path';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
const CODE_SIZE_THRESHOLD = 1_000_000; // 1MB
|
|
6
|
+
/** Hardcoded directory names that are always excluded, regardless of .gitignore. */
|
|
7
|
+
const ALWAYS_EXCLUDED_DIRS = new Set(['.git', 'node_modules']);
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Gitignore pattern support
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
/**
|
|
12
|
+
* Load .gitignore patterns from a directory.
|
|
13
|
+
* Returns an empty array if no .gitignore exists.
|
|
14
|
+
*/
|
|
15
|
+
export async function loadGitignorePatterns(sourceDir) {
|
|
16
|
+
const gitignorePath = join(sourceDir, '.gitignore');
|
|
17
|
+
if (!existsSync(gitignorePath)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const content = await readFile(gitignorePath, 'utf-8');
|
|
22
|
+
return parseGitignoreContent(content);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse raw .gitignore content into an array of usable patterns.
|
|
30
|
+
* Strips comments, blank lines, and negation patterns (for simplicity).
|
|
31
|
+
*/
|
|
32
|
+
export function parseGitignoreContent(content) {
|
|
33
|
+
return content
|
|
34
|
+
.split('\n')
|
|
35
|
+
.map((line) => line.trim())
|
|
36
|
+
.filter((line) => {
|
|
37
|
+
if (line === '')
|
|
38
|
+
return false; // blank
|
|
39
|
+
if (line.startsWith('#'))
|
|
40
|
+
return false; // comment
|
|
41
|
+
if (line.startsWith('!'))
|
|
42
|
+
return false; // negation — skip for simplicity
|
|
43
|
+
return true;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check whether a relative path matches any gitignore pattern.
|
|
48
|
+
*
|
|
49
|
+
* Supported pattern features:
|
|
50
|
+
* - `*` matches any sequence of characters except `/`
|
|
51
|
+
* - `**` matches everything (including `/`)
|
|
52
|
+
* - Trailing `/` means the pattern only matches directories
|
|
53
|
+
* - Leading `/` anchors the pattern to the root
|
|
54
|
+
* - Bare name without `/` matches any basename in the tree
|
|
55
|
+
* - Patterns with an interior `/` (non-leading, non-trailing) are anchored
|
|
56
|
+
*/
|
|
57
|
+
export function isIgnoredByPattern(
|
|
58
|
+
/** Path relative to the project root, using `/` separators */
|
|
59
|
+
relativePath, patterns,
|
|
60
|
+
/** Whether the path is a directory */
|
|
61
|
+
isDirectory) {
|
|
62
|
+
for (const raw of patterns) {
|
|
63
|
+
if (matchGitignorePattern(relativePath, raw, isDirectory)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Match a single gitignore pattern against a relative path.
|
|
71
|
+
*/
|
|
72
|
+
function matchGitignorePattern(relativePath, rawPattern, isDirectory) {
|
|
73
|
+
let pattern = rawPattern;
|
|
74
|
+
// Trailing slash → directory-only pattern
|
|
75
|
+
const dirOnly = pattern.endsWith('/');
|
|
76
|
+
if (dirOnly) {
|
|
77
|
+
if (!isDirectory)
|
|
78
|
+
return false;
|
|
79
|
+
pattern = pattern.slice(0, -1);
|
|
80
|
+
}
|
|
81
|
+
// Leading slash → anchored to root; strip the slash for matching
|
|
82
|
+
const anchored = pattern.startsWith('/');
|
|
83
|
+
if (anchored) {
|
|
84
|
+
pattern = pattern.slice(1);
|
|
85
|
+
}
|
|
86
|
+
// Determine if the pattern should be anchored:
|
|
87
|
+
// - explicitly via leading `/`
|
|
88
|
+
// - implicitly if it contains an interior `/`
|
|
89
|
+
const hasInteriorSlash = pattern.includes('/');
|
|
90
|
+
const isAnchored = anchored || hasInteriorSlash;
|
|
91
|
+
if (isAnchored) {
|
|
92
|
+
// Match against the full relative path
|
|
93
|
+
return globMatch(relativePath, pattern);
|
|
94
|
+
}
|
|
95
|
+
// Un-anchored pattern without `/` → match against any component or basename
|
|
96
|
+
// e.g. `*.pyc` matches `foo/bar/baz.pyc`
|
|
97
|
+
// Check the full path first (handles `*` matching within segments)
|
|
98
|
+
if (globMatch(relativePath, pattern))
|
|
99
|
+
return true;
|
|
100
|
+
// Check each path segment individually
|
|
101
|
+
const segments = relativePath.split('/');
|
|
102
|
+
// For a directory match, the last segment is the directory name itself
|
|
103
|
+
for (const segment of segments) {
|
|
104
|
+
if (globMatch(segment, pattern))
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Simple glob matcher supporting `*` (any chars except `/`) and `**` (anything).
|
|
111
|
+
* Converts the pattern to a RegExp.
|
|
112
|
+
*/
|
|
113
|
+
function globMatch(text, pattern) {
|
|
114
|
+
// Build a regex from the glob pattern
|
|
115
|
+
let regex = '^';
|
|
116
|
+
let i = 0;
|
|
117
|
+
while (i < pattern.length) {
|
|
118
|
+
const ch = pattern[i];
|
|
119
|
+
if (ch === '*') {
|
|
120
|
+
if (pattern[i + 1] === '*') {
|
|
121
|
+
// `**` — match everything including `/`
|
|
122
|
+
// `**/` matches zero or more directories
|
|
123
|
+
if (pattern[i + 2] === '/') {
|
|
124
|
+
regex += '(?:.*/)?';
|
|
125
|
+
i += 3;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
regex += '.*';
|
|
129
|
+
i += 2;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// `*` — match anything except `/`
|
|
134
|
+
regex += '[^/]*';
|
|
135
|
+
i += 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (ch === '?') {
|
|
139
|
+
regex += '[^/]';
|
|
140
|
+
i += 1;
|
|
141
|
+
}
|
|
142
|
+
else if (ch === '.') {
|
|
143
|
+
regex += '\\.';
|
|
144
|
+
i += 1;
|
|
145
|
+
}
|
|
146
|
+
else if (ch === '+' || ch === '(' || ch === ')' || ch === '[' || ch === ']' || ch === '{' || ch === '}' || ch === '^' || ch === '$' || ch === '|' || ch === '\\') {
|
|
147
|
+
regex += '\\' + ch;
|
|
148
|
+
i += 1;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
regex += ch;
|
|
152
|
+
i += 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
regex += '$';
|
|
156
|
+
try {
|
|
157
|
+
return new RegExp(regex).test(text);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Try to get the list of git-ignored paths using `git ls-files`.
|
|
165
|
+
* Only works inside a git repository. Returns null on failure.
|
|
166
|
+
*/
|
|
167
|
+
async function getGitIgnoredPaths(sourceDir) {
|
|
168
|
+
// Quick check: is this a git repo?
|
|
169
|
+
if (!existsSync(join(sourceDir, '.git'))) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return new Promise((resolve) => {
|
|
173
|
+
execFile('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'], { cwd: sourceDir, timeout: 10_000 }, (err, stdout) => {
|
|
174
|
+
if (err) {
|
|
175
|
+
resolve(null);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const paths = new Set(stdout
|
|
179
|
+
.split('\n')
|
|
180
|
+
.map((p) => p.replace(/\/$/, '').trim())
|
|
181
|
+
.filter(Boolean));
|
|
182
|
+
resolve(paths);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Copy project directory to a worktree-like path for non-git execution.
|
|
188
|
+
* Excludes the agent directory and respects .gitignore patterns.
|
|
189
|
+
*/
|
|
190
|
+
export async function createCopyWorktree(sourceDir, agentDirName, taskId) {
|
|
191
|
+
const worktreePath = join(sourceDir, agentDirName, 'worktrees', sanitize(taskId));
|
|
192
|
+
// Clean up any existing worktree at this path
|
|
193
|
+
if (existsSync(worktreePath)) {
|
|
194
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
await mkdir(worktreePath, { recursive: true });
|
|
197
|
+
// Try git-based ignore list first (most accurate), fall back to manual parsing
|
|
198
|
+
const gitIgnored = await getGitIgnoredPaths(sourceDir);
|
|
199
|
+
const gitignorePatterns = gitIgnored ? [] : await loadGitignorePatterns(sourceDir);
|
|
200
|
+
// Copy source directory, excluding agent dir and ignored paths
|
|
201
|
+
await copyDirectoryFiltered(sourceDir, worktreePath, agentDirName, sourceDir, gitIgnored, gitignorePatterns);
|
|
202
|
+
return {
|
|
203
|
+
worktreePath,
|
|
204
|
+
cleanup: async () => {
|
|
205
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create a reference worktree (empty directory with file map).
|
|
211
|
+
* Used for large projects where copying would be too slow.
|
|
212
|
+
*/
|
|
213
|
+
export async function createReferenceWorktree(sourceDir, agentDirName, taskId) {
|
|
214
|
+
const worktreePath = join(sourceDir, agentDirName, 'worktrees', sanitize(taskId));
|
|
215
|
+
if (existsSync(worktreePath)) {
|
|
216
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
217
|
+
}
|
|
218
|
+
await mkdir(worktreePath, { recursive: true });
|
|
219
|
+
const fileMap = await generateFileMap(sourceDir, agentDirName);
|
|
220
|
+
return {
|
|
221
|
+
worktreePath,
|
|
222
|
+
fileMap,
|
|
223
|
+
cleanup: async () => {
|
|
224
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Generate a file listing with size classifications.
|
|
230
|
+
* Files < 1MB are classified as 'code', >= 1MB as 'data'.
|
|
231
|
+
*/
|
|
232
|
+
export async function generateFileMap(sourceDir, agentDirName) {
|
|
233
|
+
const entries = [];
|
|
234
|
+
async function walk(dir) {
|
|
235
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
236
|
+
for (const item of items) {
|
|
237
|
+
const fullPath = join(dir, item.name);
|
|
238
|
+
const relPath = relative(sourceDir, fullPath);
|
|
239
|
+
// Skip agent dir, .git, node_modules
|
|
240
|
+
if (item.name === agentDirName || item.name === '.git' || item.name === 'node_modules') {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (item.isDirectory()) {
|
|
244
|
+
await walk(fullPath);
|
|
245
|
+
}
|
|
246
|
+
else if (item.isFile()) {
|
|
247
|
+
try {
|
|
248
|
+
const stats = await stat(fullPath);
|
|
249
|
+
entries.push({
|
|
250
|
+
relativePath: relPath,
|
|
251
|
+
sizeBytes: stats.size,
|
|
252
|
+
classification: stats.size < CODE_SIZE_THRESHOLD ? 'code' : 'data',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Skip files we can't stat
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
await walk(sourceDir);
|
|
262
|
+
return entries;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Recursively copy a directory, excluding the agent dir, always-excluded dirs,
|
|
266
|
+
* and paths matched by .gitignore patterns or `git ls-files` output.
|
|
267
|
+
*/
|
|
268
|
+
async function copyDirectoryFiltered(src, dest, agentDirName, rootDir, gitIgnoredPaths, gitignorePatterns) {
|
|
269
|
+
const items = await readdir(src, { withFileTypes: true });
|
|
270
|
+
for (const item of items) {
|
|
271
|
+
// Always skip agent dir and hardcoded exclusions
|
|
272
|
+
if (item.name === agentDirName || ALWAYS_EXCLUDED_DIRS.has(item.name)) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const srcPath = join(src, item.name);
|
|
276
|
+
const relPath = relative(rootDir, srcPath).split(sep).join('/');
|
|
277
|
+
// Check against git ls-files output (most accurate, if available)
|
|
278
|
+
if (gitIgnoredPaths && gitIgnoredPaths.has(relPath)) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
// Check against parsed .gitignore patterns (fallback for non-git dirs)
|
|
282
|
+
if (gitignorePatterns.length > 0 && isIgnoredByPattern(relPath, gitignorePatterns, item.isDirectory())) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const destPath = join(dest, item.name);
|
|
286
|
+
if (item.isDirectory()) {
|
|
287
|
+
// Recurse into subdirectories so we can filter at every level
|
|
288
|
+
await mkdir(destPath, { recursive: true });
|
|
289
|
+
await copyDirectoryFiltered(srcPath, destPath, agentDirName, rootDir, gitIgnoredPaths, gitignorePatterns);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
await cp(srcPath, destPath);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Compare a worktree copy against the original directory and apply changes back.
|
|
298
|
+
*
|
|
299
|
+
* - New files (exist in worktree but not original): copied to original
|
|
300
|
+
* - Modified files (different content): copied to original
|
|
301
|
+
* - Deleted files (exist in original but not worktree): reported but NOT deleted
|
|
302
|
+
*
|
|
303
|
+
* Excludes the agent dir, .git, and node_modules from comparison.
|
|
304
|
+
*
|
|
305
|
+
* Returns lists of created, modified, and deleted (pending) relative paths.
|
|
306
|
+
*/
|
|
307
|
+
export async function applyChangesFromCopy(worktreePath, originalDir, agentDirName) {
|
|
308
|
+
const created = [];
|
|
309
|
+
const modified = [];
|
|
310
|
+
const deleted = [];
|
|
311
|
+
const excludedDirs = new Set([agentDirName, ...ALWAYS_EXCLUDED_DIRS]);
|
|
312
|
+
// Walk the worktree to find new and modified files
|
|
313
|
+
async function walkWorktree(dir) {
|
|
314
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
315
|
+
for (const item of items) {
|
|
316
|
+
const fullPath = join(dir, item.name);
|
|
317
|
+
const relPath = relative(worktreePath, fullPath);
|
|
318
|
+
if (excludedDirs.has(item.name))
|
|
319
|
+
continue;
|
|
320
|
+
if (item.isDirectory()) {
|
|
321
|
+
await walkWorktree(fullPath);
|
|
322
|
+
}
|
|
323
|
+
else if (item.isFile()) {
|
|
324
|
+
const originalPath = join(originalDir, relPath);
|
|
325
|
+
if (!existsSync(originalPath)) {
|
|
326
|
+
// New file — copy to original
|
|
327
|
+
await mkdir(dirname(originalPath), { recursive: true });
|
|
328
|
+
await copyFile(fullPath, originalPath);
|
|
329
|
+
created.push(relPath);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// Check if content differs
|
|
333
|
+
try {
|
|
334
|
+
const worktreeContent = await readFile(fullPath);
|
|
335
|
+
const originalContent = await readFile(originalPath);
|
|
336
|
+
if (!worktreeContent.equals(originalContent)) {
|
|
337
|
+
await copyFile(fullPath, originalPath);
|
|
338
|
+
modified.push(relPath);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// If we can't read either file, skip
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Walk the original directory to find deleted files
|
|
349
|
+
async function walkOriginal(dir) {
|
|
350
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
351
|
+
for (const item of items) {
|
|
352
|
+
const fullPath = join(dir, item.name);
|
|
353
|
+
const relPath = relative(originalDir, fullPath);
|
|
354
|
+
if (excludedDirs.has(item.name))
|
|
355
|
+
continue;
|
|
356
|
+
if (item.isDirectory()) {
|
|
357
|
+
await walkOriginal(fullPath);
|
|
358
|
+
}
|
|
359
|
+
else if (item.isFile()) {
|
|
360
|
+
const worktreeFile = join(worktreePath, relPath);
|
|
361
|
+
if (!existsSync(worktreeFile)) {
|
|
362
|
+
deleted.push(relPath);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
await walkWorktree(worktreePath);
|
|
368
|
+
await walkOriginal(originalDir);
|
|
369
|
+
return { created, modified, deleted };
|
|
370
|
+
}
|
|
371
|
+
function sanitize(value) {
|
|
372
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=copy-worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-worktree.js","sourceRoot":"","sources":["../../src/lib/copy-worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git PR utilities for creating pull requests after task execution
|
|
3
|
+
*/
|
|
4
|
+
export interface PRResult {
|
|
5
|
+
branchName: string;
|
|
6
|
+
pushed?: boolean;
|
|
7
|
+
prUrl?: string;
|
|
8
|
+
prNumber?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Detect the default branch (main/master) for the repo
|
|
12
|
+
*/
|
|
13
|
+
export declare function getDefaultBranch(repoDir: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if the current branch has commits ahead of the base branch
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasBranchCommits(worktreePath: string, baseBranch: string): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Push the branch to origin
|
|
20
|
+
*/
|
|
21
|
+
export declare function pushBranch(worktreePath: string, branchName: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Create a pull request using the `gh` CLI
|
|
24
|
+
*/
|
|
25
|
+
export declare function createPullRequest(worktreePath: string, options: {
|
|
26
|
+
branchName: string;
|
|
27
|
+
baseBranch: string;
|
|
28
|
+
title: string;
|
|
29
|
+
body: string;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
prUrl: string;
|
|
32
|
+
prNumber: number;
|
|
33
|
+
} | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if `gh` CLI is available and authenticated
|
|
36
|
+
*/
|
|
37
|
+
export declare function isGhAvailable(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a directory is a git repo with a remote origin
|
|
40
|
+
*/
|
|
41
|
+
export declare function hasRemoteOrigin(repoDir: string): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Get the git root directory from any subdirectory
|
|
44
|
+
*/
|
|
45
|
+
export declare function getGitRoot(dir: string): Promise<string | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Auto-commit any uncommitted changes in the worktree.
|
|
48
|
+
* Fallback for when the agent doesn't commit its own changes.
|
|
49
|
+
*/
|
|
50
|
+
export declare function autoCommitChanges(worktreePath: string, taskTitle: string): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Full PR creation flow: auto-commit + push + create PR, with graceful fallbacks
|
|
53
|
+
*/
|
|
54
|
+
export declare function pushAndCreatePR(worktreePath: string, options: {
|
|
55
|
+
branchName: string;
|
|
56
|
+
taskTitle: string;
|
|
57
|
+
taskDescription?: string;
|
|
58
|
+
/** If true, push the branch but skip PR creation */
|
|
59
|
+
skipPR?: boolean;
|
|
60
|
+
/** If true, auto-commit uncommitted changes before pushing (default: true) */
|
|
61
|
+
autoCommit?: boolean;
|
|
62
|
+
}): Promise<PRResult>;
|
|
63
|
+
//# sourceMappingURL=git-pr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-pr.d.ts","sourceRoot":"","sources":["../../src/lib/git-pr.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BvE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CA4BlB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE;IACP,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyBrD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAUtD;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWpE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CA+BlB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE;IACP,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACA,OAAO,CAAC,QAAQ,CAAC,CAkEnB"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git PR utilities for creating pull requests after task execution
|
|
3
|
+
*/
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
function withGitEnv() {
|
|
8
|
+
return {
|
|
9
|
+
...process.env,
|
|
10
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Detect the default branch (main/master) for the repo
|
|
15
|
+
*/
|
|
16
|
+
export async function getDefaultBranch(repoDir) {
|
|
17
|
+
// Try symbolic-ref first (most reliable)
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execFileAsync('git', ['-C', repoDir, 'symbolic-ref', 'refs/remotes/origin/HEAD'], { env: withGitEnv(), timeout: 5_000 });
|
|
20
|
+
const ref = stdout.trim();
|
|
21
|
+
// refs/remotes/origin/main -> main
|
|
22
|
+
const parts = ref.split('/');
|
|
23
|
+
return parts[parts.length - 1];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Fallback: look for common branch names in remote refs
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const { stdout } = await execFileAsync('git', ['-C', repoDir, 'branch', '-r', '--list', 'origin/main', 'origin/master'], { env: withGitEnv(), timeout: 5_000 });
|
|
30
|
+
const branches = stdout.trim().split('\n').map((b) => b.trim());
|
|
31
|
+
if (branches.includes('origin/main'))
|
|
32
|
+
return 'main';
|
|
33
|
+
if (branches.includes('origin/master'))
|
|
34
|
+
return 'master';
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Fallback to 'main'
|
|
38
|
+
}
|
|
39
|
+
return 'main';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if the current branch has commits ahead of the base branch
|
|
43
|
+
*/
|
|
44
|
+
export async function hasBranchCommits(worktreePath, baseBranch) {
|
|
45
|
+
try {
|
|
46
|
+
const { stdout } = await execFileAsync('git', ['-C', worktreePath, 'rev-list', '--count', `origin/${baseBranch}..HEAD`], { env: withGitEnv(), timeout: 10_000 });
|
|
47
|
+
return parseInt(stdout.trim(), 10) > 0;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Push the branch to origin
|
|
55
|
+
*/
|
|
56
|
+
export async function pushBranch(worktreePath, branchName) {
|
|
57
|
+
// Log remote URL for debugging push target
|
|
58
|
+
try {
|
|
59
|
+
const { stdout: remoteUrl } = await execFileAsync('git', ['-C', worktreePath, 'remote', 'get-url', 'origin'], { env: withGitEnv(), timeout: 5_000 });
|
|
60
|
+
console.log(`[git-pr] Pushing branch ${branchName} to origin (${remoteUrl.trim()})`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.warn(`[git-pr] Could not resolve origin URL for ${worktreePath}`);
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const { stdout, stderr } = await execFileAsync('git', ['-C', worktreePath, 'push', '-u', 'origin', branchName], { env: withGitEnv(), timeout: 60_000 });
|
|
67
|
+
if (stderr)
|
|
68
|
+
console.log(`[git-pr] Push stderr: ${stderr.trim()}`);
|
|
69
|
+
if (stdout)
|
|
70
|
+
console.log(`[git-pr] Push stdout: ${stdout.trim()}`);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const execErr = err;
|
|
75
|
+
console.error(`[git-pr] Failed to push branch ${branchName}: ${execErr.message ?? err}`);
|
|
76
|
+
if (execErr.stderr)
|
|
77
|
+
console.error(`[git-pr] Push stderr: ${execErr.stderr}`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a pull request using the `gh` CLI
|
|
83
|
+
*/
|
|
84
|
+
export async function createPullRequest(worktreePath, options) {
|
|
85
|
+
try {
|
|
86
|
+
const { stdout } = await execFileAsync('gh', [
|
|
87
|
+
'pr', 'create',
|
|
88
|
+
'--base', options.baseBranch,
|
|
89
|
+
'--head', options.branchName,
|
|
90
|
+
'--title', options.title,
|
|
91
|
+
'--body', options.body,
|
|
92
|
+
], { cwd: worktreePath, env: withGitEnv(), timeout: 30_000 });
|
|
93
|
+
const prUrl = stdout.trim();
|
|
94
|
+
// Extract PR number from URL: https://github.com/user/repo/pull/123
|
|
95
|
+
const match = prUrl.match(/\/pull\/(\d+)/);
|
|
96
|
+
const prNumber = match ? parseInt(match[1], 10) : 0;
|
|
97
|
+
return { prUrl, prNumber };
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
+
console.error(`[git-pr] Failed to create PR: ${msg}`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if `gh` CLI is available and authenticated
|
|
107
|
+
*/
|
|
108
|
+
export async function isGhAvailable() {
|
|
109
|
+
try {
|
|
110
|
+
await execFileAsync('gh', ['auth', 'status'], {
|
|
111
|
+
env: withGitEnv(),
|
|
112
|
+
timeout: 10_000,
|
|
113
|
+
});
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if a directory is a git repo with a remote origin
|
|
122
|
+
*/
|
|
123
|
+
export async function hasRemoteOrigin(repoDir) {
|
|
124
|
+
try {
|
|
125
|
+
const { stdout } = await execFileAsync('git', ['-C', repoDir, 'remote', 'get-url', 'origin'], { env: withGitEnv(), timeout: 5_000 });
|
|
126
|
+
return stdout.trim().length > 0;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the git root directory from any subdirectory
|
|
134
|
+
*/
|
|
135
|
+
export async function getGitRoot(dir) {
|
|
136
|
+
try {
|
|
137
|
+
const { stdout } = await execFileAsync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], { env: withGitEnv(), timeout: 5_000 });
|
|
138
|
+
return stdout.trim() || null;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Auto-commit any uncommitted changes in the worktree.
|
|
146
|
+
* Fallback for when the agent doesn't commit its own changes.
|
|
147
|
+
*/
|
|
148
|
+
export async function autoCommitChanges(worktreePath, taskTitle) {
|
|
149
|
+
try {
|
|
150
|
+
const { stdout: statusOutput } = await execFileAsync('git', ['-C', worktreePath, 'status', '--porcelain'], { env: withGitEnv(), timeout: 10_000 });
|
|
151
|
+
if (!statusOutput.trim()) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
await execFileAsync('git', ['-C', worktreePath, 'add', '-A'], { env: withGitEnv(), timeout: 30_000 });
|
|
155
|
+
await execFileAsync('git', ['-C', worktreePath, 'commit', '-m', `${taskTitle}\n\nAuto-committed by Astro agent-runner`], { env: withGitEnv(), timeout: 30_000 });
|
|
156
|
+
console.log(`[git-pr] Auto-committed uncommitted changes for: ${taskTitle}`);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
console.warn(`[git-pr] Auto-commit failed: ${msg}`);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Full PR creation flow: auto-commit + push + create PR, with graceful fallbacks
|
|
167
|
+
*/
|
|
168
|
+
export async function pushAndCreatePR(worktreePath, options) {
|
|
169
|
+
const result = { branchName: options.branchName };
|
|
170
|
+
// Get git root from worktree to find default branch
|
|
171
|
+
const gitRoot = await getGitRoot(worktreePath);
|
|
172
|
+
if (!gitRoot) {
|
|
173
|
+
console.warn(`[git-pr] No git root found for ${worktreePath}, skipping PR`);
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
// Check if repo has a remote
|
|
177
|
+
if (!(await hasRemoteOrigin(gitRoot))) {
|
|
178
|
+
console.warn(`[git-pr] No remote origin for ${gitRoot}, skipping PR`);
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
// Get default branch
|
|
182
|
+
const baseBranch = await getDefaultBranch(gitRoot);
|
|
183
|
+
// Auto-commit any uncommitted changes the agent left behind (opt-in, default true)
|
|
184
|
+
if (options.autoCommit !== false) {
|
|
185
|
+
await autoCommitChanges(worktreePath, options.taskTitle);
|
|
186
|
+
}
|
|
187
|
+
// Check if there are commits to push
|
|
188
|
+
const hasCommits = await hasBranchCommits(worktreePath, baseBranch);
|
|
189
|
+
if (!hasCommits) {
|
|
190
|
+
console.log(`[git-pr] No commits ahead of ${baseBranch} in ${worktreePath}, skipping PR`);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
console.log(`[git-pr] Branch ${options.branchName} has commits ahead of ${baseBranch}`);
|
|
194
|
+
// Push the branch
|
|
195
|
+
const pushed = await pushBranch(worktreePath, options.branchName);
|
|
196
|
+
if (!pushed)
|
|
197
|
+
return result;
|
|
198
|
+
result.pushed = true;
|
|
199
|
+
// Skip PR creation if requested (push-only mode)
|
|
200
|
+
if (options.skipPR) {
|
|
201
|
+
console.log(`[git-pr] skipPR=true, branch pushed (${options.branchName})`);
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
// Create PR if gh is available
|
|
205
|
+
if (!(await isGhAvailable())) {
|
|
206
|
+
console.log('[git-pr] gh CLI not available, skipping PR creation (branch pushed)');
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
const body = options.taskDescription
|
|
210
|
+
? `## Task\n\n${options.taskDescription}\n\n---\n*Created by Astro task automation*`
|
|
211
|
+
: '*Created by Astro task automation*';
|
|
212
|
+
const pr = await createPullRequest(worktreePath, {
|
|
213
|
+
branchName: options.branchName,
|
|
214
|
+
baseBranch,
|
|
215
|
+
title: options.taskTitle,
|
|
216
|
+
body,
|
|
217
|
+
});
|
|
218
|
+
if (pr) {
|
|
219
|
+
result.prUrl = pr.prUrl;
|
|
220
|
+
result.prNumber = pr.prNumber;
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=git-pr.js.map
|