@dyyz1993/create-agent 2.0.1 → 2.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/package.json +1 -1
- package/src/commands/create.ts +31 -31
- package/src/commands/workspace.ts +112 -105
- package/src/lib/copy.ts +1 -0
- package/templates/agent/electron/main.js +46 -0
- package/templates/agent/electron/preload.js +5 -0
- package/templates/agent/electron-builder.json +40 -0
- package/templates/agent/eslint.config.mjs +2 -0
- package/templates/agent/package.json +60 -2
- package/templates/agent/src/mainview/App.tsx +29 -26
- package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +88 -88
- package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +105 -81
- package/templates/agent/src/mainview/components/search/SearchPanel.tsx +427 -378
- package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +3 -3
- package/templates/agent/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/agent/src/mainview/lib/api-client.ts +1 -4
- package/templates/agent/src/mainview/main.tsx +4 -10
- package/templates/agent/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/agent/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/agent/src/server-config.ts +1 -1
- package/templates/agent/src/server.ts +1 -2
- package/templates/agent/src/shared/handlers/chat.ts +5 -5
- package/templates/agent/src/shared/handlers/debug.ts +5 -1
- package/templates/agent/src/shared/handlers/git.ts +286 -243
- package/templates/agent/src/shared/http-routes.ts +1 -1
- package/templates/agent/src/shared/lib/bash-security.ts +43 -43
- package/templates/agent/tsconfig.ipc.json +5 -1
- package/templates/agent/tsconfig.json +3 -1
- package/templates/chat/package.json +3 -0
- package/templates/chat/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/chat/src/mainview/lib/api-client.ts +2 -5
- package/templates/chat/src/mainview/main.tsx +10 -7
- package/templates/chat/src/server-config.ts +1 -1
- package/templates/chat/src/server.ts +1 -2
- package/templates/chat/src/shared/handlers/chat.ts +5 -5
- package/templates/chat/src/shared/handlers/debug.ts +5 -1
- package/templates/chat/src/shared/http-routes.ts +1 -1
- package/templates/chat/tsconfig.ipc.json +5 -1
- package/templates/chat/tsconfig.json +12 -2
- package/templates/general/package.json +3 -0
- package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +101 -81
- package/templates/general/src/mainview/components/search/SearchPanel.tsx +429 -378
- package/templates/general/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/general/src/mainview/lib/api-client.ts +2 -5
- package/templates/general/src/mainview/main.tsx +10 -7
- package/templates/general/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/general/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/general/src/server-config.ts +1 -1
- package/templates/general/src/server.ts +1 -2
- package/templates/general/src/shared/handlers/chat.ts +5 -5
- package/templates/general/src/shared/handlers/debug.ts +5 -1
- package/templates/general/src/shared/handlers/git.ts +286 -243
- package/templates/general/src/shared/http-routes.ts +1 -1
- package/templates/general/tsconfig.ipc.json +5 -1
- package/templates/general/tsconfig.json +12 -2
- package/templates/shared/components/ErrorBoundary.tsx +50 -49
- package/templates/shared/http-routes.ts +210 -190
|
@@ -4,260 +4,303 @@ import type { RPCMethods, HandlerOptions } from "../rpc-schema";
|
|
|
4
4
|
import type { GitFileChange } from "../modules/git";
|
|
5
5
|
|
|
6
6
|
type RegisterFn = <K extends keyof RPCMethods & string>(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
method: K,
|
|
8
|
+
handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
|
|
9
9
|
) => void;
|
|
10
10
|
|
|
11
11
|
function execGit(args: string[], cwd: string, allowNonZero = false): string {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const proc = Bun.spawnSync(["git", ...args], { cwd, stdout: "pipe", stderr: "pipe" });
|
|
13
|
+
if (proc.exitCode !== 0 && !allowNonZero) {
|
|
14
|
+
throw new Error(proc.stderr.toString().trim() || `git ${args[0]} failed`);
|
|
15
|
+
}
|
|
16
|
+
return proc.stdout.toString();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function getRepoRoot(cwd: string): string {
|
|
20
|
-
|
|
20
|
+
return execGit(["rev-parse", "--show-toplevel"], cwd).trim();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function parseStatus(output: string): {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
staged: GitFileChange[];
|
|
25
|
+
changed: GitFileChange[];
|
|
26
|
+
untracked: string[];
|
|
27
27
|
} {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
28
|
+
const staged: GitFileChange[] = [];
|
|
29
|
+
const changed: GitFileChange[] = [];
|
|
30
|
+
const untracked: string[] = [];
|
|
31
|
+
|
|
32
|
+
for (const line of output.split("\n")) {
|
|
33
|
+
if (!line.trim()) continue;
|
|
34
|
+
const xy = line.slice(0, 2);
|
|
35
|
+
const filePath = line.slice(3).trim();
|
|
36
|
+
|
|
37
|
+
const statusMap: Record<string, "modified" | "added" | "deleted" | "renamed" | "copied"> = {
|
|
38
|
+
M: "modified",
|
|
39
|
+
A: "added",
|
|
40
|
+
D: "deleted",
|
|
41
|
+
R: "renamed",
|
|
42
|
+
C: "copied",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Index (staged) - first char
|
|
46
|
+
const indexStatus = xy[0];
|
|
47
|
+
if (
|
|
48
|
+
indexStatus !== " " &&
|
|
49
|
+
indexStatus !== "?" &&
|
|
50
|
+
indexStatus !== undefined &&
|
|
51
|
+
statusMap[indexStatus]
|
|
52
|
+
) {
|
|
53
|
+
const path = indexStatus === "R" ? filePath.split(" -> ")[1]! : filePath;
|
|
54
|
+
staged.push({ path, status: statusMap[indexStatus]! });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Working tree - second char
|
|
58
|
+
const wtStatus = xy[1];
|
|
59
|
+
if (wtStatus !== " " && wtStatus !== "?" && wtStatus !== undefined && statusMap[wtStatus]) {
|
|
60
|
+
changed.push({ path: filePath, status: statusMap[wtStatus]! });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Untracked
|
|
64
|
+
if (xy === "??") {
|
|
65
|
+
untracked.push(filePath);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { staged, changed, untracked };
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
73
|
+
const r: RegisterFn = (method, handler) => {
|
|
74
|
+
server.register(method, handler as (params: unknown) => Promise<unknown>);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
r("git.status", async (params) => {
|
|
78
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
79
|
+
const output = execGit(["status", "--porcelain=v1", "--branch"], repoRoot);
|
|
80
|
+
const lines = output.split("\n");
|
|
81
|
+
|
|
82
|
+
// Parse branch info from first line
|
|
83
|
+
const branchLine = lines[0] || "";
|
|
84
|
+
const branchMatch = branchLine.match(
|
|
85
|
+
/^## (.+?)(?:\.\.\.(\S+))?(?:\s+\[(ahead\s+(\d+))?(?:,\s*)?(behind\s+(\d+))?\])?$/
|
|
86
|
+
);
|
|
87
|
+
const branch =
|
|
88
|
+
branchMatch?.[1]?.replace("HEAD detached", "").replace(/[()]/g, "").trim() || "unknown";
|
|
89
|
+
const ahead = branchMatch?.[3] ? parseInt(branchMatch[3]) : 0;
|
|
90
|
+
const behind = branchMatch?.[5] ? parseInt(branchMatch[5]) : 0;
|
|
91
|
+
|
|
92
|
+
const { staged, changed, untracked } = parseStatus(lines.slice(1).join("\n"));
|
|
93
|
+
|
|
94
|
+
return { staged, changed, untracked, branch, ahead, behind };
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
r("git.diff", async (params) => {
|
|
98
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
99
|
+
let diff = "";
|
|
100
|
+
if (params.staged) {
|
|
101
|
+
diff = execGit(["diff", "--cached", "--", params.filePath], repoRoot);
|
|
102
|
+
} else {
|
|
103
|
+
diff = execGit(["diff", "--", params.filePath], repoRoot);
|
|
104
|
+
if (!diff) {
|
|
105
|
+
try {
|
|
106
|
+
diff = execGit(["diff", "--no-index", "/dev/null", params.filePath], repoRoot, true);
|
|
107
|
+
} catch {
|
|
108
|
+
// ignore
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get old content (HEAD version) and new content (working tree)
|
|
114
|
+
let oldContent = "";
|
|
115
|
+
let newContent = "";
|
|
116
|
+
try {
|
|
117
|
+
oldContent = execGit(["show", `HEAD:${params.filePath}`], repoRoot);
|
|
118
|
+
} catch {
|
|
119
|
+
// New file — no old content
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const { readFile } = await import("fs/promises");
|
|
123
|
+
const { join } = await import("path");
|
|
124
|
+
newContent = (await readFile(join(repoRoot, params.filePath))).toString();
|
|
125
|
+
} catch {
|
|
126
|
+
// Deleted file — no new content
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { filePath: params.filePath, diff, oldContent, newContent };
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
r("git.log", async (params) => {
|
|
133
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
134
|
+
const count = params.maxCount || 50;
|
|
135
|
+
const output = execGit(
|
|
136
|
+
["log", `--max-count=${count}`, "--pretty=format:%H|%h|%s|%an|%aI"],
|
|
137
|
+
repoRoot
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const commits = output
|
|
141
|
+
.split("\n")
|
|
142
|
+
.filter(Boolean)
|
|
143
|
+
.map((line) => {
|
|
144
|
+
const parts = line.split("|");
|
|
145
|
+
return {
|
|
146
|
+
hash: parts[0]!,
|
|
147
|
+
shortHash: parts[1]!,
|
|
148
|
+
message: parts[2]!,
|
|
149
|
+
author: parts[3]!,
|
|
150
|
+
date: parts[4]!,
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return { commits };
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
r("git.commitFiles", async (params) => {
|
|
158
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
159
|
+
const output = execGit(
|
|
160
|
+
["diff-tree", "--no-commit-id", "--name-status", "-r", params.hash],
|
|
161
|
+
repoRoot
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const statusMap: Record<string, GitFileChange["status"]> = {
|
|
165
|
+
M: "modified",
|
|
166
|
+
A: "added",
|
|
167
|
+
D: "deleted",
|
|
168
|
+
R: "renamed",
|
|
169
|
+
C: "copied",
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const files: GitFileChange[] = output
|
|
173
|
+
.split("\n")
|
|
174
|
+
.filter(Boolean)
|
|
175
|
+
.map((line) => {
|
|
176
|
+
const [status, ...pathParts] = line.split("\t");
|
|
177
|
+
const path = pathParts.join("\t"); // handle paths with tabs (renames: old\tnew)
|
|
178
|
+
return {
|
|
179
|
+
path: status === "R" ? path.split("\t").pop()! : path,
|
|
180
|
+
status: statusMap[status!] || "modified",
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return { files };
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
r("git.commitFileDiff", async (params) => {
|
|
188
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
189
|
+
const { hash, filePath } = params;
|
|
190
|
+
|
|
191
|
+
// Get the diff for this file in this commit
|
|
192
|
+
const diff = execGit(["diff", `${hash}^..${hash}`, "--", filePath], repoRoot, true);
|
|
193
|
+
|
|
194
|
+
// Get old content (parent commit version)
|
|
195
|
+
let oldContent = "";
|
|
196
|
+
try {
|
|
197
|
+
oldContent = execGit(["show", `${hash}^:${filePath}`], repoRoot);
|
|
198
|
+
} catch {
|
|
199
|
+
// File was added in this commit — no old content
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Get new content (this commit version)
|
|
203
|
+
let newContent = "";
|
|
204
|
+
try {
|
|
205
|
+
newContent = execGit(["show", `${hash}:${filePath}`], repoRoot);
|
|
206
|
+
} catch {
|
|
207
|
+
// File was deleted in this commit — no new content
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { filePath, diff, oldContent, newContent };
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
r("git.branches", async (params) => {
|
|
214
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
215
|
+
const output = execGit(["branch", "-a", "--no-color"], repoRoot);
|
|
216
|
+
const branches = output
|
|
217
|
+
.split("\n")
|
|
218
|
+
.filter(Boolean)
|
|
219
|
+
.map((line) => {
|
|
220
|
+
const isCurrent = line.startsWith("*");
|
|
221
|
+
const name = line.replace(/^\*?\s+/, "").trim();
|
|
222
|
+
const isRemote = name.startsWith("remotes/");
|
|
223
|
+
return { name, isCurrent, isRemote };
|
|
224
|
+
});
|
|
225
|
+
return { branches };
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
r("git.checkout", async (params) => {
|
|
229
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
230
|
+
execGit(["checkout", params.branch], repoRoot);
|
|
231
|
+
return { ok: true };
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
r("git.add", async (params) => {
|
|
235
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
236
|
+
execGit(["add", ...params.paths], repoRoot);
|
|
237
|
+
return { ok: true };
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
r("git.reset", async (params) => {
|
|
241
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
242
|
+
execGit(["reset", "HEAD", "--", ...params.paths], repoRoot);
|
|
243
|
+
return { ok: true };
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
r("git.commit", async (params) => {
|
|
247
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
248
|
+
const output = execGit(["commit", "-m", params.message], repoRoot);
|
|
249
|
+
// Extract hash from output like "[main abc1234] message"
|
|
250
|
+
const hashMatch = output.match(/\[[\w\-/.]+\s+([0-9a-f]{7,40})\]/);
|
|
251
|
+
const shortHash = hashMatch?.[1] || "";
|
|
252
|
+
let hash = "";
|
|
253
|
+
if (shortHash) {
|
|
254
|
+
hash = execGit(["rev-parse", shortHash], repoRoot).trim();
|
|
255
|
+
}
|
|
256
|
+
return { hash, shortHash };
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
r("git.push", async (params) => {
|
|
260
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
261
|
+
execGit(["push"], repoRoot);
|
|
262
|
+
return { ok: true };
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
r("git.pull", async (params) => {
|
|
266
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
267
|
+
execGit(["pull"], repoRoot);
|
|
268
|
+
return { ok: true };
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
r("git.worktreeList", async (params) => {
|
|
272
|
+
const repoRoot = getRepoRoot(params.repoPath);
|
|
273
|
+
const output = execGit(["worktree", "list", "--porcelain"], repoRoot);
|
|
274
|
+
const worktrees: { path: string; branch: string; isMain: boolean }[] = [];
|
|
275
|
+
let current: Partial<(typeof worktrees)[0]> = {};
|
|
276
|
+
|
|
277
|
+
for (const line of output.split("\n")) {
|
|
278
|
+
if (line.startsWith("worktree ")) {
|
|
279
|
+
if (current.path) {
|
|
280
|
+
worktrees.push({
|
|
281
|
+
path: current.path!,
|
|
282
|
+
branch: current.branch || "",
|
|
283
|
+
isMain: !!current.isMain,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
current = { path: line.slice(9), isMain: false };
|
|
287
|
+
} else if (line.startsWith("branch ")) {
|
|
288
|
+
current.branch = line.slice(7).replace("refs/heads/", "");
|
|
289
|
+
} else if (line === "bare") {
|
|
290
|
+
current.isMain = false;
|
|
291
|
+
} else if (line === "" && current.path) {
|
|
292
|
+
// first worktree is main
|
|
293
|
+
if (worktrees.length === 0) current.isMain = true;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (current.path) {
|
|
297
|
+
worktrees.push({
|
|
298
|
+
path: current.path!,
|
|
299
|
+
branch: current.branch || "",
|
|
300
|
+
isMain: !!current.isMain,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return { worktrees };
|
|
305
|
+
});
|
|
263
306
|
}
|
|
@@ -178,7 +178,7 @@ async function handleFileContent(
|
|
|
178
178
|
const range = req.headers["range"];
|
|
179
179
|
if (range) {
|
|
180
180
|
const parts = range.replace(/bytes=/, "").split("-");
|
|
181
|
-
const start = parseInt(parts[0]
|
|
181
|
+
const start = parseInt(parts[0]!, 10);
|
|
182
182
|
const end = parts[1] ? parseInt(parts[1], 10) : s.size - 1;
|
|
183
183
|
const chunkSize = end - start + 1;
|
|
184
184
|
|