cueloop 0.1.0-alpha.43 → 0.1.0-alpha.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cueloop",
3
- "version": "0.1.0-alpha.43",
3
+ "version": "0.1.0-alpha.44",
4
4
  "description": "Terminal review surface for coding agents: annotate plans and diffs, return a verdict, send structured feedback back",
5
5
  "homepage": "https://github.com/mmurakaru/cueloop#readme",
6
6
  "bugs": {
@@ -27,10 +27,10 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@cueloop/adapters": "0.1.0-alpha.43",
31
- "@cueloop/client": "0.1.0-alpha.43",
32
- "@cueloop/daemon": "0.1.0-alpha.43",
33
- "@cueloop/schema": "0.1.0-alpha.43"
30
+ "@cueloop/adapters": "0.1.0-alpha.44",
31
+ "@cueloop/client": "0.1.0-alpha.44",
32
+ "@cueloop/daemon": "0.1.0-alpha.44",
33
+ "@cueloop/schema": "0.1.0-alpha.44"
34
34
  },
35
35
  "engines": {
36
36
  "bun": ">=1.3.0"
package/src/main.ts CHANGED
@@ -19,8 +19,8 @@ import {
19
19
  } from "./open-target";
20
20
  import { sessionCommand } from "./session-commands";
21
21
  import { CLI_VERSION } from "./version";
22
- import { workingTreeDiff } from "./working-tree";
23
22
  import { DaemonClient } from "@cueloop/daemon/client";
23
+ import { workingTreeDiff } from "@cueloop/daemon/working-tree";
24
24
  import type { ReviewSession } from "@cueloop/schema";
25
25
  import { openReview, resolveWorkspace } from "@cueloop/daemon/review";
26
26
 
@@ -1,100 +0,0 @@
1
- /** Working-tree diff capture for `cueloop diff`, untracked files included. */
2
-
3
- import { join } from "node:path";
4
- import type { DiffFileContents } from "@cueloop/schema";
5
-
6
- async function git(args: string[], cwd: string): Promise<string | null> {
7
- const gitProcess = Bun.spawn(["git", ...args], { cwd, stdout: "pipe", stderr: "ignore" });
8
- const stdout = await new Response(gitProcess.stdout).text();
9
- if ((await gitProcess.exited) !== 0) return null;
10
- return stdout.trim();
11
- }
12
-
13
- /** The blob at HEAD for a path, or "" when the path is not in HEAD (new file). */
14
- async function headContents(path: string, cwd: string): Promise<string> {
15
- const gitProcess = Bun.spawn(["git", "show", `HEAD:${path}`], {
16
- cwd,
17
- stdout: "pipe",
18
- stderr: "ignore",
19
- });
20
- const stdout = await new Response(gitProcess.stdout).text();
21
- return (await gitProcess.exited) === 0 ? stdout : "";
22
- }
23
-
24
- /** The working-tree file, or "" when it is gone (deleted). */
25
- async function workingContents(path: string, cwd: string): Promise<string> {
26
- try {
27
- return await Bun.file(join(cwd, path)).text();
28
- } catch {
29
- return "";
30
- }
31
- }
32
-
33
- /** A NUL byte marks content git would treat as binary - not text-curatable. */
34
- function looksBinary(text: string): boolean {
35
- for (let index = 0; index < text.length; index++) {
36
- if (text.charCodeAt(index) === 0) return true;
37
- }
38
- return false;
39
- }
40
-
41
- /**
42
- * Full old/new contents for every tracked change against HEAD, classified by
43
- * git's own status letter. Renames/copies and binary files are omitted: a
44
- * path+text pair cannot express a rename header or a binary blob, so curation
45
- * would emit a patch that drops the old path or corrupts the file - leaving them
46
- * out keeps those changes in the patch but simply not curatable. The
47
- * NUL-delimited stream survives paths with spaces or unicode.
48
- */
49
- async function trackedFileContents(cwd: string): Promise<DiffFileContents[]> {
50
- const nameStatus = (await git(["diff", "HEAD", "--name-status", "-z"], cwd)) ?? "";
51
- const tokens = nameStatus.split("\0").filter((token) => token.length > 0);
52
- const files: DiffFileContents[] = [];
53
- let cursor = 0;
54
- while (cursor < tokens.length) {
55
- const code = tokens[cursor++]![0];
56
- if (code === "R" || code === "C") {
57
- cursor += 2;
58
- continue;
59
- }
60
- const path = tokens[cursor++]!;
61
- const oldContents = code === "A" ? "" : await headContents(path, cwd);
62
- const newContents = code === "D" ? "" : await workingContents(path, cwd);
63
- if (looksBinary(oldContents) || looksBinary(newContents)) continue;
64
- const status = code === "A" ? "added" : code === "D" ? "deleted" : "modified";
65
- files.push({ path, oldContents, newContents, status });
66
- }
67
- return files;
68
- }
69
-
70
- export interface WorkingTreeDiff {
71
- patch: string;
72
- /** Full file contents per curatable changed file, keeping curation applyable. */
73
- files: DiffFileContents[];
74
- }
75
-
76
- export async function workingTreeDiff(cwd = process.cwd()): Promise<WorkingTreeDiff> {
77
- const tracked = (await git(["diff", "HEAD"], cwd)) ?? "";
78
- const files = await trackedFileContents(cwd);
79
- // untracked files included by default
80
- const untrackedList = (await git(["ls-files", "--others", "--exclude-standard"], cwd)) ?? "";
81
- let untracked = "";
82
- for (const file of untrackedList.split("\n").filter(Boolean)) {
83
- // `git diff --no-index` exits 1 when the files differ - that is success here
84
- const gitProcess = Bun.spawn(["git", "diff", "--no-index", "--", "/dev/null", file], {
85
- cwd,
86
- stdout: "pipe",
87
- stderr: "ignore",
88
- });
89
- const stdout = await new Response(gitProcess.stdout).text();
90
- const code = await gitProcess.exited;
91
- if ((code === 0 || code === 1) && stdout.trim()) {
92
- untracked += stdout.trim() + "\n";
93
- const newContents = await workingContents(file, cwd);
94
- if (!looksBinary(newContents)) {
95
- files.push({ path: file, oldContents: "", newContents, status: "added" });
96
- }
97
- }
98
- }
99
- return { patch: [tracked, untracked].filter(Boolean).join("\n"), files };
100
- }