@quantiya/codevibe-antigravity-plugin 2.0.100 → 2.0.102
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 +713 -48
- 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
|
}
|
|
@@ -2439,6 +2448,10 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2439
2448
|
* start()/stop() lifecycle and doesn't distinguish debounce
|
|
2440
2449
|
* cycles. (Stage 2 iter-4 R1 HIGH finding 2026-05-21.) */
|
|
2441
2450
|
this.debounceCycleId = 0;
|
|
2451
|
+
this.clearDebounceTimer = null;
|
|
2452
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2453
|
+
this.clearDebounceCycleId = 0;
|
|
2454
|
+
this.clearFlushInFlight = false;
|
|
2442
2455
|
this.pipeFilePath = options.pipeFilePath ?? path6.join(os5.tmpdir(), `codevibe-agy-pane-${process.pid}.log`);
|
|
2443
2456
|
this.skipTmuxCommands = options.skipTmuxCommands ?? false;
|
|
2444
2457
|
this.captureFn = options.captureFn ?? null;
|
|
@@ -2473,6 +2486,9 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2473
2486
|
* and they paint atomically. */
|
|
2474
2487
|
this.QUESTION_SETTLE_DELAY_MS = 100;
|
|
2475
2488
|
}
|
|
2489
|
+
static {
|
|
2490
|
+
this.CLEAR_CONFIRM_DELAY_MS = 50;
|
|
2491
|
+
}
|
|
2476
2492
|
/**
|
|
2477
2493
|
* Start observing the tmux pane targeted by `sessionName`. If sessionName
|
|
2478
2494
|
* is omitted, read $CODEVIBE_AGY_TMUX_TARGET from env (set by the
|
|
@@ -2638,7 +2654,8 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2638
2654
|
const err = extractSystemErrorPrompt(snapshot);
|
|
2639
2655
|
headerText = err ? err.message : null;
|
|
2640
2656
|
} else if (kind === "approval") {
|
|
2641
|
-
|
|
2657
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
2658
|
+
headerText = fileEdit ? fileEdit.identity : extractHeader(snapshot);
|
|
2642
2659
|
}
|
|
2643
2660
|
}
|
|
2644
2661
|
let isChooser = hasPrompt && kind !== "system_error";
|
|
@@ -2697,6 +2714,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2697
2714
|
* goes idle (e.g. an animated spinner inside the approval UI).
|
|
2698
2715
|
*/
|
|
2699
2716
|
scheduleDebouncedEmit(candidate) {
|
|
2717
|
+
this.cancelClearDebounce();
|
|
2700
2718
|
const now = Date.now();
|
|
2701
2719
|
this.pendingEmitCandidate = candidate;
|
|
2702
2720
|
if (this.debounceWindowOpenedAt === null) {
|
|
@@ -2744,7 +2762,10 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2744
2762
|
let candidate = staged;
|
|
2745
2763
|
try {
|
|
2746
2764
|
const freshSnapshot = await this.captureSnapshot();
|
|
2765
|
+
if (this.generation !== scheduledGeneration || !this.started) return;
|
|
2766
|
+
if (this.debounceCycleId !== scheduledCycleId) return;
|
|
2747
2767
|
if (freshSnapshot && looksLikePromptSnapshot(freshSnapshot)) {
|
|
2768
|
+
this.cancelClearDebounce();
|
|
2748
2769
|
candidate = {
|
|
2749
2770
|
rawDelta: staged.rawDelta,
|
|
2750
2771
|
snapshot: freshSnapshot,
|
|
@@ -2764,6 +2785,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2764
2785
|
const settledHeader = settled ? extractQuestionHeader(settled) : null;
|
|
2765
2786
|
const sameBody = !!firstHeader && !!settledHeader && firstHeader.body === settledHeader.body;
|
|
2766
2787
|
if (settled && looksLikePromptSnapshot(settled) && detectActivePromptKind(settled) === "question" && sameBody) {
|
|
2788
|
+
this.cancelClearDebounce();
|
|
2767
2789
|
candidate = {
|
|
2768
2790
|
rawDelta: staged.rawDelta,
|
|
2769
2791
|
snapshot: settled,
|
|
@@ -2781,7 +2803,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2781
2803
|
if (candidate.snapshotHash === this.lastPromptHash) return;
|
|
2782
2804
|
this.lastPromptHash = candidate.snapshotHash;
|
|
2783
2805
|
this.approvalUIActive = true;
|
|
2784
|
-
this.currentApprovalHeader = extractHeader(candidate.snapshot);
|
|
2806
|
+
this.currentApprovalHeader = extractFileEditPrompt(candidate.snapshot)?.identity ?? extractHeader(candidate.snapshot);
|
|
2785
2807
|
this.emit("prompt-candidate", candidate);
|
|
2786
2808
|
}
|
|
2787
2809
|
/**
|
|
@@ -2815,6 +2837,64 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2815
2837
|
this.pendingEmitCandidate = null;
|
|
2816
2838
|
this.debounceWindowOpenedAt = null;
|
|
2817
2839
|
this.debounceCycleId += 1;
|
|
2840
|
+
this.cancelClearDebounce();
|
|
2841
|
+
}
|
|
2842
|
+
cancelClearDebounce() {
|
|
2843
|
+
if (this.clearDebounceTimer) {
|
|
2844
|
+
clearTimeout(this.clearDebounceTimer);
|
|
2845
|
+
this.clearDebounceTimer = null;
|
|
2846
|
+
}
|
|
2847
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2848
|
+
this.clearFlushInFlight = false;
|
|
2849
|
+
this.clearDebounceCycleId += 1;
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Schedule a debounced check for whether the active prompt UI has genuinely
|
|
2853
|
+
* cleared from the pane. Ink full-viewport redraws write upper scrollback
|
|
2854
|
+
* chunks (`!looksLikePromptDelta(chunk)`) before repainting the bottom
|
|
2855
|
+
* chooser; capturing immediately mid-redraw would false-fire
|
|
2856
|
+
* `prompt-cleared` and cause the repainted chooser to re-emit a duplicate card.
|
|
2857
|
+
* Bounded by DEBOUNCE_MAX_WAIT_MS (600ms) so continuous post-approval output
|
|
2858
|
+
* never starves `prompt-cleared` (Claude R1-F2, Codex R1-F3).
|
|
2859
|
+
*/
|
|
2860
|
+
scheduleDebouncedClear() {
|
|
2861
|
+
if (!this.approvalUIActive || this.clearFlushInFlight) return;
|
|
2862
|
+
const now = Date.now();
|
|
2863
|
+
if (this.clearDebounceWindowOpenedAt === null) {
|
|
2864
|
+
this.clearDebounceWindowOpenedAt = now;
|
|
2865
|
+
}
|
|
2866
|
+
const elapsed = now - this.clearDebounceWindowOpenedAt;
|
|
2867
|
+
const cappedRemaining = Math.max(0, _TmuxPaneObserver.DEBOUNCE_MAX_WAIT_MS - elapsed);
|
|
2868
|
+
if (cappedRemaining === 0 && this.clearDebounceTimer !== null) {
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const waitMs = Math.min(_TmuxPaneObserver.DEBOUNCE_QUIET_MS, cappedRemaining);
|
|
2872
|
+
if (this.clearDebounceTimer) clearTimeout(this.clearDebounceTimer);
|
|
2873
|
+
this.clearDebounceCycleId += 1;
|
|
2874
|
+
const myGeneration = this.generation;
|
|
2875
|
+
const myCycleId = this.clearDebounceCycleId;
|
|
2876
|
+
this.clearDebounceTimer = setTimeout(() => {
|
|
2877
|
+
void this.flushDebouncedClear(myGeneration, myCycleId);
|
|
2878
|
+
}, waitMs);
|
|
2879
|
+
if (this.clearDebounceTimer.unref) this.clearDebounceTimer.unref();
|
|
2880
|
+
}
|
|
2881
|
+
async flushDebouncedClear(scheduledGeneration, scheduledCycleId) {
|
|
2882
|
+
if (this.generation !== scheduledGeneration || !this.started) return;
|
|
2883
|
+
if (this.clearDebounceCycleId !== scheduledCycleId) return;
|
|
2884
|
+
this.clearDebounceTimer = null;
|
|
2885
|
+
if (!this.approvalUIActive) {
|
|
2886
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
this.clearFlushInFlight = true;
|
|
2890
|
+
try {
|
|
2891
|
+
await this.checkPromptCleared(scheduledGeneration, scheduledCycleId);
|
|
2892
|
+
} finally {
|
|
2893
|
+
if (this.clearDebounceCycleId === scheduledCycleId) {
|
|
2894
|
+
this.clearFlushInFlight = false;
|
|
2895
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2818
2898
|
}
|
|
2819
2899
|
/**
|
|
2820
2900
|
* Public hook for tests / drivers: trigger a read+process cycle as if
|
|
@@ -2836,6 +2916,11 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2836
2916
|
this.debounceTimer = null;
|
|
2837
2917
|
await this.flushDebouncedEmit(this.generation, this.debounceCycleId);
|
|
2838
2918
|
}
|
|
2919
|
+
if (this.clearDebounceTimer) {
|
|
2920
|
+
clearTimeout(this.clearDebounceTimer);
|
|
2921
|
+
this.clearDebounceTimer = null;
|
|
2922
|
+
await this.flushDebouncedClear(this.generation, this.clearDebounceCycleId);
|
|
2923
|
+
}
|
|
2839
2924
|
}
|
|
2840
2925
|
async processFileChanges() {
|
|
2841
2926
|
if (this.processing) {
|
|
@@ -2853,7 +2938,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2853
2938
|
if (!chunk) continue;
|
|
2854
2939
|
if (!looksLikePromptDelta(chunk)) {
|
|
2855
2940
|
if (this.approvalUIActive) {
|
|
2856
|
-
|
|
2941
|
+
this.scheduleDebouncedClear();
|
|
2857
2942
|
}
|
|
2858
2943
|
continue;
|
|
2859
2944
|
}
|
|
@@ -2861,10 +2946,11 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2861
2946
|
if (this.generation !== myGeneration || !this.started) return;
|
|
2862
2947
|
if (!snapshot || !looksLikePromptSnapshot(snapshot)) {
|
|
2863
2948
|
if (this.approvalUIActive) {
|
|
2864
|
-
|
|
2949
|
+
this.scheduleDebouncedClear();
|
|
2865
2950
|
}
|
|
2866
2951
|
continue;
|
|
2867
2952
|
}
|
|
2953
|
+
this.cancelClearDebounce();
|
|
2868
2954
|
const snapshotHash = hashPromptSnapshot(snapshot);
|
|
2869
2955
|
if (snapshotHash === this.lastPromptHash) {
|
|
2870
2956
|
continue;
|
|
@@ -2888,13 +2974,31 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2888
2974
|
}
|
|
2889
2975
|
}
|
|
2890
2976
|
/** Called when we suspect the approval UI may have vanished. Does a
|
|
2891
|
-
* fresh capture; if the pane no longer shows approval UI
|
|
2892
|
-
* 'prompt-cleared'. Generation-checked
|
|
2893
|
-
|
|
2977
|
+
* fresh capture; if the pane no longer shows approval UI across two
|
|
2978
|
+
* consecutive captures, fires 'prompt-cleared'. Generation-checked
|
|
2979
|
+
* and cycle-checked to suppress emit after stop() or concurrent prompt
|
|
2980
|
+
* re-assertion (Claude R1-F3, Codex R1-F4). */
|
|
2981
|
+
async checkPromptCleared(myGeneration, expectedClearCycleId) {
|
|
2894
2982
|
try {
|
|
2895
2983
|
const snapshot = await this.captureSnapshot();
|
|
2896
2984
|
if (this.generation !== myGeneration || !this.started) return;
|
|
2897
|
-
if (
|
|
2985
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
if (looksLikePromptSnapshot(snapshot)) {
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
await sleep(_TmuxPaneObserver.CLEAR_CONFIRM_DELAY_MS);
|
|
2992
|
+
if (this.generation !== myGeneration || !this.started) return;
|
|
2993
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
const confirmSnapshot = await this.captureSnapshot();
|
|
2997
|
+
if (this.generation !== myGeneration || !this.started) return;
|
|
2998
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
if (!looksLikePromptSnapshot(confirmSnapshot)) {
|
|
2898
3002
|
this.approvalUIActive = false;
|
|
2899
3003
|
this.lastPromptHash = null;
|
|
2900
3004
|
this.currentApprovalHeader = null;
|
|
@@ -2944,6 +3048,19 @@ function isComposerBoxAt(lines, idx) {
|
|
|
2944
3048
|
return false;
|
|
2945
3049
|
}
|
|
2946
3050
|
var ANSI_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
3051
|
+
var ANSI_NON_REP_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-ac-~])/g;
|
|
3052
|
+
function stripAnsiAndExpandRep(input) {
|
|
3053
|
+
if (!input.includes("\x1B")) return input;
|
|
3054
|
+
const withoutNonRep = input.replace(ANSI_NON_REP_ESCAPE_REGEX, "");
|
|
3055
|
+
const expanded = withoutNonRep.replace(
|
|
3056
|
+
/([^\x00-\x1f\x7f])\x1B\[(\d*)b/gu,
|
|
3057
|
+
(_, ch, countStr) => {
|
|
3058
|
+
const count = Math.min(Math.max(Number.parseInt(countStr || "1", 10) || 1, 1), 512);
|
|
3059
|
+
return ch.repeat(1 + count);
|
|
3060
|
+
}
|
|
3061
|
+
);
|
|
3062
|
+
return expanded.replace(ANSI_ESCAPE_REGEX, "");
|
|
3063
|
+
}
|
|
2947
3064
|
function isPaneInputSafetyBlocked(input) {
|
|
2948
3065
|
const rawText = Array.isArray(input) ? input.join("\n") : input;
|
|
2949
3066
|
if (!rawText) return { blocked: false, kind: null };
|
|
@@ -2962,7 +3079,7 @@ function isPaneInputSafetyBlocked(input) {
|
|
|
2962
3079
|
if (isQuestionFooterLine(trimmed)) {
|
|
2963
3080
|
footerIdx = i;
|
|
2964
3081
|
break;
|
|
2965
|
-
} else if (/\besc to cancel\b/i.test(trimmed) && !isAgyStatusFooterLine(trimmed)) {
|
|
3082
|
+
} 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
3083
|
footerIdx = i;
|
|
2967
3084
|
break;
|
|
2968
3085
|
}
|
|
@@ -3048,7 +3165,7 @@ function isPaneInputSafetyBlocked(input) {
|
|
|
3048
3165
|
candidateHeaders.push({ lineIdx: i, kind: "question" });
|
|
3049
3166
|
} else if (/Allow access to this file\?/i.test(trimmed)) {
|
|
3050
3167
|
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)) {
|
|
3168
|
+
} 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
3169
|
candidateHeaders.push({ lineIdx: i, kind: "approval" });
|
|
3053
3170
|
}
|
|
3054
3171
|
}
|
|
@@ -3116,7 +3233,7 @@ function looksLikePromptDelta(chunk) {
|
|
|
3116
3233
|
const text = chunk.includes("\x1B") ? chunk.replace(ANSI_ESCAPE_REGEX, "") : chunk;
|
|
3117
3234
|
return (
|
|
3118
3235
|
// 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)
|
|
3236
|
+
/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
3237
|
);
|
|
3121
3238
|
}
|
|
3122
3239
|
function isNonSystemErrorWarningLine(trimmed) {
|
|
@@ -3152,7 +3269,7 @@ function isSystemErrorBannerLineInContext(lines, idx) {
|
|
|
3152
3269
|
if (/^[─━│╭╰╮╯\s_-]*$/.test(nextTrimmed)) break;
|
|
3153
3270
|
if (/^[│\s]*[❯>]/.test(nextTrimmed)) break;
|
|
3154
3271
|
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;
|
|
3272
|
+
if (/Requesting permission for:|Question \d+\/\d+:|^\s*(?:File access|Pending edit|Create file)\s*$|^[>\s]*[1-9]\. \S/i.test(nextTrimmed)) break;
|
|
3156
3273
|
const idMatch = nextTrimmed.match(/^Error ID:\s*([0-9a-fA-F-]*)$/i);
|
|
3157
3274
|
if (idMatch) {
|
|
3158
3275
|
if (/[0-9a-fA-F]/.test(idMatch[1])) return true;
|
|
@@ -3272,7 +3389,7 @@ function hasTrailingAgyComposer(lines, afterLineIdx, fenceMask) {
|
|
|
3272
3389
|
);
|
|
3273
3390
|
if (hasStatusLine) {
|
|
3274
3391
|
const nonDecorativeNonStatus = trailingLines.filter(
|
|
3275
|
-
(l) => !isAgyStatusFooterLine(l) && !isQuestionFooterLine(l) && !/^[─━│╭╰╮╯\s_-]{3,}$/.test(l)
|
|
3392
|
+
(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
3393
|
);
|
|
3277
3394
|
if (nonDecorativeNonStatus.length > 0) {
|
|
3278
3395
|
return true;
|
|
@@ -3344,9 +3461,10 @@ function isSystemErrorActiveFromIndex(lines, errorIdx) {
|
|
|
3344
3461
|
if (/^●\s*(?:[A-Za-z_-]+)\(/.test(trimmed)) return false;
|
|
3345
3462
|
if (/Requesting permission for:/i.test(trimmed)) return false;
|
|
3346
3463
|
if (/Question \d+\/\d+:/i.test(trimmed)) return false;
|
|
3347
|
-
if (/^\s*File access\s*$/i.test(trimmed)) return false;
|
|
3464
|
+
if (/^\s*(?:File access|Pending edit|Create file)\s*$/i.test(trimmed)) return false;
|
|
3348
3465
|
if (/^[>\s]*[1-9]\. \S/.test(trimmed)) return false;
|
|
3349
3466
|
if (/Allow access to this file\?/i.test(trimmed)) return false;
|
|
3467
|
+
if (/^(?:Accept this file edit\?|Allow creation of this file\?)$/i.test(trimmed)) return false;
|
|
3350
3468
|
if (/Do you want to proceed\?/i.test(trimmed)) return false;
|
|
3351
3469
|
if (/\benter Select\b/i.test(trimmed)) return false;
|
|
3352
3470
|
if (/^Error ID:\s*[0-9a-fA-F-]*/i.test(trimmed)) {
|
|
@@ -3420,12 +3538,15 @@ function isLegacyYNActiveInLines(lines) {
|
|
|
3420
3538
|
return { active: true, lineIdx: ynIdx };
|
|
3421
3539
|
}
|
|
3422
3540
|
function looksLikePromptSnapshot(snapshot) {
|
|
3423
|
-
const stripped = snapshot
|
|
3541
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3424
3542
|
const activeKind = detectActivePromptKind(snapshot);
|
|
3425
3543
|
if (activeKind !== null) {
|
|
3426
3544
|
if (activeKind === "system_error") return true;
|
|
3427
3545
|
const recent = stripped.trimEnd().split("\n").slice(-30).join("\n");
|
|
3428
3546
|
if (activeKind === "approval") {
|
|
3547
|
+
if (extractFileEditPrompt(snapshot) !== null) {
|
|
3548
|
+
return true;
|
|
3549
|
+
}
|
|
3429
3550
|
const hasApprovalHeader = /Requesting permission for:/i.test(recent) || /Do you want to proceed\?/i.test(recent);
|
|
3430
3551
|
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(recent);
|
|
3431
3552
|
const hasApprovalFooter = /\besc to cancel\b/i.test(recent);
|
|
@@ -3562,14 +3683,333 @@ function extractFileAccessPrompt(snapshot) {
|
|
|
3562
3683
|
const fullLine = actionLine ? `Allow access to this file? (${actionLine})` : lines[questionIndex].trim();
|
|
3563
3684
|
return { identity, fullLine, body };
|
|
3564
3685
|
}
|
|
3686
|
+
function parseInlineDiffLine(rawLine) {
|
|
3687
|
+
if (/^[>\s❯]*[1-9]\d*[.)]\s+/.test(rawLine)) {
|
|
3688
|
+
return null;
|
|
3689
|
+
}
|
|
3690
|
+
const remMatch = rawLine.match(/^\s*(\d+)\s{1,3}-(?:\s{1,2}(.*)|$)/);
|
|
3691
|
+
if (remMatch) {
|
|
3692
|
+
return {
|
|
3693
|
+
type: "remove",
|
|
3694
|
+
lineNum: Number.parseInt(remMatch[1], 10),
|
|
3695
|
+
content: remMatch[2] ?? ""
|
|
3696
|
+
};
|
|
3697
|
+
}
|
|
3698
|
+
const addMatch = rawLine.match(/^\s*(\d+)\s{1,3}\+(?:\s{1,2}(.*)|$)/);
|
|
3699
|
+
if (addMatch) {
|
|
3700
|
+
return {
|
|
3701
|
+
type: "add",
|
|
3702
|
+
lineNum: Number.parseInt(addMatch[1], 10),
|
|
3703
|
+
content: addMatch[2] ?? ""
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
const ctxMatch = rawLine.match(/^\s*(\d+)(?:\s{5}(.*)|\s{3,4}(.*)|\s*)$/);
|
|
3707
|
+
if (ctxMatch) {
|
|
3708
|
+
return {
|
|
3709
|
+
type: "context",
|
|
3710
|
+
lineNum: Number.parseInt(ctxMatch[1], 10),
|
|
3711
|
+
content: ctxMatch[2] ?? ctxMatch[3] ?? ""
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
return null;
|
|
3715
|
+
}
|
|
3716
|
+
function isFoldedDiffMarkerLine(trimmed) {
|
|
3717
|
+
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);
|
|
3718
|
+
}
|
|
3719
|
+
function parseTargetStatSuffix(raw) {
|
|
3720
|
+
const m = raw.match(/^(.*?)\s+((?:\+(\d+)(?:\s+-(\d+))?|-(\d+)))\s*$/);
|
|
3721
|
+
if (!m) {
|
|
3722
|
+
return { cleanPath: raw.trim() };
|
|
3723
|
+
}
|
|
3724
|
+
const added = m[3] !== void 0 ? Number.parseInt(m[3], 10) : m[5] !== void 0 ? 0 : void 0;
|
|
3725
|
+
const removed = m[4] !== void 0 ? Number.parseInt(m[4], 10) : m[5] !== void 0 ? Number.parseInt(m[5], 10) : 0;
|
|
3726
|
+
return {
|
|
3727
|
+
cleanPath: m[1].trim(),
|
|
3728
|
+
stat: m[2].replace(/\s+/g, " ").trim(),
|
|
3729
|
+
added,
|
|
3730
|
+
removed
|
|
3731
|
+
};
|
|
3732
|
+
}
|
|
3733
|
+
function extractFileEditTargetAndStat(line) {
|
|
3734
|
+
if (parseInlineDiffLine(line) !== null) return null;
|
|
3735
|
+
const trimmed = line.trim();
|
|
3736
|
+
if (!trimmed) return null;
|
|
3737
|
+
const actionMatch = trimmed.match(/^(?:New file|Create file|Delete file|Write|Edit|Create):\s*(\S.*)$/i);
|
|
3738
|
+
if (actionMatch) {
|
|
3739
|
+
const parsed2 = parseTargetStatSuffix(actionMatch[1]);
|
|
3740
|
+
if (!parsed2.cleanPath) return null;
|
|
3741
|
+
return {
|
|
3742
|
+
path: parsed2.cleanPath,
|
|
3743
|
+
stat: parsed2.stat,
|
|
3744
|
+
added: parsed2.added,
|
|
3745
|
+
removed: parsed2.removed
|
|
3746
|
+
};
|
|
3747
|
+
}
|
|
3748
|
+
const parsed = parseTargetStatSuffix(trimmed);
|
|
3749
|
+
if (parsed.stat && parsed.cleanPath) {
|
|
3750
|
+
const candidatePath = parsed.cleanPath;
|
|
3751
|
+
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))) {
|
|
3752
|
+
return {
|
|
3753
|
+
path: candidatePath,
|
|
3754
|
+
stat: parsed.stat,
|
|
3755
|
+
added: parsed.added,
|
|
3756
|
+
removed: parsed.removed
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
return null;
|
|
3761
|
+
}
|
|
3762
|
+
function matchFileEditTargetLine(line) {
|
|
3763
|
+
return extractFileEditTargetAndStat(line)?.path ?? null;
|
|
3764
|
+
}
|
|
3765
|
+
function stitchWrappedPathAbove(lines, fromIndex, initialPath) {
|
|
3766
|
+
let stitched = initialPath;
|
|
3767
|
+
let topIndex = fromIndex;
|
|
3768
|
+
for (let prevIdx = fromIndex - 1; prevIdx >= Math.max(0, fromIndex - 3); prevIdx -= 1) {
|
|
3769
|
+
const prevRaw = lines[prevIdx];
|
|
3770
|
+
const prevTrimmed = prevRaw.trim();
|
|
3771
|
+
if (!prevTrimmed) break;
|
|
3772
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) break;
|
|
3773
|
+
if (/^(?:Pending edit|Create file|File access|Question\b|Requesting permission\b)/i.test(prevTrimmed)) break;
|
|
3774
|
+
if (parseInlineDiffLine(prevRaw) !== null || isFoldedDiffMarkerLine(prevTrimmed)) break;
|
|
3775
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(prevTrimmed) || /^[│\s]*[❯>]\s*$/.test(prevTrimmed)) break;
|
|
3776
|
+
const actionPrefix = prevTrimmed.match(/^(?:New file|Create file|Delete file|Write|Edit|Create):\s*(\S.*)$/i);
|
|
3777
|
+
if (actionPrefix) {
|
|
3778
|
+
stitched = `${actionPrefix[1].trim()}${stitched}`;
|
|
3779
|
+
topIndex = prevIdx;
|
|
3780
|
+
break;
|
|
3781
|
+
}
|
|
3782
|
+
const isValidPathSegment = !/\s/.test(prevTrimmed) && (prevTrimmed.includes("/") || prevTrimmed.startsWith("~") || prevTrimmed.startsWith(".") || stitched === "" && /\.[a-z0-9]{1,10}$/i.test(prevTrimmed));
|
|
3783
|
+
if (isValidPathSegment && (stitched === "" || prevTrimmed.length >= 40)) {
|
|
3784
|
+
stitched = `${prevTrimmed}${stitched}`;
|
|
3785
|
+
topIndex = prevIdx;
|
|
3786
|
+
if (stitched.startsWith("/") || stitched.startsWith("~/") || stitched.startsWith("./")) {
|
|
3787
|
+
break;
|
|
3788
|
+
}
|
|
3789
|
+
continue;
|
|
3790
|
+
}
|
|
3791
|
+
break;
|
|
3792
|
+
}
|
|
3793
|
+
return { path: stitched, topIndex };
|
|
3794
|
+
}
|
|
3795
|
+
function extractFileEditPrompt(snapshot) {
|
|
3796
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3797
|
+
const lines = stripped.trimEnd().split("\n");
|
|
3798
|
+
const lineInFence = computeFenceMask(lines);
|
|
3799
|
+
const windowStart = Math.max(0, lines.length - 30);
|
|
3800
|
+
let questionIndex = -1;
|
|
3801
|
+
for (let i = lines.length - 1; i >= windowStart; i -= 1) {
|
|
3802
|
+
if (lineInFence[i]) continue;
|
|
3803
|
+
if (/^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(lines[i]) && isStandaloneFileEditContext(lines, i, lineInFence)) {
|
|
3804
|
+
questionIndex = i;
|
|
3805
|
+
break;
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
if (questionIndex < 0) return null;
|
|
3809
|
+
const questionText = lines[questionIndex].trim();
|
|
3810
|
+
let headingIndex = -1;
|
|
3811
|
+
let headingText = null;
|
|
3812
|
+
let targetIndex = -1;
|
|
3813
|
+
let targetPath = null;
|
|
3814
|
+
let headerStat;
|
|
3815
|
+
let headerAdded;
|
|
3816
|
+
let headerRemoved;
|
|
3817
|
+
let topBodyIndex = questionIndex;
|
|
3818
|
+
const itemsReversed = [];
|
|
3819
|
+
let currentHunkReversed = [];
|
|
3820
|
+
for (let index = questionIndex - 1; index >= 0; index -= 1) {
|
|
3821
|
+
const rawLine = lines[index];
|
|
3822
|
+
const trimmed = rawLine.trim();
|
|
3823
|
+
if (!trimmed) continue;
|
|
3824
|
+
if (/^(?:Pending edit|Create file)$/i.test(trimmed)) {
|
|
3825
|
+
headingIndex = index;
|
|
3826
|
+
headingText = trimmed;
|
|
3827
|
+
topBodyIndex = index + 1;
|
|
3828
|
+
break;
|
|
3829
|
+
}
|
|
3830
|
+
if (/^[─━═_-]{3,}$/.test(trimmed)) {
|
|
3831
|
+
topBodyIndex = index + 1;
|
|
3832
|
+
for (let prevIdx = index - 1; prevIdx >= Math.max(0, index - 4); prevIdx -= 1) {
|
|
3833
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3834
|
+
if (!prevTrimmed) continue;
|
|
3835
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3836
|
+
headingIndex = prevIdx;
|
|
3837
|
+
headingText = prevTrimmed;
|
|
3838
|
+
}
|
|
3839
|
+
break;
|
|
3840
|
+
}
|
|
3841
|
+
break;
|
|
3842
|
+
}
|
|
3843
|
+
const diffLine = parseInlineDiffLine(rawLine);
|
|
3844
|
+
if (diffLine && targetIndex < 0) {
|
|
3845
|
+
currentHunkReversed.push(diffLine);
|
|
3846
|
+
topBodyIndex = index;
|
|
3847
|
+
continue;
|
|
3848
|
+
}
|
|
3849
|
+
if (isFoldedDiffMarkerLine(trimmed) && targetIndex < 0) {
|
|
3850
|
+
if (currentHunkReversed.length > 0) {
|
|
3851
|
+
itemsReversed.push({ kind: "hunk", lines: currentHunkReversed.reverse() });
|
|
3852
|
+
currentHunkReversed = [];
|
|
3853
|
+
}
|
|
3854
|
+
itemsReversed.push({ kind: "omitted", marker: trimmed });
|
|
3855
|
+
topBodyIndex = index;
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
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)) {
|
|
3859
|
+
topBodyIndex = index;
|
|
3860
|
+
continue;
|
|
3861
|
+
}
|
|
3862
|
+
const extractedTarget = extractFileEditTargetAndStat(rawLine);
|
|
3863
|
+
let matchedTarget = extractedTarget?.path ?? null;
|
|
3864
|
+
let matchedStat = extractedTarget?.stat;
|
|
3865
|
+
let matchedAdded = extractedTarget?.added;
|
|
3866
|
+
let matchedRemoved = extractedTarget?.removed;
|
|
3867
|
+
let targetTopIdx = index;
|
|
3868
|
+
if (!matchedTarget && targetIndex < 0 && /^(?:\+\d+(?:\s+-\d+)?|-\d+)$/.test(trimmed) && index > 0) {
|
|
3869
|
+
const bareStat = parseTargetStatSuffix(`placeholder ${trimmed}`);
|
|
3870
|
+
const stitched = stitchWrappedPathAbove(lines, index, "");
|
|
3871
|
+
if (stitched.path) {
|
|
3872
|
+
matchedTarget = stitched.path;
|
|
3873
|
+
matchedStat = bareStat.stat;
|
|
3874
|
+
matchedAdded = bareStat.added;
|
|
3875
|
+
matchedRemoved = bareStat.removed;
|
|
3876
|
+
targetTopIdx = stitched.topIndex;
|
|
3877
|
+
}
|
|
3878
|
+
} else if (matchedTarget && targetIndex < 0 && !matchedTarget.startsWith("/") && !matchedTarget.startsWith("~/") && !matchedTarget.startsWith("./")) {
|
|
3879
|
+
const stitched = stitchWrappedPathAbove(lines, index, matchedTarget);
|
|
3880
|
+
matchedTarget = stitched.path;
|
|
3881
|
+
targetTopIdx = stitched.topIndex;
|
|
3882
|
+
}
|
|
3883
|
+
if (!matchedTarget && targetIndex < 0 && !/\s/.test(trimmed) && (trimmed.includes("/") || /\.[a-z0-9]{1,8}$/i.test(trimmed)) && !/^[─━═_-]{3,}$/.test(trimmed)) {
|
|
3884
|
+
for (let prevIdx = index - 1; prevIdx >= Math.max(0, index - 4); prevIdx -= 1) {
|
|
3885
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3886
|
+
if (!prevTrimmed) continue;
|
|
3887
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) continue;
|
|
3888
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3889
|
+
matchedTarget = trimmed;
|
|
3890
|
+
}
|
|
3891
|
+
break;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
if (matchedTarget && targetIndex < 0) {
|
|
3895
|
+
targetIndex = targetTopIdx;
|
|
3896
|
+
targetPath = matchedTarget;
|
|
3897
|
+
headerStat = matchedStat;
|
|
3898
|
+
headerAdded = matchedAdded;
|
|
3899
|
+
headerRemoved = matchedRemoved;
|
|
3900
|
+
topBodyIndex = targetTopIdx;
|
|
3901
|
+
for (let prevIdx = targetTopIdx - 1; prevIdx >= Math.max(0, targetTopIdx - 6); prevIdx -= 1) {
|
|
3902
|
+
const prevTrimmed = lines[prevIdx].trim();
|
|
3903
|
+
if (!prevTrimmed) continue;
|
|
3904
|
+
if (/^[─━═_-]{3,}$/.test(prevTrimmed)) {
|
|
3905
|
+
continue;
|
|
3906
|
+
}
|
|
3907
|
+
if (/^(?:Pending edit|Create file)$/i.test(prevTrimmed)) {
|
|
3908
|
+
headingIndex = prevIdx;
|
|
3909
|
+
headingText = prevTrimmed;
|
|
3910
|
+
}
|
|
3911
|
+
break;
|
|
3912
|
+
}
|
|
3913
|
+
break;
|
|
3914
|
+
}
|
|
3915
|
+
break;
|
|
3916
|
+
}
|
|
3917
|
+
if (currentHunkReversed.length > 0) {
|
|
3918
|
+
itemsReversed.push({ kind: "hunk", lines: currentHunkReversed.reverse() });
|
|
3919
|
+
}
|
|
3920
|
+
const items = itemsReversed.reverse();
|
|
3921
|
+
const hunks = items.filter((item) => item.kind === "hunk").map((item) => item.lines);
|
|
3922
|
+
const omittedMarkers = items.filter((item) => item.kind === "omitted").map((item) => item.marker);
|
|
3923
|
+
const visibleAdded = hunks.reduce(
|
|
3924
|
+
(sum, hunk) => sum + hunk.filter((l) => l.type === "add").length,
|
|
3925
|
+
0
|
|
3926
|
+
);
|
|
3927
|
+
const visibleRemoved = hunks.reduce(
|
|
3928
|
+
(sum, hunk) => sum + hunk.filter((l) => l.type === "remove").length,
|
|
3929
|
+
0
|
|
3930
|
+
);
|
|
3931
|
+
const partialReasons = [];
|
|
3932
|
+
if (!targetPath && (hunks.length > 0 || omittedMarkers.length > 0)) {
|
|
3933
|
+
partialReasons.push("file header scrolled off-screen");
|
|
3934
|
+
}
|
|
3935
|
+
if (headerStat && (headerAdded !== void 0 && visibleAdded < headerAdded || headerRemoved !== void 0 && visibleRemoved < headerRemoved)) {
|
|
3936
|
+
partialReasons.push(
|
|
3937
|
+
`showing +${visibleAdded} -${visibleRemoved} of ${headerStat} total changes`
|
|
3938
|
+
);
|
|
3939
|
+
}
|
|
3940
|
+
const allOmissionNotes = [...partialReasons, ...omittedMarkers];
|
|
3941
|
+
const isPartial = allOmissionNotes.length > 0;
|
|
3942
|
+
const omissionNote = isPartial ? allOmissionNotes.join("; ") : void 0;
|
|
3943
|
+
const isCreate = /^Allow creation of this file\?$/i.test(questionText) || /^Create file$/i.test(headingText || "");
|
|
3944
|
+
const identity = targetPath || (isCreate ? "create file" : "file edit");
|
|
3945
|
+
const fullLine = targetPath ? `${questionText} (${targetPath})` : questionText;
|
|
3946
|
+
let body = "";
|
|
3947
|
+
if (hunks.length > 0 || omittedMarkers.length > 0) {
|
|
3948
|
+
const allAdditions = hunks.length > 0 && hunks.every((hunk) => hunk.every((l) => l.type === "add"));
|
|
3949
|
+
const diffOut = ["```diff"];
|
|
3950
|
+
if (targetPath) {
|
|
3951
|
+
const fromHeader = isCreate && allAdditions ? "--- /dev/null" : `--- a/${targetPath}`;
|
|
3952
|
+
const toHeader = `+++ b/${targetPath}`;
|
|
3953
|
+
diffOut.push(fromHeader, toHeader);
|
|
3954
|
+
}
|
|
3955
|
+
if (headerStat) {
|
|
3956
|
+
diffOut.push(`# diff-stat: ${headerStat}`);
|
|
3957
|
+
}
|
|
3958
|
+
for (const reason of partialReasons) {
|
|
3959
|
+
diffOut.push(`# partial-diff: ${reason}`);
|
|
3960
|
+
}
|
|
3961
|
+
for (const item of items) {
|
|
3962
|
+
if (item.kind === "omitted") {
|
|
3963
|
+
diffOut.push(`# omitted: ${item.marker}`);
|
|
3964
|
+
continue;
|
|
3965
|
+
}
|
|
3966
|
+
const hunk = item.lines;
|
|
3967
|
+
const oldCount = hunk.filter((l) => l.type === "remove" || l.type === "context").length;
|
|
3968
|
+
const newCount = hunk.filter((l) => l.type === "add" || l.type === "context").length;
|
|
3969
|
+
const firstOld = hunk.find((l) => l.type === "remove" || l.type === "context")?.lineNum ?? (oldCount === 0 ? 0 : 1);
|
|
3970
|
+
const firstNew = hunk.find((l) => l.type === "add" || l.type === "context")?.lineNum ?? (newCount === 0 ? 0 : 1);
|
|
3971
|
+
diffOut.push(`@@ -${firstOld},${oldCount} +${firstNew},${newCount} @@`);
|
|
3972
|
+
for (const l of hunk) {
|
|
3973
|
+
if (l.type === "remove") {
|
|
3974
|
+
diffOut.push(`-${l.content}`);
|
|
3975
|
+
} else if (l.type === "add") {
|
|
3976
|
+
diffOut.push(`+${l.content}`);
|
|
3977
|
+
} else {
|
|
3978
|
+
diffOut.push(` ${l.content}`);
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
diffOut.push("```");
|
|
3983
|
+
body = diffOut.join("\n");
|
|
3984
|
+
} else {
|
|
3985
|
+
const startIdx = headingIndex >= 0 ? headingIndex + 1 : topBodyIndex;
|
|
3986
|
+
const contextLines = lines.slice(startIdx, questionIndex).map((l) => l.trim()).filter((l) => {
|
|
3987
|
+
if (!l) return false;
|
|
3988
|
+
if (/^(?:Pending edit|Create file)$/i.test(l)) return false;
|
|
3989
|
+
if (/^[─━═_-]{3,}$/.test(l)) return false;
|
|
3990
|
+
if (/^shift\+tab\s+to\s+auto-approve\s+file\s+(?:edits?|writes?)$/i.test(l)) return false;
|
|
3991
|
+
return true;
|
|
3992
|
+
});
|
|
3993
|
+
body = contextLines.join("\n").trim();
|
|
3994
|
+
}
|
|
3995
|
+
return {
|
|
3996
|
+
identity,
|
|
3997
|
+
filePath: targetPath ?? void 0,
|
|
3998
|
+
fullLine,
|
|
3999
|
+
body,
|
|
4000
|
+
questionText,
|
|
4001
|
+
...headerStat ? { diffStat: headerStat } : {},
|
|
4002
|
+
...isPartial ? { partial: true, ...omissionNote ? { omissionNote } : {} } : {}
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
3565
4005
|
function extractHeader(snapshot) {
|
|
3566
|
-
const stripped = snapshot
|
|
4006
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3567
4007
|
const matches = Array.from(stripped.matchAll(/Requesting permission for:\s*([^\n]+)/gi));
|
|
3568
4008
|
if (matches.length === 0) return null;
|
|
3569
4009
|
return matches[matches.length - 1][1].trim();
|
|
3570
4010
|
}
|
|
3571
4011
|
function extractFullHeaderLine(snapshot) {
|
|
3572
|
-
const stripped = snapshot
|
|
4012
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3573
4013
|
const matches = Array.from(stripped.matchAll(/Requesting permission for:\s*([^\n]+)/gi));
|
|
3574
4014
|
if (matches.length === 0) return null;
|
|
3575
4015
|
return matches[matches.length - 1][0].trim();
|
|
@@ -3605,7 +4045,7 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3605
4045
|
for (let k = boundaryIdx - 1; k >= checkTop; k--) {
|
|
3606
4046
|
const l = lines[k].trim();
|
|
3607
4047
|
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)) {
|
|
4048
|
+
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
4049
|
const sliceBetween = lines.slice(k + 1, boundaryIdx);
|
|
3610
4050
|
let optCount = 0;
|
|
3611
4051
|
let hasFooter = false;
|
|
@@ -3613,6 +4053,10 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3613
4053
|
for (const raw of sliceBetween) {
|
|
3614
4054
|
const s = raw.trim();
|
|
3615
4055
|
if (!s) continue;
|
|
4056
|
+
if (parseInlineDiffLine(raw) !== null || isFoldedDiffMarkerLine(raw)) {
|
|
4057
|
+
inDiff = true;
|
|
4058
|
+
continue;
|
|
4059
|
+
}
|
|
3616
4060
|
if (inDiff) {
|
|
3617
4061
|
if (/^[ +\-\\]/.test(raw)) {
|
|
3618
4062
|
continue;
|
|
@@ -3624,7 +4068,7 @@ function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
|
3624
4068
|
inDiff = false;
|
|
3625
4069
|
continue;
|
|
3626
4070
|
}
|
|
3627
|
-
if (/^Do you want to proceed
|
|
4071
|
+
if (/^(?:Do you want to proceed\?|Accept this file edit\?|Allow creation of this file\?)/i.test(raw)) {
|
|
3628
4072
|
inDiff = false;
|
|
3629
4073
|
continue;
|
|
3630
4074
|
}
|
|
@@ -3656,7 +4100,7 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3656
4100
|
if (/\benter\s+Select\b/i.test(l) || /\benter\s+to\s+(?:select|submit)\b/i.test(l)) {
|
|
3657
4101
|
return false;
|
|
3658
4102
|
}
|
|
3659
|
-
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l)) {
|
|
4103
|
+
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
4104
|
return false;
|
|
3661
4105
|
}
|
|
3662
4106
|
if (/\b(?:allow|deny)\s+access\b/i.test(l)) {
|
|
@@ -3675,7 +4119,7 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3675
4119
|
for (let j = questionIdx - 1; j >= start; j--) {
|
|
3676
4120
|
const l = lines[j].trim();
|
|
3677
4121
|
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)) {
|
|
4122
|
+
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
4123
|
return false;
|
|
3680
4124
|
}
|
|
3681
4125
|
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
@@ -3722,10 +4166,10 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3722
4166
|
if (/Question \d+\/\d+:/i.test(l)) {
|
|
3723
4167
|
return false;
|
|
3724
4168
|
}
|
|
3725
|
-
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l)) {
|
|
4169
|
+
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
4170
|
return false;
|
|
3727
4171
|
}
|
|
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)) {
|
|
4172
|
+
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
4173
|
return false;
|
|
3730
4174
|
}
|
|
3731
4175
|
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l) || /^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
@@ -3737,11 +4181,100 @@ function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
|
3737
4181
|
}
|
|
3738
4182
|
return true;
|
|
3739
4183
|
}
|
|
4184
|
+
function isStandaloneFileEditContext(lines, questionIdx, fenceMask) {
|
|
4185
|
+
if (fenceMask && fenceMask[questionIdx]) {
|
|
4186
|
+
return false;
|
|
4187
|
+
}
|
|
4188
|
+
const windowEnd = Math.min(lines.length, questionIdx + 15);
|
|
4189
|
+
let optionCount = 0;
|
|
4190
|
+
let hasFileEditOption = false;
|
|
4191
|
+
let hasFooter = false;
|
|
4192
|
+
let promptEndIdx = questionIdx;
|
|
4193
|
+
for (let j = questionIdx + 1; j < windowEnd; j++) {
|
|
4194
|
+
if (fenceMask && fenceMask[j]) continue;
|
|
4195
|
+
const raw = lines[j];
|
|
4196
|
+
const l = raw.trim();
|
|
4197
|
+
if (!l) continue;
|
|
4198
|
+
if (/\benter\s+Select\b/i.test(l) || /\benter\s+to\s+(?:select|submit)\b/i.test(l)) {
|
|
4199
|
+
return false;
|
|
4200
|
+
}
|
|
4201
|
+
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l) || /Allow access to this file\?/i.test(l)) {
|
|
4202
|
+
return false;
|
|
4203
|
+
}
|
|
4204
|
+
if (isAgyStatusFooterLine(l) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(l) || isComposerBoxAt(lines, j)) {
|
|
4205
|
+
if (/^esc to cancel\b/i.test(l)) {
|
|
4206
|
+
hasFooter = true;
|
|
4207
|
+
}
|
|
4208
|
+
break;
|
|
4209
|
+
}
|
|
4210
|
+
if (optionCount > 0 && isResumedTurnSignal(l)) {
|
|
4211
|
+
break;
|
|
4212
|
+
}
|
|
4213
|
+
if (/^[>\s❯]*[1-9]\.\s+\S/.test(l)) {
|
|
4214
|
+
optionCount++;
|
|
4215
|
+
promptEndIdx = j;
|
|
4216
|
+
if (/\b(?:accept|reject|allow|deny|create|change|file|yes|no)\b/i.test(l)) {
|
|
4217
|
+
hasFileEditOption = true;
|
|
4218
|
+
}
|
|
4219
|
+
continue;
|
|
4220
|
+
}
|
|
4221
|
+
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)) {
|
|
4222
|
+
hasFooter = true;
|
|
4223
|
+
promptEndIdx = j;
|
|
4224
|
+
continue;
|
|
4225
|
+
}
|
|
4226
|
+
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
4227
|
+
break;
|
|
4228
|
+
}
|
|
4229
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l)) {
|
|
4230
|
+
break;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
if (optionCount < 2 || !hasFileEditOption || !hasFooter) {
|
|
4234
|
+
return false;
|
|
4235
|
+
}
|
|
4236
|
+
if (hasTrailingAgyComposer(lines, promptEndIdx, fenceMask)) {
|
|
4237
|
+
return false;
|
|
4238
|
+
}
|
|
4239
|
+
const checkTop = Math.max(0, questionIdx - 120);
|
|
4240
|
+
let hasStructuralEditAnchor = false;
|
|
4241
|
+
for (let j = questionIdx - 1; j >= checkTop; j--) {
|
|
4242
|
+
const raw = lines[j];
|
|
4243
|
+
const l = raw.trim();
|
|
4244
|
+
if (!l) continue;
|
|
4245
|
+
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)) {
|
|
4246
|
+
hasStructuralEditAnchor = true;
|
|
4247
|
+
continue;
|
|
4248
|
+
}
|
|
4249
|
+
if (/^(?:Pending edit|Create file)$/i.test(l)) {
|
|
4250
|
+
hasStructuralEditAnchor = true;
|
|
4251
|
+
break;
|
|
4252
|
+
}
|
|
4253
|
+
if (parseInlineDiffLine(raw) !== null || isFoldedDiffMarkerLine(l)) {
|
|
4254
|
+
hasStructuralEditAnchor = true;
|
|
4255
|
+
continue;
|
|
4256
|
+
}
|
|
4257
|
+
if (/^Reason:\s*\S/i.test(l) || /^[⚠⚠️]\s*/.test(l) || /^Esc disabled during file edits\b/i.test(l)) {
|
|
4258
|
+
continue;
|
|
4259
|
+
}
|
|
4260
|
+
const targetCandidate = matchFileEditTargetLine(raw);
|
|
4261
|
+
if (targetCandidate) {
|
|
4262
|
+
hasStructuralEditAnchor = true;
|
|
4263
|
+
break;
|
|
4264
|
+
}
|
|
4265
|
+
if (hasStructuralEditAnchor) {
|
|
4266
|
+
break;
|
|
4267
|
+
}
|
|
4268
|
+
return false;
|
|
4269
|
+
}
|
|
4270
|
+
return hasStructuralEditAnchor;
|
|
4271
|
+
}
|
|
3740
4272
|
function detectActivePromptKind(snapshot) {
|
|
3741
|
-
const stripped = snapshot
|
|
4273
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
3742
4274
|
const lines = stripped.trimEnd().split("\n");
|
|
3743
4275
|
const windowStart = Math.max(0, lines.length - 30);
|
|
3744
4276
|
const approvalRe = /Requesting permission for:/i;
|
|
4277
|
+
const fileEditQuestionRe = /^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i;
|
|
3745
4278
|
const fileAccessRe = /^\s*File access\s*$/i;
|
|
3746
4279
|
const fileAccessQuestionRe = /^\s*Allow access to this file\?\s*$/i;
|
|
3747
4280
|
const lineInFence = computeFenceMask(lines);
|
|
@@ -3752,8 +4285,10 @@ function detectActivePromptKind(snapshot) {
|
|
|
3752
4285
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3753
4286
|
const line = lines[i];
|
|
3754
4287
|
if (i >= windowStart) {
|
|
3755
|
-
if (
|
|
3756
|
-
if (
|
|
4288
|
+
if (lastApprovalLineIdx < 0 && !lineInFence[i]) {
|
|
4289
|
+
if (approvalRe.test(line)) {
|
|
4290
|
+
lastApprovalLineIdx = i;
|
|
4291
|
+
} else if (fileEditQuestionRe.test(line) && isStandaloneFileEditContext(lines, i, lineInFence)) {
|
|
3757
4292
|
lastApprovalLineIdx = i;
|
|
3758
4293
|
}
|
|
3759
4294
|
}
|
|
@@ -3828,7 +4363,9 @@ function detectActivePromptKind(snapshot) {
|
|
|
3828
4363
|
}
|
|
3829
4364
|
} else if (latestChooserIdx === lastApprovalLineIdx) {
|
|
3830
4365
|
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(remainingLines);
|
|
3831
|
-
const hasApprovalFooter = /\besc to cancel\b/i.test(
|
|
4366
|
+
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(
|
|
4367
|
+
remainingLines
|
|
4368
|
+
);
|
|
3832
4369
|
if (hasApprovalOptions && hasApprovalFooter) {
|
|
3833
4370
|
return "approval";
|
|
3834
4371
|
}
|
|
@@ -4134,7 +4671,7 @@ function isDurationToken(word) {
|
|
|
4134
4671
|
return /^\d+[dhms](?:\d+[dhms])*$/i.test(word);
|
|
4135
4672
|
}
|
|
4136
4673
|
function extractSystemErrorPrompt(snapshot) {
|
|
4137
|
-
const stripped = snapshot
|
|
4674
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
4138
4675
|
const lines = stripped.split("\n");
|
|
4139
4676
|
let bannerIdx = -1;
|
|
4140
4677
|
let rawMessage = "";
|
|
@@ -4240,7 +4777,7 @@ function isQuestionFooterLine(line) {
|
|
|
4240
4777
|
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
4778
|
}
|
|
4242
4779
|
function extractQuestionHeader(snapshot) {
|
|
4243
|
-
const stripped = snapshot
|
|
4780
|
+
const stripped = stripAnsiAndExpandRep(snapshot);
|
|
4244
4781
|
const lines = stripped.split("\n");
|
|
4245
4782
|
const lineInFence = computeFenceMask(lines);
|
|
4246
4783
|
const prefixRe = /^\s*(?:[❯>]\s*)?(?:\?\s*(?:Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)?|Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)/i;
|
|
@@ -4257,9 +4794,17 @@ function extractQuestionHeader(snapshot) {
|
|
|
4257
4794
|
return null;
|
|
4258
4795
|
}
|
|
4259
4796
|
function hashPromptSnapshot(snapshot) {
|
|
4260
|
-
let normalized = snapshot
|
|
4261
|
-
|
|
4797
|
+
let normalized = stripAnsiAndExpandRep(snapshot).replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
4798
|
+
const activeKind = detectActivePromptKind(normalized);
|
|
4799
|
+
if (activeKind === "system_error") {
|
|
4262
4800
|
normalized = normalized.replace(/\bResets\s+in\s+\d+[dhms](?:\s*\d+[dhms])*\.?/gi, "Resets in <duration>");
|
|
4801
|
+
} else if (activeKind === "approval") {
|
|
4802
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
4803
|
+
if (fileEdit?.body) {
|
|
4804
|
+
normalized = `${normalized}
|
|
4805
|
+
@@file-edit-body@@
|
|
4806
|
+
${fileEdit.body}`;
|
|
4807
|
+
}
|
|
4263
4808
|
}
|
|
4264
4809
|
return (0, import_crypto.createHash)("sha256").update(normalized).digest("hex");
|
|
4265
4810
|
}
|
|
@@ -4304,14 +4849,37 @@ function parseApprovalSnapshot(snapshot) {
|
|
|
4304
4849
|
return parseSystemErrorSnapshot(stripped, snapshot, systemError);
|
|
4305
4850
|
}
|
|
4306
4851
|
}
|
|
4852
|
+
if (kind === "approval") {
|
|
4853
|
+
const lines = stripped.trimEnd().split("\n");
|
|
4854
|
+
const windowStart = Math.max(0, lines.length - 30);
|
|
4855
|
+
let lastCmdApprovalIdx = -1;
|
|
4856
|
+
let lastFileEditIdx = -1;
|
|
4857
|
+
for (let i = lines.length - 1; i >= windowStart; i -= 1) {
|
|
4858
|
+
const l = lines[i];
|
|
4859
|
+
if (lastCmdApprovalIdx < 0 && (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l))) {
|
|
4860
|
+
lastCmdApprovalIdx = i;
|
|
4861
|
+
}
|
|
4862
|
+
if (lastFileEditIdx < 0 && /^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(l)) {
|
|
4863
|
+
lastFileEditIdx = i;
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
if (lastFileEditIdx > lastCmdApprovalIdx) {
|
|
4867
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
4868
|
+
if (fileEdit) {
|
|
4869
|
+
return parseFileEditSnapshot(stripped, snapshot, fileEdit);
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
}
|
|
4307
4873
|
return parseApprovalUISnapshot(stripped, snapshot);
|
|
4308
4874
|
}
|
|
4309
4875
|
function buildApprovalSemanticKey(candidate) {
|
|
4310
4876
|
const normalize = (value) => (value ?? "").replace(/\s+/g, " ").trim();
|
|
4877
|
+
const rawBody = (candidate.body ?? "").replace(/\r\n/g, "\n");
|
|
4878
|
+
const isDiffBody = /```diff\b/i.test(rawBody) || /^(?:Accept this file edit\?|Allow creation of this file\?)/i.test(candidate.fullHeaderLine ?? "");
|
|
4311
4879
|
const payload = {
|
|
4312
4880
|
kind: candidate.kind,
|
|
4313
4881
|
headerText: normalize(candidate.headerText),
|
|
4314
|
-
body: normalize(
|
|
4882
|
+
body: isDiffBody ? rawBody.trim() : normalize(rawBody),
|
|
4315
4883
|
options: (candidate.options ?? []).map((option) => ({
|
|
4316
4884
|
number: option.number,
|
|
4317
4885
|
text: normalize(option.text)
|
|
@@ -4322,6 +4890,13 @@ function buildApprovalSemanticKey(candidate) {
|
|
|
4322
4890
|
};
|
|
4323
4891
|
return (0, import_crypto2.createHash)("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
4324
4892
|
}
|
|
4893
|
+
function buildApprovalRedrawSemanticKey(candidate) {
|
|
4894
|
+
const rightTrimmedBody = (candidate.body ?? "").replace(/\r\n/g, "\n").split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n");
|
|
4895
|
+
return buildApprovalSemanticKey({
|
|
4896
|
+
...candidate,
|
|
4897
|
+
body: rightTrimmedBody
|
|
4898
|
+
});
|
|
4899
|
+
}
|
|
4325
4900
|
function hasNegativeApprovalOption(options) {
|
|
4326
4901
|
return options.some((option) => /^(?:no|deny|reject|cancel)\b/i.test(option.text.trim()));
|
|
4327
4902
|
}
|
|
@@ -4349,6 +4924,30 @@ function parseFileAccessSnapshot(stripped, originalSnapshot, prompt) {
|
|
|
4349
4924
|
paneHash: hashPromptSnapshot(originalSnapshot)
|
|
4350
4925
|
};
|
|
4351
4926
|
}
|
|
4927
|
+
function parseFileEditSnapshot(stripped, originalSnapshot, prompt) {
|
|
4928
|
+
const lines = stripped.split("\n");
|
|
4929
|
+
let questionIndex = -1;
|
|
4930
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
4931
|
+
if (/^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(lines[index])) {
|
|
4932
|
+
questionIndex = index;
|
|
4933
|
+
break;
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
const belowQuestion = questionIndex >= 0 ? lines.slice(questionIndex + 1).join("\n") : stripped;
|
|
4937
|
+
const options = parseOptions(belowQuestion);
|
|
4938
|
+
if (options.length < 2 || !hasNegativeApprovalOption(options)) return null;
|
|
4939
|
+
return {
|
|
4940
|
+
kind: "approval",
|
|
4941
|
+
headerText: prompt.identity,
|
|
4942
|
+
fullHeaderLine: prompt.fullLine,
|
|
4943
|
+
command: void 0,
|
|
4944
|
+
filePath: prompt.filePath,
|
|
4945
|
+
options,
|
|
4946
|
+
submitMap: buildApprovalSubmitMap(options),
|
|
4947
|
+
body: prompt.body,
|
|
4948
|
+
paneHash: hashPromptSnapshot(originalSnapshot)
|
|
4949
|
+
};
|
|
4950
|
+
}
|
|
4352
4951
|
function parseSystemErrorSnapshot(stripped, originalSnapshot, prompt) {
|
|
4353
4952
|
return {
|
|
4354
4953
|
kind: "system_error",
|
|
@@ -4478,7 +5077,7 @@ function buildQuestionSubmitMap(options) {
|
|
|
4478
5077
|
function isWriteInInputActive(paneText, expectedHeader, expectedStep, expectedBodyText) {
|
|
4479
5078
|
if (!paneText) return false;
|
|
4480
5079
|
if (detectActivePromptKind(paneText) !== "question") return false;
|
|
4481
|
-
const stripped = paneText
|
|
5080
|
+
const stripped = stripAnsiAndExpandRep(paneText);
|
|
4482
5081
|
const lines = stripped.trimEnd().split("\n");
|
|
4483
5082
|
const windowStart = Math.max(0, lines.length - 30);
|
|
4484
5083
|
let headerIdx = -1;
|
|
@@ -4611,12 +5210,13 @@ function extractIdentity(headerText) {
|
|
|
4611
5210
|
function guessToolTypeFromIdentity(c) {
|
|
4612
5211
|
if (c.kind === "question") return "ASK_QUESTION";
|
|
4613
5212
|
if (c.command) return "RUN_COMMAND";
|
|
4614
|
-
if (c.filePath
|
|
5213
|
+
if (c.filePath || c.headerText === "file edit" || c.headerText === "create file" || c.headerText === "file creation") {
|
|
5214
|
+
return "CODE_ACTION";
|
|
5215
|
+
}
|
|
4615
5216
|
return "GENERIC";
|
|
4616
5217
|
}
|
|
4617
|
-
var ANSI_ESCAPE_REGEX2 = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
4618
5218
|
function stripAnsi(s) {
|
|
4619
|
-
return s
|
|
5219
|
+
return stripAnsiAndExpandRep(s);
|
|
4620
5220
|
}
|
|
4621
5221
|
|
|
4622
5222
|
// src/event-mapper.ts
|
|
@@ -4806,6 +5406,10 @@ function hookToolNameToTranscriptType(hookName) {
|
|
|
4806
5406
|
switch (upper) {
|
|
4807
5407
|
case "LIST_DIR":
|
|
4808
5408
|
return "LIST_DIRECTORY";
|
|
5409
|
+
case "READ_FILE":
|
|
5410
|
+
return "VIEW_FILE";
|
|
5411
|
+
case "EDIT_FILE":
|
|
5412
|
+
case "WRITE_FILE":
|
|
4809
5413
|
case "REPLACE_FILE_CONTENT":
|
|
4810
5414
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
4811
5415
|
case "WRITE_TO_FILE":
|
|
@@ -4842,6 +5446,8 @@ function extractFilePathFromArgs(toolName, args) {
|
|
|
4842
5446
|
if (!args || typeof args !== "object") return void 0;
|
|
4843
5447
|
const a = args;
|
|
4844
5448
|
for (const key of [
|
|
5449
|
+
"Path",
|
|
5450
|
+
"AbsolutePath",
|
|
4845
5451
|
"TargetFile",
|
|
4846
5452
|
"FilePath",
|
|
4847
5453
|
"file_path",
|
|
@@ -5266,6 +5872,32 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5266
5872
|
});
|
|
5267
5873
|
return null;
|
|
5268
5874
|
}
|
|
5875
|
+
if (candidate.kind === "approval") {
|
|
5876
|
+
const candidateSemanticKey = buildApprovalSemanticKey(candidate);
|
|
5877
|
+
const candidateRedrawSemanticKey = buildApprovalRedrawSemanticKey(candidate);
|
|
5878
|
+
const existing = [...this.pendingPrompts.values()].find(
|
|
5879
|
+
(p) => p.conversationId === conversationId && p.kind === candidate.kind && (p.paneSemanticKey === candidateSemanticKey || p.paneRedrawSemanticKey !== void 0 && p.paneRedrawSemanticKey === candidateRedrawSemanticKey)
|
|
5880
|
+
);
|
|
5881
|
+
if (existing) {
|
|
5882
|
+
if (existing.paneHash && existing.paneHash !== candidate.paneHash) {
|
|
5883
|
+
this.paneOnlyEmittedHashes.delete(existing.paneHash);
|
|
5884
|
+
}
|
|
5885
|
+
existing.paneHash = candidate.paneHash;
|
|
5886
|
+
if (candidate.paneHash) {
|
|
5887
|
+
this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
5888
|
+
}
|
|
5889
|
+
existing.paneSemanticKey = candidateSemanticKey;
|
|
5890
|
+
existing.paneRedrawSemanticKey = candidateRedrawSemanticKey;
|
|
5891
|
+
existing.body = candidate.body;
|
|
5892
|
+
logger.debug("Skipping pane-only re-emit for active approval with matching semantic key", {
|
|
5893
|
+
promptId: existing.promptId,
|
|
5894
|
+
paneSemanticKey: candidateSemanticKey.substring(0, 16),
|
|
5895
|
+
paneRedrawSemanticKey: candidateRedrawSemanticKey.substring(0, 16),
|
|
5896
|
+
paneHash: candidate.paneHash.substring(0, 16)
|
|
5897
|
+
});
|
|
5898
|
+
return null;
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5269
5901
|
if (candidate.kind === "question") {
|
|
5270
5902
|
const paneQuestionText = candidate.headerText || candidate.fullHeaderLine;
|
|
5271
5903
|
const candidateSemanticKey = buildApprovalSemanticKey(candidate);
|
|
@@ -5478,7 +6110,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5478
6110
|
toolType: matchingCall?.toolType ?? guessToolTypeFromIdentity(candidate),
|
|
5479
6111
|
command: matchingCall?.command ?? candidate.command,
|
|
5480
6112
|
filePath: matchingCall?.filePath ?? candidate.filePath,
|
|
5481
|
-
rawToolName: matchingCall?.rawToolName ?? (candidate.kind === "question" ? "ask_question" : candidate.command ?? candidate.filePath ?? "unknown"),
|
|
6113
|
+
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
6114
|
questionText: synthLineMatch?.stitchedHeaderText ?? (candidate.kind === "question" && candidate.headerText ? candidate.headerText : matchingCall?.questionText),
|
|
5483
6115
|
// NOTE (Codex R3-F1 / Claude R3-F2): Pane options are authoritative ground truth
|
|
5484
6116
|
// that directly correspond to desktop keystrokes (submitMap). Transcript options
|
|
@@ -5502,6 +6134,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5502
6134
|
body: synthLineMatch ? synthLineMatch.remainingBody || void 0 : candidate.body,
|
|
5503
6135
|
paneOptions: candidate.options,
|
|
5504
6136
|
paneSemanticKey: buildApprovalSemanticKey(candidate),
|
|
6137
|
+
paneRedrawSemanticKey: buildApprovalRedrawSemanticKey(candidate),
|
|
5505
6138
|
kind: candidate.kind,
|
|
5506
6139
|
isHookPrompt: isHookCall,
|
|
5507
6140
|
// Carry hook ownership forward so pane redraws do not drop it! (Codex S2-Codex-R6-F15)
|
|
@@ -5822,6 +6455,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5822
6455
|
body: candidate.body,
|
|
5823
6456
|
paneOptions: candidate.options,
|
|
5824
6457
|
paneSemanticKey: buildApprovalSemanticKey(candidate),
|
|
6458
|
+
paneRedrawSemanticKey: buildApprovalRedrawSemanticKey(candidate),
|
|
5825
6459
|
kind: candidate.kind,
|
|
5826
6460
|
emittedAt: Date.now(),
|
|
5827
6461
|
ttlMs: this.promptTtlMs
|
|
@@ -6800,8 +7434,11 @@ function friendlyHookToolName(rawName, resultType) {
|
|
|
6800
7434
|
return "LS";
|
|
6801
7435
|
case "GREP_SEARCH":
|
|
6802
7436
|
return "Grep";
|
|
7437
|
+
case "READ_FILE":
|
|
6803
7438
|
case "VIEW_FILE":
|
|
6804
7439
|
return "Read";
|
|
7440
|
+
case "EDIT_FILE":
|
|
7441
|
+
case "WRITE_FILE":
|
|
6805
7442
|
case "WRITE_TO_FILE":
|
|
6806
7443
|
case "REPLACE_FILE_CONTENT":
|
|
6807
7444
|
case "MULTI_REPLACE_FILE_CONTENT":
|
|
@@ -9494,14 +10131,19 @@ var McpServer = class _McpServer {
|
|
|
9494
10131
|
});
|
|
9495
10132
|
}
|
|
9496
10133
|
const toolName = diffParsed ? "Edit" : state.pendingCall?.toolType;
|
|
9497
|
-
const filePath =
|
|
10134
|
+
const filePath = state.pendingCall?.filePath ?? diffParsed?.filePath;
|
|
9498
10135
|
const toolInput = diffParsed ? {
|
|
9499
|
-
file_path:
|
|
10136
|
+
file_path: filePath,
|
|
9500
10137
|
old_string: diffParsed.oldString,
|
|
9501
10138
|
new_string: diffParsed.newString,
|
|
9502
10139
|
old_start_line: diffParsed.oldStartLine,
|
|
9503
10140
|
new_start_line: diffParsed.newStartLine,
|
|
9504
|
-
diff: diffParsed.rawDiff
|
|
10141
|
+
diff: diffParsed.rawDiff,
|
|
10142
|
+
...diffParsed.diffStat ? { diff_stat: diffParsed.diffStat } : {},
|
|
10143
|
+
...diffParsed.partial ? {
|
|
10144
|
+
partial: true,
|
|
10145
|
+
...diffParsed.omissionNote ? { omission_note: diffParsed.omissionNote } : {}
|
|
10146
|
+
} : {}
|
|
9505
10147
|
} : void 0;
|
|
9506
10148
|
const optionsForMobile = state.pendingCall ? {
|
|
9507
10149
|
promptId: state.promptId,
|
|
@@ -9535,7 +10177,8 @@ var McpServer = class _McpServer {
|
|
|
9535
10177
|
})()
|
|
9536
10178
|
} : {}
|
|
9537
10179
|
} : { promptId: state.promptId };
|
|
9538
|
-
const
|
|
10180
|
+
const baseContent = state.paneDisplayHeader || state.matchedPaneHeader || state.pendingCall?.command || state.pendingCall?.filePath || "approval requested";
|
|
10181
|
+
const content = diffParsed?.partial && diffParsed.omissionNote && !baseContent.includes(diffParsed.omissionNote) ? `${baseContent} \u2014 partial diff (${diffParsed.omissionNote})` : baseContent;
|
|
9539
10182
|
const input = {
|
|
9540
10183
|
sessionId: session.sessionId,
|
|
9541
10184
|
type: import_codevibe_core7.EventType.INTERACTIVE_PROMPT,
|
|
@@ -10451,6 +11094,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10451
11094
|
const rawDiff = fenceMatch[1];
|
|
10452
11095
|
const oldLines = [];
|
|
10453
11096
|
const newLines = [];
|
|
11097
|
+
const omissionNotes = [];
|
|
11098
|
+
let diffStat;
|
|
11099
|
+
let partial = false;
|
|
10454
11100
|
const hunkHeaderPattern = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
10455
11101
|
let currentOldLine = 0;
|
|
10456
11102
|
let currentNewLine = 0;
|
|
@@ -10465,6 +11111,17 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10465
11111
|
return p;
|
|
10466
11112
|
};
|
|
10467
11113
|
for (const line of rawDiff.split("\n")) {
|
|
11114
|
+
const statComment = line.match(/^#\s*diff-stat:\s*((?:\+\d+(?:\s+-\d+)?|-\d+))\s*$/i);
|
|
11115
|
+
if (statComment) {
|
|
11116
|
+
diffStat = statComment[1].replace(/\s+/g, " ").trim();
|
|
11117
|
+
continue;
|
|
11118
|
+
}
|
|
11119
|
+
const omissionComment = line.match(/^#\s*(?:partial-diff|omitted):\s*(.+)$/i);
|
|
11120
|
+
if (omissionComment) {
|
|
11121
|
+
partial = true;
|
|
11122
|
+
omissionNotes.push(omissionComment[1].trim());
|
|
11123
|
+
continue;
|
|
11124
|
+
}
|
|
10468
11125
|
const hunkMatch = line.match(hunkHeaderPattern);
|
|
10469
11126
|
if (hunkMatch) {
|
|
10470
11127
|
hunkCount += 1;
|
|
@@ -10472,12 +11129,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10472
11129
|
currentNewLine = Number.parseInt(hunkMatch[2], 10);
|
|
10473
11130
|
continue;
|
|
10474
11131
|
}
|
|
10475
|
-
if (line.startsWith("+++ ")) {
|
|
11132
|
+
if (hunkCount === 0 && line.startsWith("+++ ")) {
|
|
10476
11133
|
const pathVal = stripPrefix(line.slice(4));
|
|
10477
11134
|
if (pathVal) filePath = pathVal;
|
|
10478
11135
|
continue;
|
|
10479
11136
|
}
|
|
10480
|
-
if (line.startsWith("--- ")) {
|
|
11137
|
+
if (hunkCount === 0 && line.startsWith("--- ")) {
|
|
10481
11138
|
if (!filePath) {
|
|
10482
11139
|
const pathVal = stripPrefix(line.slice(4));
|
|
10483
11140
|
if (pathVal) filePath = pathVal;
|
|
@@ -10504,8 +11161,9 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10504
11161
|
oldStartLine = void 0;
|
|
10505
11162
|
newStartLine = void 0;
|
|
10506
11163
|
}
|
|
11164
|
+
const isFileEditHeader = /^(?:Accept this file edit\?|Allow creation of this file\?)/i.test(header ?? "");
|
|
10507
11165
|
if (!filePath) {
|
|
10508
|
-
const searchTargets = [body, header ?? ""];
|
|
11166
|
+
const searchTargets = isFileEditHeader ? [header ?? ""] : [body, header ?? ""];
|
|
10509
11167
|
for (const target of searchTargets) {
|
|
10510
11168
|
if (!target) continue;
|
|
10511
11169
|
const m = target.match(/\[.*?\]\(file:\/\/([^\s\)]+)\)/) || target.match(/file:\/\/([^\s\)]+)/);
|
|
@@ -10525,7 +11183,12 @@ function parseFencedDiffFromBody(body, header) {
|
|
|
10525
11183
|
newString: newLines.join("\n"),
|
|
10526
11184
|
oldStartLine,
|
|
10527
11185
|
newStartLine,
|
|
10528
|
-
rawDiff
|
|
11186
|
+
rawDiff,
|
|
11187
|
+
...diffStat ? { diffStat } : {},
|
|
11188
|
+
...partial ? {
|
|
11189
|
+
partial: true,
|
|
11190
|
+
...omissionNotes.length > 0 ? { omissionNote: omissionNotes.join("; ") } : {}
|
|
11191
|
+
} : {}
|
|
10529
11192
|
};
|
|
10530
11193
|
}
|
|
10531
11194
|
var __testing = {
|
|
@@ -10550,6 +11213,7 @@ var __testing = {
|
|
|
10550
11213
|
commandDigest,
|
|
10551
11214
|
detectActivePromptKind,
|
|
10552
11215
|
extractFileAccessPrompt,
|
|
11216
|
+
extractFileEditPrompt,
|
|
10553
11217
|
extractSystemErrorPrompt,
|
|
10554
11218
|
generateLaunchSessionId,
|
|
10555
11219
|
getActiveConversationFromCliLog,
|
|
@@ -10564,5 +11228,6 @@ var __testing = {
|
|
|
10564
11228
|
readTranscriptFirstType,
|
|
10565
11229
|
reconstructFooter,
|
|
10566
11230
|
runForeignSpawnIdle,
|
|
10567
|
-
safeIsoTimestamp
|
|
11231
|
+
safeIsoTimestamp,
|
|
11232
|
+
stripAnsiAndExpandRep
|
|
10568
11233
|
});
|
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.102",
|
|
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",
|