@quantiya/codevibe-antigravity-plugin 2.0.99 → 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 +686 -71
- 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",
|
|
@@ -4475,10 +4965,10 @@ function buildQuestionSubmitMap(options) {
|
|
|
4475
4965
|
}
|
|
4476
4966
|
return map;
|
|
4477
4967
|
}
|
|
4478
|
-
function isWriteInInputActive(paneText, expectedHeader, expectedStep) {
|
|
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;
|
|
@@ -4493,10 +4983,27 @@ function isWriteInInputActive(paneText, expectedHeader, expectedStep) {
|
|
|
4493
4983
|
const extracted = extractQuestionHeader(rawHeader);
|
|
4494
4984
|
if (!extracted) return false;
|
|
4495
4985
|
if (expectedHeader) {
|
|
4986
|
+
const normHeaderBlock = (s) => s.replace(/^[❯>\s*•-]+\s*/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
4496
4987
|
const expectedExtracted = extractQuestionHeader(expectedHeader);
|
|
4497
|
-
const
|
|
4498
|
-
const
|
|
4499
|
-
|
|
4988
|
+
const baseExpectedHeader = expectedExtracted ? expectedExtracted.body : expectedHeader;
|
|
4989
|
+
const expectedBodyLines = expectedBodyText ? expectedBodyText.split("\n").map((l) => l.trim()).filter(Boolean) : [];
|
|
4990
|
+
const allowedExpectedBodies = /* @__PURE__ */ new Set();
|
|
4991
|
+
for (let j = 0; j <= expectedBodyLines.length; j++) {
|
|
4992
|
+
allowedExpectedBodies.add(
|
|
4993
|
+
normHeaderBlock([baseExpectedHeader, ...expectedBodyLines.slice(0, j)].join(" "))
|
|
4994
|
+
);
|
|
4995
|
+
}
|
|
4996
|
+
const contLines = [];
|
|
4997
|
+
for (let j = headerIdx + 1; j < lines.length; j++) {
|
|
4998
|
+
const t = lines[j].trim();
|
|
4999
|
+
if (!t) continue;
|
|
5000
|
+
if (/^Your answer:\s*/i.test(t) || t.startsWith("(Enter to submit") || t.includes("Navigate") && t.includes("Select") || t.startsWith("\u2191/\u2193") || /^[❯>\s]*\d+\.\s+/.test(lines[j]) || /^[─━═-]{4,}$/.test(t)) {
|
|
5001
|
+
break;
|
|
5002
|
+
}
|
|
5003
|
+
contLines.push(t);
|
|
5004
|
+
}
|
|
5005
|
+
const paneBody = normHeaderBlock([extracted.body, ...contLines].join(" "));
|
|
5006
|
+
if (!allowedExpectedBodies.has(paneBody)) {
|
|
4500
5007
|
return false;
|
|
4501
5008
|
}
|
|
4502
5009
|
}
|
|
@@ -4594,12 +5101,13 @@ function extractIdentity(headerText) {
|
|
|
4594
5101
|
function guessToolTypeFromIdentity(c) {
|
|
4595
5102
|
if (c.kind === "question") return "ASK_QUESTION";
|
|
4596
5103
|
if (c.command) return "RUN_COMMAND";
|
|
4597
|
-
if (c.filePath
|
|
5104
|
+
if (c.filePath || c.headerText === "file edit" || c.headerText === "create file" || c.headerText === "file creation") {
|
|
5105
|
+
return "CODE_ACTION";
|
|
5106
|
+
}
|
|
4598
5107
|
return "GENERIC";
|
|
4599
5108
|
}
|
|
4600
|
-
var ANSI_ESCAPE_REGEX2 = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
4601
5109
|
function stripAnsi(s) {
|
|
4602
|
-
return s
|
|
5110
|
+
return stripAnsiAndExpandRep(s);
|
|
4603
5111
|
}
|
|
4604
5112
|
|
|
4605
5113
|
// src/event-mapper.ts
|
|
@@ -4789,6 +5297,10 @@ function hookToolNameToTranscriptType(hookName) {
|
|
|
4789
5297
|
switch (upper) {
|
|
4790
5298
|
case "LIST_DIR":
|
|
4791
5299
|
return "LIST_DIRECTORY";
|
|
5300
|
+
case "READ_FILE":
|
|
5301
|
+
return "VIEW_FILE";
|
|
5302
|
+
case "EDIT_FILE":
|
|
5303
|
+
case "WRITE_FILE":
|
|
4792
5304
|
case "REPLACE_FILE_CONTENT":
|
|
4793
5305
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
4794
5306
|
case "WRITE_TO_FILE":
|
|
@@ -4825,6 +5337,8 @@ function extractFilePathFromArgs(toolName, args) {
|
|
|
4825
5337
|
if (!args || typeof args !== "object") return void 0;
|
|
4826
5338
|
const a = args;
|
|
4827
5339
|
for (const key of [
|
|
5340
|
+
"Path",
|
|
5341
|
+
"AbsolutePath",
|
|
4828
5342
|
"TargetFile",
|
|
4829
5343
|
"FilePath",
|
|
4830
5344
|
"file_path",
|
|
@@ -5059,6 +5573,33 @@ function questionsMatch(hookText, paneText) {
|
|
|
5059
5573
|
if (!h || !p) return false;
|
|
5060
5574
|
return h === p;
|
|
5061
5575
|
}
|
|
5576
|
+
function matchPaneQuestionLines(hookText, candidate) {
|
|
5577
|
+
const firstHeaderText = (candidate.headerText || candidate.fullHeaderLine || "").trim();
|
|
5578
|
+
const firstFullHeaderLine = (candidate.fullHeaderLine || candidate.headerText || "").trim();
|
|
5579
|
+
if (!hookText || !firstHeaderText) return null;
|
|
5580
|
+
const rawBodyLines = candidate.body ? candidate.body.split("\n") : [];
|
|
5581
|
+
for (let k = 0; k <= rawBodyLines.length; k++) {
|
|
5582
|
+
if (k > 0 && !rawBodyLines[k - 1].trim()) {
|
|
5583
|
+
break;
|
|
5584
|
+
}
|
|
5585
|
+
const consumedLines = rawBodyLines.slice(0, k).map((l) => l.trim()).filter(Boolean);
|
|
5586
|
+
const stitchedHeaderText = [firstHeaderText, ...consumedLines].join(" ").replace(/\s+/g, " ").trim();
|
|
5587
|
+
const stitchedFullHeaderLine = [firstFullHeaderLine, ...consumedLines].join(" ").replace(/\s+/g, " ").trim();
|
|
5588
|
+
if (questionsMatch(hookText, stitchedHeaderText) || questionsMatch(hookText, stitchedFullHeaderLine)) {
|
|
5589
|
+
const remainingBody = rawBodyLines.slice(k).join("\n").trim();
|
|
5590
|
+
return {
|
|
5591
|
+
matched: true,
|
|
5592
|
+
stitchedHeaderText,
|
|
5593
|
+
stitchedFullHeaderLine,
|
|
5594
|
+
remainingBody
|
|
5595
|
+
};
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
return null;
|
|
5599
|
+
}
|
|
5600
|
+
function paneCandidateMatchesQuestion(hookText, candidate) {
|
|
5601
|
+
return matchPaneQuestionLines(hookText, candidate) !== null;
|
|
5602
|
+
}
|
|
5062
5603
|
function correlatesQuestionInvocation(call, delivery) {
|
|
5063
5604
|
if (!call) return false;
|
|
5064
5605
|
const tn = (delivery.toolName ?? "").toLowerCase().trim();
|
|
@@ -5232,10 +5773,11 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5232
5773
|
const scoreStep = (stepIdx) => {
|
|
5233
5774
|
if (stepIdx < 0 || stepIdx >= sqs.length) return { score: -1, textMatch: false };
|
|
5234
5775
|
const targetQ = sqs[stepIdx];
|
|
5235
|
-
const textMatch =
|
|
5776
|
+
const textMatch = paneCandidateMatchesQuestion(targetQ.question, candidate);
|
|
5236
5777
|
if (!textMatch) return { score: -1, textMatch: false };
|
|
5237
|
-
const
|
|
5238
|
-
const
|
|
5778
|
+
const normOpt = (s) => s.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
5779
|
+
const qOptTexts = (targetQ.options || []).map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
5780
|
+
const cOptTexts = (candidate.options || []).map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
5239
5781
|
if (cOptTexts.length > 0 && qOptTexts.length > 0) {
|
|
5240
5782
|
const hasTrailingWriteIn = cOptTexts.length === qOptTexts.length + 1 && /^\s*write-?in\b/i.test(cOptTexts[cOptTexts.length - 1]);
|
|
5241
5783
|
const cEffectiveTexts = hasTrailingWriteIn ? cOptTexts.slice(0, -1) : cOptTexts;
|
|
@@ -5279,6 +5821,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5279
5821
|
return null;
|
|
5280
5822
|
}
|
|
5281
5823
|
const targetStepQ = sqs[bestStepIdx];
|
|
5824
|
+
const stepLineMatch = matchPaneQuestionLines(targetStepQ?.question, candidate);
|
|
5282
5825
|
if (targetStepQ?.isMultiSelect) {
|
|
5283
5826
|
candidate.submitMap = {};
|
|
5284
5827
|
}
|
|
@@ -5298,9 +5841,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5298
5841
|
}
|
|
5299
5842
|
existing.paneSemanticKey = candidateSemanticKey;
|
|
5300
5843
|
existing.paneHash = candidate.paneHash;
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5844
|
+
existing.matchedPaneHeader = stepLineMatch?.stitchedHeaderText ?? candidate.headerText ?? existing.matchedPaneHeader;
|
|
5845
|
+
existing.paneDisplayHeader = stepLineMatch?.stitchedFullHeaderLine ?? candidate.fullHeaderLine ?? existing.paneDisplayHeader;
|
|
5846
|
+
existing.body = stepLineMatch ? stepLineMatch.remainingBody || void 0 : candidate.body;
|
|
5304
5847
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
5305
5848
|
logger.info("Upgraded hook-driven question prompt to actionable from pane observer", {
|
|
5306
5849
|
promptId: existing.promptId,
|
|
@@ -5329,7 +5872,16 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5329
5872
|
if (!existing.paneSemanticKey) {
|
|
5330
5873
|
existing.paneSemanticKey = candidateSemanticKey;
|
|
5331
5874
|
}
|
|
5332
|
-
if (
|
|
5875
|
+
if (stepLineMatch) {
|
|
5876
|
+
existing.matchedPaneHeader = stepLineMatch.stitchedHeaderText;
|
|
5877
|
+
existing.paneDisplayHeader = stepLineMatch.stitchedFullHeaderLine;
|
|
5878
|
+
existing.body = stepLineMatch.remainingBody || void 0;
|
|
5879
|
+
if (existing.pendingCall && targetStepQ?.question) {
|
|
5880
|
+
existing.pendingCall.questionText = stepLineMatch.stitchedHeaderText;
|
|
5881
|
+
}
|
|
5882
|
+
} else if (candidate.body) {
|
|
5883
|
+
existing.body = candidate.body;
|
|
5884
|
+
}
|
|
5333
5885
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
5334
5886
|
return null;
|
|
5335
5887
|
}
|
|
@@ -5352,7 +5904,8 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5352
5904
|
for (const p of [...this.pendingPrompts.values()]) {
|
|
5353
5905
|
if (p.conversationId === conversationId && p.kind === "question") {
|
|
5354
5906
|
const hookQuestionText = p.pendingCall?.questionText || p.matchedPaneHeader || p.paneDisplayHeader;
|
|
5355
|
-
const
|
|
5907
|
+
const lineMatch = matchPaneQuestionLines(hookQuestionText, candidate);
|
|
5908
|
+
const isSameQuestion = lineMatch !== null;
|
|
5356
5909
|
if (isSameQuestion) {
|
|
5357
5910
|
const targetQ = p.pendingCall?.structuredQuestions?.[p.wizardStepIndex ?? 0];
|
|
5358
5911
|
const isAuthoritativeMultiSelect = Boolean(targetQ?.isMultiSelect);
|
|
@@ -5368,9 +5921,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5368
5921
|
}
|
|
5369
5922
|
p.paneSemanticKey = candidateSemanticKey;
|
|
5370
5923
|
p.paneHash = candidate.paneHash;
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5924
|
+
p.matchedPaneHeader = lineMatch?.stitchedHeaderText ?? candidate.headerText ?? p.matchedPaneHeader;
|
|
5925
|
+
p.paneDisplayHeader = lineMatch?.stitchedFullHeaderLine ?? candidate.fullHeaderLine ?? p.paneDisplayHeader;
|
|
5926
|
+
p.body = lineMatch ? lineMatch.remainingBody || void 0 : candidate.body;
|
|
5374
5927
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
5375
5928
|
logger.info("Upgraded hook-driven question prompt to actionable from pane observer", {
|
|
5376
5929
|
promptId: p.promptId,
|
|
@@ -5382,6 +5935,14 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5382
5935
|
if (!p.paneSemanticKey) {
|
|
5383
5936
|
p.paneSemanticKey = candidateSemanticKey;
|
|
5384
5937
|
}
|
|
5938
|
+
if (lineMatch) {
|
|
5939
|
+
p.matchedPaneHeader = lineMatch.stitchedHeaderText;
|
|
5940
|
+
p.paneDisplayHeader = lineMatch.stitchedFullHeaderLine;
|
|
5941
|
+
p.body = lineMatch.remainingBody || void 0;
|
|
5942
|
+
if (p.pendingCall) {
|
|
5943
|
+
p.pendingCall.questionText = lineMatch.stitchedHeaderText;
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5385
5946
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
5386
5947
|
return null;
|
|
5387
5948
|
}
|
|
@@ -5400,6 +5961,8 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5400
5961
|
} else if (matchingCall) {
|
|
5401
5962
|
matchingCall.matchedPromptId = promptId;
|
|
5402
5963
|
}
|
|
5964
|
+
const matchedStepQuestionText = candidate.kind === "question" ? liveWizard?.call.structuredQuestions?.[liveWizard.activeStepIndex ?? 0]?.question ?? matchingCall?.structuredQuestions?.find((sq) => paneCandidateMatchesQuestion(sq.question, candidate))?.question ?? matchingCall?.questionText : void 0;
|
|
5965
|
+
const synthLineMatch = candidate.kind === "question" && matchedStepQuestionText ? matchPaneQuestionLines(matchedStepQuestionText, candidate) : null;
|
|
5403
5966
|
const syntheticCall = {
|
|
5404
5967
|
conversationId,
|
|
5405
5968
|
intentStepIndex: matchingCall?.intentStepIndex ?? -1,
|
|
@@ -5412,8 +5975,8 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5412
5975
|
toolType: matchingCall?.toolType ?? guessToolTypeFromIdentity(candidate),
|
|
5413
5976
|
command: matchingCall?.command ?? candidate.command,
|
|
5414
5977
|
filePath: matchingCall?.filePath ?? candidate.filePath,
|
|
5415
|
-
rawToolName: matchingCall?.rawToolName ?? (candidate.kind === "question" ? "ask_question" : candidate.command ?? candidate.filePath ?? "unknown"),
|
|
5416
|
-
questionText: candidate.kind === "question" && candidate.headerText ? candidate.headerText : matchingCall?.questionText,
|
|
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"),
|
|
5979
|
+
questionText: synthLineMatch?.stitchedHeaderText ?? (candidate.kind === "question" && candidate.headerText ? candidate.headerText : matchingCall?.questionText),
|
|
5417
5980
|
// NOTE (Codex R3-F1 / Claude R3-F2): Pane options are authoritative ground truth
|
|
5418
5981
|
// that directly correspond to desktop keystrokes (submitMap). Transcript options
|
|
5419
5982
|
// serve solely as fallback when pane options cannot be parsed.
|
|
@@ -5431,9 +5994,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5431
5994
|
conversationId,
|
|
5432
5995
|
pendingCall: syntheticCall,
|
|
5433
5996
|
submitMap: candidate.submitMap,
|
|
5434
|
-
matchedPaneHeader: candidate.headerText,
|
|
5435
|
-
paneDisplayHeader: candidate.fullHeaderLine,
|
|
5436
|
-
body: candidate.body,
|
|
5997
|
+
matchedPaneHeader: synthLineMatch?.stitchedHeaderText ?? candidate.headerText,
|
|
5998
|
+
paneDisplayHeader: synthLineMatch?.stitchedFullHeaderLine ?? candidate.fullHeaderLine,
|
|
5999
|
+
body: synthLineMatch ? synthLineMatch.remainingBody || void 0 : candidate.body,
|
|
5437
6000
|
paneOptions: candidate.options,
|
|
5438
6001
|
paneSemanticKey: buildApprovalSemanticKey(candidate),
|
|
5439
6002
|
kind: candidate.kind,
|
|
@@ -5471,8 +6034,19 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5471
6034
|
if (p.conversationId === call.conversationId && p.kind === "question") {
|
|
5472
6035
|
const existingText = p.pendingCall?.questionText || p.matchedPaneHeader || p.paneDisplayHeader;
|
|
5473
6036
|
const isSameInvocation = !p.pendingCall || p.pendingCall.intentStepIndex === -1 || call.intentStepIndex === -1 || p.pendingCall.intentStepIndex === call.intentStepIndex;
|
|
5474
|
-
|
|
6037
|
+
const hookLineMatch = matchPaneQuestionLines(questionText, {
|
|
6038
|
+
headerText: p.matchedPaneHeader,
|
|
6039
|
+
fullHeaderLine: p.paneDisplayHeader,
|
|
6040
|
+
body: p.body
|
|
6041
|
+
});
|
|
6042
|
+
if (isSameInvocation && (questionsMatch(existingText, questionText) || hookLineMatch !== null)) {
|
|
6043
|
+
if (hookLineMatch) {
|
|
6044
|
+
p.matchedPaneHeader = hookLineMatch.stitchedHeaderText;
|
|
6045
|
+
p.paneDisplayHeader = hookLineMatch.stitchedFullHeaderLine;
|
|
6046
|
+
p.body = hookLineMatch.remainingBody || void 0;
|
|
6047
|
+
}
|
|
5475
6048
|
if (p.pendingCall) {
|
|
6049
|
+
p.pendingCall.questionText = hookLineMatch?.stitchedHeaderText ?? call.questionText ?? p.pendingCall.questionText;
|
|
5476
6050
|
p.pendingCall.structuredQuestions = call.structuredQuestions;
|
|
5477
6051
|
if (call.intentStepIndex !== -1) {
|
|
5478
6052
|
p.pendingCall.intentStepIndex = call.intentStepIndex;
|
|
@@ -5485,10 +6059,20 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5485
6059
|
const isHookMultiSelect = Boolean(
|
|
5486
6060
|
call.isMultiSelect || call.structuredQuestions && call.structuredQuestions[0]?.isMultiSelect
|
|
5487
6061
|
);
|
|
5488
|
-
|
|
6062
|
+
const normOpt = (s) => s.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
6063
|
+
const hookRawOpts = call.structuredQuestions?.[0]?.options ?? call.questionOptions ?? [];
|
|
6064
|
+
const qOptTexts = hookRawOpts.map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
6065
|
+
const cOptTexts = (p.paneOptions || []).map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
6066
|
+
const hasTrailingWriteIn = cOptTexts.length === qOptTexts.length + 1 && /^\s*write-?in\b/i.test(cOptTexts[cOptTexts.length - 1]);
|
|
6067
|
+
const cEffectiveTexts = hasTrailingWriteIn ? cOptTexts.slice(0, -1) : cOptTexts;
|
|
6068
|
+
const hasStructuredQuestion = Boolean(call.structuredQuestions && call.structuredQuestions.length > 0);
|
|
6069
|
+
const hasHookOptionMismatch = cOptTexts.length > 0 && (hasStructuredQuestion && qOptTexts.length === 0 || qOptTexts.length > 0 && (cEffectiveTexts.length !== qOptTexts.length || qOptTexts.some((q) => !cEffectiveTexts.includes(q)) || cEffectiveTexts.some((c) => !qOptTexts.includes(c))));
|
|
6070
|
+
if ((isHookMultiSelect || hasHookOptionMismatch) && Object.keys(p.submitMap || {}).length > 0) {
|
|
5489
6071
|
p.submitMap = {};
|
|
5490
|
-
logger.info("emitHookQuestionPrompt: revoked actionable submitMap
|
|
5491
|
-
promptId: p.promptId
|
|
6072
|
+
logger.info("emitHookQuestionPrompt: revoked actionable submitMap due to multi-select or option mismatch", {
|
|
6073
|
+
promptId: p.promptId,
|
|
6074
|
+
isHookMultiSelect,
|
|
6075
|
+
hasHookOptionMismatch
|
|
5492
6076
|
});
|
|
5493
6077
|
this.emit("prompt-upgraded", p);
|
|
5494
6078
|
}
|
|
@@ -5892,7 +6476,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5892
6476
|
if (candidate.kind === "question") {
|
|
5893
6477
|
const questionCalls = Array.from(this.pendingCalls.values()).filter((c) => c.conversationId === conversationId).filter((c) => c.resultStepIndex === void 0).filter((c) => {
|
|
5894
6478
|
if (c.structuredQuestions && candidate.headerText) {
|
|
5895
|
-
const matchesStructured = c.structuredQuestions.some((sq) =>
|
|
6479
|
+
const matchesStructured = c.structuredQuestions.some((sq) => paneCandidateMatchesQuestion(sq.question, candidate));
|
|
5896
6480
|
if (matchesStructured) return true;
|
|
5897
6481
|
}
|
|
5898
6482
|
const associatedPromptId = c.matchedPromptId || c.promptId;
|
|
@@ -5904,9 +6488,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5904
6488
|
if (questionCalls.length > 0 && candidate.headerText) {
|
|
5905
6489
|
const textMatch = questionCalls.find((c) => {
|
|
5906
6490
|
if (!c.questionText) return false;
|
|
5907
|
-
if (
|
|
6491
|
+
if (paneCandidateMatchesQuestion(c.questionText, candidate)) return true;
|
|
5908
6492
|
if (c.structuredQuestions) {
|
|
5909
|
-
return c.structuredQuestions.some((sq) =>
|
|
6493
|
+
return c.structuredQuestions.some((sq) => paneCandidateMatchesQuestion(sq.question, candidate));
|
|
5910
6494
|
}
|
|
5911
6495
|
return false;
|
|
5912
6496
|
});
|
|
@@ -6139,7 +6723,7 @@ var PromptResponder = class {
|
|
|
6139
6723
|
deliveryPhase
|
|
6140
6724
|
};
|
|
6141
6725
|
}
|
|
6142
|
-
if (isQuestionPrompt && isWriteInInputActive(snapshot, state.matchedPaneHeader, expectedStep)) {
|
|
6726
|
+
if (isQuestionPrompt && (isWriteInInputActive(snapshot, state.matchedPaneHeader, expectedStep, state.body) || isWriteInInputActive(snapshot, void 0, expectedStep))) {
|
|
6143
6727
|
return {
|
|
6144
6728
|
ok: false,
|
|
6145
6729
|
reason: "write-in-already-active",
|
|
@@ -6199,7 +6783,7 @@ var PromptResponder = class {
|
|
|
6199
6783
|
await delay(100);
|
|
6200
6784
|
try {
|
|
6201
6785
|
const liveSnap = await this.paneObserver.captureSnapshot();
|
|
6202
|
-
if (liveSnap && isWriteInInputActive(liveSnap, state.matchedPaneHeader, expectedStep)) {
|
|
6786
|
+
if (liveSnap && isWriteInInputActive(liveSnap, state.matchedPaneHeader, expectedStep, state.body)) {
|
|
6203
6787
|
writeInReady = true;
|
|
6204
6788
|
break;
|
|
6205
6789
|
}
|
|
@@ -6237,7 +6821,7 @@ var PromptResponder = class {
|
|
|
6237
6821
|
if (this.paneObserver) {
|
|
6238
6822
|
try {
|
|
6239
6823
|
const postHookSnap = await this.paneObserver.captureSnapshot();
|
|
6240
|
-
if (!postHookSnap || !isWriteInInputActive(postHookSnap, state.matchedPaneHeader, expectedStep)) {
|
|
6824
|
+
if (!postHookSnap || !isWriteInInputActive(postHookSnap, state.matchedPaneHeader, expectedStep, state.body)) {
|
|
6241
6825
|
return {
|
|
6242
6826
|
ok: false,
|
|
6243
6827
|
reason: "prompt-probe-failed",
|
|
@@ -6266,7 +6850,7 @@ var PromptResponder = class {
|
|
|
6266
6850
|
if (this.paneObserver) {
|
|
6267
6851
|
try {
|
|
6268
6852
|
const preEnterSnap = await this.paneObserver.captureSnapshot();
|
|
6269
|
-
if (!preEnterSnap || !isWriteInInputActive(preEnterSnap, state.matchedPaneHeader, expectedStep)) {
|
|
6853
|
+
if (!preEnterSnap || !isWriteInInputActive(preEnterSnap, state.matchedPaneHeader, expectedStep, state.body)) {
|
|
6270
6854
|
return {
|
|
6271
6855
|
ok: false,
|
|
6272
6856
|
reason: "prompt-probe-failed",
|
|
@@ -6713,8 +7297,11 @@ function friendlyHookToolName(rawName, resultType) {
|
|
|
6713
7297
|
return "LS";
|
|
6714
7298
|
case "GREP_SEARCH":
|
|
6715
7299
|
return "Grep";
|
|
7300
|
+
case "READ_FILE":
|
|
6716
7301
|
case "VIEW_FILE":
|
|
6717
7302
|
return "Read";
|
|
7303
|
+
case "EDIT_FILE":
|
|
7304
|
+
case "WRITE_FILE":
|
|
6718
7305
|
case "WRITE_TO_FILE":
|
|
6719
7306
|
case "REPLACE_FILE_CONTENT":
|
|
6720
7307
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
@@ -9407,14 +9994,19 @@ var McpServer = class _McpServer {
|
|
|
9407
9994
|
});
|
|
9408
9995
|
}
|
|
9409
9996
|
const toolName = diffParsed ? "Edit" : state.pendingCall?.toolType;
|
|
9410
|
-
const filePath =
|
|
9997
|
+
const filePath = state.pendingCall?.filePath ?? diffParsed?.filePath;
|
|
9411
9998
|
const toolInput = diffParsed ? {
|
|
9412
|
-
file_path:
|
|
9999
|
+
file_path: filePath,
|
|
9413
10000
|
old_string: diffParsed.oldString,
|
|
9414
10001
|
new_string: diffParsed.newString,
|
|
9415
10002
|
old_start_line: diffParsed.oldStartLine,
|
|
9416
10003
|
new_start_line: diffParsed.newStartLine,
|
|
9417
|
-
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
|
+
} : {}
|
|
9418
10010
|
} : void 0;
|
|
9419
10011
|
const optionsForMobile = state.pendingCall ? {
|
|
9420
10012
|
promptId: state.promptId,
|
|
@@ -9448,7 +10040,8 @@ var McpServer = class _McpServer {
|
|
|
9448
10040
|
})()
|
|
9449
10041
|
} : {}
|
|
9450
10042
|
} : { promptId: state.promptId };
|
|
9451
|
-
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;
|
|
9452
10045
|
const input = {
|
|
9453
10046
|
sessionId: session.sessionId,
|
|
9454
10047
|
type: import_codevibe_core7.EventType.INTERACTIVE_PROMPT,
|
|
@@ -10364,6 +10957,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10364
10957
|
const rawDiff = fenceMatch[1];
|
|
10365
10958
|
const oldLines = [];
|
|
10366
10959
|
const newLines = [];
|
|
10960
|
+
const omissionNotes = [];
|
|
10961
|
+
let diffStat;
|
|
10962
|
+
let partial = false;
|
|
10367
10963
|
const hunkHeaderPattern = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
10368
10964
|
let currentOldLine = 0;
|
|
10369
10965
|
let currentNewLine = 0;
|
|
@@ -10378,6 +10974,17 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10378
10974
|
return p;
|
|
10379
10975
|
};
|
|
10380
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
|
+
}
|
|
10381
10988
|
const hunkMatch = line.match(hunkHeaderPattern);
|
|
10382
10989
|
if (hunkMatch) {
|
|
10383
10990
|
hunkCount += 1;
|
|
@@ -10385,12 +10992,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10385
10992
|
currentNewLine = Number.parseInt(hunkMatch[2], 10);
|
|
10386
10993
|
continue;
|
|
10387
10994
|
}
|
|
10388
|
-
if (line.startsWith("+++ ")) {
|
|
10995
|
+
if (hunkCount === 0 && line.startsWith("+++ ")) {
|
|
10389
10996
|
const pathVal = stripPrefix(line.slice(4));
|
|
10390
10997
|
if (pathVal) filePath = pathVal;
|
|
10391
10998
|
continue;
|
|
10392
10999
|
}
|
|
10393
|
-
if (line.startsWith("--- ")) {
|
|
11000
|
+
if (hunkCount === 0 && line.startsWith("--- ")) {
|
|
10394
11001
|
if (!filePath) {
|
|
10395
11002
|
const pathVal = stripPrefix(line.slice(4));
|
|
10396
11003
|
if (pathVal) filePath = pathVal;
|
|
@@ -10417,8 +11024,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10417
11024
|
oldStartLine = void 0;
|
|
10418
11025
|
newStartLine = void 0;
|
|
10419
11026
|
}
|
|
11027
|
+
const isFileEditHeader = /^(?:Accept this file edit\?|Allow creation of this file\?)/i.test(header ?? "");
|
|
10420
11028
|
if (!filePath) {
|
|
10421
|
-
const searchTargets = [body, header ?? ""];
|
|
11029
|
+
const searchTargets = isFileEditHeader ? [header ?? ""] : [body, header ?? ""];
|
|
10422
11030
|
for (const target of searchTargets) {
|
|
10423
11031
|
if (!target) continue;
|
|
10424
11032
|
const m = target.match(/\[.*?\]\(file:\/\/([^\s\)]+)\)/) || target.match(/file:\/\/([^\s\)]+)/);
|
|
@@ -10438,7 +11046,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10438
11046
|
newString: newLines.join("\n"),
|
|
10439
11047
|
oldStartLine,
|
|
10440
11048
|
newStartLine,
|
|
10441
|
-
rawDiff
|
|
11049
|
+
rawDiff,
|
|
11050
|
+
...diffStat ? { diffStat } : {},
|
|
11051
|
+
...partial ? {
|
|
11052
|
+
partial: true,
|
|
11053
|
+
...omissionNotes.length > 0 ? { omissionNote: omissionNotes.join("; ") } : {}
|
|
11054
|
+
} : {}
|
|
10442
11055
|
};
|
|
10443
11056
|
}
|
|
10444
11057
|
var __testing = {
|
|
@@ -10463,6 +11076,7 @@ var __testing = {
|
|
|
10463
11076
|
commandDigest,
|
|
10464
11077
|
detectActivePromptKind,
|
|
10465
11078
|
extractFileAccessPrompt,
|
|
11079
|
+
extractFileEditPrompt,
|
|
10466
11080
|
extractSystemErrorPrompt,
|
|
10467
11081
|
generateLaunchSessionId,
|
|
10468
11082
|
getActiveConversationFromCliLog,
|
|
@@ -10477,5 +11091,6 @@ var __testing = {
|
|
|
10477
11091
|
readTranscriptFirstType,
|
|
10478
11092
|
reconstructFooter,
|
|
10479
11093
|
runForeignSpawnIdle,
|
|
10480
|
-
safeIsoTimestamp
|
|
11094
|
+
safeIsoTimestamp,
|
|
11095
|
+
stripAnsiAndExpandRep
|
|
10481
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",
|