@saadnvd1/agent-os 0.3.464 → 0.3.466

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 (57) hide show
  1. package/README.md +20 -0
  2. package/app/api/fresh-start/route.ts +5 -3
  3. package/app/api/git/commit/route.ts +4 -3
  4. package/app/api/git/file-content/route.ts +4 -15
  5. package/app/api/git/history/[hash]/diff/route.ts +1 -1
  6. package/app/api/git/history/[hash]/route.ts +1 -1
  7. package/app/api/git/history/route.ts +1 -1
  8. package/app/api/git/push/route.ts +2 -2
  9. package/app/api/sessions/[id]/pr/route.ts +2 -2
  10. package/components/Chat/ChatPanel.tsx +23 -1
  11. package/components/Chat/Messages.tsx +2 -3
  12. package/components/Chat/StaleChip.tsx +55 -0
  13. package/components/Workspaces/FreshStartForm.tsx +29 -6
  14. package/data/chat/useChat.ts +10 -2
  15. package/lib/away/deploy.ts +3 -7
  16. package/lib/away/run.ts +6 -5
  17. package/lib/build.ts +2 -6
  18. package/lib/bus/index.ts +3 -0
  19. package/lib/chat/client-send.ts +11 -3
  20. package/lib/chat/drivers/claude-mapper.ts +11 -1
  21. package/lib/chat/drivers/claude.ts +9 -1
  22. package/lib/chat/events.ts +14 -1
  23. package/lib/chat/files.ts +31 -9
  24. package/lib/chat/refresh.ts +14 -2
  25. package/lib/chat/runner.ts +8 -1
  26. package/lib/chat/stale.ts +95 -0
  27. package/lib/chat/worker/host.ts +133 -4
  28. package/lib/chat/worker/protocol.ts +3 -0
  29. package/lib/done/remote.ts +5 -6
  30. package/lib/done/testing.ts +2 -7
  31. package/lib/done/worktree.ts +2 -2
  32. package/lib/git-exec.ts +334 -0
  33. package/lib/git-history.ts +29 -41
  34. package/lib/git-status.ts +45 -124
  35. package/lib/git.ts +44 -159
  36. package/lib/lumifyhub/publish.ts +2 -2
  37. package/lib/orchestrator/deliver.ts +9 -1
  38. package/lib/orchestrator/diff.ts +15 -13
  39. package/lib/orchestrator/rebase.ts +20 -24
  40. package/lib/orchestrator/repo-slug.ts +4 -7
  41. package/lib/orchestrator/repo.ts +24 -35
  42. package/lib/pr-generation.ts +2 -1
  43. package/lib/pr.ts +16 -57
  44. package/lib/schedules/start.ts +1 -0
  45. package/lib/sessions/worktree-setup.ts +17 -13
  46. package/lib/stacks/start.ts +7 -11
  47. package/lib/tasks/branch-worktree.ts +3 -2
  48. package/lib/tasks/finish.ts +5 -6
  49. package/lib/tasks/gh.ts +5 -0
  50. package/lib/tasks/move-testing.ts +2 -2
  51. package/lib/tasks/move.ts +3 -2
  52. package/lib/tasks/project-ref.ts +12 -19
  53. package/lib/tasks/session.ts +5 -5
  54. package/lib/worktree-hooks.ts +3 -7
  55. package/lib/worktree-placed.ts +3 -10
  56. package/lib/worktrees.ts +4 -42
  57. package/package.json +1 -1
package/lib/git-status.ts CHANGED
@@ -1,33 +1,20 @@
1
- import { execFile, execFileSync } from "child_process";
2
- import { promisify } from "util";
3
1
  import { realpathSync, unlinkSync } from "fs";
4
2
  import { basename, dirname, join, resolve, sep } from "path";
5
3
  import { homedir } from "os";
6
- import { remoteDefaultBranch } from "./pr";
7
-
8
- const execFileAsync = promisify(execFile);
9
-
10
- // The reads the git panel polls run off the event loop: a slow repo
11
- // shouldn't stall every terminal and status push while git works.
12
- async function git(cwd: string, args: string[]): Promise<string> {
13
- // Paths are names, never globs or pathspec magic: "[draft].md" or "*"
14
- // must not reach other files.
15
- const { stdout } = await execFileAsync(
16
- "git",
17
- ["--literal-pathspecs", ...args],
18
- {
19
- cwd,
20
- encoding: "utf-8",
21
- maxBuffer: 10 * 1024 * 1024,
22
- // A hung git (a stalled mount, a stuck hook) fails the request instead.
23
- timeout: 30_000,
24
- killSignal: "SIGKILL",
25
- // Status reads never take the index lock a commit or rebase needs.
26
- env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
27
- }
28
- );
29
- return stdout;
30
- }
4
+ import {
5
+ currentBranch as readBranch,
6
+ git as runGit,
7
+ gitOk,
8
+ gitPush,
9
+ gitResult,
10
+ type GitOptions,
11
+ } from "./git-exec";
12
+
13
+ // The reads the git panel polls, and its stage/unstage/discard: paths are
14
+ // names, never globs or pathspec magic, so "[draft].md" or "*" can't reach
15
+ // other files. A hung git fails the request at 30s.
16
+ const READ: GitOptions = { literalPathspecs: true, timeout: 30_000 };
17
+ const git = (cwd: string, args: string[]) => runGit(cwd, args, READ);
31
18
 
32
19
  // For commands whose exit code isn't failure (a diff with changes exits 1):
33
20
  // whatever they printed, or nothing.
@@ -40,6 +27,9 @@ async function gitOutput(cwd: string, args: string[]): Promise<string> {
40
27
  }
41
28
  }
42
29
 
30
+ // A commit or push can wait on a hook or a remote.
31
+ const WRITE: GitOptions = { timeout: 120_000 };
32
+
43
33
  /**
44
34
  * Expand ~ to home directory in paths
45
35
  */
@@ -282,51 +272,25 @@ export async function discardChanges(
282
272
  unlinkSync(inside);
283
273
  }
284
274
 
285
- /**
286
- * Check if directory is a git repository
287
- */
288
- export async function isGitRepo(workingDir: string): Promise<boolean> {
289
- try {
290
- await git(workingDir, ["rev-parse", "--git-dir"]);
291
- return true;
292
- } catch {
293
- return false;
294
- }
295
- }
296
-
297
- // Writes and the few sync reads: argv only, never a shell, so a branch name,
298
- // a commit message or a path is always data.
299
- function gitSync(cwd: string, args: string[], input?: string): string {
300
- return execFileSync("git", args, {
301
- cwd,
302
- encoding: "utf-8",
303
- input,
304
- stdio: ["pipe", "pipe", "pipe"],
305
- maxBuffer: 10 * 1024 * 1024,
306
- });
307
- }
275
+ export { isGitRepo } from "./git";
308
276
 
309
277
  /**
310
278
  * Get the root of the git repository
311
279
  */
312
- export function getGitRoot(workingDir: string): string {
280
+ export async function getGitRoot(workingDir: string): Promise<string> {
313
281
  try {
314
- return gitSync(workingDir, ["rev-parse", "--show-toplevel"]).trim();
282
+ return (await runGit(workingDir, ["rev-parse", "--show-toplevel"])).trim();
315
283
  } catch {
316
284
  return workingDir;
317
285
  }
318
286
  }
319
287
 
320
- function currentBranch(workingDir: string): string {
321
- return gitSync(workingDir, ["branch", "--show-current"]).trim();
322
- }
323
-
324
288
  /**
325
289
  * Check if on main/master branch
326
290
  */
327
- export function isMainBranch(workingDir: string): boolean {
291
+ export async function isMainBranch(workingDir: string): Promise<boolean> {
328
292
  try {
329
- const branch = currentBranch(workingDir);
293
+ const branch = await readBranch(workingDir);
330
294
  return branch === "main" || branch === "master";
331
295
  } catch {
332
296
  return false;
@@ -337,59 +301,25 @@ export function isMainBranch(workingDir: string): boolean {
337
301
  * A name `git checkout -b` takes as a branch and nothing else: no option
338
302
  * ("-f", "--orphan=x"), nothing git itself refuses as a ref.
339
303
  */
340
- export function isValidBranchName(workingDir: string, name: string): boolean {
304
+ export async function isValidBranchName(
305
+ workingDir: string,
306
+ name: string
307
+ ): Promise<boolean> {
341
308
  if (!name || name.startsWith("-")) return false;
342
- try {
343
- gitSync(workingDir, ["check-ref-format", "--branch", name]);
344
- return true;
345
- } catch {
346
- return false;
347
- }
309
+ return gitOk(workingDir, ["check-ref-format", "--branch", name]);
348
310
  }
349
311
 
350
312
  /**
351
313
  * Create a new branch and switch to it
352
314
  */
353
- export function createBranch(workingDir: string, branchName: string): void {
354
- if (!isValidBranchName(workingDir, branchName)) {
315
+ export async function createBranch(
316
+ workingDir: string,
317
+ branchName: string
318
+ ): Promise<void> {
319
+ if (!(await isValidBranchName(workingDir, branchName))) {
355
320
  throw new Error(`Invalid branch name: ${branchName}`);
356
321
  }
357
- gitSync(workingDir, ["checkout", "-b", branchName, "--"]);
358
- }
359
-
360
- // A commit or push can wait on a hook or a remote: off the event loop, with a
361
- // ceiling (a hung ssh is killed at it), and no git credential prompt.
362
- function gitWrite(
363
- cwd: string,
364
- args: string[],
365
- input?: string
366
- ): Promise<{ stdout: string; stderr: string }> {
367
- return new Promise((resolve, reject) => {
368
- const child = execFile(
369
- "git",
370
- args,
371
- {
372
- cwd,
373
- encoding: "utf-8",
374
- maxBuffer: 10 * 1024 * 1024,
375
- timeout: 120_000,
376
- killSignal: "SIGKILL",
377
- env: {
378
- ...process.env,
379
- GIT_TERMINAL_PROMPT: "0",
380
- },
381
- },
382
- (error, stdout, stderr) => {
383
- if (!error) return resolve({ stdout, stderr });
384
- // "nothing to commit" is on stdout.
385
- const reason = stderr.trim() || stdout.trim() || error.message;
386
- reject(new Error(`git ${args[0]} failed: ${reason}`));
387
- }
388
- );
389
- // git exiting before it read the message is its failure, not a crash.
390
- child.stdin?.on("error", () => {});
391
- child.stdin?.end(input ?? "");
392
- });
322
+ await runGit(workingDir, ["checkout", "-b", branchName, "--"]);
393
323
  }
394
324
 
395
325
  /**
@@ -400,7 +330,11 @@ export async function commit(
400
330
  workingDir: string,
401
331
  message: string
402
332
  ): Promise<string> {
403
- return (await gitWrite(workingDir, ["commit", "-F", "-"], message)).stdout;
333
+ const { stdout } = await gitResult(workingDir, ["commit", "-F", "-"], {
334
+ ...WRITE,
335
+ input: message,
336
+ });
337
+ return stdout;
404
338
  }
405
339
 
406
340
  /**
@@ -412,43 +346,30 @@ export async function push(
412
346
  ): Promise<string> {
413
347
  const args = ["push"];
414
348
  if (setUpstream) {
415
- const branch = currentBranch(workingDir);
416
- if (!isValidBranchName(workingDir, branch)) {
349
+ const branch = await readBranch(workingDir);
350
+ if (!(await isValidBranchName(workingDir, branch))) {
417
351
  throw new Error(`Can't push from "${branch || "a detached HEAD"}"`);
418
352
  }
419
353
  args.push("-u", "origin", `refs/heads/${branch}:refs/heads/${branch}`);
420
354
  }
421
355
  // git push reports progress and the remote's messages on stderr.
422
- const { stdout, stderr } = await gitWrite(workingDir, args);
423
- return `${stdout}${stderr}`;
356
+ return gitPush(workingDir, args.slice(1), WRITE);
424
357
  }
425
358
 
426
359
  /**
427
360
  * Check if branch has upstream
428
361
  */
429
- export function hasUpstream(workingDir: string): boolean {
430
- try {
431
- gitSync(workingDir, ["rev-parse", "--abbrev-ref", "@{upstream}"]);
432
- return true;
433
- } catch {
434
- return false;
435
- }
362
+ export function hasUpstream(workingDir: string): Promise<boolean> {
363
+ return gitOk(workingDir, ["rev-parse", "--abbrev-ref", "@{upstream}"]);
436
364
  }
437
365
 
438
366
  /**
439
367
  * Get remote URL
440
368
  */
441
- export function getRemoteUrl(workingDir: string): string | null {
369
+ export async function getRemoteUrl(workingDir: string): Promise<string | null> {
442
370
  try {
443
- return gitSync(workingDir, ["remote", "get-url", "origin"]).trim();
371
+ return (await runGit(workingDir, ["remote", "get-url", "origin"])).trim();
444
372
  } catch {
445
373
  return null;
446
374
  }
447
375
  }
448
-
449
- /**
450
- * Get the default branch name (main or master)
451
- */
452
- export function getDefaultBranch(workingDir: string): string {
453
- return remoteDefaultBranch(workingDir);
454
- }
package/lib/git.ts CHANGED
@@ -1,98 +1,61 @@
1
1
  /**
2
- * Git utilities for worktree management
2
+ * Repository questions AgentOS asks: is this a repo, which branch is the
3
+ * default, which branches exist. Every git call goes through lib/git-exec.
3
4
  */
4
5
 
5
- import { execFile } from "child_process";
6
- import { promisify } from "util";
6
+ import * as os from "os";
7
7
  import * as path from "path";
8
+ import { currentBranch, git, gitOk, resolveRef } from "./git-exec";
8
9
 
9
- const execFileAsync = promisify(execFile);
10
+ const expandHome = (p: string) => p.replace(/^~(?=$|\/)/, os.homedir());
10
11
 
11
12
  /**
12
- * Check if a directory is a git repository
13
+ * Check if a directory is a git repository. A slow mount gets the git
14
+ * panel's 30s before it reads as "not a repository".
13
15
  */
14
16
  export async function isGitRepo(dirPath: string): Promise<boolean> {
15
- try {
16
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
17
- await execFileAsync("git", ["-C", resolvedPath, "rev-parse", "--git-dir"], {
18
- timeout: 5000,
19
- });
20
- return true;
21
- } catch {
22
- return false;
23
- }
17
+ return gitOk(expandHome(dirPath), ["rev-parse", "--git-dir"], {
18
+ timeout: 30_000,
19
+ });
24
20
  }
25
21
 
26
22
  /**
27
- * Get the current branch name
23
+ * The checked-out branch, "HEAD" when detached.
28
24
  */
29
25
  export async function getCurrentBranch(dirPath: string): Promise<string> {
30
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
31
- const { stdout } = await execFileAsync(
32
- "git",
33
- ["-C", resolvedPath, "rev-parse", "--abbrev-ref", "HEAD"],
34
- { timeout: 5000 }
26
+ return (
27
+ (await currentBranch(expandHome(dirPath), { timeout: 5000 })) || "HEAD"
35
28
  );
36
- return stdout.trim();
37
29
  }
38
30
 
39
31
  /**
40
- * Get the default branch (main or master)
32
+ * The default branch: the remote's, else main, else master, else the
33
+ * current one.
41
34
  */
42
35
  export async function getDefaultBranch(dirPath: string): Promise<string> {
43
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
44
- try {
45
- // Try to get the default branch from remote
46
- const { stdout } = await execFileAsync(
47
- "git",
48
- ["-C", resolvedPath, "symbolic-ref", "refs/remotes/origin/HEAD"],
49
- { timeout: 5000 }
50
- );
51
- const name = stdout.trim().replace(/^refs\/remotes\/origin\//, "");
52
- // The remote picks this name: anything that isn't a plain branch name
53
- // (a "$(...)" a hostile repo could set) is ignored.
54
- if (name && isBranchName(name)) {
55
- return name;
56
- }
57
- } catch {
58
- // Ignore
59
- }
60
-
61
- // Fallback: check if main or master exists
62
- try {
63
- await execFileAsync(
64
- "git",
65
- ["-C", resolvedPath, "rev-parse", "--verify", "main"],
66
- {
67
- timeout: 5000,
68
- }
69
- );
70
- return "main";
71
- } catch {
72
- try {
73
- await execFileAsync(
74
- "git",
75
- ["-C", resolvedPath, "rev-parse", "--verify", "master"],
76
- {
77
- timeout: 5000,
78
- }
79
- );
80
- return "master";
81
- } catch {
82
- // Return current branch as fallback
83
- return getCurrentBranch(resolvedPath);
84
- }
85
- }
36
+ const dir = expandHome(dirPath);
37
+ const opts = { timeout: 5000 };
38
+ const remote = await git(
39
+ dir,
40
+ ["symbolic-ref", "refs/remotes/origin/HEAD"],
41
+ opts
42
+ ).catch(() => "");
43
+ const name = remote.trim().replace(/^refs\/remotes\/origin\//, "");
44
+ // The remote picks this name: anything that isn't a plain branch name
45
+ // (a "$(...)" a hostile repo could set) is ignored.
46
+ if (name && isBranchName(name)) return name;
47
+ if (await resolveRef(dir, "main", opts)) return "main";
48
+ if (await resolveRef(dir, "master", opts)) return "master";
49
+ return getCurrentBranch(dir);
86
50
  }
87
51
 
88
52
  /**
89
53
  * Get list of local branches
90
54
  */
91
55
  export async function getBranches(dirPath: string): Promise<string[]> {
92
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
93
- const { stdout } = await execFileAsync(
94
- "git",
95
- ["-C", resolvedPath, "branch", "--format=%(refname:short)"],
56
+ const stdout = await git(
57
+ expandHome(dirPath),
58
+ ["branch", "--format=%(refname:short)"],
96
59
  { timeout: 5000 }
97
60
  );
98
61
  return stdout
@@ -108,32 +71,18 @@ export async function branchExists(
108
71
  dirPath: string,
109
72
  branchName: string
110
73
  ): Promise<boolean> {
111
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
112
- try {
113
- await execFileAsync(
114
- "git",
115
- [
116
- "-C",
117
- resolvedPath,
118
- "rev-parse",
119
- "--verify",
120
- "--end-of-options",
121
- branchName,
122
- ],
123
- { timeout: 5000 }
124
- );
125
- return true;
126
- } catch {
127
- return false;
128
- }
74
+ return gitOk(
75
+ expandHome(dirPath),
76
+ ["rev-parse", "--verify", "--end-of-options", branchName],
77
+ { timeout: 5000 }
78
+ );
129
79
  }
130
80
 
131
81
  /**
132
82
  * Get the repository name from path
133
83
  */
134
84
  export function getRepoName(dirPath: string): string {
135
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
136
- return path.basename(resolvedPath);
85
+ return path.basename(expandHome(dirPath));
137
86
  }
138
87
 
139
88
  /**
@@ -169,11 +118,10 @@ export async function remoteBranchExists(
169
118
  dirPath: string,
170
119
  branchName: string
171
120
  ): Promise<boolean> {
172
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
173
121
  try {
174
- const { stdout } = await execFileAsync(
175
- "git",
176
- ["-C", resolvedPath, "ls-remote", "--heads", "origin", "--", branchName],
122
+ const stdout = await git(
123
+ expandHome(dirPath),
124
+ ["ls-remote", "--heads", "origin", "--", branchName],
177
125
  { timeout: 10000 }
178
126
  );
179
127
  return stdout.trim().length > 0;
@@ -194,72 +142,9 @@ export async function renameBranch(
194
142
  ): Promise<void> {
195
143
  if (await remoteBranchExists(dirPath, oldBranchName))
196
144
  throw new Error(`${oldBranchName} is on the remote; keeping its name`);
197
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
198
- await execFileAsync(
199
- "git",
200
- ["-C", resolvedPath, "branch", "-m", "--", oldBranchName, newBranchName],
145
+ await git(
146
+ expandHome(dirPath),
147
+ ["branch", "-m", "--", oldBranchName, newBranchName],
201
148
  { timeout: 10000 }
202
149
  );
203
150
  }
204
-
205
- /**
206
- * Get git status summary (files changed, ahead/behind)
207
- */
208
- export async function getGitStatus(dirPath: string): Promise<{
209
- staged: number;
210
- unstaged: number;
211
- untracked: number;
212
- ahead: number;
213
- behind: number;
214
- }> {
215
- const resolvedPath = dirPath.replace(/^~/, process.env.HOME || "");
216
-
217
- // Get file counts
218
- const { stdout: statusOutput } = await execFileAsync(
219
- "git",
220
- ["-C", resolvedPath, "status", "--porcelain"],
221
- { timeout: 5000 }
222
- );
223
-
224
- const lines = statusOutput.trim().split("\n").filter(Boolean);
225
- let staged = 0;
226
- let unstaged = 0;
227
- let untracked = 0;
228
-
229
- for (const line of lines) {
230
- const index = line[0];
231
- const worktree = line[1];
232
- if (index === "?" && worktree === "?") {
233
- untracked++;
234
- } else {
235
- if (index !== " " && index !== "?") staged++;
236
- if (worktree !== " " && worktree !== "?") unstaged++;
237
- }
238
- }
239
-
240
- // Get ahead/behind counts
241
- let ahead = 0;
242
- let behind = 0;
243
- try {
244
- // No upstream throws, and counts as level.
245
- const { stdout: aheadBehind } = await execFileAsync(
246
- "git",
247
- [
248
- "-C",
249
- resolvedPath,
250
- "rev-list",
251
- "--left-right",
252
- "--count",
253
- "HEAD...@{upstream}",
254
- ],
255
- { timeout: 5000 }
256
- );
257
- const [a, b] = aheadBehind.trim().split(/\s+/);
258
- ahead = parseInt(a, 10) || 0;
259
- behind = parseInt(b, 10) || 0;
260
- } catch {
261
- // No upstream, ignore
262
- }
263
-
264
- return { staged, unstaged, untracked, ahead, behind };
265
- }
@@ -13,7 +13,7 @@ import {
13
13
  type Session,
14
14
  } from "../db";
15
15
  import { getProject } from "../projects";
16
- import { run } from "../tasks/gh";
16
+ import { git as runGit } from "../git-exec";
17
17
  import { LumifyHubError } from "./client";
18
18
  import { requireClient } from "./connection";
19
19
  import { linkedWorkspaceFor } from "./links";
@@ -33,7 +33,7 @@ function localProject(projectId: string): Project {
33
33
  }
34
34
 
35
35
  const git = (dir: string, ...args: string[]) =>
36
- run("git", args, dir).then((out) => out.trim());
36
+ runGit(dir, args).then((out) => out.trim());
37
37
 
38
38
  async function commonDir(dir: string): Promise<string | null> {
39
39
  return git(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")
@@ -1,4 +1,5 @@
1
1
  import type { ChatOrigin, ChatState } from "../chat/events";
2
+ import type { CompactFirst } from "../chat/stale";
2
3
  import {
3
4
  isAnswer,
4
5
  deliveriesSince,
@@ -22,7 +23,12 @@ export const AGENTOS_SENDER = "agentos";
22
23
 
23
24
  export type ChatSender = (
24
25
  sessionId: string,
25
- input: { text: string; from: string; origin: ChatOrigin }
26
+ input: {
27
+ text: string;
28
+ from: string;
29
+ origin: ChatOrigin;
30
+ compact: CompactFirst;
31
+ }
26
32
  ) => Promise<void>;
27
33
 
28
34
  // After a failed send, wait 5s, then 10s, 20s ... up to 5 minutes.
@@ -97,6 +103,8 @@ export async function deliverEvents(opts: {
97
103
  text,
98
104
  from: AGENTOS_SENDER,
99
105
  origin,
106
+ // An orchestrator idle past its cache is compacted first.
107
+ compact: "auto",
100
108
  });
101
109
  failing.delete(workspaceId);
102
110
  return text;
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import path from "path";
10
- import { run } from "../tasks/gh";
10
+ import { git } from "../git-exec";
11
11
  import { SECRET_ASSIGNMENT, SECRET_TOKENS } from "./untrusted";
12
12
 
13
13
  export interface ChangedFile {
@@ -38,30 +38,32 @@ export async function changedFiles(
38
38
  base: string,
39
39
  sha: string
40
40
  ): Promise<ChangedFile[]> {
41
- const raw = await run(
42
- "git",
43
- ["diff", "--raw", "--no-renames", `${base}...${sha}`],
44
- repo
45
- );
41
+ const raw = await git(repo, [
42
+ "diff",
43
+ "--raw",
44
+ "--no-renames",
45
+ `${base}...${sha}`,
46
+ ]);
46
47
  const files = parseRaw(raw);
47
48
  for (const f of files)
48
49
  if (f.link !== undefined && f.status !== "D")
49
- f.link = (await run("git", ["show", `${sha}:${f.path}`], repo)).trim();
50
+ f.link = (await git(repo, ["show", `${sha}:${f.path}`])).trim();
50
51
  return files;
51
52
  }
52
53
 
53
54
  // The whole change, as a reviewer reads it.
54
55
  export function fullDiff(repo: string, base: string, sha: string) {
55
- return run("git", ["diff", "--no-color", `${base}...${sha}`], repo);
56
+ return git(repo, ["diff", "--no-color", `${base}...${sha}`]);
56
57
  }
57
58
 
58
59
  // The lines the change adds, for the secrets check.
59
60
  export async function addedLines(repo: string, base: string, sha: string) {
60
- const diff = await run(
61
- "git",
62
- ["diff", "-U0", "--no-color", `${base}...${sha}`],
63
- repo
64
- );
61
+ const diff = await git(repo, [
62
+ "diff",
63
+ "-U0",
64
+ "--no-color",
65
+ `${base}...${sha}`,
66
+ ]);
65
67
  return diff
66
68
  .split("\n")
67
69
  .filter((l) => l.startsWith("+") && !l.startsWith("+++"))