@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.
Files changed (203) hide show
  1. package/LICENSE +76 -0
  2. package/README.md +178 -0
  3. package/dist/cli.d.ts +15 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +401 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/index.d.ts +9 -0
  8. package/dist/commands/index.d.ts.map +1 -0
  9. package/dist/commands/index.js +9 -0
  10. package/dist/commands/index.js.map +1 -0
  11. package/dist/commands/mcp.d.ts +16 -0
  12. package/dist/commands/mcp.d.ts.map +1 -0
  13. package/dist/commands/mcp.js +19 -0
  14. package/dist/commands/mcp.js.map +1 -0
  15. package/dist/commands/setup.d.ts +20 -0
  16. package/dist/commands/setup.d.ts.map +1 -0
  17. package/dist/commands/setup.js +585 -0
  18. package/dist/commands/setup.js.map +1 -0
  19. package/dist/commands/start.d.ts +16 -0
  20. package/dist/commands/start.d.ts.map +1 -0
  21. package/dist/commands/start.js +638 -0
  22. package/dist/commands/start.js.map +1 -0
  23. package/dist/commands/status.d.ts +5 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +63 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/commands/stop.d.ts +5 -0
  28. package/dist/commands/stop.d.ts.map +1 -0
  29. package/dist/commands/stop.js +85 -0
  30. package/dist/commands/stop.js.map +1 -0
  31. package/dist/execution/direct-strategy.d.ts +18 -0
  32. package/dist/execution/direct-strategy.d.ts.map +1 -0
  33. package/dist/execution/direct-strategy.js +156 -0
  34. package/dist/execution/direct-strategy.js.map +1 -0
  35. package/dist/execution/docker-strategy.d.ts +26 -0
  36. package/dist/execution/docker-strategy.d.ts.map +1 -0
  37. package/dist/execution/docker-strategy.js +222 -0
  38. package/dist/execution/docker-strategy.js.map +1 -0
  39. package/dist/execution/index.d.ts +14 -0
  40. package/dist/execution/index.d.ts.map +1 -0
  41. package/dist/execution/index.js +13 -0
  42. package/dist/execution/index.js.map +1 -0
  43. package/dist/execution/kubernetes-exec-strategy.d.ts +23 -0
  44. package/dist/execution/kubernetes-exec-strategy.d.ts.map +1 -0
  45. package/dist/execution/kubernetes-exec-strategy.js +232 -0
  46. package/dist/execution/kubernetes-exec-strategy.js.map +1 -0
  47. package/dist/execution/registry.d.ts +41 -0
  48. package/dist/execution/registry.d.ts.map +1 -0
  49. package/dist/execution/registry.js +84 -0
  50. package/dist/execution/registry.js.map +1 -0
  51. package/dist/execution/slurm-strategy.d.ts +22 -0
  52. package/dist/execution/slurm-strategy.d.ts.map +1 -0
  53. package/dist/execution/slurm-strategy.js +219 -0
  54. package/dist/execution/slurm-strategy.js.map +1 -0
  55. package/dist/execution/types.d.ts +72 -0
  56. package/dist/execution/types.d.ts.map +1 -0
  57. package/dist/execution/types.js +10 -0
  58. package/dist/execution/types.js.map +1 -0
  59. package/dist/index.d.ts +22 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +22 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/lib/api-client.d.ts +35 -0
  64. package/dist/lib/api-client.d.ts.map +1 -0
  65. package/dist/lib/api-client.js +126 -0
  66. package/dist/lib/api-client.js.map +1 -0
  67. package/dist/lib/config.d.ts +174 -0
  68. package/dist/lib/config.d.ts.map +1 -0
  69. package/dist/lib/config.js +399 -0
  70. package/dist/lib/config.js.map +1 -0
  71. package/dist/lib/copy-worktree.d.ts +73 -0
  72. package/dist/lib/copy-worktree.d.ts.map +1 -0
  73. package/dist/lib/copy-worktree.js +374 -0
  74. package/dist/lib/copy-worktree.js.map +1 -0
  75. package/dist/lib/git-pr.d.ts +63 -0
  76. package/dist/lib/git-pr.d.ts.map +1 -0
  77. package/dist/lib/git-pr.js +224 -0
  78. package/dist/lib/git-pr.js.map +1 -0
  79. package/dist/lib/hardware-id.d.ts +25 -0
  80. package/dist/lib/hardware-id.d.ts.map +1 -0
  81. package/dist/lib/hardware-id.js +186 -0
  82. package/dist/lib/hardware-id.js.map +1 -0
  83. package/dist/lib/hpc-context.d.ts +35 -0
  84. package/dist/lib/hpc-context.d.ts.map +1 -0
  85. package/dist/lib/hpc-context.js +167 -0
  86. package/dist/lib/hpc-context.js.map +1 -0
  87. package/dist/lib/prompt-templates.d.ts +195 -0
  88. package/dist/lib/prompt-templates.d.ts.map +1 -0
  89. package/dist/lib/prompt-templates.js +353 -0
  90. package/dist/lib/prompt-templates.js.map +1 -0
  91. package/dist/lib/providers.d.ts +27 -0
  92. package/dist/lib/providers.d.ts.map +1 -0
  93. package/dist/lib/providers.js +372 -0
  94. package/dist/lib/providers.js.map +1 -0
  95. package/dist/lib/repo-context.d.ts +18 -0
  96. package/dist/lib/repo-context.d.ts.map +1 -0
  97. package/dist/lib/repo-context.js +61 -0
  98. package/dist/lib/repo-context.js.map +1 -0
  99. package/dist/lib/repo-utils.d.ts +35 -0
  100. package/dist/lib/repo-utils.d.ts.map +1 -0
  101. package/dist/lib/repo-utils.js +222 -0
  102. package/dist/lib/repo-utils.js.map +1 -0
  103. package/dist/lib/resources.d.ts +17 -0
  104. package/dist/lib/resources.d.ts.map +1 -0
  105. package/dist/lib/resources.js +227 -0
  106. package/dist/lib/resources.js.map +1 -0
  107. package/dist/lib/slurm-detect.d.ts +15 -0
  108. package/dist/lib/slurm-detect.d.ts.map +1 -0
  109. package/dist/lib/slurm-detect.js +148 -0
  110. package/dist/lib/slurm-detect.js.map +1 -0
  111. package/dist/lib/slurm-executor.d.ts +70 -0
  112. package/dist/lib/slurm-executor.d.ts.map +1 -0
  113. package/dist/lib/slurm-executor.js +402 -0
  114. package/dist/lib/slurm-executor.js.map +1 -0
  115. package/dist/lib/slurm-job-monitor.d.ts +52 -0
  116. package/dist/lib/slurm-job-monitor.d.ts.map +1 -0
  117. package/dist/lib/slurm-job-monitor.js +212 -0
  118. package/dist/lib/slurm-job-monitor.js.map +1 -0
  119. package/dist/lib/ssh-discovery.d.ts +17 -0
  120. package/dist/lib/ssh-discovery.d.ts.map +1 -0
  121. package/dist/lib/ssh-discovery.js +287 -0
  122. package/dist/lib/ssh-discovery.js.map +1 -0
  123. package/dist/lib/ssh-installer.d.ts +69 -0
  124. package/dist/lib/ssh-installer.d.ts.map +1 -0
  125. package/dist/lib/ssh-installer.js +230 -0
  126. package/dist/lib/ssh-installer.js.map +1 -0
  127. package/dist/lib/streaming-prompt.d.ts +48 -0
  128. package/dist/lib/streaming-prompt.d.ts.map +1 -0
  129. package/dist/lib/streaming-prompt.js +91 -0
  130. package/dist/lib/streaming-prompt.js.map +1 -0
  131. package/dist/lib/task-executor.d.ts +114 -0
  132. package/dist/lib/task-executor.d.ts.map +1 -0
  133. package/dist/lib/task-executor.js +753 -0
  134. package/dist/lib/task-executor.js.map +1 -0
  135. package/dist/lib/websocket-client.d.ts +200 -0
  136. package/dist/lib/websocket-client.d.ts.map +1 -0
  137. package/dist/lib/websocket-client.js +781 -0
  138. package/dist/lib/websocket-client.js.map +1 -0
  139. package/dist/lib/workdir-safety.d.ts +63 -0
  140. package/dist/lib/workdir-safety.d.ts.map +1 -0
  141. package/dist/lib/workdir-safety.js +247 -0
  142. package/dist/lib/workdir-safety.js.map +1 -0
  143. package/dist/lib/worktree-include.d.ts +14 -0
  144. package/dist/lib/worktree-include.d.ts.map +1 -0
  145. package/dist/lib/worktree-include.js +68 -0
  146. package/dist/lib/worktree-include.js.map +1 -0
  147. package/dist/lib/worktree-setup.d.ts +18 -0
  148. package/dist/lib/worktree-setup.d.ts.map +1 -0
  149. package/dist/lib/worktree-setup.js +60 -0
  150. package/dist/lib/worktree-setup.js.map +1 -0
  151. package/dist/lib/worktree.d.ts +37 -0
  152. package/dist/lib/worktree.d.ts.map +1 -0
  153. package/dist/lib/worktree.js +411 -0
  154. package/dist/lib/worktree.js.map +1 -0
  155. package/dist/mcp/index.d.ts +8 -0
  156. package/dist/mcp/index.d.ts.map +1 -0
  157. package/dist/mcp/index.js +8 -0
  158. package/dist/mcp/index.js.map +1 -0
  159. package/dist/mcp/server.d.ts +45 -0
  160. package/dist/mcp/server.d.ts.map +1 -0
  161. package/dist/mcp/server.js +153 -0
  162. package/dist/mcp/server.js.map +1 -0
  163. package/dist/mcp/session-bridge.d.ts +87 -0
  164. package/dist/mcp/session-bridge.d.ts.map +1 -0
  165. package/dist/mcp/session-bridge.js +317 -0
  166. package/dist/mcp/session-bridge.js.map +1 -0
  167. package/dist/mcp/tools.d.ts +70 -0
  168. package/dist/mcp/tools.d.ts.map +1 -0
  169. package/dist/mcp/tools.js +234 -0
  170. package/dist/mcp/tools.js.map +1 -0
  171. package/dist/mcp/types.d.ts +197 -0
  172. package/dist/mcp/types.d.ts.map +1 -0
  173. package/dist/mcp/types.js +16 -0
  174. package/dist/mcp/types.js.map +1 -0
  175. package/dist/providers/base-adapter.d.ts +56 -0
  176. package/dist/providers/base-adapter.d.ts.map +1 -0
  177. package/dist/providers/base-adapter.js +5 -0
  178. package/dist/providers/base-adapter.js.map +1 -0
  179. package/dist/providers/claude-code-adapter.d.ts +27 -0
  180. package/dist/providers/claude-code-adapter.d.ts.map +1 -0
  181. package/dist/providers/claude-code-adapter.js +298 -0
  182. package/dist/providers/claude-code-adapter.js.map +1 -0
  183. package/dist/providers/claude-sdk-adapter.d.ts +60 -0
  184. package/dist/providers/claude-sdk-adapter.d.ts.map +1 -0
  185. package/dist/providers/claude-sdk-adapter.js +632 -0
  186. package/dist/providers/claude-sdk-adapter.js.map +1 -0
  187. package/dist/providers/codex-adapter.d.ts +21 -0
  188. package/dist/providers/codex-adapter.d.ts.map +1 -0
  189. package/dist/providers/codex-adapter.js +197 -0
  190. package/dist/providers/codex-adapter.js.map +1 -0
  191. package/dist/providers/index.d.ts +26 -0
  192. package/dist/providers/index.d.ts.map +1 -0
  193. package/dist/providers/index.js +58 -0
  194. package/dist/providers/index.js.map +1 -0
  195. package/dist/providers/slurm-adapter.d.ts +26 -0
  196. package/dist/providers/slurm-adapter.d.ts.map +1 -0
  197. package/dist/providers/slurm-adapter.js +146 -0
  198. package/dist/providers/slurm-adapter.js.map +1 -0
  199. package/dist/types.d.ts +592 -0
  200. package/dist/types.d.ts.map +1 -0
  201. package/dist/types.js +5 -0
  202. package/dist/types.js.map +1 -0
  203. 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