@joeytheman/gemini-mcp-tool 1.3.0 → 2.3.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/README.md +127 -35
- package/dist/__tests__/agyModelSteps.test.d.ts +2 -0
- package/dist/__tests__/agyModelSteps.test.d.ts.map +1 -0
- package/dist/__tests__/agyModelSteps.test.js +58 -0
- package/dist/__tests__/agyModelSteps.test.js.map +1 -0
- package/dist/__tests__/ask-gemini.test.js +99 -37
- package/dist/__tests__/ask-gemini.test.js.map +1 -1
- package/dist/__tests__/capture-script.chromium.test.d.ts +2 -0
- package/dist/__tests__/capture-script.chromium.test.d.ts.map +1 -0
- package/dist/__tests__/capture-script.chromium.test.js +235 -0
- package/dist/__tests__/capture-script.chromium.test.js.map +1 -0
- package/dist/__tests__/layout-snippet.chromium.test.d.ts +2 -0
- package/dist/__tests__/layout-snippet.chromium.test.d.ts.map +1 -0
- package/dist/__tests__/layout-snippet.chromium.test.js +333 -0
- package/dist/__tests__/layout-snippet.chromium.test.js.map +1 -0
- package/dist/__tests__/layout-snippet.test.d.ts +2 -0
- package/dist/__tests__/layout-snippet.test.d.ts.map +1 -0
- package/dist/__tests__/layout-snippet.test.js +12 -0
- package/dist/__tests__/layout-snippet.test.js.map +1 -0
- package/dist/__tests__/live-pass.test.d.ts +2 -0
- package/dist/__tests__/live-pass.test.d.ts.map +1 -0
- package/dist/__tests__/live-pass.test.js +1249 -0
- package/dist/__tests__/live-pass.test.js.map +1 -0
- package/dist/__tests__/screen-review.test.d.ts +2 -0
- package/dist/__tests__/screen-review.test.d.ts.map +1 -0
- package/dist/__tests__/screen-review.test.js +553 -0
- package/dist/__tests__/screen-review.test.js.map +1 -0
- package/dist/__tests__/server-progress.test.d.ts +2 -0
- package/dist/__tests__/server-progress.test.d.ts.map +1 -0
- package/dist/__tests__/server-progress.test.js +63 -0
- package/dist/__tests__/server-progress.test.js.map +1 -0
- package/dist/__tests__/server.test.js +17 -19
- package/dist/__tests__/server.test.js.map +1 -1
- package/dist/__tests__/simple-tools.test.d.ts +2 -0
- package/dist/__tests__/simple-tools.test.d.ts.map +1 -0
- package/dist/__tests__/simple-tools.test.js +30 -0
- package/dist/__tests__/simple-tools.test.js.map +1 -0
- package/dist/__tests__/tools.test.js +91 -0
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/__tests__/utils/agyExecutor.test.d.ts +2 -0
- package/dist/__tests__/utils/agyExecutor.test.d.ts.map +1 -0
- package/dist/__tests__/utils/agyExecutor.test.js +731 -0
- package/dist/__tests__/utils/agyExecutor.test.js.map +1 -0
- package/dist/__tests__/utils/agyTranscriptRecovery.test.d.ts +2 -0
- package/dist/__tests__/utils/agyTranscriptRecovery.test.d.ts.map +1 -0
- package/dist/__tests__/utils/agyTranscriptRecovery.test.js +127 -0
- package/dist/__tests__/utils/agyTranscriptRecovery.test.js.map +1 -0
- package/dist/__tests__/utils/chunkCache.test.js +26 -2
- package/dist/__tests__/utils/chunkCache.test.js.map +1 -1
- package/dist/__tests__/utils/commandExecutor.test.js +185 -7
- package/dist/__tests__/utils/commandExecutor.test.js.map +1 -1
- package/dist/__tests__/utils/fileReferences.test.d.ts +2 -0
- package/dist/__tests__/utils/fileReferences.test.d.ts.map +1 -0
- package/dist/__tests__/utils/fileReferences.test.js +87 -0
- package/dist/__tests__/utils/fileReferences.test.js.map +1 -0
- package/dist/__tests__/utils/gitStatusGuard.test.d.ts +2 -0
- package/dist/__tests__/utils/gitStatusGuard.test.d.ts.map +1 -0
- package/dist/__tests__/utils/gitStatusGuard.test.js +106 -0
- package/dist/__tests__/utils/gitStatusGuard.test.js.map +1 -0
- package/dist/__tests__/utils/progressUpdates.test.d.ts +2 -0
- package/dist/__tests__/utils/progressUpdates.test.d.ts.map +1 -0
- package/dist/__tests__/utils/progressUpdates.test.js +62 -0
- package/dist/__tests__/utils/progressUpdates.test.js.map +1 -0
- package/dist/__tests__/utils/responseCache.test.js +31 -11
- package/dist/__tests__/utils/responseCache.test.js.map +1 -1
- package/dist/__tests__/utils/test-helpers.d.ts +4 -0
- package/dist/__tests__/utils/test-helpers.d.ts.map +1 -1
- package/dist/__tests__/utils/test-helpers.js +16 -0
- package/dist/__tests__/utils/test-helpers.js.map +1 -1
- package/dist/constants.d.ts +91 -26
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +101 -28
- package/dist/constants.js.map +1 -1
- package/dist/index.js +20 -95
- package/dist/index.js.map +1 -1
- package/dist/layoutCheckSnippet.generated.d.ts +2 -0
- package/dist/layoutCheckSnippet.generated.d.ts.map +1 -0
- package/dist/layoutCheckSnippet.generated.js +3 -0
- package/dist/layoutCheckSnippet.generated.js.map +1 -0
- package/dist/tools/ask-gemini.tool.d.ts +34 -0
- package/dist/tools/ask-gemini.tool.d.ts.map +1 -1
- package/dist/tools/ask-gemini.tool.js +40 -23
- package/dist/tools/ask-gemini.tool.js.map +1 -1
- package/dist/tools/brainstorm.tool.d.ts.map +1 -1
- package/dist/tools/brainstorm.tool.js +7 -5
- package/dist/tools/brainstorm.tool.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/live-pass.tool.d.ts +206 -0
- package/dist/tools/live-pass.tool.d.ts.map +1 -0
- package/dist/tools/live-pass.tool.js +670 -0
- package/dist/tools/live-pass.tool.js.map +1 -0
- package/dist/tools/registry.d.ts +6 -4
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +14 -11
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/screen-review.tool.d.ts +124 -0
- package/dist/tools/screen-review.tool.d.ts.map +1 -0
- package/dist/tools/screen-review.tool.js +449 -0
- package/dist/tools/screen-review.tool.js.map +1 -0
- package/dist/tools/simple-tools.d.ts.map +1 -1
- package/dist/tools/simple-tools.js +9 -3
- package/dist/tools/simple-tools.js.map +1 -1
- package/dist/utils/agyExecutor.d.ts +76 -0
- package/dist/utils/agyExecutor.d.ts.map +1 -0
- package/dist/utils/agyExecutor.js +449 -0
- package/dist/utils/agyExecutor.js.map +1 -0
- package/dist/utils/agyModelSteps.d.ts +5 -0
- package/dist/utils/agyModelSteps.d.ts.map +1 -0
- package/dist/utils/agyModelSteps.js +33 -0
- package/dist/utils/agyModelSteps.js.map +1 -0
- package/dist/utils/agyReport.d.ts +19 -0
- package/dist/utils/agyReport.d.ts.map +1 -0
- package/dist/utils/agyReport.js +62 -0
- package/dist/utils/agyReport.js.map +1 -0
- package/dist/utils/agyTranscriptRecovery.d.ts +16 -0
- package/dist/utils/agyTranscriptRecovery.d.ts.map +1 -0
- package/dist/utils/agyTranscriptRecovery.js +149 -0
- package/dist/utils/agyTranscriptRecovery.js.map +1 -0
- package/dist/utils/artifactFile.d.ts +6 -0
- package/dist/utils/artifactFile.d.ts.map +1 -0
- package/dist/utils/artifactFile.js +55 -0
- package/dist/utils/artifactFile.js.map +1 -0
- package/dist/utils/captureScript.d.ts +24 -0
- package/dist/utils/captureScript.d.ts.map +1 -0
- package/dist/utils/captureScript.js +83 -0
- package/dist/utils/captureScript.js.map +1 -0
- package/dist/utils/chunkCache.d.ts.map +1 -1
- package/dist/utils/chunkCache.js +19 -5
- package/dist/utils/chunkCache.js.map +1 -1
- package/dist/utils/commandExecutor.d.ts +7 -1
- package/dist/utils/commandExecutor.d.ts.map +1 -1
- package/dist/utils/commandExecutor.js +162 -22
- package/dist/utils/commandExecutor.js.map +1 -1
- package/dist/utils/fileReferences.d.ts +45 -0
- package/dist/utils/fileReferences.d.ts.map +1 -0
- package/dist/utils/fileReferences.js +169 -0
- package/dist/utils/fileReferences.js.map +1 -0
- package/dist/utils/gitCheckout.d.ts +14 -0
- package/dist/utils/gitCheckout.d.ts.map +1 -0
- package/dist/utils/gitCheckout.js +36 -0
- package/dist/utils/gitCheckout.js.map +1 -0
- package/dist/utils/gitStatusGuard.d.ts +35 -0
- package/dist/utils/gitStatusGuard.d.ts.map +1 -0
- package/dist/utils/gitStatusGuard.js +82 -0
- package/dist/utils/gitStatusGuard.js.map +1 -0
- package/dist/utils/layoutCheck.d.ts +58 -0
- package/dist/utils/layoutCheck.d.ts.map +1 -0
- package/dist/utils/layoutCheck.js +84 -0
- package/dist/utils/layoutCheck.js.map +1 -0
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +0 -6
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/progressUpdates.d.ts +14 -0
- package/dist/utils/progressUpdates.d.ts.map +1 -0
- package/dist/utils/progressUpdates.js +40 -0
- package/dist/utils/progressUpdates.js.map +1 -0
- package/dist/utils/responseCache.d.ts +3 -3
- package/dist/utils/responseCache.d.ts.map +1 -1
- package/dist/utils/responseCache.js +11 -6
- package/dist/utils/responseCache.js.map +1 -1
- package/package.json +19 -16
- package/dist/__tests__/utils/geminiExecutor.test.d.ts +0 -2
- package/dist/__tests__/utils/geminiExecutor.test.d.ts.map +0 -1
- package/dist/__tests__/utils/geminiExecutor.test.js +0 -337
- package/dist/__tests__/utils/geminiExecutor.test.js.map +0 -1
- package/dist/utils/geminiExecutor.d.ts +0 -17
- package/dist/utils/geminiExecutor.d.ts.map +0 -1
- package/dist/utils/geminiExecutor.js +0 -238
- package/dist/utils/geminiExecutor.js.map +0 -1
|
@@ -1,20 +1,140 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import { Logger } from "./logger.js";
|
|
3
|
-
|
|
3
|
+
import { AGY_LIMITS, ERROR_MESSAGES } from "../constants.js";
|
|
4
|
+
// Node clamps longer delays to 1 ms, so long limits need more than one timer.
|
|
5
|
+
const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
6
|
+
export async function executeCommand(command, args, onProgress, cwd, limits = {}) {
|
|
4
7
|
return new Promise((resolve, reject) => {
|
|
8
|
+
if (limits.signal?.aborted) {
|
|
9
|
+
reject(new Error(ERROR_MESSAGES.AGY_CANCELLED));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
5
12
|
const startTime = Date.now();
|
|
6
13
|
Logger.commandExecution(command, args, startTime);
|
|
14
|
+
// Give limited POSIX commands their own process group. agy's MCP children
|
|
15
|
+
// inherit it, so a stop can reach them even if agy ignores SIGTERM.
|
|
16
|
+
const grouped = process.platform !== "win32" &&
|
|
17
|
+
(limits.killAfterMs !== undefined || limits.signal !== undefined);
|
|
7
18
|
const childProcess = spawn(command, args, {
|
|
8
19
|
env: process.env,
|
|
9
|
-
shell:
|
|
20
|
+
shell: false,
|
|
10
21
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11
22
|
...(cwd && { cwd }),
|
|
23
|
+
...(grouped && { detached: true }),
|
|
12
24
|
});
|
|
13
25
|
// Use array buffers for O(n) performance instead of O(n²) string concatenation
|
|
14
26
|
const stdoutChunks = [];
|
|
15
27
|
const stderrChunks = [];
|
|
16
28
|
let isResolved = false;
|
|
29
|
+
let stopError;
|
|
30
|
+
let resourceExhausted = false;
|
|
31
|
+
let ceilingTimer;
|
|
32
|
+
let graceTimer;
|
|
33
|
+
let closeTimer;
|
|
34
|
+
const settle = () => {
|
|
35
|
+
isResolved = true;
|
|
36
|
+
clearTimeout(ceilingTimer);
|
|
37
|
+
clearTimeout(graceTimer);
|
|
38
|
+
clearTimeout(closeTimer);
|
|
39
|
+
limits.signal?.removeEventListener("abort", onAbort);
|
|
40
|
+
};
|
|
41
|
+
const signalProcessGroup = (signal) => {
|
|
42
|
+
try {
|
|
43
|
+
if (grouped && childProcess.pid !== undefined) {
|
|
44
|
+
process.kill(-childProcess.pid, signal);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
childProcess.kill(signal);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A process can exit between the timer and the signal. If the group
|
|
52
|
+
// signal failed for another reason, still try to stop agy itself.
|
|
53
|
+
if (grouped)
|
|
54
|
+
childProcess.kill(signal);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const signalWindowsTree = () => {
|
|
58
|
+
if (childProcess.pid === undefined) {
|
|
59
|
+
childProcess.kill("SIGKILL");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Windows has no negative-PID group signal. Kill the tree while the
|
|
63
|
+
// parent PID still exists, before its children can become orphaned.
|
|
64
|
+
const killer = spawn("taskkill", ["/PID", String(childProcess.pid), "/T", "/F"], {
|
|
65
|
+
shell: false,
|
|
66
|
+
stdio: "ignore",
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
});
|
|
69
|
+
killer.on("error", (error) => {
|
|
70
|
+
Logger.error("Could not stop the Windows process tree:", error);
|
|
71
|
+
childProcess.kill("SIGKILL");
|
|
72
|
+
});
|
|
73
|
+
killer.on("close", (code) => {
|
|
74
|
+
if (code !== 0 && !isResolved) {
|
|
75
|
+
Logger.error(`taskkill could not stop the Windows process tree (exit ${code})`);
|
|
76
|
+
childProcess.kill("SIGKILL");
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
const finishStopped = (code) => {
|
|
81
|
+
if (isResolved)
|
|
82
|
+
return;
|
|
83
|
+
settle();
|
|
84
|
+
Logger.commandComplete(startTime, code);
|
|
85
|
+
reject(stopError);
|
|
86
|
+
};
|
|
87
|
+
const forceStop = () => {
|
|
88
|
+
if (isResolved)
|
|
89
|
+
return;
|
|
90
|
+
if (process.platform === "win32")
|
|
91
|
+
signalWindowsTree();
|
|
92
|
+
else
|
|
93
|
+
signalProcessGroup("SIGKILL");
|
|
94
|
+
// A detached descendant can keep inherited pipes open after agy dies,
|
|
95
|
+
// so bound the wait for close.
|
|
96
|
+
closeTimer = setTimeout(() => {
|
|
97
|
+
childProcess.stdout.destroy?.();
|
|
98
|
+
childProcess.stderr.destroy?.();
|
|
99
|
+
finishStopped(childProcess.exitCode ?? null);
|
|
100
|
+
}, AGY_LIMITS.KILL_CLOSE_WAIT_MS);
|
|
101
|
+
};
|
|
102
|
+
const stop = (error) => {
|
|
103
|
+
if (isResolved || stopError)
|
|
104
|
+
return;
|
|
105
|
+
stopError = error;
|
|
106
|
+
clearTimeout(ceilingTimer);
|
|
107
|
+
limits.signal?.removeEventListener("abort", onAbort);
|
|
108
|
+
Logger.error(error.message);
|
|
109
|
+
if (process.platform === "win32")
|
|
110
|
+
signalWindowsTree();
|
|
111
|
+
else
|
|
112
|
+
signalProcessGroup("SIGTERM");
|
|
113
|
+
graceTimer = setTimeout(forceStop, AGY_LIMITS.KILL_GRACE_MS);
|
|
114
|
+
};
|
|
115
|
+
function onAbort() {
|
|
116
|
+
stop(new Error(ERROR_MESSAGES.AGY_CANCELLED));
|
|
117
|
+
}
|
|
118
|
+
if (limits.killAfterMs !== undefined) {
|
|
119
|
+
const limitMs = limits.killAfterMs;
|
|
120
|
+
const deadline = Date.now() + limitMs;
|
|
121
|
+
const scheduleCeiling = () => {
|
|
122
|
+
const remaining = deadline - Date.now();
|
|
123
|
+
if (remaining <= 0) {
|
|
124
|
+
stop(new Error(`${ERROR_MESSAGES.AGY_HUNG}: limit ${Math.round(limitMs / 1000)}s`));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
ceilingTimer = setTimeout(scheduleCeiling, Math.min(remaining, MAX_TIMER_DELAY_MS));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
scheduleCeiling();
|
|
131
|
+
}
|
|
132
|
+
limits.signal?.addEventListener("abort", onAbort, { once: true });
|
|
133
|
+
if (limits.signal?.aborted)
|
|
134
|
+
onAbort();
|
|
17
135
|
childProcess.stdout.on("data", (data) => {
|
|
136
|
+
if (stopError)
|
|
137
|
+
return;
|
|
18
138
|
const chunk = data.toString();
|
|
19
139
|
stdoutChunks.push(chunk);
|
|
20
140
|
// Report immediately if callback provided (no substring calculation needed)
|
|
@@ -24,10 +144,14 @@ export async function executeCommand(command, args, onProgress, cwd) {
|
|
|
24
144
|
});
|
|
25
145
|
// CLI level errors
|
|
26
146
|
childProcess.stderr.on("data", (data) => {
|
|
147
|
+
if (stopError)
|
|
148
|
+
return;
|
|
27
149
|
const chunk = data.toString();
|
|
28
150
|
stderrChunks.push(chunk);
|
|
29
|
-
//
|
|
30
|
-
|
|
151
|
+
// Surface resource/quota failures from the backend without attempting
|
|
152
|
+
// fallback. Build the structured error once, on the first marker.
|
|
153
|
+
if (!resourceExhausted && chunk.includes("RESOURCE_EXHAUSTED")) {
|
|
154
|
+
resourceExhausted = true;
|
|
31
155
|
const stderrSoFar = stderrChunks.join('');
|
|
32
156
|
const modelMatch = stderrSoFar.match(/Quota exceeded for quota metric '([^']+)'/);
|
|
33
157
|
const statusMatch = stderrSoFar.match(/status["\s]*[:=]\s*(\d+)/);
|
|
@@ -42,36 +166,52 @@ export async function executeCommand(command, args, onProgress, cwd) {
|
|
|
42
166
|
details: {
|
|
43
167
|
model: model,
|
|
44
168
|
reason: reason,
|
|
45
|
-
statusText:
|
|
169
|
+
statusText: `Too Many Requests from Antigravity CLI backend`,
|
|
46
170
|
}
|
|
47
171
|
}
|
|
48
172
|
};
|
|
49
|
-
Logger.error(`
|
|
173
|
+
Logger.error(`Antigravity resource error: ${JSON.stringify(errorJson, null, 2)}`);
|
|
50
174
|
}
|
|
51
175
|
});
|
|
52
176
|
childProcess.on("error", (error) => {
|
|
53
177
|
if (!isResolved) {
|
|
54
|
-
|
|
178
|
+
if (stopError)
|
|
179
|
+
return;
|
|
180
|
+
settle();
|
|
55
181
|
Logger.error(`Process error:`, error);
|
|
182
|
+
Logger.commandComplete(startTime, null);
|
|
56
183
|
reject(new Error(`Failed to spawn command: ${error.message}`));
|
|
57
184
|
}
|
|
58
185
|
});
|
|
59
186
|
childProcess.on("close", (code) => {
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
187
|
+
if (isResolved)
|
|
188
|
+
return;
|
|
189
|
+
if (stopError) {
|
|
190
|
+
// A parent can exit before its children. Clear any survivors from its
|
|
191
|
+
// process group before reporting that the request has stopped.
|
|
192
|
+
if (grouped && childProcess.pid !== undefined)
|
|
193
|
+
signalProcessGroup("SIGKILL");
|
|
194
|
+
finishStopped(code);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
settle();
|
|
198
|
+
// Join array buffers efficiently
|
|
199
|
+
const stdout = stdoutChunks.join('');
|
|
200
|
+
const stderr = stderrChunks.join('');
|
|
201
|
+
// Treat an exit-0 run as failed only when it reported quota exhaustion
|
|
202
|
+
// AND produced no usable stdout — so a quota error with empty output is
|
|
203
|
+
// surfaced (not silently "recovered"), while a run that still returned a
|
|
204
|
+
// real answer is kept.
|
|
205
|
+
const quotaFailure = resourceExhausted && stdout.trim() === "";
|
|
206
|
+
if (code === 0 && !quotaFailure) {
|
|
207
|
+
Logger.commandComplete(startTime, code, stdout.length);
|
|
208
|
+
resolve(stdout.trim());
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
Logger.commandComplete(startTime, code);
|
|
212
|
+
Logger.error(`Failed with exit code ${code}`);
|
|
213
|
+
const errorMessage = stderr.trim() || (resourceExhausted ? "RESOURCE_EXHAUSTED" : "Unknown error");
|
|
214
|
+
reject(new Error(`Command failed with exit code ${code}: ${errorMessage}`));
|
|
75
215
|
}
|
|
76
216
|
});
|
|
77
217
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandExecutor.js","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"commandExecutor.js","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-reference handling for agy prompts.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `agy --print` resolves an `@file` reference ONLY when the
|
|
5
|
+
* file's directory is part of the workspace (`--add-dir`). cwd is never
|
|
6
|
+
* auto-registered. An `@file` outside any added dir makes agy hang until
|
|
7
|
+
* `--print-timeout`. So for every reference in a prompt that actually resolves
|
|
8
|
+
* to a real file under a trusted root (the working directory or an explicitly
|
|
9
|
+
* supplied includeDirectories entry), we register that root with `--add-dir`.
|
|
10
|
+
*
|
|
11
|
+
* We never inline file contents: agy reads the file itself, keeping the
|
|
12
|
+
* `--print` argv small and preserving the large-file use case (inlining would
|
|
13
|
+
* risk E2BIG). References that don't resolve under a trusted root are left
|
|
14
|
+
* untouched — the prompt still runs.
|
|
15
|
+
*/
|
|
16
|
+
/** Normalize a comma-separated string or array into a trimmed, non-empty list. */
|
|
17
|
+
export declare function normalizeList(value?: string | string[]): string[];
|
|
18
|
+
/** True when `p` resolves to a filesystem root (e.g. `/` or `C:\`). */
|
|
19
|
+
export declare function isFilesystemRoot(p: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a tool's `workingDirectory` argument: it must be an absolute path to
|
|
22
|
+
* an existing directory that is not a filesystem root, because it becomes both
|
|
23
|
+
* agy's cwd and its workspace root.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveWorkingDirectory(requested: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* `target` with its deepest EXISTING ancestor realpath'd, so a path that does
|
|
28
|
+
* not exist yet can still be compared against a realpath'd root.
|
|
29
|
+
*/
|
|
30
|
+
export declare function realpathOfExistingPrefix(target: string): string;
|
|
31
|
+
/** True when `candidate` is `root` itself or lives under it. */
|
|
32
|
+
export declare function isWithin(root: string, candidate: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Normalize file references in a prompt and collect the `--add-dir` roots agy
|
|
35
|
+
* needs to read them. Returns the (possibly rewritten) prompt and the deduped
|
|
36
|
+
* list of trust roots that contain a resolved reference. Never throws.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveFileReferences(prompt: string, opts: {
|
|
39
|
+
cwd?: string;
|
|
40
|
+
includeDirectories?: string | string[];
|
|
41
|
+
}): {
|
|
42
|
+
prompt: string;
|
|
43
|
+
addDirs: string[];
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=fileReferences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileReferences.d.ts","sourceRoot":"","sources":["../../src/utils/fileReferences.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAIjE;AAgBD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAejE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAa/D;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAGjE;AAyCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA0BvC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { ERROR_MESSAGES } from '../constants.js';
|
|
4
|
+
/**
|
|
5
|
+
* File-reference handling for agy prompts.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: `agy --print` resolves an `@file` reference ONLY when the
|
|
8
|
+
* file's directory is part of the workspace (`--add-dir`). cwd is never
|
|
9
|
+
* auto-registered. An `@file` outside any added dir makes agy hang until
|
|
10
|
+
* `--print-timeout`. So for every reference in a prompt that actually resolves
|
|
11
|
+
* to a real file under a trusted root (the working directory or an explicitly
|
|
12
|
+
* supplied includeDirectories entry), we register that root with `--add-dir`.
|
|
13
|
+
*
|
|
14
|
+
* We never inline file contents: agy reads the file itself, keeping the
|
|
15
|
+
* `--print` argv small and preserving the large-file use case (inlining would
|
|
16
|
+
* risk E2BIG). References that don't resolve under a trusted root are left
|
|
17
|
+
* untouched — the prompt still runs.
|
|
18
|
+
*/
|
|
19
|
+
/** Normalize a comma-separated string or array into a trimmed, non-empty list. */
|
|
20
|
+
export function normalizeList(value) {
|
|
21
|
+
if (!value)
|
|
22
|
+
return [];
|
|
23
|
+
const items = Array.isArray(value) ? value : value.split(',');
|
|
24
|
+
return items.map(item => item.trim()).filter(Boolean);
|
|
25
|
+
}
|
|
26
|
+
// `@` reference: an `@` that is NOT part of an email/scoped continuation
|
|
27
|
+
// (preceded by a word char, another `@`, `/`, or `.`), then a run of non-space,
|
|
28
|
+
// non-`@` characters. Existence under a trust root — not shape — gates whether
|
|
29
|
+
// it is treated as a file, so extensionless names (Makefile, LICENSE) work.
|
|
30
|
+
const AT_REF = /(?<![\w@/.])@([^\s@]+)/g;
|
|
31
|
+
// `file:` reference: `file:` at a word boundary, not a `file://` URL.
|
|
32
|
+
const FILE_REF = /(?<![\w])file:(?!\/\/)(\S+)/g;
|
|
33
|
+
const TRAILING_PUNCT = /[),.;:!?\]}'"`]+$/;
|
|
34
|
+
function stripTrailingPunct(token) {
|
|
35
|
+
return token.replace(TRAILING_PUNCT, '');
|
|
36
|
+
}
|
|
37
|
+
/** True when `p` resolves to a filesystem root (e.g. `/` or `C:\`). */
|
|
38
|
+
export function isFilesystemRoot(p) {
|
|
39
|
+
try {
|
|
40
|
+
const resolved = path.resolve(p);
|
|
41
|
+
return resolved === path.parse(resolved).root;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a tool's `workingDirectory` argument: it must be an absolute path to
|
|
49
|
+
* an existing directory that is not a filesystem root, because it becomes both
|
|
50
|
+
* agy's cwd and its workspace root.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveWorkingDirectory(requested) {
|
|
53
|
+
// Realpath'd, so that later containment checks compare like with like: on
|
|
54
|
+
// macOS /tmp/x and /private/tmp/x are the same directory but not the same
|
|
55
|
+
// string, and a frame reported under one would look OUTSIDE the other.
|
|
56
|
+
let real = null;
|
|
57
|
+
try {
|
|
58
|
+
const candidate = fs.realpathSync(path.resolve(requested));
|
|
59
|
+
if (fs.statSync(candidate).isDirectory())
|
|
60
|
+
real = candidate;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
real = null;
|
|
64
|
+
}
|
|
65
|
+
if (!path.isAbsolute(requested) || !real || isFilesystemRoot(real)) {
|
|
66
|
+
throw new Error(`${ERROR_MESSAGES.INVALID_WORKING_DIRECTORY}. Received '${requested}'.`);
|
|
67
|
+
}
|
|
68
|
+
return real;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* `target` with its deepest EXISTING ancestor realpath'd, so a path that does
|
|
72
|
+
* not exist yet can still be compared against a realpath'd root.
|
|
73
|
+
*/
|
|
74
|
+
export function realpathOfExistingPrefix(target) {
|
|
75
|
+
let current = path.resolve(target);
|
|
76
|
+
const missing = [];
|
|
77
|
+
for (;;) {
|
|
78
|
+
try {
|
|
79
|
+
return path.join(fs.realpathSync(current), ...missing);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
const parent = path.dirname(current);
|
|
83
|
+
if (parent === current)
|
|
84
|
+
return path.resolve(target);
|
|
85
|
+
missing.unshift(path.basename(current));
|
|
86
|
+
current = parent;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** True when `candidate` is `root` itself or lives under it. */
|
|
91
|
+
export function isWithin(root, candidate) {
|
|
92
|
+
const rel = path.relative(root, candidate);
|
|
93
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Realpath'd, existing directories that references may resolve under: the
|
|
97
|
+
* working directory plus each includeDirectories entry.
|
|
98
|
+
*/
|
|
99
|
+
function resolveTrustRoots(cwd, includeDirectories) {
|
|
100
|
+
const baseCwd = cwd || process.cwd();
|
|
101
|
+
const candidates = [baseCwd, ...normalizeList(includeDirectories).map(d => path.resolve(baseCwd, d))];
|
|
102
|
+
const roots = [];
|
|
103
|
+
for (const candidate of candidates) {
|
|
104
|
+
try {
|
|
105
|
+
const real = fs.realpathSync(candidate);
|
|
106
|
+
if (fs.statSync(real).isDirectory())
|
|
107
|
+
roots.push(real);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Non-existent / unreadable root — skip.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return [...new Set(roots)];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a token to the trust root it lives under. agy joins the reference
|
|
117
|
+
* path to an added dir, so the root that resolves the token is exactly what
|
|
118
|
+
* must be added. Returns null for tokens that don't resolve to a real file, or
|
|
119
|
+
* that escape every root via absolute/`..`/symlink.
|
|
120
|
+
*/
|
|
121
|
+
function rootForToken(rawToken, roots) {
|
|
122
|
+
const token = stripTrailingPunct(rawToken);
|
|
123
|
+
if (!token)
|
|
124
|
+
return null;
|
|
125
|
+
for (const root of roots) {
|
|
126
|
+
try {
|
|
127
|
+
const real = fs.realpathSync(path.resolve(root, token));
|
|
128
|
+
if (isWithin(root, real) && fs.statSync(real).isFile())
|
|
129
|
+
return root;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Does not resolve under this root — try the next one.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Normalize file references in a prompt and collect the `--add-dir` roots agy
|
|
139
|
+
* needs to read them. Returns the (possibly rewritten) prompt and the deduped
|
|
140
|
+
* list of trust roots that contain a resolved reference. Never throws.
|
|
141
|
+
*/
|
|
142
|
+
export function resolveFileReferences(prompt, opts) {
|
|
143
|
+
const roots = resolveTrustRoots(opts.cwd, opts.includeDirectories);
|
|
144
|
+
if (roots.length === 0)
|
|
145
|
+
return { prompt, addDirs: [] };
|
|
146
|
+
const addDirs = new Set();
|
|
147
|
+
// Rewrite a `file:` reference to `@` ONLY when it resolves to a real file
|
|
148
|
+
// under a trusted root — otherwise we'd hand agy a dangling @ref that hangs.
|
|
149
|
+
// The converted token drops trailing punctuation so agy sees the exact path.
|
|
150
|
+
const rewritten = prompt.replace(FILE_REF, (match, rawToken) => {
|
|
151
|
+
const root = rootForToken(rawToken, roots);
|
|
152
|
+
if (!root)
|
|
153
|
+
return match;
|
|
154
|
+
addDirs.add(root);
|
|
155
|
+
return `@${stripTrailingPunct(rawToken)}`;
|
|
156
|
+
});
|
|
157
|
+
// Collect the trust root for every `@` reference that resolves.
|
|
158
|
+
AT_REF.lastIndex = 0;
|
|
159
|
+
let m;
|
|
160
|
+
while ((m = AT_REF.exec(rewritten)) !== null) {
|
|
161
|
+
if (addDirs.size >= roots.length)
|
|
162
|
+
break; // every root already registered
|
|
163
|
+
const root = rootForToken(m[1], roots);
|
|
164
|
+
if (root)
|
|
165
|
+
addDirs.add(root);
|
|
166
|
+
}
|
|
167
|
+
return { prompt: rewritten, addDirs: [...addDirs] };
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=fileReferences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileReferences.js","sourceRoot":"","sources":["../../src/utils/fileReferences.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Checkout {
|
|
2
|
+
/** Null on a detached HEAD. */
|
|
3
|
+
branch: string | null;
|
|
4
|
+
head: string;
|
|
5
|
+
}
|
|
6
|
+
/** The branch and commit checked out in `directory`, or null when it is not a git checkout with a commit. */
|
|
7
|
+
export declare function readCheckout(directory: string): Promise<Checkout | null>;
|
|
8
|
+
/**
|
|
9
|
+
* The merge base of `ref` and HEAD in `directory`. Throws with git's own reason
|
|
10
|
+
* when the ref does not resolve; `--end-of-options` keeps a ref spelled like an
|
|
11
|
+
* option from being read as one.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveMergeBase(directory: string, ref: string): Promise<string>;
|
|
14
|
+
//# sourceMappingURL=gitCheckout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitCheckout.d.ts","sourceRoot":"","sources":["../../src/utils/gitCheckout.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,QAAQ;IACvB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAMD,6GAA6G;AAC7G,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAS9E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQtF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
const GIT_TIMEOUT_MS = 10_000;
|
|
5
|
+
function git(directory, args) {
|
|
6
|
+
return execFileAsync('git', args, { cwd: directory, timeout: GIT_TIMEOUT_MS });
|
|
7
|
+
}
|
|
8
|
+
/** The branch and commit checked out in `directory`, or null when it is not a git checkout with a commit. */
|
|
9
|
+
export async function readCheckout(directory) {
|
|
10
|
+
try {
|
|
11
|
+
const { stdout } = await git(directory, ['rev-parse', 'HEAD', '--abbrev-ref', 'HEAD']);
|
|
12
|
+
const [head, branch] = stdout.trim().split('\n');
|
|
13
|
+
if (!head || !branch)
|
|
14
|
+
return null;
|
|
15
|
+
return { head, branch: branch === 'HEAD' ? null : branch };
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The merge base of `ref` and HEAD in `directory`. Throws with git's own reason
|
|
23
|
+
* when the ref does not resolve; `--end-of-options` keeps a ref spelled like an
|
|
24
|
+
* option from being read as one.
|
|
25
|
+
*/
|
|
26
|
+
export async function resolveMergeBase(directory, ref) {
|
|
27
|
+
try {
|
|
28
|
+
const { stdout } = await git(directory, ['merge-base', '--end-of-options', ref, 'HEAD']);
|
|
29
|
+
return stdout.trim();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const stderr = error.stderr?.trim();
|
|
33
|
+
throw new Error(stderr || error.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=gitCheckout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitCheckout.js","sourceRoot":"","sources":["../../src/utils/gitCheckout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,cAAc,GAAG,MAAM,CAAC;AAQ9B,SAAS,GAAG,CAAC,SAAiB,EAAE,IAAc;IAC5C,OAAO,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,6GAA6G;AAC7G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAiB;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;QACvF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB,EAAE,GAAW;IACnE,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,SAAS,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QACzF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAI,KAA6B,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,MAAM,IAAK,KAAe,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Before/after snapshots of a working tree, so a tool that hands Gemini a shell
|
|
3
|
+
* can report uninvited edits. Never throws: a snapshot that cannot be taken is
|
|
4
|
+
* `null` and simply disables the comparison.
|
|
5
|
+
*
|
|
6
|
+
* Status lines alone are not enough. These tools run against a dirty PR
|
|
7
|
+
* worktree, where a file Gemini rewrites was already ` M` before the run and
|
|
8
|
+
* stays ` M` after — the very files under review. So each dirty path's content
|
|
9
|
+
* is hashed too, and the diff reports content changes as well as status changes.
|
|
10
|
+
*
|
|
11
|
+
* Async because `git status` on a cold repository takes seconds, and this runs
|
|
12
|
+
* inside an MCP call whose keepalive notifications need the event loop.
|
|
13
|
+
*/
|
|
14
|
+
export interface WorkingTreeSnapshot {
|
|
15
|
+
/** Repository root: porcelain paths are relative to it, never to cwd. */
|
|
16
|
+
root: string;
|
|
17
|
+
lines: string[];
|
|
18
|
+
/** Absolute path -> sha256 of its content, for every dirty file we could hash. */
|
|
19
|
+
hashes: Map<string, string>;
|
|
20
|
+
/** True when MAX_HASHED_FILES cut the hashing short. */
|
|
21
|
+
truncated: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface WorkingTreeDiff {
|
|
24
|
+
added: string[];
|
|
25
|
+
removed: string[];
|
|
26
|
+
/** Paths whose content changed while keeping the same status. */
|
|
27
|
+
modified: string[];
|
|
28
|
+
/** True when either snapshot hashed only part of the dirty set. */
|
|
29
|
+
truncated: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function snapshotWorkingTree(cwd: string): Promise<WorkingTreeSnapshot | null>;
|
|
32
|
+
export declare function diffWorkingTree(before: WorkingTreeSnapshot | null, after: WorkingTreeSnapshot | null, opts?: {
|
|
33
|
+
ignoreDirectory?: string;
|
|
34
|
+
}): WorkingTreeDiff;
|
|
35
|
+
//# sourceMappingURL=gitStatusGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitStatusGuard.d.ts","sourceRoot":"","sources":["../../src/utils/gitStatusGuard.ts"],"names":[],"mappings":"AAeA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;CACpB;AAoBD,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CA8B1F;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAClC,KAAK,EAAE,mBAAmB,GAAG,IAAI,EACjC,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,eAAe,CAsBjB"}
|