@quantiya/codevibe-antigravity-plugin 2.0.100 → 2.0.101
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/agy-hook.js +1 -0
- package/dist/server.js +569 -41
- package/package.json +2 -2
package/dist/agy-hook.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(server_exports, {
|
|
|
43
43
|
commandDigest: () => commandDigest,
|
|
44
44
|
detectActivePromptKind: () => detectActivePromptKind,
|
|
45
45
|
extractFileAccessPrompt: () => extractFileAccessPrompt,
|
|
46
|
+
extractFileEditPrompt: () => extractFileEditPrompt,
|
|
46
47
|
extractSystemErrorPrompt: () => extractSystemErrorPrompt,
|
|
47
48
|
generateLaunchSessionId: () => generateLaunchSessionId,
|
|
48
49
|
getActiveConversationFromCliLog: () => getActiveConversationFromCliLog,
|
|
@@ -57,7 +58,8 @@ __export(server_exports, {
|
|
|
57
58
|
readTranscriptFirstType: () => readTranscriptFirstType,
|
|
58
59
|
reconstructFooter: () => reconstructFooter,
|
|
59
60
|
runForeignSpawnIdle: () => runForeignSpawnIdle,
|
|
60
|
-
safeIsoTimestamp: () => safeIsoTimestamp
|
|
61
|
+
safeIsoTimestamp: () => safeIsoTimestamp,
|
|
62
|
+
stripAnsiAndExpandRep: () => stripAnsiAndExpandRep
|
|
61
63
|
});
|
|
62
64
|
module.exports = __toCommonJS(server_exports);
|
|
63
65
|
var crypto3 = __toESM(require("crypto"));
|
|
@@ -2326,6 +2328,7 @@ function inspectHookRegistration(paths) {
|
|
|
2326
2328
|
// src/tool-prompt-match.ts
|
|
2327
2329
|
var TOOL_COMMAND_KEYS = ["CommandLine", "Command", "command"];
|
|
2328
2330
|
var TOOL_PATH_KEYS = [
|
|
2331
|
+
"Path",
|
|
2329
2332
|
"AbsolutePath",
|
|
2330
2333
|
"TargetFile",
|
|
2331
2334
|
"FilePath",
|
|
@@ -2334,7 +2337,13 @@ var TOOL_PATH_KEYS = [
|
|
|
2334
2337
|
"path",
|
|
2335
2338
|
"DirectoryPath"
|
|
2336
2339
|
];
|
|
2337
|
-
var EDIT_TOOLS = /* @__PURE__ */ new Set([
|
|
2340
|
+
var EDIT_TOOLS = /* @__PURE__ */ new Set([
|
|
2341
|
+
"EDIT_FILE",
|
|
2342
|
+
"WRITE_FILE",
|
|
2343
|
+
"WRITE_TO_FILE",
|
|
2344
|
+
"REPLACE_FILE_CONTENT",
|
|
2345
|
+
"MULTI_REPLACE_FILE_CONTENT"
|
|
2346
|
+
]);
|
|
2338
2347
|
function stripWrappingQuotes(value) {
|
|
2339
2348
|
return value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
2340
2349
|
}
|
|
@@ -2638,7 +2647,8 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2638
2647
|
const err = extractSystemErrorPrompt(snapshot);
|
|
2639
2648
|
headerText = err ? err.message : null;
|
|
2640
2649
|
} else if (kind === "approval") {
|
|
2641
|
-
|
|
2650
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
2651
|
+
headerText = fileEdit ? fileEdit.identity : extractHeader(snapshot);
|
|
2642
2652
|
}
|
|
2643
2653
|
}
|
|
2644
2654
|
let isChooser = hasPrompt && kind !== "system_error";
|
|
@@ -2781,7 +2791,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2781
2791
|
if (candidate.snapshotHash === this.lastPromptHash) return;
|
|
2782
2792
|
this.lastPromptHash = candidate.snapshotHash;
|
|
2783
2793
|
this.approvalUIActive = true;
|
|
2784
|
-
this.currentApprovalHeader = extractHeader(candidate.snapshot);
|
|
2794
|
+
this.currentApprovalHeader = extractFileEditPrompt(candidate.snapshot)?.identity ?? extractHeader(candidate.snapshot);
|
|
2785
2795
|
this.emit("prompt-candidate", candidate);
|
|
2786
2796
|
}
|
|
2787
2797
|
/**
|
|
@@ -2944,6 +2954,19 @@ function isComposerBoxAt(lines, idx) {
|
|
|
2944
2954
|
return false;
|
|
2945
2955
|
}
|
|
2946
2956
|
var ANSI_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
2957
|
+
var ANSI_NON_REP_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-ac-~])/g;
|
|
2958
|
+
function stripAnsiAndExpandRep(input) {
|
|
2959
|
+
if (!input.includes("\x1B")) return input;
|
|
2960
|
+
const withoutNonRep = input.replace(ANSI_NON_REP_ESCAPE_REGEX, "");
|
|
2961
|
+
const expanded = withoutNonRep.replace(
|
|
2962
|
+
/([^\x00-\x1f\x7f])\x1B\[(\d*)b/gu,
|
|
2963
|
+
(_, ch, countStr) => {
|
|
2964
|
+
const count = Math.min(Math.max(Number.parseInt(countStr || "1", 10) || 1, 1), 512);
|
|
2965
|
+
return ch.repeat(1 + count);
|
|
2966
|
+
}
|
|
2967
|
+
);
|
|
2968
|
+
return expanded.replace(ANSI_ESCAPE_REGEX, "");
|
|
2969
|
+
}
|
|
2947
2970
|
function isPaneInputSafetyBlocked(input) {
|
|
2948
2971
|
const rawText = Array.isArray(input) ? input.join("\n") : input;
|
|
2949
2972
|
if (!rawText) return { blocked: false, kind: null };
|
|
@@ -2962,7 +2985,7 @@ function isPaneInputSafetyBlocked(input) {
|
|
|
2962
2985
|
if (isQuestionFooterLine(trimmed)) {
|
|
2963
2986
|
footerIdx = i;
|
|
2964
2987
|
break;
|
|
2965
|
-
} else if (/\besc to cancel\b/i.test(trimmed) && !isAgyStatusFooterLine(trimmed)) {
|
|
2988
|
+
} else if ((/\besc to cancel\b/i.test(trimmed) || /(?:↑\/↓|↑|↓)\s*Navigate\b.*\b(?:tab\s+Amend|f\s+full\s+diff|ctrl\+r\s+Review)\b/i.test(trimmed)) && !isAgyStatusFooterLine(trimmed)) {
|
|
2966
2989
|
footerIdx = i;
|
|
2967
2990
|
break;
|
|
2968
2991
|
}
|
|
@@ -3048,7 +3071,7 @@ function isPaneInputSafetyBlocked(input) {
|
|
|
3048
3071
|
candidateHeaders.push({ lineIdx: i, kind: "question" });
|
|
3049
3072
|
} else if (/Allow access to this file\?/i.test(trimmed)) {
|
|
3050
3073
|
candidateHeaders.push({ lineIdx: i, kind: "file_access" });
|
|
3051
|
-
} else if (/Requesting permission for:/i.test(trimmed) || /Do you want to (?:proceed|run|execute)/i.test(trimmed)) {
|
|
3074
|
+
} else if (/Requesting permission for:/i.test(trimmed) || /Do you want to (?:proceed|run|execute)/i.test(trimmed) || /^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(trimmed)) {
|
|
3052
3075
|
candidateHeaders.push({ lineIdx: i, kind: "approval" });
|
|
3053
3076
|
}
|
|
3054
3077
|
}
|
|
@@ -3116,7 +3139,7 @@ function looksLikePromptDelta(chunk) {
|
|
|
3116
3139
|
const text = chunk.includes("\x1B") ? chunk.replace(ANSI_ESCAPE_REGEX, "") : chunk;
|
|
3117
3140
|
return (
|
|
3118
3141
|
// Approval UI signatures
|
|
3119
|
-
/Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(text) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(text) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /^\s*(?:[❯>]\s*)?\?\s+(?!for\s+(?:shortcuts|help|commands))\S/im.test(text) || /Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:/i.test(text) || /\benter\s+(?:to\s+)?(?:select|submit(?:\s+all)?|next)\b/i.test(text) || /\b(?:navigate|use\s+arrow\s+keys)\b/i.test(text) || /\b\d+[\/:of\s]+\d+:\s+\S/i.test(text) || /\bSubmit(?:\s+All)?\b/i.test(text) || /(?:^|[\s·|│])(?:←|<-)\s*Back\b/i.test(text) || /^File access\s*$/im.test(text) || /^\s*Allow access to this file\?\s*$/im.test(text) || /(?:Yes, allow access|No, deny access)/i.test(text) || /(?:↑\/↓\s+Navigate|ctrl\+r\s+Review)/i.test(text) || /Our servers are experiencing/i.test(text) || /Error ID:\s*[0-9a-fA-F-]*/i.test(text) || /(?:⚠️|⚠\uFE0F?)\s+.*(?:high[- ]traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error|quota\b|upgrade your subscription|increase your limits|resets in\s+\d|credits balance is too low|out of credits|rate[- ]limit|capacity exhausted|exhausted your quota|internal error|currently unreachable|network issue connecting|authentication expired|authentication issue|terminated due to error|invalid output|invalid tool call)/i.test(text)
|
|
3142
|
+
/Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(text) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(text) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /^\s*(?:[❯>]\s*)?\?\s+(?!for\s+(?:shortcuts|help|commands))\S/im.test(text) || /Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:/i.test(text) || /\benter\s+(?:to\s+)?(?:select|submit(?:\s+all)?|next)\b/i.test(text) || /\b(?:navigate|use\s+arrow\s+keys)\b/i.test(text) || /\b\d+[\/:of\s]+\d+:\s+\S/i.test(text) || /\bSubmit(?:\s+All)?\b/i.test(text) || /(?:^|[\s·|│])(?:←|<-)\s*Back\b/i.test(text) || /^File access\s*$/im.test(text) || /^\s*Allow access to this file\?\s*$/im.test(text) || /(?:Yes, allow access|No, deny access)/i.test(text) || /(?:↑\/↓\s+Navigate|ctrl\+r\s+Review)/i.test(text) || /^(?:Pending edit|Create file)\s*$/im.test(text) || /^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/im.test(text) || /(?:Yes, accept this change|No, reject this change|Yes, allow creation|No, deny creation)/i.test(text) || /(?:tab\s+Amend|f\s+full\s+diff)/i.test(text) || /Our servers are experiencing/i.test(text) || /Error ID:\s*[0-9a-fA-F-]*/i.test(text) || /(?:⚠️|⚠\uFE0F?)\s+.*(?:high[- ]traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error|quota\b|upgrade your subscription|increase your limits|resets in\s+\d|credits balance is too low|out of credits|rate[- ]limit|capacity exhausted|exhausted your quota|internal error|currently unreachable|network issue connecting|authentication expired|authentication issue|terminated due to error|invalid output|invalid tool call)/i.test(text)
|
|
3120
3143
|
);
|
|
3121
3144
|
}
|
|
3122
3145
|
function isNonSystemErrorWarningLine(trimmed) {
|
|
@@ -3152,7 +3175,7 @@ function isSystemErrorBannerLineInContext(lines, idx) {
|
|
|
3152
3175
|
if (/^[─━│╭╰╮╯\s_-]*$/.test(nextTrimmed)) break;
|
|
3153
3176
|
if (/^[│\s]*[❯>]/.test(nextTrimmed)) break;
|
|
3154
3177
|
if (/^(?:⚠️|⚠\uFE0F?)\s+/.test(nextTrimmed)) break;
|
|
3155
|
-
if (/Requesting permission for:|Question \d+\/\d+:|^\s*File access\s*$|^[>\s]*[1-9]\. \S/i.test(nextTrimmed)) break;
|
|
3178
|
+
if (/Requesting permission for:|Question \d+\/\d+:|^\s*(?:File access|Pending edit|Create file)\s*$|^[>\s]*[1-9]\. \S/i.test(nextTrimmed)) break;
|
|
3156
3179
|
const idMatch = nextTrimmed.match(/^Error ID:\s*([0-9a-fA-F-]*)$/i);
|
|
3157
3180
|
if (idMatch) {
|
|
3158
3181
|
if (/[0-9a-fA-F]/.test(idMatch[1])) return true;
|
|
@@ -3272,7 +3295,7 @@ function hasTrailingAgyComposer(lines, afterLineIdx, fenceMask) {
|
|
|
3272
3295
|
);
|
|
3273
3296
|
if (hasStatusLine) {
|
|
3274
3297
|
const nonDecorativeNonStatus = trailingLines.filter(
|
|
3275
|
-
(l) => !isAgyStatusFooterLine(l) && !isQuestionFooterLine(l) && !/^[─━│╭╰╮╯\s_-]{3,}$/.test(l)
|
|
3298
|
+
(l) => !isAgyStatusFooterLine(l) && !isQuestionFooterLine(l) && !/(?:↑\/↓|↑|↓)\s*Navigate\b.*\b(?:tab\s+Amend|f\s+full\s+diff|ctrl\+r\s+Review)\b/i.test(l) && !/^[─━│╭╰╮╯\s_-]{3,}$/.test(l)
|
|
3276
3299
|
);
|
|
3277
3300
|
if (nonDecorativeNonStatus.length > 0) {
|
|
3278
3301
|
return true;
|
|
@@ -3344,9 +3367,10 @@ function isSystemErrorActiveFromIndex(lines, errorIdx) {
|
|
|
3344
3367
|
if (/^●\s*(?:[A-Za-z_-]+)\(/.test(trimmed)) return false;
|
|
3345
3368
|
if (/Requesting permission for:/i.test(trimmed)) return false;
|
|
3346
3369
|
if (/Question \d+\/\d+:/i.test(trimmed)) return false;
|
|
3347
|
-
if (/^\s*File access\s*$/i.test(trimmed)) return false;
|
|
3370
|
+
if (/^\s*(?:File access|Pending edit|Create file)\s*$/i.test(trimmed)) return false;
|
|
3348
3371
|
if (/^[>\s]*[1-9]\. \S/.test(trimmed)) return false;
|
|
3349
3372
|
if (/Allow access to this file\?/i.test(trimmed)) return false;
|
|
3373
|
+
if (/^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(trimmed)) return false;
|
|
3350
3374
|
if (/Do you want to proceed\?/i.test(trimmed)) return false;
|
|
3351
3375
|
if (/\benter Select\b/i.test(trimmed)) return false;
|
|
3352
3376
|
if (/^Error ID:\s*[0-9a-fA-F-]*/i.test(trimmed)) {
|
|
@@ -3420,12 +3444,15 @@ function isLegacyYNActiveInLines(lines) {
|
|
|
3420
3444
|
return { active: true, lineIdx: ynIdx };
|
|
3421
3445
|
}
|
|
3422
3446
|
function looksLikePromptSnapshot(snapshot) {
|
|
3423
|
-
const stripped = snapshot
|
|
3447
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3424
3448
|
const activeKind = detectActivePromptKind(snapshot);
|
|
3425
3449
|
if (activeKind !== null) {
|
|
3426
3450
|
if (activeKind === "system_error") return true;
|
|
3427
3451
|
const recent = stripped.trimEnd().split("\n").slice(-30).join("\n");
|
|
3428
3452
|
if (activeKind === "approval") {
|
|
3453
|
+
if (extractFileEditPrompt(snapshot) !== null) {
|
|
3454
|
+
return true;
|
|
3455
|
+
}
|
|
3429
3456
|
const hasApprovalHeader = /Requesting permission for:/i.test(recent) || /Do you want to proceed\?/i.test(recent);
|
|
3430
3457
|
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(recent);
|
|
3431
3458
|
const hasApprovalFooter = /\besc to cancel\b/i.test(recent);
|
|
@@ -3562,14 +3589,333 @@ function extractFileAccessPrompt(snapshot) {
|
|
|
3562
3589
|
const fullLine = actionLine ? `Allow access to this file? (${actionLine})` : lines[questionIndex].trim();
|
|
3563
3590
|
return { identity, fullLine, body };
|
|
3564
3591
|
}
|
|
3592
|
+
function parseInlineDiffLine(rawLine) {
|
|
3593
|
+
if (/^[>\s❯]*[1-9]\d*[.)]\s+/.test(rawLine)) {
|
|
3594
|
+
return null;
|
|
3595
|
+
}
|
|
3596
|
+
const remMatch = rawLine.match(/^\s*(\d+)\s{1,3}-(?:\s{1,2}(.*)|$)/);
|
|
3597
|
+
if (remMatch) {
|
|
3598
|
+
return {
|
|
3599
|
+
type: "remove",
|
|
3600
|
+
lineNum: Number.parseInt(remMatch[1], 10),
|
|
3601
|
+
content: remMatch[2] ?? ""
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
const addMatch = rawLine.match(/^\s*(\d+)\s{1,3}\+(?:\s{1,2}(.*)|$)/);
|
|
3605
|
+
if (addMatch) {
|
|
3606
|
+
return {
|
|
3607
|
+
type: "add",
|
|
3608
|
+
lineNum: Number.parseInt(addMatch[1], 10),
|
|
3609
|
+
content: addMatch[2] ?? ""
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
const ctxMatch = rawLine.match(/^\s*(\d+)(?:\s{5}(.*)|\s{3,4}(.*)|\s*)$/);
|
|
3613
|
+
if (ctxMatch) {
|
|
3614
|
+
return {
|
|
3615
|
+
type: "context",
|
|
3616
|
+
lineNum: Number.parseInt(ctxMatch[1], 10),
|
|
3617
|
+
content: ctxMatch[2] ?? ctxMatch[3] ?? ""
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3620
|
+
return null;
|
|
3621
|
+
}
|
|
3622
|
+
function isFoldedDiffMarkerLine(trimmed) {
|
|
3623
|
+
return /^@@\s+\d+\s+lines\s+hidden\s+@@$/i.test(trimmed) || /^\(\d+\s+lines\s+hidden\)$/i.test(trimmed) || /^(?:…|\.\.\.)?\s*\d+\s+more\s+lines(?:\s*\(.*\))?$/i.test(trimmed);
|
|
3624
|
+
}
|
|
3625
|
+
function parseTargetStatSuffix(raw) {
|
|
3626
|
+
const m = raw.match(/^(.*?)\s+((?:\+(\d+)(?:\s+-(\d+))?|-(\d+)))\s*$/);
|
|
3627
|
+
if (!m) {
|
|
3628
|
+
return { cleanPath: raw.trim() };
|
|
3629
|
+
}
|
|
3630
|
+
const added = m[3] !== void 0 ? Number.parseInt(m[3], 10) : m[5] !== void 0 ? 0 : void 0;
|
|
3631
|
+
const removed = m[4] !== void 0 ? Number.parseInt(m[4], 10) : m[5] !== void 0 ? Number.parseInt(m[5], 10) : 0;
|
|
3632
|
+
return {
|
|
3633
|
+
cleanPath: m[1].trim(),
|
|
3634
|
+
stat: m[2].replace(/\s+/g, " ").trim(),
|
|
3635
|
+
added,
|
|
3636
|
+
removed
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
function extractFileEditTargetAndStat(line) {
|
|
3640
|
+
if (parseInlineDiffLine(line) !== null) return null;
|
|
3641
|
+
const trimmed = line.trim();
|
|
3642
|
+
if (!trimmed) return null;
|
|
3643
|
+
const actionMatch = trimmed.match(/^(?:New file|Create file|Delete file|Write|Edit|Create):\s*(\S.*)$/i);
|
|
3644
|
+
if (actionMatch) {
|
|
3645
|
+
const parsed2 = parseTargetStatSuffix(actionMatch[1]);
|
|
3646
|
+
if (!parsed2.cleanPath) return null;
|
|
3647
|
+
return {
|
|
3648
|
+
path: parsed2.cleanPath,
|
|
3649
|
+
stat: parsed2.stat,
|
|
3650
|
+
added: parsed2.added,
|
|
3651
|
+
removed: parsed2.removed
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
const parsed = parseTargetStatSuffix(trimmed);
|
|
3655
|
+
if (parsed.stat && parsed.cleanPath) {
|
|
3656
|
+
const candidatePath = parsed.cleanPath;
|
|
3657
|
+
if (!/^[+-]\d+$/.test(candidatePath) && !/^(?:Pending edit|Create file|File access|Question\b|Requesting permission\b)/i.test(candidatePath) && (candidatePath.includes("/") || candidatePath.startsWith("~") || candidatePath.startsWith(".") || /\.[a-z0-9]{1,10}$/i.test(candidatePath) || /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(candidatePath))) {
|
|
3658
|
+
return {
|
|
3659
|
+
path: candidatePath,
|
|
3660
|
+
stat: parsed.stat,
|
|
3661
|
+
added: parsed.added,
|
|
3662
|
+
removed: parsed.removed
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
return null;
|
|
3667
|
+
}
|
|
3668
|
+
function matchFileEditTargetLine(line) {
|
|
3669
|
+
return extractFileEditTargetAndStat(line)?.path ?? null;
|
|
3670
|
+
}
|
|
3671
|
+
function stitchWrappedPathAbove(lines, fromIndex, initialPath) {
|
|
3672
|
+
let stitched = initialPath;
|
|
3673
|
+
let topIndex = fromIndex;
|
|
3674
|
+
for (let prevIdx = fromIndex - 1; prevIdx >= Math.max(0, fromIndex - 3); prevIdx -= 1) {
|
|
3675
|
+
const prevRaw = lines[prevIdx];
|
|
3676
|
+
const prevTrimmed = prevRaw.trim();
|
|
3677
|
+
if (!prevTrimmed) break;
|
|
3678
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) break;
|
|
3679
|
+
if (/^(?:Pending edit|Create file|File access|Question\b|Requesting permission\b)/i.test(prevTrimmed)) break;
|
|
3680
|
+
if (parseInlineDiffLine(prevRaw) !== null || isFoldedDiffMarkerLine(prevTrimmed)) break;
|
|
3681
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(prevTrimmed) || /^[│\s]*[❯>]\s*$/.test(prevTrimmed)) break;
|
|
3682
|
+
const actionPrefix = prevTrimmed.match(/^(?:New file|Create file|Delete file|Write|Edit|Create):\s*(\S.*)$/i);
|
|
3683
|
+
if (actionPrefix) {
|
|
3684
|
+
stitched = `${actionPrefix[1].trim()}${stitched}`;
|
|
3685
|
+
topIndex = prevIdx;
|
|
3686
|
+
break;
|
|
3687
|
+
}
|
|
3688
|
+
const isValidPathSegment = !/\s/.test(prevTrimmed) && (prevTrimmed.includes("/") || prevTrimmed.startsWith("~") || prevTrimmed.startsWith(".") || stitched === "" && /\.[a-z0-9]{1,10}$/i.test(prevTrimmed));
|
|
3689
|
+
if (isValidPathSegment && (stitched === "" || prevTrimmed.length >= 40)) {
|
|
3690
|
+
stitched = `${prevTrimmed}${stitched}`;
|
|
3691
|
+
topIndex = prevIdx;
|
|
3692
|
+
if (stitched.startsWith("/") || stitched.startsWith("~/") || stitched.startsWith("./")) {
|
|
3693
|
+
break;
|
|
3694
|
+
}
|
|
3695
|
+
continue;
|
|
3696
|
+
}
|
|
3697
|
+
break;
|
|
3698
|
+
}
|
|
3699
|
+
return { path: stitched, topIndex };
|
|
3700
|
+
}
|
|
3701
|
+
function extractFileEditPrompt(snapshot) {
|
|
3702
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3703
|
+
const lines = stripped.trimEnd().split("\n");
|
|
3704
|
+
const lineInFence = computeFenceMask(lines);
|
|
3705
|
+
const windowStart = Math.max(0, lines.length - 30);
|
|
3706
|
+
let questionIndex = -1;
|
|
3707
|
+
for (let i = lines.length - 1; i >= windowStart; i -= 1) {
|
|
3708
|
+
if (lineInFence[i]) continue;
|
|
3709
|
+
if (/^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(lines[i]) && isStandaloneFileEditContext(lines, i, lineInFence)) {
|
|
3710
|
+
questionIndex = i;
|
|
3711
|
+
break;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
if (questionIndex < 0) return null;
|
|
3715
|
+
const questionText = lines[questionIndex].trim();
|
|
3716
|
+
let headingIndex = -1;
|
|
3717
|
+
let headingText = null;
|
|
3718
|
+
let targetIndex = -1;
|
|
3719
|
+
let targetPath = null;
|
|
3720
|
+
let headerStat;
|
|
3721
|
+
let headerAdded;
|
|
3722
|
+
let headerRemoved;
|
|
3723
|
+
let topBodyIndex = questionIndex;
|
|
3724
|
+
const itemsReversed = [];
|
|
3725
|
+
let currentHunkReversed = [];
|
|
3726
|
+
for (let index = questionIndex - 1; index >= 0; index -= 1) {
|
|
3727
|
+
const rawLine = lines[index];
|
|
3728
|
+
const trimmed = rawLine.trim();
|
|
3729
|
+
if (!trimmed) continue;
|
|
3730
|
+
if (/^(?:Pending edit|Create file)$/i.test(trimmed)) {
|
|
3731
|
+
headingIndex = index;
|
|
3732
|
+
headingText = trimmed;
|
|
3733
|
+
topBodyIndex = index + 1;
|
|
3734
|
+
break;
|
|
3735
|
+
}
|
|
3736
|
+
if (/^[─━═_-]{3,}$/.test(trimmed)) {
|
|
3737
|
+
topBodyIndex = index + 1;
|
|
3738
|
+
for (let prevIdx = index - 1; prevIdx >= Math.max(0, index - 4); prevIdx -= 1) {
|
|
3739
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3740
|
+
if (!prevTrimmed) continue;
|
|
3741
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3742
|
+
headingIndex = prevIdx;
|
|
3743
|
+
headingText = prevTrimmed;
|
|
3744
|
+
}
|
|
3745
|
+
break;
|
|
3746
|
+
}
|
|
3747
|
+
break;
|
|
3748
|
+
}
|
|
3749
|
+
const diffLine = parseInlineDiffLine(rawLine);
|
|
3750
|
+
if (diffLine && targetIndex < 0) {
|
|
3751
|
+
currentHunkReversed.push(diffLine);
|
|
3752
|
+
topBodyIndex = index;
|
|
3753
|
+
continue;
|
|
3754
|
+
}
|
|
3755
|
+
if (isFoldedDiffMarkerLine(trimmed) && targetIndex < 0) {
|
|
3756
|
+
if (currentHunkReversed.length > 0) {
|
|
3757
|
+
itemsReversed.push({ kind: "hunk", lines: currentHunkReversed.reverse() });
|
|
3758
|
+
currentHunkReversed = [];
|
|
3759
|
+
}
|
|
3760
|
+
itemsReversed.push({ kind: "omitted", marker: trimmed });
|
|
3761
|
+
topBodyIndex = index;
|
|
3762
|
+
continue;
|
|
3763
|
+
}
|
|
3764
|
+
if (/^shift\+tab\s+to\s+auto-approve\s+file\s+(?:edits?|writes?)$/i.test(trimmed) || /^Reason:\s*\S/i.test(trimmed) || /^[⚠⚠️]\s*/.test(trimmed) || /^Esc disabled during file edits\b/i.test(trimmed) || /^>\s*Displayed as code block\.\s*Widen terminal to view inline\.$/i.test(trimmed) || /^\(no content\)$/i.test(trimmed)) {
|
|
3765
|
+
topBodyIndex = index;
|
|
3766
|
+
continue;
|
|
3767
|
+
}
|
|
3768
|
+
const extractedTarget = extractFileEditTargetAndStat(rawLine);
|
|
3769
|
+
let matchedTarget = extractedTarget?.path ?? null;
|
|
3770
|
+
let matchedStat = extractedTarget?.stat;
|
|
3771
|
+
let matchedAdded = extractedTarget?.added;
|
|
3772
|
+
let matchedRemoved = extractedTarget?.removed;
|
|
3773
|
+
let targetTopIdx = index;
|
|
3774
|
+
if (!matchedTarget && targetIndex < 0 && /^(?:\+\d+(?:\s+-\d+)?|-\d+)$/.test(trimmed) && index > 0) {
|
|
3775
|
+
const bareStat = parseTargetStatSuffix(`placeholder ${trimmed}`);
|
|
3776
|
+
const stitched = stitchWrappedPathAbove(lines, index, "");
|
|
3777
|
+
if (stitched.path) {
|
|
3778
|
+
matchedTarget = stitched.path;
|
|
3779
|
+
matchedStat = bareStat.stat;
|
|
3780
|
+
matchedAdded = bareStat.added;
|
|
3781
|
+
matchedRemoved = bareStat.removed;
|
|
3782
|
+
targetTopIdx = stitched.topIndex;
|
|
3783
|
+
}
|
|
3784
|
+
} else if (matchedTarget && targetIndex < 0 && !matchedTarget.startsWith("/") && !matchedTarget.startsWith("~/") && !matchedTarget.startsWith("./")) {
|
|
3785
|
+
const stitched = stitchWrappedPathAbove(lines, index, matchedTarget);
|
|
3786
|
+
matchedTarget = stitched.path;
|
|
3787
|
+
targetTopIdx = stitched.topIndex;
|
|
3788
|
+
}
|
|
3789
|
+
if (!matchedTarget && targetIndex < 0 && !/\s/.test(trimmed) && (trimmed.includes("/") || /\.[a-z0-9]{1,8}$/i.test(trimmed)) && !/^[─━═_-]{3,}$/.test(trimmed)) {
|
|
3790
|
+
for (let prevIdx = index - 1; prevIdx >= Math.max(0, index - 4); prevIdx -= 1) {
|
|
3791
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3792
|
+
if (!prevTrimmed) continue;
|
|
3793
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) continue;
|
|
3794
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3795
|
+
matchedTarget = trimmed;
|
|
3796
|
+
}
|
|
3797
|
+
break;
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
if (matchedTarget && targetIndex < 0) {
|
|
3801
|
+
targetIndex = targetTopIdx;
|
|
3802
|
+
targetPath = matchedTarget;
|
|
3803
|
+
headerStat = matchedStat;
|
|
3804
|
+
headerAdded = matchedAdded;
|
|
3805
|
+
headerRemoved = matchedRemoved;
|
|
3806
|
+
topBodyIndex = targetTopIdx;
|
|
3807
|
+
for (let prevIdx = targetTopIdx - 1; prevIdx >= Math.max(0, targetTopIdx - 6); prevIdx -= 1) {
|
|
3808
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3809
|
+
if (!prevTrimmed) continue;
|
|
3810
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) {
|
|
3811
|
+
continue;
|
|
3812
|
+
}
|
|
3813
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3814
|
+
headingIndex = prevIdx;
|
|
3815
|
+
headingText = prevTrimmed;
|
|
3816
|
+
}
|
|
3817
|
+
break;
|
|
3818
|
+
}
|
|
3819
|
+
break;
|
|
3820
|
+
}
|
|
3821
|
+
break;
|
|
3822
|
+
}
|
|
3823
|
+
if (currentHunkReversed.length > 0) {
|
|
3824
|
+
itemsReversed.push({ kind: "hunk", lines: currentHunkReversed.reverse() });
|
|
3825
|
+
}
|
|
3826
|
+
const items = itemsReversed.reverse();
|
|
3827
|
+
const hunks = items.filter((item) => item.kind === "hunk").map((item) => item.lines);
|
|
3828
|
+
const omittedMarkers = items.filter((item) => item.kind === "omitted").map((item) => item.marker);
|
|
3829
|
+
const visibleAdded = hunks.reduce(
|
|
3830
|
+
(sum, hunk) => sum + hunk.filter((l) => l.type === "add").length,
|
|
3831
|
+
0
|
|
3832
|
+
);
|
|
3833
|
+
const visibleRemoved = hunks.reduce(
|
|
3834
|
+
(sum, hunk) => sum + hunk.filter((l) => l.type === "remove").length,
|
|
3835
|
+
0
|
|
3836
|
+
);
|
|
3837
|
+
const partialReasons = [];
|
|
3838
|
+
if (!targetPath && (hunks.length > 0 || omittedMarkers.length > 0)) {
|
|
3839
|
+
partialReasons.push("file header scrolled off-screen");
|
|
3840
|
+
}
|
|
3841
|
+
if (headerStat && (headerAdded !== void 0 && visibleAdded < headerAdded || headerRemoved !== void 0 && visibleRemoved < headerRemoved)) {
|
|
3842
|
+
partialReasons.push(
|
|
3843
|
+
`showing +${visibleAdded} -${visibleRemoved} of ${headerStat} total changes`
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
3846
|
+
const allOmissionNotes = [...partialReasons, ...omittedMarkers];
|
|
3847
|
+
const isPartial = allOmissionNotes.length > 0;
|
|
3848
|
+
const omissionNote = isPartial ? allOmissionNotes.join("; ") : void 0;
|
|
3849
|
+
const isCreate = /^Allow creation of this file\?$/i.test(questionText) || /^Create file$/i.test(headingText || "");
|
|
3850
|
+
const identity = targetPath || (isCreate ? "create file" : "file edit");
|
|
3851
|
+
const fullLine = targetPath ? `${questionText} (${targetPath})` : questionText;
|
|
3852
|
+
let body = "";
|
|
3853
|
+
if (hunks.length > 0 || omittedMarkers.length > 0) {
|
|
3854
|
+
const allAdditions = hunks.length > 0 && hunks.every((hunk) => hunk.every((l) => l.type === "add"));
|
|
3855
|
+
const diffOut = ["```diff"];
|
|
3856
|
+
if (targetPath) {
|
|
3857
|
+
const fromHeader = isCreate && allAdditions ? "--- /dev/null" : `--- a/${targetPath}`;
|
|
3858
|
+
const toHeader = `+++ b/${targetPath}`;
|
|
3859
|
+
diffOut.push(fromHeader, toHeader);
|
|
3860
|
+
}
|
|
3861
|
+
if (headerStat) {
|
|
3862
|
+
diffOut.push(`# diff-stat: ${headerStat}`);
|
|
3863
|
+
}
|
|
3864
|
+
for (const reason of partialReasons) {
|
|
3865
|
+
diffOut.push(`# partial-diff: ${reason}`);
|
|
3866
|
+
}
|
|
3867
|
+
for (const item of items) {
|
|
3868
|
+
if (item.kind === "omitted") {
|
|
3869
|
+
diffOut.push(`# omitted: ${item.marker}`);
|
|
3870
|
+
continue;
|
|
3871
|
+
}
|
|
3872
|
+
const hunk = item.lines;
|
|
3873
|
+
const oldCount = hunk.filter((l) => l.type === "remove" || l.type === "context").length;
|
|
3874
|
+
const newCount = hunk.filter((l) => l.type === "add" || l.type === "context").length;
|
|
3875
|
+
const firstOld = hunk.find((l) => l.type === "remove" || l.type === "context")?.lineNum ?? (oldCount === 0 ? 0 : 1);
|
|
3876
|
+
const firstNew = hunk.find((l) => l.type === "add" || l.type === "context")?.lineNum ?? (newCount === 0 ? 0 : 1);
|
|
3877
|
+
diffOut.push(`@@ -${firstOld},${oldCount} +${firstNew},${newCount} @@`);
|
|
3878
|
+
for (const l of hunk) {
|
|
3879
|
+
if (l.type === "remove") {
|
|
3880
|
+
diffOut.push(`-${l.content}`);
|
|
3881
|
+
} else if (l.type === "add") {
|
|
3882
|
+
diffOut.push(`+${l.content}`);
|
|
3883
|
+
} else {
|
|
3884
|
+
diffOut.push(` ${l.content}`);
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
diffOut.push("```");
|
|
3889
|
+
body = diffOut.join("\n");
|
|
3890
|
+
} else {
|
|
3891
|
+
const startIdx = headingIndex >= 0 ? headingIndex + 1 : topBodyIndex;
|
|
3892
|
+
const contextLines = lines.slice(startIdx, questionIndex).map((l) => l.trim()).filter((l) => {
|
|
3893
|
+
if (!l) return false;
|
|
3894
|
+
if (/^(?:Pending edit|Create file)$/i.test(l)) return false;
|
|
3895
|
+
if (/^[─━═_-]{3,}$/.test(l)) return false;
|
|
3896
|
+
if (/^shift\+tab\s+to\s+auto-approve\s+file\s+(?:edits?|writes?)$/i.test(l)) return false;
|
|
3897
|
+
return true;
|
|
3898
|
+
});
|
|
3899
|
+
body = contextLines.join("\n").trim();
|
|
3900
|
+
}
|
|
3901
|
+
return {
|
|
3902
|
+
identity,
|
|
3903
|
+
filePath: targetPath ?? void 0,
|
|
3904
|
+
fullLine,
|
|
3905
|
+
body,
|
|
3906
|
+
questionText,
|
|
3907
|
+
...headerStat ? { diffStat: headerStat } : {},
|
|
3908
|
+
...isPartial ? { partial: true, ...omissionNote ? { omissionNote } : {} } : {}
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3565
3911
|
function extractHeader(snapshot) {
|
|
3566
|
-
const stripped = snapshot
|
|
3912
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3567
3913
|
const matches = Array.from(stripped.matchAll(/Requesting permission for:\s*([^\n]+)/gi));
|
|
3568
3914
|
if (matches.length === 0) return null;
|
|
3569
3915
|
return matches[matches.length - 1][1].trim();
|
|
3570
3916
|
}
|
|
3571
3917
|
function extractFullHeaderLine(snapshot) {
|
|
3572
|
-
const stripped = snapshot
|
|
3918
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3573
3919
|
const matches = Array.from(stripped.matchAll(/Requesting permission for:\s*([^\n]+)/gi));
|
|
3574
3920
|
if (matches.length === 0) return null;
|
|
3575
3921
|
return matches[matches.length - 1][0].trim();
|
|
@@ -3605,7 +3951,7 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3605
3951
|
for (let k = boundaryIdx - 1; k >= checkTop; k--) {
|
|
3606
3952
|
const l = lines[k].trim();
|
|
3607
3953
|
if (!l) continue;
|
|
3608
|
-
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
3954
|
+
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
3609
3955
|
const sliceBetween = lines.slice(k + 1, boundaryIdx);
|
|
3610
3956
|
let optCount = 0;
|
|
3611
3957
|
let hasFooter = false;
|
|
@@ -3613,6 +3959,10 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3613
3959
|
for (const raw of sliceBetween) {
|
|
3614
3960
|
const s = raw.trim();
|
|
3615
3961
|
if (!s) continue;
|
|
3962
|
+
if (parseInlineDiffLine(raw) !== null || isFoldedDiffMarkerLine(raw)) {
|
|
3963
|
+
inDiff = true;
|
|
3964
|
+
continue;
|
|
3965
|
+
}
|
|
3616
3966
|
if (inDiff) {
|
|
3617
3967
|
if (/^[ +\-\\]/.test(raw)) {
|
|
3618
3968
|
continue;
|
|
@@ -3624,7 +3974,7 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3624
3974
|
inDiff = false;
|
|
3625
3975
|
continue;
|
|
3626
3976
|
}
|
|
3627
|
-
if (/^Do you want to proceed
|
|
3977
|
+
if (/^(?:Do you want to proceed\?|Accept this file edit\?|Allow creation of this file\?)/i.test(raw)) {
|
|
3628
3978
|
inDiff = false;
|
|
3629
3979
|
continue;
|
|
3630
3980
|
}
|
|
@@ -3656,7 +4006,7 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3656
4006
|
if (/\benter\s+Select\b/i.test(l) || /\benter\s+to\s+(?:select|submit)\b/i.test(l)) {
|
|
3657
4007
|
return false;
|
|
3658
4008
|
}
|
|
3659
|
-
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l)) {
|
|
4009
|
+
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l) || /^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(l)) {
|
|
3660
4010
|
return false;
|
|
3661
4011
|
}
|
|
3662
4012
|
if (/\b(?:allow|deny)\s+access\b/i.test(l)) {
|
|
@@ -3675,7 +4025,7 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3675
4025
|
for (let j = questionIdx - 1; j >= start; j--) {
|
|
3676
4026
|
const l = lines[j].trim();
|
|
3677
4027
|
if (!l) continue;
|
|
3678
|
-
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l) || /Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
4028
|
+
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l) || parseInlineDiffLine(lines[j]) !== null || /Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(l) || /^(?:Pending edit|Create file)$/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
3679
4029
|
return false;
|
|
3680
4030
|
}
|
|
3681
4031
|
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
@@ -3722,10 +4072,10 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3722
4072
|
if (/Question \d+\/\d+:/i.test(l)) {
|
|
3723
4073
|
return false;
|
|
3724
4074
|
}
|
|
3725
|
-
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l)) {
|
|
4075
|
+
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(l) || /^(?:Pending edit|Create file)$/i.test(l)) {
|
|
3726
4076
|
return false;
|
|
3727
4077
|
}
|
|
3728
|
-
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l)) {
|
|
4078
|
+
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l) || parseInlineDiffLine(lines[j]) !== null) {
|
|
3729
4079
|
return false;
|
|
3730
4080
|
}
|
|
3731
4081
|
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l) || /^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
@@ -3737,11 +4087,100 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3737
4087
|
}
|
|
3738
4088
|
return true;
|
|
3739
4089
|
}
|
|
4090
|
+
function isStandaloneFileEditContext(lines, questionIdx, fenceMask) {
|
|
4091
|
+
if (fenceMask && fenceMask[questionIdx]) {
|
|
4092
|
+
return false;
|
|
4093
|
+
}
|
|
4094
|
+
const windowEnd = Math.min(lines.length, questionIdx + 15);
|
|
4095
|
+
let optionCount = 0;
|
|
4096
|
+
let hasFileEditOption = false;
|
|
4097
|
+
let hasFooter = false;
|
|
4098
|
+
let promptEndIdx = questionIdx;
|
|
4099
|
+
for (let j = questionIdx + 1; j < windowEnd; j++) {
|
|
4100
|
+
if (fenceMask && fenceMask[j]) continue;
|
|
4101
|
+
const raw = lines[j];
|
|
4102
|
+
const l = raw.trim();
|
|
4103
|
+
if (!l) continue;
|
|
4104
|
+
if (/\benter\s+Select\b/i.test(l) || /\benter\s+to\s+(?:select|submit)\b/i.test(l)) {
|
|
4105
|
+
return false;
|
|
4106
|
+
}
|
|
4107
|
+
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l) || /Allow access to this file\?/i.test(l)) {
|
|
4108
|
+
return false;
|
|
4109
|
+
}
|
|
4110
|
+
if (isAgyStatusFooterLine(l) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(l) || isComposerBoxAt(lines, j)) {
|
|
4111
|
+
if (/^esc to cancel\b/i.test(l)) {
|
|
4112
|
+
hasFooter = true;
|
|
4113
|
+
}
|
|
4114
|
+
break;
|
|
4115
|
+
}
|
|
4116
|
+
if (optionCount > 0 && isResumedTurnSignal(l)) {
|
|
4117
|
+
break;
|
|
4118
|
+
}
|
|
4119
|
+
if (/^[>\s❯]*[1-9]\.\s+\S/.test(l)) {
|
|
4120
|
+
optionCount++;
|
|
4121
|
+
promptEndIdx = j;
|
|
4122
|
+
if (/\b(?:accept|reject|allow|deny|create|change|file|yes|no)\b/i.test(l)) {
|
|
4123
|
+
hasFileEditOption = true;
|
|
4124
|
+
}
|
|
4125
|
+
continue;
|
|
4126
|
+
}
|
|
4127
|
+
if (/^(?:↑\/↓|↑|↓)\s*Navigate\b.*\b(?:tab\s+Amend|f\s+full\s+diff|ctrl\+r\s+Review)\b/i.test(l) || /^esc to cancel\b/i.test(l)) {
|
|
4128
|
+
hasFooter = true;
|
|
4129
|
+
promptEndIdx = j;
|
|
4130
|
+
continue;
|
|
4131
|
+
}
|
|
4132
|
+
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
4133
|
+
break;
|
|
4134
|
+
}
|
|
4135
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l)) {
|
|
4136
|
+
break;
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
if (optionCount < 2 || !hasFileEditOption || !hasFooter) {
|
|
4140
|
+
return false;
|
|
4141
|
+
}
|
|
4142
|
+
if (hasTrailingAgyComposer(lines, promptEndIdx, fenceMask)) {
|
|
4143
|
+
return false;
|
|
4144
|
+
}
|
|
4145
|
+
const checkTop = Math.max(0, questionIdx - 120);
|
|
4146
|
+
let hasStructuralEditAnchor = false;
|
|
4147
|
+
for (let j = questionIdx - 1; j >= checkTop; j--) {
|
|
4148
|
+
const raw = lines[j];
|
|
4149
|
+
const l = raw.trim();
|
|
4150
|
+
if (!l) continue;
|
|
4151
|
+
if (/^shift\+tab\s+to\s+auto-approve\s+file\s+(?:edits?|writes?)$/i.test(l) || /^>\s*Displayed as code block\.\s*Widen terminal to view inline\.$/i.test(l) || /^\(no content\)$/i.test(l)) {
|
|
4152
|
+
hasStructuralEditAnchor = true;
|
|
4153
|
+
continue;
|
|
4154
|
+
}
|
|
4155
|
+
if (/^(?:Pending edit|Create file)$/i.test(l)) {
|
|
4156
|
+
hasStructuralEditAnchor = true;
|
|
4157
|
+
break;
|
|
4158
|
+
}
|
|
4159
|
+
if (parseInlineDiffLine(raw) !== null || isFoldedDiffMarkerLine(l)) {
|
|
4160
|
+
hasStructuralEditAnchor = true;
|
|
4161
|
+
continue;
|
|
4162
|
+
}
|
|
4163
|
+
if (/^Reason:\s*\S/i.test(l) || /^[⚠⚠️]\s*/.test(l) || /^Esc disabled during file edits\b/i.test(l)) {
|
|
4164
|
+
continue;
|
|
4165
|
+
}
|
|
4166
|
+
const targetCandidate = matchFileEditTargetLine(raw);
|
|
4167
|
+
if (targetCandidate) {
|
|
4168
|
+
hasStructuralEditAnchor = true;
|
|
4169
|
+
break;
|
|
4170
|
+
}
|
|
4171
|
+
if (hasStructuralEditAnchor) {
|
|
4172
|
+
break;
|
|
4173
|
+
}
|
|
4174
|
+
return false;
|
|
4175
|
+
}
|
|
4176
|
+
return hasStructuralEditAnchor;
|
|
4177
|
+
}
|
|
3740
4178
|
function detectActivePromptKind(snapshot) {
|
|
3741
|
-
const stripped = snapshot
|
|
4179
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3742
4180
|
const lines = stripped.trimEnd().split("\n");
|
|
3743
4181
|
const windowStart = Math.max(0, lines.length - 30);
|
|
3744
4182
|
const approvalRe = /Requesting permission for:/i;
|
|
4183
|
+
const fileEditQuestionRe = /^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i;
|
|
3745
4184
|
const fileAccessRe = /^\s*File access\s*$/i;
|
|
3746
4185
|
const fileAccessQuestionRe = /^\s*Allow access to this file\?\s*$/i;
|
|
3747
4186
|
const lineInFence = computeFenceMask(lines);
|
|
@@ -3752,8 +4191,10 @@ function detectActivePromptKind(snapshot) {
|
|
|
3752
4191
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3753
4192
|
const line = lines[i];
|
|
3754
4193
|
if (i >= windowStart) {
|
|
3755
|
-
if (
|
|
3756
|
-
if (
|
|
4194
|
+
if (lastApprovalLineIdx < 0 && !lineInFence[i]) {
|
|
4195
|
+
if (approvalRe.test(line)) {
|
|
4196
|
+
lastApprovalLineIdx = i;
|
|
4197
|
+
} else if (fileEditQuestionRe.test(line) && isStandaloneFileEditContext(lines, i, lineInFence)) {
|
|
3757
4198
|
lastApprovalLineIdx = i;
|
|
3758
4199
|
}
|
|
3759
4200
|
}
|
|
@@ -3828,7 +4269,9 @@ function detectActivePromptKind(snapshot) {
|
|
|
3828
4269
|
}
|
|
3829
4270
|
} else if (latestChooserIdx === lastApprovalLineIdx) {
|
|
3830
4271
|
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(remainingLines);
|
|
3831
|
-
const hasApprovalFooter = /\besc to cancel\b/i.test(
|
|
4272
|
+
const hasApprovalFooter = /\besc to cancel\b|(?:↑\/↓|↑|↓)\s*Navigate\b.*\b(?:tab\s+Amend|f\s+full\s+diff|ctrl\+r\s+Review)\b/i.test(
|
|
4273
|
+
remainingLines
|
|
4274
|
+
);
|
|
3832
4275
|
if (hasApprovalOptions && hasApprovalFooter) {
|
|
3833
4276
|
return "approval";
|
|
3834
4277
|
}
|
|
@@ -4134,7 +4577,7 @@ function isDurationToken(word) {
|
|
|
4134
4577
|
return /^\d+[dhms](?:\d+[dhms])*$/i.test(word);
|
|
4135
4578
|
}
|
|
4136
4579
|
function extractSystemErrorPrompt(snapshot) {
|
|
4137
|
-
const stripped = snapshot
|
|
4580
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
4138
4581
|
const lines = stripped.split("\n");
|
|
4139
4582
|
let bannerIdx = -1;
|
|
4140
4583
|
let rawMessage = "";
|
|
@@ -4240,7 +4683,7 @@ function isQuestionFooterLine(line) {
|
|
|
4240
4683
|
return /^(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip)(?:\s*[·│,]\s*(?:enter|<enter>)\s+(?:to\s+)?(?:select|submit(?:\s+all)?))?(?:\s*[·│,]\s*ctrl\+[a-z0-9]+\s+[a-zA-Z]+)*$/i.test(trimmed) || /^(?:enter|<enter>)\s+(?:to\s+)?(?:select|submit(?:\s+all)?)(?:\s*[·│,]\s*(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip|back))?(?:\s*[·│,]\s*ctrl\+[a-z0-9]+\s+[a-zA-Z]+)*$/i.test(trimmed) || /^(?:space|<space>)\s+(?:to\s+)?(?:select|toggle)(?:\s*[·│,]\s*(?:enter|<enter>)\s+(?:to\s+)?(?:select|submit(?:\s+all)?))?(?:\s*[·│,]\s*(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip|back))?$/i.test(trimmed) || /^(?:↑\/↓|↑|↓|\^|v)\s*(?:\/|\s+)?Navigate(?:\s*[·│,]\s*(?:(?:→|->)\s*Next|(?:←|<-)\s*Back))*(?:\s*[·│,]\s*(?:enter|<enter>)?\s*(?:Select|Submit(?:\s+All)?|to select))(?:\s*[·│,]\s*(?:esc|<esc>)?\s*(?:Skip|Cancel|Back))?(?:\s*[·│,]\s*(?:(?:→|->)\s*Next|(?:←|<-)\s*Back))*(?:\s*[·│,]\s*ctrl\+[a-z0-9]+\s+[a-zA-Z]+)*$/i.test(trimmed) || /^(?:↑\/↓|↑|↓|\^|v)\s*(?:\/|\s+)?Navigate(?:\s*[·│,]\s*(?:esc|<esc>)?\s*(?:Skip|Cancel|Back))?$/i.test(trimmed) || /^(?:←|<-)\s*Back\s*[·│,]\s*(?:enter|<enter>)\s+(?:to\s+)?(?:select|submit(?:\s+all)?)(?:\s*[·│,]\s*(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip))?$/i.test(trimmed) || /^press\s+<?(?:enter|return|space)>?\s+to\s+select$/i.test(trimmed) || /^space\s+to\s+select$/i.test(trimmed) || /^(?:\(?use\s+arrow\s+keys(?:\s+to\s+navigate)?\)?)$/i.test(trimmed) || /^(?:[\u2800-\u28FF]+(?:\s*waiting(?:\.\.\.)?)?|waiting(?:\.\.\.)?)$/i.test(trimmed);
|
|
4241
4684
|
}
|
|
4242
4685
|
function extractQuestionHeader(snapshot) {
|
|
4243
|
-
const stripped = snapshot
|
|
4686
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
4244
4687
|
const lines = stripped.split("\n");
|
|
4245
4688
|
const lineInFence = computeFenceMask(lines);
|
|
4246
4689
|
const prefixRe = /^\s*(?:[❯>]\s*)?(?:\?\s*(?:Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)?|Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)/i;
|
|
@@ -4257,7 +4700,7 @@ function extractQuestionHeader(snapshot) {
|
|
|
4257
4700
|
return null;
|
|
4258
4701
|
}
|
|
4259
4702
|
function hashPromptSnapshot(snapshot) {
|
|
4260
|
-
let normalized = snapshot
|
|
4703
|
+
let normalized = stripAnsiAndExpandRep(snapshot).replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
4261
4704
|
if (detectActivePromptKind(normalized) === "system_error") {
|
|
4262
4705
|
normalized = normalized.replace(/\bResets\s+in\s+\d+[dhms](?:\s*\d+[dhms])*\.?/gi, "Resets in <duration>");
|
|
4263
4706
|
}
|
|
@@ -4304,14 +4747,37 @@ function parseApprovalSnapshot(snapshot) {
|
|
|
4304
4747
|
return parseSystemErrorSnapshot(stripped, snapshot, systemError);
|
|
4305
4748
|
}
|
|
4306
4749
|
}
|
|
4750
|
+
if (kind === "approval") {
|
|
4751
|
+
const lines = stripped.trimEnd().split("\n");
|
|
4752
|
+
const windowStart = Math.max(0, lines.length - 30);
|
|
4753
|
+
let lastCmdApprovalIdx = -1;
|
|
4754
|
+
let lastFileEditIdx = -1;
|
|
4755
|
+
for (let i = lines.length - 1; i >= windowStart; i -= 1) {
|
|
4756
|
+
const l = lines[i];
|
|
4757
|
+
if (lastCmdApprovalIdx < 0 && (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l))) {
|
|
4758
|
+
lastCmdApprovalIdx = i;
|
|
4759
|
+
}
|
|
4760
|
+
if (lastFileEditIdx < 0 && /^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(l)) {
|
|
4761
|
+
lastFileEditIdx = i;
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
if (lastFileEditIdx > lastCmdApprovalIdx) {
|
|
4765
|
+
const fileEdit = extractFileEditPrompt(stripped);
|
|
4766
|
+
if (fileEdit) {
|
|
4767
|
+
return parseFileEditSnapshot(stripped, snapshot, fileEdit);
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4307
4771
|
return parseApprovalUISnapshot(stripped, snapshot);
|
|
4308
4772
|
}
|
|
4309
4773
|
function buildApprovalSemanticKey(candidate) {
|
|
4310
4774
|
const normalize = (value) => (value ?? "").replace(/\s+/g, " ").trim();
|
|
4775
|
+
const rawBody = (candidate.body ?? "").replace(/\r\n/g, "\n");
|
|
4776
|
+
const isDiffBody = /```diff\b/i.test(rawBody) || /^(?:Accept this file edit\?|Allow creation of this file\?)/i.test(candidate.fullHeaderLine ?? "");
|
|
4311
4777
|
const payload = {
|
|
4312
4778
|
kind: candidate.kind,
|
|
4313
4779
|
headerText: normalize(candidate.headerText),
|
|
4314
|
-
body: normalize(
|
|
4780
|
+
body: isDiffBody ? rawBody.trim() : normalize(rawBody),
|
|
4315
4781
|
options: (candidate.options ?? []).map((option) => ({
|
|
4316
4782
|
number: option.number,
|
|
4317
4783
|
text: normalize(option.text)
|
|
@@ -4349,6 +4815,30 @@ function parseFileAccessSnapshot(stripped, originalSnapshot, prompt) {
|
|
|
4349
4815
|
paneHash: hashPromptSnapshot(originalSnapshot)
|
|
4350
4816
|
};
|
|
4351
4817
|
}
|
|
4818
|
+
function parseFileEditSnapshot(stripped, originalSnapshot, prompt) {
|
|
4819
|
+
const lines = stripped.split("\n");
|
|
4820
|
+
let questionIndex = -1;
|
|
4821
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
4822
|
+
if (/^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(lines[index])) {
|
|
4823
|
+
questionIndex = index;
|
|
4824
|
+
break;
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
const belowQuestion = questionIndex >= 0 ? lines.slice(questionIndex + 1).join("\n") : stripped;
|
|
4828
|
+
const options = parseOptions(belowQuestion);
|
|
4829
|
+
if (options.length < 2 || !hasNegativeApprovalOption(options)) return null;
|
|
4830
|
+
return {
|
|
4831
|
+
kind: "approval",
|
|
4832
|
+
headerText: prompt.identity,
|
|
4833
|
+
fullHeaderLine: prompt.fullLine,
|
|
4834
|
+
command: void 0,
|
|
4835
|
+
filePath: prompt.filePath,
|
|
4836
|
+
options,
|
|
4837
|
+
submitMap: buildApprovalSubmitMap(options),
|
|
4838
|
+
body: prompt.body,
|
|
4839
|
+
paneHash: hashPromptSnapshot(originalSnapshot)
|
|
4840
|
+
};
|
|
4841
|
+
}
|
|
4352
4842
|
function parseSystemErrorSnapshot(stripped, originalSnapshot, prompt) {
|
|
4353
4843
|
return {
|
|
4354
4844
|
kind: "system_error",
|
|
@@ -4478,7 +4968,7 @@ function buildQuestionSubmitMap(options) {
|
|
|
4478
4968
|
function isWriteInInputActive(paneText, expectedHeader, expectedStep, expectedBodyText) {
|
|
4479
4969
|
if (!paneText) return false;
|
|
4480
4970
|
if (detectActivePromptKind(paneText) !== "question") return false;
|
|
4481
|
-
const stripped = paneText
|
|
4971
|
+
const stripped = stripAnsiAndExpandRep(paneText);
|
|
4482
4972
|
const lines = stripped.trimEnd().split("\n");
|
|
4483
4973
|
const windowStart = Math.max(0, lines.length - 30);
|
|
4484
4974
|
let headerIdx = -1;
|
|
@@ -4611,12 +5101,13 @@ function extractIdentity(headerText) {
|
|
|
4611
5101
|
function guessToolTypeFromIdentity(c) {
|
|
4612
5102
|
if (c.kind === "question") return "ASK_QUESTION";
|
|
4613
5103
|
if (c.command) return "RUN_COMMAND";
|
|
4614
|
-
if (c.filePath
|
|
5104
|
+
if (c.filePath || c.headerText === "file edit" || c.headerText === "create file" || c.headerText === "file creation") {
|
|
5105
|
+
return "CODE_ACTION";
|
|
5106
|
+
}
|
|
4615
5107
|
return "GENERIC";
|
|
4616
5108
|
}
|
|
4617
|
-
var ANSI_ESCAPE_REGEX2 = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
4618
5109
|
function stripAnsi(s) {
|
|
4619
|
-
return s
|
|
5110
|
+
return stripAnsiAndExpandRep(s);
|
|
4620
5111
|
}
|
|
4621
5112
|
|
|
4622
5113
|
// src/event-mapper.ts
|
|
@@ -4806,6 +5297,10 @@ function hookToolNameToTranscriptType(hookName) {
|
|
|
4806
5297
|
switch (upper) {
|
|
4807
5298
|
case "LIST_DIR":
|
|
4808
5299
|
return "LIST_DIRECTORY";
|
|
5300
|
+
case "READ_FILE":
|
|
5301
|
+
return "VIEW_FILE";
|
|
5302
|
+
case "EDIT_FILE":
|
|
5303
|
+
case "WRITE_FILE":
|
|
4809
5304
|
case "REPLACE_FILE_CONTENT":
|
|
4810
5305
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
4811
5306
|
case "WRITE_TO_FILE":
|
|
@@ -4842,6 +5337,8 @@ function extractFilePathFromArgs(toolName, args) {
|
|
|
4842
5337
|
if (!args || typeof args !== "object") return void 0;
|
|
4843
5338
|
const a = args;
|
|
4844
5339
|
for (const key of [
|
|
5340
|
+
"Path",
|
|
5341
|
+
"AbsolutePath",
|
|
4845
5342
|
"TargetFile",
|
|
4846
5343
|
"FilePath",
|
|
4847
5344
|
"file_path",
|
|
@@ -5478,7 +5975,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5478
5975
|
toolType: matchingCall?.toolType ?? guessToolTypeFromIdentity(candidate),
|
|
5479
5976
|
command: matchingCall?.command ?? candidate.command,
|
|
5480
5977
|
filePath: matchingCall?.filePath ?? candidate.filePath,
|
|
5481
|
-
rawToolName: matchingCall?.rawToolName ?? (candidate.kind === "question" ? "ask_question" : candidate.command ?? candidate.filePath ?? "unknown"),
|
|
5978
|
+
rawToolName: matchingCall?.rawToolName ?? (candidate.kind === "question" ? "ask_question" : candidate.headerText === "create file" || candidate.headerText === "file creation" ? "write_file" : candidate.headerText === "file edit" ? "edit_file" : candidate.command ?? candidate.filePath ?? "unknown"),
|
|
5482
5979
|
questionText: synthLineMatch?.stitchedHeaderText ?? (candidate.kind === "question" && candidate.headerText ? candidate.headerText : matchingCall?.questionText),
|
|
5483
5980
|
// NOTE (Codex R3-F1 / Claude R3-F2): Pane options are authoritative ground truth
|
|
5484
5981
|
// that directly correspond to desktop keystrokes (submitMap). Transcript options
|
|
@@ -6800,8 +7297,11 @@ function friendlyHookToolName(rawName, resultType) {
|
|
|
6800
7297
|
return "LS";
|
|
6801
7298
|
case "GREP_SEARCH":
|
|
6802
7299
|
return "Grep";
|
|
7300
|
+
case "READ_FILE":
|
|
6803
7301
|
case "VIEW_FILE":
|
|
6804
7302
|
return "Read";
|
|
7303
|
+
case "EDIT_FILE":
|
|
7304
|
+
case "WRITE_FILE":
|
|
6805
7305
|
case "WRITE_TO_FILE":
|
|
6806
7306
|
case "REPLACE_FILE_CONTENT":
|
|
6807
7307
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
@@ -9494,14 +9994,19 @@ var McpServer = class _McpServer {
|
|
|
9494
9994
|
});
|
|
9495
9995
|
}
|
|
9496
9996
|
const toolName = diffParsed ? "Edit" : state.pendingCall?.toolType;
|
|
9497
|
-
const filePath =
|
|
9997
|
+
const filePath = state.pendingCall?.filePath ?? diffParsed?.filePath;
|
|
9498
9998
|
const toolInput = diffParsed ? {
|
|
9499
|
-
file_path:
|
|
9999
|
+
file_path: filePath,
|
|
9500
10000
|
old_string: diffParsed.oldString,
|
|
9501
10001
|
new_string: diffParsed.newString,
|
|
9502
10002
|
old_start_line: diffParsed.oldStartLine,
|
|
9503
10003
|
new_start_line: diffParsed.newStartLine,
|
|
9504
|
-
diff: diffParsed.rawDiff
|
|
10004
|
+
diff: diffParsed.rawDiff,
|
|
10005
|
+
...diffParsed.diffStat ? { diff_stat: diffParsed.diffStat } : {},
|
|
10006
|
+
...diffParsed.partial ? {
|
|
10007
|
+
partial: true,
|
|
10008
|
+
...diffParsed.omissionNote ? { omission_note: diffParsed.omissionNote } : {}
|
|
10009
|
+
} : {}
|
|
9505
10010
|
} : void 0;
|
|
9506
10011
|
const optionsForMobile = state.pendingCall ? {
|
|
9507
10012
|
promptId: state.promptId,
|
|
@@ -9535,7 +10040,8 @@ var McpServer = class _McpServer {
|
|
|
9535
10040
|
})()
|
|
9536
10041
|
} : {}
|
|
9537
10042
|
} : { promptId: state.promptId };
|
|
9538
|
-
const
|
|
10043
|
+
const baseContent = state.paneDisplayHeader || state.matchedPaneHeader || state.pendingCall?.command || state.pendingCall?.filePath || "approval requested";
|
|
10044
|
+
const content = diffParsed?.partial && diffParsed.omissionNote && !baseContent.includes(diffParsed.omissionNote) ? `${baseContent} \u2014 partial diff (${diffParsed.omissionNote})` : baseContent;
|
|
9539
10045
|
const input = {
|
|
9540
10046
|
sessionId: session.sessionId,
|
|
9541
10047
|
type: import_codevibe_core7.EventType.INTERACTIVE_PROMPT,
|
|
@@ -10451,6 +10957,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10451
10957
|
const rawDiff = fenceMatch[1];
|
|
10452
10958
|
const oldLines = [];
|
|
10453
10959
|
const newLines = [];
|
|
10960
|
+
const omissionNotes = [];
|
|
10961
|
+
let diffStat;
|
|
10962
|
+
let partial = false;
|
|
10454
10963
|
const hunkHeaderPattern = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
10455
10964
|
let currentOldLine = 0;
|
|
10456
10965
|
let currentNewLine = 0;
|
|
@@ -10465,6 +10974,17 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10465
10974
|
return p;
|
|
10466
10975
|
};
|
|
10467
10976
|
for (const line of rawDiff.split("\n")) {
|
|
10977
|
+
const statComment = line.match(/^#\s*diff-stat:\s*((?:\+\d+(?:\s+-\d+)?|-\d+))\s*$/i);
|
|
10978
|
+
if (statComment) {
|
|
10979
|
+
diffStat = statComment[1].replace(/\s+/g, " ").trim();
|
|
10980
|
+
continue;
|
|
10981
|
+
}
|
|
10982
|
+
const omissionComment = line.match(/^#\s*(?:partial-diff|omitted):\s*(.+)$/i);
|
|
10983
|
+
if (omissionComment) {
|
|
10984
|
+
partial = true;
|
|
10985
|
+
omissionNotes.push(omissionComment[1].trim());
|
|
10986
|
+
continue;
|
|
10987
|
+
}
|
|
10468
10988
|
const hunkMatch = line.match(hunkHeaderPattern);
|
|
10469
10989
|
if (hunkMatch) {
|
|
10470
10990
|
hunkCount += 1;
|
|
@@ -10472,12 +10992,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10472
10992
|
currentNewLine = Number.parseInt(hunkMatch[2], 10);
|
|
10473
10993
|
continue;
|
|
10474
10994
|
}
|
|
10475
|
-
if (line.startsWith("+++ ")) {
|
|
10995
|
+
if (hunkCount === 0 && line.startsWith("+++ ")) {
|
|
10476
10996
|
const pathVal = stripPrefix(line.slice(4));
|
|
10477
10997
|
if (pathVal) filePath = pathVal;
|
|
10478
10998
|
continue;
|
|
10479
10999
|
}
|
|
10480
|
-
if (line.startsWith("--- ")) {
|
|
11000
|
+
if (hunkCount === 0 && line.startsWith("--- ")) {
|
|
10481
11001
|
if (!filePath) {
|
|
10482
11002
|
const pathVal = stripPrefix(line.slice(4));
|
|
10483
11003
|
if (pathVal) filePath = pathVal;
|
|
@@ -10504,8 +11024,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10504
11024
|
oldStartLine = void 0;
|
|
10505
11025
|
newStartLine = void 0;
|
|
10506
11026
|
}
|
|
11027
|
+
const isFileEditHeader = /^(?:Accept this file edit\?|Allow creation of this file\?)/i.test(header ?? "");
|
|
10507
11028
|
if (!filePath) {
|
|
10508
|
-
const searchTargets = [body, header ?? ""];
|
|
11029
|
+
const searchTargets = isFileEditHeader ? [header ?? ""] : [body, header ?? ""];
|
|
10509
11030
|
for (const target of searchTargets) {
|
|
10510
11031
|
if (!target) continue;
|
|
10511
11032
|
const m = target.match(/\[.*?\]\(file:\/\/([^\s\)]+)\)/) || target.match(/file:\/\/([^\s\)]+)/);
|
|
@@ -10525,7 +11046,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10525
11046
|
newString: newLines.join("\n"),
|
|
10526
11047
|
oldStartLine,
|
|
10527
11048
|
newStartLine,
|
|
10528
|
-
rawDiff
|
|
11049
|
+
rawDiff,
|
|
11050
|
+
...diffStat ? { diffStat } : {},
|
|
11051
|
+
...partial ? {
|
|
11052
|
+
partial: true,
|
|
11053
|
+
...omissionNotes.length > 0 ? { omissionNote: omissionNotes.join("; ") } : {}
|
|
11054
|
+
} : {}
|
|
10529
11055
|
};
|
|
10530
11056
|
}
|
|
10531
11057
|
var __testing = {
|
|
@@ -10550,6 +11076,7 @@ var __testing = {
|
|
|
10550
11076
|
commandDigest,
|
|
10551
11077
|
detectActivePromptKind,
|
|
10552
11078
|
extractFileAccessPrompt,
|
|
11079
|
+
extractFileEditPrompt,
|
|
10553
11080
|
extractSystemErrorPrompt,
|
|
10554
11081
|
generateLaunchSessionId,
|
|
10555
11082
|
getActiveConversationFromCliLog,
|
|
@@ -10564,5 +11091,6 @@ var __testing = {
|
|
|
10564
11091
|
readTranscriptFirstType,
|
|
10565
11092
|
reconstructFooter,
|
|
10566
11093
|
runForeignSpawnIdle,
|
|
10567
|
-
safeIsoTimestamp
|
|
11094
|
+
safeIsoTimestamp,
|
|
11095
|
+
stripAnsiAndExpandRep
|
|
10568
11096
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.101",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"codevibe": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"node": ">=22.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@quantiya/codevibe-core": "^2.0.
|
|
49
|
+
"@quantiya/codevibe-core": "^2.0.89",
|
|
50
50
|
"chokidar": "^5.0.0",
|
|
51
51
|
"dotenv": "^16.6.1",
|
|
52
52
|
"express": "^5.1.0",
|