@mandipadk7/kavi 0.1.0 → 0.1.2

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/dist/git.js CHANGED
@@ -51,6 +51,53 @@ export async function getBranchCommit(repoRoot, ref) {
51
51
  }
52
52
  return result.stdout.trim();
53
53
  }
54
+ function parsePathList(output) {
55
+ return output.split(/\r?\n/).map((line)=>line.trim()).filter(Boolean);
56
+ }
57
+ function uniqueSorted(values) {
58
+ return [
59
+ ...new Set(values)
60
+ ].sort();
61
+ }
62
+ async function isUntrackedPath(worktreePath, filePath) {
63
+ const result = await runCommand("git", [
64
+ "ls-files",
65
+ "--others",
66
+ "--exclude-standard",
67
+ "--",
68
+ filePath
69
+ ], {
70
+ cwd: worktreePath
71
+ });
72
+ if (result.code !== 0) {
73
+ throw new Error(result.stderr.trim() || `Unable to inspect untracked status for ${filePath}.`);
74
+ }
75
+ return parsePathList(result.stdout).includes(filePath);
76
+ }
77
+ async function buildSyntheticAddedFilePatch(worktreePath, filePath) {
78
+ const absolutePath = path.join(worktreePath, filePath);
79
+ const content = await fs.readFile(absolutePath, "utf8");
80
+ const normalized = content.replaceAll("\r", "");
81
+ const endsWithNewline = normalized.endsWith("\n");
82
+ const lines = normalized.length === 0 ? [] : normalized.replace(/\n$/, "").split("\n");
83
+ const patchLines = [
84
+ `diff --git a/${filePath} b/${filePath}`,
85
+ "new file mode 100644",
86
+ "--- /dev/null",
87
+ `+++ b/${filePath}`
88
+ ];
89
+ if (lines.length > 0) {
90
+ patchLines.push(`@@ -0,0 +1,${lines.length} @@`);
91
+ patchLines.push(...lines.map((line)=>`+${line}`));
92
+ if (!endsWithNewline) {
93
+ patchLines.push("\");
94
+ }
95
+ }
96
+ return {
97
+ stat: lines.length > 0 ? `new file | ${lines.length} insertion${lines.length === 1 ? "" : "s"}(+)` : "new file | empty",
98
+ patch: patchLines.join("\n")
99
+ };
100
+ }
54
101
  export async function resolveTargetBranch(repoRoot, configuredBranch) {
55
102
  const exists = await runCommand("git", [
56
103
  "show-ref",
@@ -149,6 +196,130 @@ export async function createGitignoreEntries(repoRoot) {
149
196
  const prefix = content.trimEnd() ? "\n" : "";
150
197
  await fs.writeFile(gitignorePath, `${content.trimEnd()}${prefix}${missing.join("\n")}\n`, "utf8");
151
198
  }
199
+ export async function listWorktreeChangedPaths(worktreePath, baseCommit) {
200
+ const [committed, staged, unstaged, untracked] = await Promise.all([
201
+ runCommand("git", [
202
+ "diff",
203
+ "--name-only",
204
+ `${baseCommit}..HEAD`
205
+ ], {
206
+ cwd: worktreePath
207
+ }),
208
+ runCommand("git", [
209
+ "diff",
210
+ "--name-only",
211
+ "--cached"
212
+ ], {
213
+ cwd: worktreePath
214
+ }),
215
+ runCommand("git", [
216
+ "diff",
217
+ "--name-only"
218
+ ], {
219
+ cwd: worktreePath
220
+ }),
221
+ runCommand("git", [
222
+ "ls-files",
223
+ "--others",
224
+ "--exclude-standard"
225
+ ], {
226
+ cwd: worktreePath
227
+ })
228
+ ]);
229
+ const outputs = [
230
+ committed,
231
+ staged,
232
+ unstaged,
233
+ untracked
234
+ ];
235
+ const failures = outputs.filter((result)=>result.code !== 0);
236
+ if (failures.length > 0) {
237
+ throw new Error(failures.map((result)=>result.stderr.trim() || "Unable to inspect worktree changes.").join("\n"));
238
+ }
239
+ return uniqueSorted([
240
+ ...parsePathList(committed.stdout),
241
+ ...parsePathList(staged.stdout),
242
+ ...parsePathList(unstaged.stdout),
243
+ ...parsePathList(untracked.stdout)
244
+ ]);
245
+ }
246
+ export async function getWorktreeDiffReview(agent, worktreePath, baseCommit, filePath) {
247
+ const changedPaths = await listWorktreeChangedPaths(worktreePath, baseCommit);
248
+ const selectedPath = filePath && changedPaths.includes(filePath) ? filePath : changedPaths[0] ?? null;
249
+ if (!selectedPath) {
250
+ return {
251
+ agent,
252
+ changedPaths,
253
+ selectedPath: null,
254
+ stat: "No changed files in this worktree.",
255
+ patch: ""
256
+ };
257
+ }
258
+ if (await isUntrackedPath(worktreePath, selectedPath)) {
259
+ const synthetic = await buildSyntheticAddedFilePatch(worktreePath, selectedPath);
260
+ return {
261
+ agent,
262
+ changedPaths,
263
+ selectedPath,
264
+ stat: synthetic.stat,
265
+ patch: synthetic.patch
266
+ };
267
+ }
268
+ const [statResult, patchResult] = await Promise.all([
269
+ runCommand("git", [
270
+ "diff",
271
+ "--stat",
272
+ "--find-renames",
273
+ baseCommit,
274
+ "--",
275
+ selectedPath
276
+ ], {
277
+ cwd: worktreePath
278
+ }),
279
+ runCommand("git", [
280
+ "diff",
281
+ "--find-renames",
282
+ "--unified=3",
283
+ baseCommit,
284
+ "--",
285
+ selectedPath
286
+ ], {
287
+ cwd: worktreePath
288
+ })
289
+ ]);
290
+ if (statResult.code !== 0) {
291
+ throw new Error(statResult.stderr.trim() || `Unable to build diff stat for ${selectedPath}.`);
292
+ }
293
+ if (patchResult.code !== 0) {
294
+ throw new Error(patchResult.stderr.trim() || `Unable to build diff patch for ${selectedPath}.`);
295
+ }
296
+ return {
297
+ agent,
298
+ changedPaths,
299
+ selectedPath,
300
+ stat: statResult.stdout.trim() || "No diff stat available.",
301
+ patch: patchResult.stdout.trim() || "No textual patch available."
302
+ };
303
+ }
304
+ export async function findOverlappingWorktreePaths(worktrees, baseCommit) {
305
+ const pathSets = await Promise.all(worktrees.map(async (worktree)=>({
306
+ agent: worktree.agent,
307
+ paths: await listWorktreeChangedPaths(worktree.path, baseCommit)
308
+ })));
309
+ const overlaps = [];
310
+ for(let index = 0; index < pathSets.length; index += 1){
311
+ const current = pathSets[index];
312
+ const rest = pathSets.slice(index + 1);
313
+ for (const other of rest){
314
+ for (const filePath of current.paths){
315
+ if (other.paths.includes(filePath)) {
316
+ overlaps.push(filePath);
317
+ }
318
+ }
319
+ }
320
+ }
321
+ return uniqueSorted(overlaps);
322
+ }
152
323
  export async function landBranches(repoRoot, targetBranch, worktrees, validationCommand, sessionId, integrationRoot) {
153
324
  const commandsRun = [];
154
325
  const snapshotCommits = [];