@caupulican/pi-adaptative 0.76.1 → 0.77.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 (132) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +9 -0
  3. package/dist/cli/args.d.ts +2 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +23 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session-services.d.ts +1 -0
  8. package/dist/core/agent-session-services.d.ts.map +1 -1
  9. package/dist/core/agent-session-services.js +1 -0
  10. package/dist/core/agent-session-services.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +4 -1
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +25 -4
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/bash-executor.d.ts +2 -0
  16. package/dist/core/bash-executor.d.ts.map +1 -1
  17. package/dist/core/bash-executor.js +26 -0
  18. package/dist/core/bash-executor.js.map +1 -1
  19. package/dist/core/extensions/runner.d.ts +1 -1
  20. package/dist/core/extensions/runner.d.ts.map +1 -1
  21. package/dist/core/extensions/runner.js +8 -2
  22. package/dist/core/extensions/runner.js.map +1 -1
  23. package/dist/core/extensions/types.d.ts +7 -5
  24. package/dist/core/extensions/types.d.ts.map +1 -1
  25. package/dist/core/extensions/types.js.map +1 -1
  26. package/dist/core/model-registry.d.ts.map +1 -1
  27. package/dist/core/model-registry.js +65 -13
  28. package/dist/core/model-registry.js.map +1 -1
  29. package/dist/core/model-resolver.d.ts.map +1 -1
  30. package/dist/core/model-resolver.js +1 -1
  31. package/dist/core/model-resolver.js.map +1 -1
  32. package/dist/core/resolve-config-value.d.ts +9 -1
  33. package/dist/core/resolve-config-value.d.ts.map +1 -1
  34. package/dist/core/resolve-config-value.js +134 -11
  35. package/dist/core/resolve-config-value.js.map +1 -1
  36. package/dist/core/sdk.d.ts +2 -0
  37. package/dist/core/sdk.d.ts.map +1 -1
  38. package/dist/core/sdk.js +4 -5
  39. package/dist/core/sdk.js.map +1 -1
  40. package/dist/core/session-manager.d.ts +3 -5
  41. package/dist/core/session-manager.d.ts.map +1 -1
  42. package/dist/core/session-manager.js +42 -17
  43. package/dist/core/session-manager.js.map +1 -1
  44. package/dist/core/system-prompt.d.ts.map +1 -1
  45. package/dist/core/system-prompt.js +0 -3
  46. package/dist/core/system-prompt.js.map +1 -1
  47. package/dist/core/tools/bash.d.ts.map +1 -1
  48. package/dist/core/tools/bash.js +175 -1
  49. package/dist/core/tools/bash.js.map +1 -1
  50. package/dist/core/tools/edit.d.ts.map +1 -1
  51. package/dist/core/tools/edit.js +4 -0
  52. package/dist/core/tools/edit.js.map +1 -1
  53. package/dist/core/tools/file-encoding-policy.d.ts +19 -0
  54. package/dist/core/tools/file-encoding-policy.d.ts.map +1 -0
  55. package/dist/core/tools/file-encoding-policy.js +54 -0
  56. package/dist/core/tools/file-encoding-policy.js.map +1 -0
  57. package/dist/core/tools/find.d.ts +1 -0
  58. package/dist/core/tools/find.d.ts.map +1 -1
  59. package/dist/core/tools/find.js +77 -60
  60. package/dist/core/tools/find.js.map +1 -1
  61. package/dist/core/tools/git-filter.d.ts +41 -0
  62. package/dist/core/tools/git-filter.d.ts.map +1 -0
  63. package/dist/core/tools/git-filter.js +660 -0
  64. package/dist/core/tools/git-filter.js.map +1 -0
  65. package/dist/core/tools/grep.d.ts.map +1 -1
  66. package/dist/core/tools/grep.js +28 -3
  67. package/dist/core/tools/grep.js.map +1 -1
  68. package/dist/core/tools/ls.d.ts +7 -5
  69. package/dist/core/tools/ls.d.ts.map +1 -1
  70. package/dist/core/tools/ls.js +39 -5
  71. package/dist/core/tools/ls.js.map +1 -1
  72. package/dist/core/tools/read.d.ts +3 -0
  73. package/dist/core/tools/read.d.ts.map +1 -1
  74. package/dist/core/tools/read.js +98 -13
  75. package/dist/core/tools/read.js.map +1 -1
  76. package/dist/core/tools/write.d.ts +2 -0
  77. package/dist/core/tools/write.d.ts.map +1 -1
  78. package/dist/core/tools/write.js +23 -3
  79. package/dist/core/tools/write.js.map +1 -1
  80. package/dist/index.d.ts +1 -0
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +1 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/main.d.ts.map +1 -1
  85. package/dist/main.js +15 -2
  86. package/dist/main.js.map +1 -1
  87. package/dist/migrations.d.ts.map +1 -1
  88. package/dist/migrations.js +118 -1
  89. package/dist/migrations.js.map +1 -1
  90. package/dist/modes/interactive/components/login-dialog.d.ts +1 -3
  91. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  92. package/dist/modes/interactive/components/login-dialog.js +2 -4
  93. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  94. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  95. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  96. package/dist/modes/interactive/interactive-mode.js +51 -6
  97. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  98. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  99. package/dist/modes/interactive/theme/theme.js +10 -0
  100. package/dist/modes/interactive/theme/theme.js.map +1 -1
  101. package/dist/utils/deprecation.d.ts +4 -0
  102. package/dist/utils/deprecation.d.ts.map +1 -0
  103. package/dist/utils/deprecation.js +13 -0
  104. package/dist/utils/deprecation.js.map +1 -0
  105. package/dist/utils/json.d.ts +3 -0
  106. package/dist/utils/json.d.ts.map +1 -0
  107. package/dist/utils/json.js +7 -0
  108. package/dist/utils/json.js.map +1 -0
  109. package/docs/custom-provider.md +13 -10
  110. package/docs/extensions.md +12 -6
  111. package/docs/models.md +25 -12
  112. package/docs/providers.md +13 -5
  113. package/docs/quickstart.md +1 -0
  114. package/docs/rpc.md +2 -1
  115. package/docs/sdk.md +6 -0
  116. package/docs/session-format.md +1 -1
  117. package/docs/sessions.md +8 -0
  118. package/docs/usage.md +9 -0
  119. package/examples/extensions/README.md +1 -0
  120. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  121. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  122. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  123. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  124. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  125. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  126. package/examples/extensions/input-transform-streaming.ts +39 -0
  127. package/examples/extensions/sandbox/package-lock.json +2 -2
  128. package/examples/extensions/sandbox/package.json +1 -1
  129. package/examples/extensions/with-deps/package-lock.json +2 -2
  130. package/examples/extensions/with-deps/package.json +1 -1
  131. package/npm-shrinkwrap.json +56 -56
  132. package/package.json +5 -5
@@ -0,0 +1,660 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { waitForChildProcess } from "../../utils/child-process.js";
5
+ import { killProcessTree, trackDetachedChildPid, untrackDetachedChildPid } from "../../utils/shell.js";
6
+ const SUPPORTED_SUBCOMMANDS = new Set([
7
+ "status",
8
+ "log",
9
+ "diff",
10
+ "show",
11
+ "add",
12
+ "commit",
13
+ "push",
14
+ "pull",
15
+ "branch",
16
+ "fetch",
17
+ "stash",
18
+ "worktree",
19
+ ]);
20
+ export function unicodeTruncate(str, maxLength) {
21
+ const chars = Array.from(str);
22
+ if (chars.length <= maxLength)
23
+ return str;
24
+ return `${chars.slice(0, maxLength).join("")}...`;
25
+ }
26
+ export function tokenizeCommand(command) {
27
+ const args = [];
28
+ let current = "";
29
+ let inDoubleQuotes = false;
30
+ let inSingleQuotes = false;
31
+ let escapeNext = false;
32
+ for (let i = 0; i < command.length; i++) {
33
+ const char = command[i];
34
+ if (escapeNext) {
35
+ current += char;
36
+ escapeNext = false;
37
+ continue;
38
+ }
39
+ if (char === "\\" && !inSingleQuotes) {
40
+ escapeNext = true;
41
+ continue;
42
+ }
43
+ if (char === '"' && !inSingleQuotes) {
44
+ inDoubleQuotes = !inDoubleQuotes;
45
+ }
46
+ else if (char === "'" && !inDoubleQuotes) {
47
+ inSingleQuotes = !inSingleQuotes;
48
+ }
49
+ else if (/\s/.test(char) && !inDoubleQuotes && !inSingleQuotes) {
50
+ if (current) {
51
+ args.push(current);
52
+ current = "";
53
+ }
54
+ }
55
+ else {
56
+ current += char;
57
+ }
58
+ }
59
+ if (inDoubleQuotes || inSingleQuotes || escapeNext)
60
+ return null;
61
+ if (current)
62
+ args.push(current);
63
+ return args;
64
+ }
65
+ export function parseCommandPrefixes(command) {
66
+ const tokens = tokenizeCommand(command);
67
+ if (!tokens || tokens.length === 0)
68
+ return null;
69
+ const envVars = {};
70
+ let i = 0;
71
+ const envPattern = /^([a-zA-Z_][a-zA-Z0-9_]*)=(.*)$/;
72
+ while (i < tokens.length) {
73
+ const token = tokens[i];
74
+ const match = token.match(envPattern);
75
+ if (!match)
76
+ break;
77
+ let value = match[2];
78
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
79
+ value = value.slice(1, -1);
80
+ }
81
+ envVars[match[1]] = value;
82
+ i++;
83
+ }
84
+ return { envVars, coreCommandTokens: tokens.slice(i) };
85
+ }
86
+ export function isComplexShellCommand(command) {
87
+ return /[|><&;\n\r$`()*?[\]#]/.test(command);
88
+ }
89
+ function quoteForShell(arg) {
90
+ if (/^[A-Za-z0-9_./:=+-]+$/.test(arg))
91
+ return arg;
92
+ return `'${arg.replace(/'/g, `'"'"'`)}'`;
93
+ }
94
+ function gitCommand(globalOptions, args) {
95
+ return ["git", ...globalOptions, ...args].map(quoteForShell).join(" ");
96
+ }
97
+ function rawText(res, combine = false) {
98
+ if (combine)
99
+ return `${res.stderr}${res.stderr && res.stdout ? "\n" : ""}${res.stdout}`;
100
+ return res.stderr || res.stdout;
101
+ }
102
+ function resultFromQuery(res, output, exitCode = res.status ?? 0) {
103
+ return { output, exitCode, rawOut: rawText(res), rawBytes: res.rawBytes };
104
+ }
105
+ export async function runGitQuery(cwd, globalOptions, args, options) {
106
+ if (options?.signal?.aborted)
107
+ throw new Error("aborted");
108
+ const child = spawn("git", [...globalOptions, ...args], {
109
+ cwd,
110
+ detached: process.platform !== "win32",
111
+ env: { ...process.env, LC_ALL: "C" },
112
+ stdio: ["ignore", "pipe", "pipe"],
113
+ windowsHide: true,
114
+ });
115
+ if (child.pid)
116
+ trackDetachedChildPid(child.pid);
117
+ const stdoutChunks = [];
118
+ const stderrChunks = [];
119
+ let timedOut = false;
120
+ const timeoutSeconds = options?.timeout;
121
+ let timeoutHandle;
122
+ const killChild = () => {
123
+ if (child.pid)
124
+ killProcessTree(child.pid);
125
+ };
126
+ try {
127
+ if (timeoutSeconds !== undefined && timeoutSeconds > 0) {
128
+ timeoutHandle = setTimeout(() => {
129
+ timedOut = true;
130
+ killChild();
131
+ }, timeoutSeconds * 1000);
132
+ }
133
+ if (options?.signal) {
134
+ if (options.signal.aborted)
135
+ killChild();
136
+ else
137
+ options.signal.addEventListener("abort", killChild, { once: true });
138
+ }
139
+ child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
140
+ child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
141
+ const status = await waitForChildProcess(child);
142
+ if (options?.signal?.aborted)
143
+ throw new Error("aborted");
144
+ if (timedOut)
145
+ throw new Error(`timeout:${timeoutSeconds}`);
146
+ const stdoutBuffer = Buffer.concat(stdoutChunks);
147
+ const stderrBuffer = Buffer.concat(stderrChunks);
148
+ const rawBytes = Buffer.concat([stderrBuffer, stdoutBuffer]);
149
+ return {
150
+ stdout: stdoutBuffer.toString("utf-8"),
151
+ stderr: stderrBuffer.toString("utf-8"),
152
+ status,
153
+ rawBytes,
154
+ };
155
+ }
156
+ finally {
157
+ if (child.pid)
158
+ untrackDetachedChildPid(child.pid);
159
+ if (timeoutHandle)
160
+ clearTimeout(timeoutHandle);
161
+ if (options?.signal)
162
+ options.signal.removeEventListener("abort", killChild);
163
+ }
164
+ }
165
+ export function classifyGitCommand(command, parentEnv) {
166
+ if (isComplexShellCommand(command))
167
+ return { eligible: false };
168
+ const parsed = parseCommandPrefixes(command);
169
+ if (!parsed || parsed.coreCommandTokens.length === 0)
170
+ return { eligible: false };
171
+ const { envVars, coreCommandTokens } = parsed;
172
+ const toolFilterDisabled = process.env.PI_TOOL_FILTER_DISABLED === "1" ||
173
+ parentEnv?.PI_TOOL_FILTER_DISABLED === "1" ||
174
+ envVars.PI_TOOL_FILTER_DISABLED === "1";
175
+ const gitFilterDisabled = process.env.PI_GIT_FILTER_DISABLED === "1" ||
176
+ parentEnv?.PI_GIT_FILTER_DISABLED === "1" ||
177
+ envVars.PI_GIT_FILTER_DISABLED === "1";
178
+ if (toolFilterDisabled || gitFilterDisabled)
179
+ return { eligible: false };
180
+ const envKeys = Object.keys(envVars).filter((key) => key !== "PI_TOOL_FILTER_DISABLED" && key !== "PI_GIT_FILTER_DISABLED");
181
+ if (envKeys.length > 0)
182
+ return { eligible: false };
183
+ const cmdName = coreCommandTokens[0];
184
+ if (cmdName !== "git" && cmdName !== "yadm")
185
+ return { eligible: false };
186
+ let idx = 1;
187
+ const globalOptions = [];
188
+ while (idx < coreCommandTokens.length) {
189
+ const token = coreCommandTokens[idx];
190
+ if (token === "-C" || token === "-c" || token === "--git-dir" || token === "--work-tree") {
191
+ if (idx + 1 >= coreCommandTokens.length)
192
+ return { eligible: false };
193
+ globalOptions.push(token, coreCommandTokens[idx + 1]);
194
+ idx += 2;
195
+ }
196
+ else if (token.startsWith("--git-dir=") || token.startsWith("--work-tree=")) {
197
+ globalOptions.push(token);
198
+ idx++;
199
+ }
200
+ else if (token === "--no-pager" ||
201
+ token === "--no-optional-locks" ||
202
+ token === "--bare" ||
203
+ token === "--literal-pathspecs") {
204
+ globalOptions.push(token);
205
+ idx++;
206
+ }
207
+ else {
208
+ break;
209
+ }
210
+ }
211
+ if (idx === coreCommandTokens.length)
212
+ return { eligible: false };
213
+ const subcommand = coreCommandTokens[idx];
214
+ if (!SUPPORTED_SUBCOMMANDS.has(subcommand))
215
+ return { eligible: false };
216
+ return {
217
+ eligible: true,
218
+ subcommand,
219
+ globalOptions,
220
+ subcommandArgs: coreCommandTokens.slice(idx + 1),
221
+ localEnv: envVars,
222
+ };
223
+ }
224
+ export function detectGitState(gitDir) {
225
+ const states = [];
226
+ if (!gitDir)
227
+ return states;
228
+ if (existsSync(join(gitDir, "rebase-merge")) || existsSync(join(gitDir, "rebase-apply"))) {
229
+ states.push("rebase in progress");
230
+ }
231
+ if (existsSync(join(gitDir, "MERGE_HEAD")))
232
+ states.push("merge in progress");
233
+ if (existsSync(join(gitDir, "CHERRY_PICK_HEAD")))
234
+ states.push("cherry-pick in progress");
235
+ if (existsSync(join(gitDir, "REVERT_HEAD")))
236
+ states.push("revert in progress");
237
+ if (existsSync(join(gitDir, "BISECT_LOG")))
238
+ states.push("bisect in progress");
239
+ if (existsSync(join(gitDir, "applying")))
240
+ states.push("am in progress");
241
+ return states;
242
+ }
243
+ async function handleStatus(cwd, globalOptions, args, options) {
244
+ const isSimple = args.length === 0 || args.every((arg) => arg === "-s" || arg === "--short" || arg === "-b" || arg === "--branch");
245
+ if (!isSimple) {
246
+ const res = await runGitQuery(cwd, globalOptions, ["status", ...args], options);
247
+ const rawOut = rawText(res);
248
+ if (res.status !== 0)
249
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
250
+ const cleanedLines = res.stdout
251
+ .split("\n")
252
+ .map((line) => line.trimEnd())
253
+ .filter((line) => line.length > 0 && !line.trim().startsWith("(use "));
254
+ const output = cleanedLines.join("\n");
255
+ return resultFromQuery(res, output || "success", 0);
256
+ }
257
+ const res = await runGitQuery(cwd, globalOptions, ["status", "--porcelain=v1", "-b"], options);
258
+ let rawOut = rawText(res);
259
+ if (res.status !== 0) {
260
+ if (rawOut.includes("not a git repository"))
261
+ rawOut = "fatal: not a git repository";
262
+ return { output: rawOut.trim(), exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
263
+ }
264
+ const lines = res.stdout.split("\n").filter((line) => line.trim().length > 0);
265
+ let branchLine = "";
266
+ const fileLines = [];
267
+ for (const line of lines) {
268
+ if (line.startsWith("##"))
269
+ branchLine = line;
270
+ else
271
+ fileLines.push(line);
272
+ }
273
+ let statePrefix = "";
274
+ const gitDirRes = await runGitQuery(cwd, globalOptions, ["rev-parse", "--git-dir"], options);
275
+ if (gitDirRes.status === 0) {
276
+ const gitDir = resolve(cwd, gitDirRes.stdout.trim());
277
+ const states = detectGitState(gitDir);
278
+ if (states.length > 0)
279
+ statePrefix = `[${states.join(", ")}]\n`;
280
+ }
281
+ if (branchLine.includes("HEAD (no branch)")) {
282
+ const headHashRes = await runGitQuery(cwd, globalOptions, ["rev-parse", "--short", "HEAD"], options);
283
+ const hash = headHashRes.status === 0 ? headHashRes.stdout.trim() : "unknown";
284
+ branchLine = `## HEAD (detached at ${hash})`;
285
+ }
286
+ if (fileLines.length === 0) {
287
+ return resultFromQuery(res, `${statePrefix}${branchLine}\nnothing to commit, working tree clean`, 0);
288
+ }
289
+ return resultFromQuery(res, `${statePrefix}${branchLine}\n${fileLines.join("\n")}`, 0);
290
+ }
291
+ async function handleLog(cwd, globalOptions, args, options) {
292
+ const hasLimit = args.some((arg) => /^-n\d+$/.test(arg) || arg === "-n" || /^-?\d+$/.test(arg) || arg.startsWith("--max-count"));
293
+ const hasPretty = args.some((arg) => arg.startsWith("--pretty") || arg.startsWith("--format") || arg === "--oneline");
294
+ if (hasLimit || hasPretty) {
295
+ const res = await runGitQuery(cwd, globalOptions, ["log", ...args], options);
296
+ return resultFromQuery(res, rawText(res), res.status ?? 0);
297
+ }
298
+ const res = await runGitQuery(cwd, globalOptions, ["log", "-n", "10", "--no-merges"], options);
299
+ const rawOut = rawText(res);
300
+ if (res.status !== 0)
301
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
302
+ const commits = res.stdout.split(/(?=^commit [0-9a-f]{7,40})/m).filter((commit) => commit.trim().length > 0);
303
+ const compactedCommits = [];
304
+ const trailerPrefixes = [
305
+ "Signed-off-by:",
306
+ "Co-authored-by:",
307
+ "Reported-by:",
308
+ "Reviewed-by:",
309
+ "Tested-by:",
310
+ "Suggested-by:",
311
+ "CC:",
312
+ ];
313
+ for (const commit of commits) {
314
+ const lines = commit.split("\n");
315
+ const commitLine = lines[0];
316
+ if (!commitLine)
317
+ continue;
318
+ const shortCommitLine = commitLine.replace(/^commit ([0-9a-f]{7})[0-9a-f]+/, "commit $1");
319
+ const bodyLines = [];
320
+ for (let i = 1; i < lines.length; i++) {
321
+ const line = lines[i];
322
+ const trimmed = line.trim();
323
+ if (!trimmed)
324
+ continue;
325
+ if (line.startsWith("Author:") || line.startsWith("Date:") || line.startsWith("Merge:"))
326
+ continue;
327
+ if (trailerPrefixes.some((prefix) => trimmed.startsWith(prefix)))
328
+ continue;
329
+ bodyLines.push(line);
330
+ }
331
+ const displayBody = bodyLines.slice(0, 3).map((line) => unicodeTruncate(line, 160));
332
+ const omitted = bodyLines.length - displayBody.length;
333
+ if (omitted > 0)
334
+ displayBody.push(` ... (${omitted} lines omitted)`);
335
+ compactedCommits.push(`${shortCommitLine}\n${displayBody.join("\n")}`);
336
+ }
337
+ return resultFromQuery(res, compactedCommits.join("\n\n"), 0);
338
+ }
339
+ export function compactDiff(diffOutput, maxLines = 150) {
340
+ const lines = diffOutput.split("\n");
341
+ const output = [];
342
+ let linesCount = 0;
343
+ let truncated = false;
344
+ let contextBuffer = [];
345
+ let trailingContextRemaining = 0;
346
+ for (const line of lines) {
347
+ if (linesCount >= maxLines) {
348
+ truncated = true;
349
+ break;
350
+ }
351
+ if (line.startsWith("diff --git") ||
352
+ line.startsWith("--- ") ||
353
+ line.startsWith("+++ ") ||
354
+ line.startsWith("index ")) {
355
+ contextBuffer = [];
356
+ trailingContextRemaining = 0;
357
+ output.push(line);
358
+ linesCount++;
359
+ continue;
360
+ }
361
+ if (line.startsWith("@@ ")) {
362
+ contextBuffer = [];
363
+ trailingContextRemaining = 0;
364
+ output.push(line);
365
+ linesCount++;
366
+ continue;
367
+ }
368
+ if (line.startsWith("+") || line.startsWith("-")) {
369
+ for (const ctx of contextBuffer) {
370
+ if (linesCount >= maxLines) {
371
+ truncated = true;
372
+ break;
373
+ }
374
+ output.push(ctx);
375
+ linesCount++;
376
+ }
377
+ contextBuffer = [];
378
+ if (truncated)
379
+ break;
380
+ output.push(line);
381
+ linesCount++;
382
+ trailingContextRemaining = 3;
383
+ continue;
384
+ }
385
+ if (line.startsWith(" ")) {
386
+ if (trailingContextRemaining > 0) {
387
+ output.push(line);
388
+ linesCount++;
389
+ trailingContextRemaining--;
390
+ }
391
+ else {
392
+ contextBuffer.push(line);
393
+ if (contextBuffer.length > 3)
394
+ contextBuffer.shift();
395
+ }
396
+ }
397
+ else {
398
+ output.push(line);
399
+ linesCount++;
400
+ }
401
+ }
402
+ return { compacted: output.join("\n"), truncated };
403
+ }
404
+ async function handleDiff(cwd, globalOptions, args, options) {
405
+ const cleanArgs = [...args];
406
+ const noCompactIndex = cleanArgs.indexOf("--no-compact");
407
+ const optOut = noCompactIndex !== -1;
408
+ if (optOut)
409
+ cleanArgs.splice(noCompactIndex, 1);
410
+ const statFlags = ["--stat", "--numstat", "--shortstat", "--summary", "--name-only", "--name-status", "--check"];
411
+ const isStatOnly = cleanArgs.some((arg) => statFlags.includes(arg));
412
+ if (optOut || isStatOnly) {
413
+ const res = await runGitQuery(cwd, globalOptions, ["diff", ...cleanArgs], options);
414
+ return resultFromQuery(res, rawText(res), res.status ?? 0);
415
+ }
416
+ const hasDoubleDash = cleanArgs.includes("--");
417
+ if (!hasDoubleDash) {
418
+ const pathIdx = cleanArgs.findIndex((arg) => {
419
+ if (arg.startsWith("-"))
420
+ return false;
421
+ if (!(arg.includes("/") || arg.includes(".") || existsSync(join(cwd, arg))))
422
+ return false;
423
+ return existsSync(resolve(cwd, arg));
424
+ });
425
+ if (pathIdx !== -1)
426
+ cleanArgs.splice(pathIdx, 0, "--");
427
+ }
428
+ const statRes = await runGitQuery(cwd, globalOptions, ["diff", "--stat", ...cleanArgs], options);
429
+ if (statRes.status !== 0)
430
+ return resultFromQuery(statRes, rawText(statRes), statRes.status ?? 1);
431
+ const diffRes = await runGitQuery(cwd, globalOptions, ["diff", ...cleanArgs], options);
432
+ if (diffRes.status !== 0)
433
+ return resultFromQuery(diffRes, rawText(diffRes), diffRes.status ?? 1);
434
+ const { compacted, truncated } = compactDiff(diffRes.stdout);
435
+ let output = `${statRes.stdout.trimEnd()}\n\n${compacted}`.trim();
436
+ if (truncated)
437
+ output += "\n\n[Diff truncated. Re-run with: git diff --no-compact]";
438
+ return { output, exitCode: 0, rawOut: diffRes.stdout, rawBytes: diffRes.rawBytes };
439
+ }
440
+ async function handleShow(cwd, globalOptions, args, options) {
441
+ const statFlags = ["--stat", "--numstat", "--shortstat", "--summary", "--name-only", "--name-status", "--check"];
442
+ const hasStatOnly = args.some((arg) => statFlags.includes(arg));
443
+ const hasPretty = args.some((arg) => arg.startsWith("--pretty") || arg.startsWith("--format") || arg === "--oneline");
444
+ const hasBlob = args.some((arg) => !arg.startsWith("-") && arg.includes(":"));
445
+ if (hasStatOnly || hasPretty || hasBlob) {
446
+ const res = await runGitQuery(cwd, globalOptions, ["show", ...args], options);
447
+ return resultFromQuery(res, rawText(res), res.status ?? 0);
448
+ }
449
+ const showRes = await runGitQuery(cwd, globalOptions, ["show", ...args], options);
450
+ const rawOut = rawText(showRes);
451
+ if (showRes.status !== 0)
452
+ return { output: rawOut, exitCode: showRes.status ?? 1, rawOut, rawBytes: showRes.rawBytes };
453
+ const lines = showRes.stdout.split("\n");
454
+ const diffStart = lines.findIndex((line) => line.startsWith("diff --git"));
455
+ const headerLines = diffStart === -1 ? lines : lines.slice(0, diffStart);
456
+ const diffLines = diffStart === -1 ? [] : lines.slice(diffStart);
457
+ const summaryLines = headerLines.filter((line) => {
458
+ const trimmed = line.trim();
459
+ if (!trimmed)
460
+ return false;
461
+ if (line.startsWith("Author:") || line.startsWith("Date:") || line.startsWith("Merge:"))
462
+ return false;
463
+ return true;
464
+ });
465
+ const shortSummary = summaryLines
466
+ .slice(0, 4)
467
+ .map((line) => unicodeTruncate(line.replace(/^commit ([0-9a-f]{7})[0-9a-f]+/, "commit $1"), 160));
468
+ const { compacted, truncated } = compactDiff(diffLines.join("\n"));
469
+ let output = shortSummary.join("\n");
470
+ if (compacted.trim())
471
+ output += `\n\n${compacted}`;
472
+ if (truncated)
473
+ output += "\n\n[Diff truncated.]";
474
+ return resultFromQuery(showRes, output.trim(), 0);
475
+ }
476
+ async function handlePassthroughGit(cwd, globalOptions, args, options) {
477
+ const res = await runGitQuery(cwd, globalOptions, args, options);
478
+ return resultFromQuery(res, rawText(res, true).trim(), res.status ?? 0);
479
+ }
480
+ async function handleAdd(cwd, globalOptions, args, options) {
481
+ if (args.length === 0)
482
+ return handlePassthroughGit(cwd, globalOptions, ["add"], options);
483
+ const addRes = await runGitQuery(cwd, globalOptions, ["add", ...args], options);
484
+ const rawOut = rawText(addRes);
485
+ if (addRes.status !== 0)
486
+ return { output: rawOut, exitCode: addRes.status ?? 1, rawOut, rawBytes: addRes.rawBytes };
487
+ const statRes = await runGitQuery(cwd, globalOptions, ["diff", "--cached", "--stat"], options);
488
+ if (statRes.status === 0 && statRes.stdout.trim().length > 0) {
489
+ return { output: `Staged changes:\n${statRes.stdout.trim()}`, exitCode: 0, rawOut, rawBytes: addRes.rawBytes };
490
+ }
491
+ return { output: rawOut.trim() || "Successfully staged.", exitCode: 0, rawOut, rawBytes: addRes.rawBytes };
492
+ }
493
+ async function handleCommit(cwd, globalOptions, args, options) {
494
+ const hasMsg = args.some((arg) => arg === "-m" || arg === "-F" || arg.startsWith("--message") || arg.startsWith("--file"));
495
+ if (!hasMsg)
496
+ return { output: "", exitCode: -100, rawOut: "" };
497
+ const res = await runGitQuery(cwd, globalOptions, ["commit", ...args], options);
498
+ const rawOut = rawText(res, true);
499
+ if (res.status !== 0)
500
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
501
+ if (rawOut.includes("nothing to commit") || rawOut.includes("working tree clean")) {
502
+ return { output: "nothing to commit, working tree clean", exitCode: 0, rawOut, rawBytes: res.rawBytes };
503
+ }
504
+ const firstLine = rawOut
505
+ .split("\n")
506
+ .find((line) => line.trim().length > 0)
507
+ ?.trim();
508
+ return { output: firstLine || "Committed successfully.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
509
+ }
510
+ async function handlePush(cwd, globalOptions, args, options) {
511
+ const res = await runGitQuery(cwd, globalOptions, ["push", ...args], options);
512
+ const rawOut = rawText(res, true);
513
+ const outputLines = rawOut
514
+ .split("\n")
515
+ .map((line) => line.trimEnd())
516
+ .filter((line) => {
517
+ const trimmed = line.trim();
518
+ if (!trimmed)
519
+ return false;
520
+ return !["Writing objects:", "Counting objects:", "Delta compression", "Compressing objects:", "Total "].some((prefix) => trimmed.startsWith(prefix));
521
+ });
522
+ if (res.status !== 0)
523
+ return { output: outputLines.join("\n"), exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
524
+ if (outputLines.some((line) => line.includes("Everything up-to-date"))) {
525
+ return { output: "Everything up-to-date.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
526
+ }
527
+ const remoteMessages = outputLines.filter((line) => line.trim().startsWith("remote:"));
528
+ const pushDetail = outputLines.find((line) => line.includes("->"));
529
+ const summary = pushDetail ? `Pushed: ${pushDetail.trim()}` : "Push successful.";
530
+ return { output: [...remoteMessages, summary].join("\n"), exitCode: 0, rawOut, rawBytes: res.rawBytes };
531
+ }
532
+ async function handlePull(cwd, globalOptions, args, options) {
533
+ const res = await runGitQuery(cwd, globalOptions, ["pull", ...args], options);
534
+ const rawOut = rawText(res, true);
535
+ if (res.status !== 0)
536
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
537
+ if (rawOut.includes("Already up to date."))
538
+ return { output: "Already up to date.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
539
+ const lines = rawOut.split("\n");
540
+ const summary = lines.filter((line) => line.includes("Fast-forward") || line.includes("file changed") || line.includes("files changed"));
541
+ return { output: summary.join("\n") || "Pull successful.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
542
+ }
543
+ async function handleFetch(cwd, globalOptions, args, options) {
544
+ const res = await runGitQuery(cwd, globalOptions, ["fetch", ...args], options);
545
+ const rawOut = rawText(res, true);
546
+ if (res.status !== 0)
547
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
548
+ const refs = rawOut
549
+ .split("\n")
550
+ .filter((line) => line.includes("[new branch]") || line.includes("[new tag]") || line.includes("->"));
551
+ return {
552
+ output: refs.length
553
+ ? `Fetched:\n${refs.map((line) => line.trim()).join("\n")}`
554
+ : "Fetch successful (no new refs).",
555
+ exitCode: 0,
556
+ rawOut,
557
+ rawBytes: res.rawBytes,
558
+ };
559
+ }
560
+ async function handleBranch(cwd, globalOptions, args, options) {
561
+ if (args.includes("--show-current")) {
562
+ const res = await runGitQuery(cwd, globalOptions, ["branch", "--show-current"], options);
563
+ return { output: res.stdout.trim(), exitCode: res.status ?? 0, rawOut: rawText(res), rawBytes: res.rawBytes };
564
+ }
565
+ const isWrite = args.some((arg) => arg === "-d" || arg === "-D" || arg === "-m" || arg === "-M" || (!arg.startsWith("-") && args.length === 1));
566
+ if (isWrite) {
567
+ const res = await runGitQuery(cwd, globalOptions, ["branch", ...args], options);
568
+ const rawOut = rawText(res);
569
+ if (res.status !== 0)
570
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
571
+ return { output: rawOut.trim() || "Branch updated successfully.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
572
+ }
573
+ const res = await runGitQuery(cwd, globalOptions, ["branch", "--no-color", ...args], options);
574
+ const rawOut = rawText(res);
575
+ if (res.status !== 0)
576
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
577
+ const lines = res.stdout.split("\n").filter((line) => line.trim().length > 0);
578
+ const remoteBranches = lines.filter((line) => line.includes("remotes/"));
579
+ const localBranches = lines.filter((line) => !line.includes("remotes/"));
580
+ const remoteDisplay = remoteBranches.slice(0, 5);
581
+ const omitted = remoteBranches.length - remoteDisplay.length;
582
+ if (omitted > 0)
583
+ remoteDisplay.push(` remotes/... (${omitted} more remote branches)`);
584
+ return { output: [...localBranches, ...remoteDisplay].join("\n"), exitCode: 0, rawOut, rawBytes: res.rawBytes };
585
+ }
586
+ async function handleStash(cwd, globalOptions, args, options) {
587
+ const sub = args[0] || "push";
588
+ const res = await runGitQuery(cwd, globalOptions, ["stash", ...args], options);
589
+ const rawOut = rawText(res);
590
+ if (res.status !== 0)
591
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
592
+ if (sub === "list")
593
+ return { output: res.stdout.trim() || "No stashes found.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
594
+ if (sub === "show") {
595
+ const { compacted, truncated } = compactDiff(res.stdout);
596
+ return {
597
+ output: `${compacted.trim()}${truncated ? "\n\n[Diff truncated.]" : ""}`,
598
+ exitCode: 0,
599
+ rawOut,
600
+ rawBytes: res.rawBytes,
601
+ };
602
+ }
603
+ if (res.stdout.includes("No local changes to save"))
604
+ return { output: "No local changes to save.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
605
+ const firstLine = res.stdout
606
+ .split("\n")
607
+ .find((line) => line.trim().length > 0)
608
+ ?.trim();
609
+ return { output: firstLine || "Stash successful.", exitCode: 0, rawOut, rawBytes: res.rawBytes };
610
+ }
611
+ async function handleWorktree(cwd, globalOptions, args, options) {
612
+ const sub = args[0] || "list";
613
+ const res = await runGitQuery(cwd, globalOptions, ["worktree", ...(sub === "list" ? ["list"] : args)], options);
614
+ const rawOut = rawText(res, true);
615
+ if (res.status !== 0)
616
+ return { output: rawOut, exitCode: res.status ?? 1, rawOut, rawBytes: res.rawBytes };
617
+ if (sub !== "list")
618
+ return { output: rawOut.trim(), exitCode: 0, rawOut, rawBytes: res.rawBytes };
619
+ const home = process.env.HOME || "";
620
+ const output = res.stdout
621
+ .split("\n")
622
+ .map((line) => (home && line.startsWith(home) ? `~${line.slice(home.length)}` : line))
623
+ .join("\n")
624
+ .trim();
625
+ return { output, exitCode: 0, rawOut, rawBytes: res.rawBytes };
626
+ }
627
+ export async function executeFilteredGit(cwd, subcommand, globalOptions, subcommandArgs, options) {
628
+ switch (subcommand) {
629
+ case "status":
630
+ return handleStatus(cwd, globalOptions, subcommandArgs, options);
631
+ case "log":
632
+ return handleLog(cwd, globalOptions, subcommandArgs, options);
633
+ case "diff":
634
+ return handleDiff(cwd, globalOptions, subcommandArgs, options);
635
+ case "show":
636
+ return handleShow(cwd, globalOptions, subcommandArgs, options);
637
+ case "add":
638
+ return handleAdd(cwd, globalOptions, subcommandArgs, options);
639
+ case "commit":
640
+ return handleCommit(cwd, globalOptions, subcommandArgs, options);
641
+ case "push":
642
+ return handlePush(cwd, globalOptions, subcommandArgs, options);
643
+ case "pull":
644
+ return handlePull(cwd, globalOptions, subcommandArgs, options);
645
+ case "branch":
646
+ return handleBranch(cwd, globalOptions, subcommandArgs, options);
647
+ case "fetch":
648
+ return handleFetch(cwd, globalOptions, subcommandArgs, options);
649
+ case "stash":
650
+ return handleStash(cwd, globalOptions, subcommandArgs, options);
651
+ case "worktree":
652
+ return handleWorktree(cwd, globalOptions, subcommandArgs, options);
653
+ default:
654
+ return { output: "", exitCode: -100, rawOut: "" };
655
+ }
656
+ }
657
+ export function makeGitCommandForDisplay(globalOptions, subcommand, args) {
658
+ return gitCommand(globalOptions, [subcommand, ...args]);
659
+ }
660
+ //# sourceMappingURL=git-filter.js.map