pi-repoprompt-mcp 0.6.2 → 0.7.1
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.
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
RpTab,
|
|
29
29
|
RpToolMeta,
|
|
30
30
|
McpContent,
|
|
31
|
+
McpToolResult,
|
|
31
32
|
AutoSelectionEntryData,
|
|
32
33
|
AutoSelectionEntrySliceData,
|
|
33
34
|
AutoSelectionEntryRangeData,
|
|
@@ -69,7 +70,7 @@ import { buildInvalidationV1 } from "./readcache/meta.js";
|
|
|
69
70
|
import { clearReplayRuntimeState, createReplayRuntimeState } from "./readcache/replay.js";
|
|
70
71
|
import type { RpReadcacheMetaV1, ScopeKey } from "./readcache/types.js";
|
|
71
72
|
import { getStoreStats, pruneObjectsOlderThan } from "./readcache/object-store.js";
|
|
72
|
-
import { resolveReadFilePath } from "./readcache/resolve.js";
|
|
73
|
+
import { clearRootsCache, resolveReadFilePath } from "./readcache/resolve.js";
|
|
73
74
|
|
|
74
75
|
import {
|
|
75
76
|
applyFullReadToSelectionState,
|
|
@@ -81,6 +82,13 @@ import {
|
|
|
81
82
|
isWholeFileReadFromArgs,
|
|
82
83
|
toPosixPath,
|
|
83
84
|
} from "./auto-select.js";
|
|
85
|
+
import {
|
|
86
|
+
clearPendingTransitionSelectionState,
|
|
87
|
+
getPendingTransitionState,
|
|
88
|
+
setPendingTransitionSelectionState,
|
|
89
|
+
setPendingTransitionTargetState,
|
|
90
|
+
} from "./transition-state.js";
|
|
91
|
+
import type { PendingTransitionRetryMode, PendingTransitionTargetIdentity } from "./transition-state.js";
|
|
84
92
|
|
|
85
93
|
function parseSummaryCount(value: string | undefined): number | undefined {
|
|
86
94
|
if (!value) {
|
|
@@ -570,12 +578,34 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
570
578
|
clearReplayRuntimeState(readcacheRuntimeState);
|
|
571
579
|
};
|
|
572
580
|
|
|
581
|
+
type AutoSelectionSyncOptions = {
|
|
582
|
+
provisionTab?: boolean;
|
|
583
|
+
recoverClosedTab?: boolean;
|
|
584
|
+
reuseSoleEmptyTab?: boolean;
|
|
585
|
+
allowSyntheticSource?: boolean;
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
const STARTUP_AUTO_SELECTION_SYNC_OPTIONS: AutoSelectionSyncOptions = {
|
|
589
|
+
provisionTab: true,
|
|
590
|
+
recoverClosedTab: false,
|
|
591
|
+
reuseSoleEmptyTab: false,
|
|
592
|
+
allowSyntheticSource: true,
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const TRANSITION_AUTO_SELECTION_SYNC_OPTIONS: AutoSelectionSyncOptions = {
|
|
596
|
+
provisionTab: false,
|
|
597
|
+
recoverClosedTab: true,
|
|
598
|
+
reuseSoleEmptyTab: true,
|
|
599
|
+
allowSyntheticSource: false,
|
|
600
|
+
};
|
|
601
|
+
|
|
573
602
|
let activeAutoSelectionState: AutoSelectionEntryData | null = null;
|
|
574
603
|
let autoSelectionUpdateQueue: Promise<void> = Promise.resolve();
|
|
604
|
+
let ownsLiveAutoSelection = false;
|
|
575
605
|
|
|
576
|
-
function runAutoSelectionUpdate(task: () => Promise<
|
|
606
|
+
function runAutoSelectionUpdate<T>(task: () => Promise<T>): Promise<T> {
|
|
577
607
|
const queued = autoSelectionUpdateQueue.then(task, task);
|
|
578
|
-
autoSelectionUpdateQueue = queued.
|
|
608
|
+
autoSelectionUpdateQueue = queued.then(() => undefined, () => undefined);
|
|
579
609
|
return queued;
|
|
580
610
|
}
|
|
581
611
|
|
|
@@ -804,12 +834,120 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
804
834
|
return findAutoSelectionStateInEntries(entries, binding) ?? makeEmptyAutoSelectionState(binding);
|
|
805
835
|
}
|
|
806
836
|
|
|
837
|
+
function resetAutoSelectionRuntimeState(): void {
|
|
838
|
+
activeAutoSelectionState = null;
|
|
839
|
+
autoSelectionUpdateQueue = Promise.resolve();
|
|
840
|
+
ownsLiveAutoSelection = false;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function commitLiveAutoSelectionState(state: AutoSelectionEntryData | null): void {
|
|
844
|
+
activeAutoSelectionState = state ? normalizeAutoSelectionState(state) : null;
|
|
845
|
+
ownsLiveAutoSelection = true;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function hasManagedAutoSelectionPaths(state: AutoSelectionEntryData | null): boolean {
|
|
849
|
+
return state !== null && autoSelectionManagedPaths(state).length > 0;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function updatePendingTransitionSelectionFromLiveState(): void {
|
|
853
|
+
if (!ownsLiveAutoSelection) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (!hasManagedAutoSelectionPaths(activeAutoSelectionState)) {
|
|
858
|
+
clearPendingTransitionSelectionState();
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
setPendingTransitionSelectionState(activeAutoSelectionState);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function autoSelectionRetryModeForSessionStartReason(
|
|
866
|
+
reason: "startup" | "reload" | "new" | "resume" | "fork"
|
|
867
|
+
): PendingTransitionRetryMode {
|
|
868
|
+
return reason === "startup" || reason === "reload" ? "startup" : "transition";
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function autoSelectionRetryModeForSyncOptions(
|
|
872
|
+
options: AutoSelectionSyncOptions
|
|
873
|
+
): PendingTransitionRetryMode {
|
|
874
|
+
return options.provisionTab === false ? "transition" : "startup";
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function autoSelectionSyncOptionsForRetryMode(
|
|
878
|
+
retryMode: PendingTransitionRetryMode
|
|
879
|
+
): AutoSelectionSyncOptions {
|
|
880
|
+
return retryMode === "startup"
|
|
881
|
+
? STARTUP_AUTO_SELECTION_SYNC_OPTIONS
|
|
882
|
+
: TRANSITION_AUTO_SELECTION_SYNC_OPTIONS;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function autoSelectionSyncOptionsForSessionStartReason(
|
|
886
|
+
reason: "startup" | "reload" | "new" | "resume" | "fork"
|
|
887
|
+
): AutoSelectionSyncOptions {
|
|
888
|
+
return autoSelectionSyncOptionsForRetryMode(autoSelectionRetryModeForSessionStartReason(reason));
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function reconnectAutoSelectionSyncOptions(): AutoSelectionSyncOptions {
|
|
892
|
+
return autoSelectionSyncOptionsForRetryMode(getPendingTransitionState()?.retryMode ?? "startup");
|
|
893
|
+
}
|
|
894
|
+
|
|
807
895
|
function persistAutoSelectionState(state: AutoSelectionEntryData): void {
|
|
808
896
|
const normalized = normalizeAutoSelectionState(state);
|
|
809
|
-
|
|
897
|
+
commitLiveAutoSelectionState(normalized);
|
|
810
898
|
pi.appendEntry(AUTO_SELECTION_ENTRY_TYPE, normalized);
|
|
811
899
|
}
|
|
812
900
|
|
|
901
|
+
function getPendingTransitionTargetIdentity(ctx: ExtensionContext): PendingTransitionTargetIdentity {
|
|
902
|
+
return {
|
|
903
|
+
sessionFile: ctx.sessionManager.getSessionFile() ?? null,
|
|
904
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function samePendingTransitionTargetIdentity(
|
|
909
|
+
left: PendingTransitionTargetIdentity | null,
|
|
910
|
+
right: PendingTransitionTargetIdentity | null
|
|
911
|
+
): boolean {
|
|
912
|
+
return left?.sessionFile === right?.sessionFile && left?.sessionId === right?.sessionId;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function seedPendingTransitionTargetForSessionStart(
|
|
916
|
+
ctx: ExtensionContext,
|
|
917
|
+
options: AutoSelectionSyncOptions
|
|
918
|
+
): void {
|
|
919
|
+
const binding = getBinding();
|
|
920
|
+
const state = config.autoSelectReadSlices === true && binding?.tab
|
|
921
|
+
? getAutoSelectionStateFromBranch(ctx, binding)
|
|
922
|
+
: null;
|
|
923
|
+
|
|
924
|
+
setPendingTransitionTargetState(
|
|
925
|
+
getPendingTransitionTargetIdentity(ctx),
|
|
926
|
+
binding,
|
|
927
|
+
state,
|
|
928
|
+
autoSelectionRetryModeForSyncOptions(options)
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function throwOnMcpToolResultError(result: McpToolResult, fallbackMessage: string): void {
|
|
933
|
+
if (!result.isError) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
throw new Error(extractTextContent(result.content) || fallbackMessage);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function isIgnorableOldBindingRemovalError(error: unknown): boolean {
|
|
941
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
942
|
+
const lower = message.toLowerCase();
|
|
943
|
+
|
|
944
|
+
return (
|
|
945
|
+
(lower.includes("window") && lower.includes("not found")) ||
|
|
946
|
+
(lower.includes("tab") && lower.includes("not found")) ||
|
|
947
|
+
(lower.includes("context") && lower.includes("not found"))
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
|
|
813
951
|
function bindingArgsForAutoSelectionState(state: AutoSelectionEntryData): Record<string, unknown> {
|
|
814
952
|
return {
|
|
815
953
|
_windowID: state.windowId,
|
|
@@ -836,11 +974,12 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
836
974
|
return;
|
|
837
975
|
}
|
|
838
976
|
|
|
839
|
-
await client.callTool(manageSelectionToolName, {
|
|
977
|
+
const result = await client.callTool(manageSelectionToolName, {
|
|
840
978
|
op: "remove",
|
|
841
979
|
paths,
|
|
842
980
|
...bindingArgsForAutoSelectionState(state),
|
|
843
981
|
});
|
|
982
|
+
throwOnMcpToolResultError(result, "RepoPrompt manage_selection remove failed");
|
|
844
983
|
}
|
|
845
984
|
|
|
846
985
|
async function addAutoSelectionFullPaths(
|
|
@@ -853,12 +992,13 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
853
992
|
return;
|
|
854
993
|
}
|
|
855
994
|
|
|
856
|
-
await client.callTool(manageSelectionToolName, {
|
|
995
|
+
const result = await client.callTool(manageSelectionToolName, {
|
|
857
996
|
op: "add",
|
|
858
997
|
mode: "full",
|
|
859
998
|
paths,
|
|
860
999
|
...bindingArgsForAutoSelectionState(state),
|
|
861
1000
|
});
|
|
1001
|
+
throwOnMcpToolResultError(result, "RepoPrompt manage_selection add(full) failed");
|
|
862
1002
|
}
|
|
863
1003
|
|
|
864
1004
|
async function addAutoSelectionSlices(
|
|
@@ -871,11 +1011,12 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
871
1011
|
return;
|
|
872
1012
|
}
|
|
873
1013
|
|
|
874
|
-
await client.callTool(manageSelectionToolName, {
|
|
1014
|
+
const result = await client.callTool(manageSelectionToolName, {
|
|
875
1015
|
op: "add",
|
|
876
1016
|
slices,
|
|
877
1017
|
...bindingArgsForAutoSelectionState(state),
|
|
878
1018
|
});
|
|
1019
|
+
throwOnMcpToolResultError(result, "RepoPrompt manage_selection add(slices) failed");
|
|
879
1020
|
}
|
|
880
1021
|
|
|
881
1022
|
async function reconcileAutoSelectionWithinBinding(
|
|
@@ -1005,8 +1146,10 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1005
1146
|
currentState,
|
|
1006
1147
|
autoSelectionManagedPaths(currentState)
|
|
1007
1148
|
);
|
|
1008
|
-
} catch {
|
|
1009
|
-
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
if (!isIgnorableOldBindingRemovalError(error)) {
|
|
1151
|
+
throw error;
|
|
1152
|
+
}
|
|
1010
1153
|
}
|
|
1011
1154
|
|
|
1012
1155
|
await addAutoSelectionFullPaths(client, manageSelectionToolName, desiredState, desiredState.fullPaths);
|
|
@@ -1022,8 +1165,10 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1022
1165
|
currentState,
|
|
1023
1166
|
autoSelectionManagedPaths(currentState)
|
|
1024
1167
|
);
|
|
1025
|
-
} catch {
|
|
1026
|
-
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
if (!isIgnorableOldBindingRemovalError(error)) {
|
|
1170
|
+
throw error;
|
|
1171
|
+
}
|
|
1027
1172
|
}
|
|
1028
1173
|
return;
|
|
1029
1174
|
}
|
|
@@ -1136,56 +1281,86 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1136
1281
|
|
|
1137
1282
|
async function syncAutoSelectionToCurrentBranch(
|
|
1138
1283
|
ctx: ExtensionContext,
|
|
1139
|
-
options:
|
|
1284
|
+
options: AutoSelectionSyncOptions = reconnectAutoSelectionSyncOptions(),
|
|
1285
|
+
pendingTargetPolicy: "reuse" | "refresh" = "reuse"
|
|
1140
1286
|
): Promise<RpBinding | null> {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1287
|
+
return await runAutoSelectionUpdate(async () => {
|
|
1288
|
+
const transitionTargetIdentity = getPendingTransitionTargetIdentity(ctx);
|
|
1289
|
+
const pendingTransitionState = getPendingTransitionState();
|
|
1290
|
+
const pendingTargetMatchesCurrentSession = samePendingTransitionTargetIdentity(
|
|
1291
|
+
pendingTransitionState?.targetIdentity ?? null,
|
|
1292
|
+
transitionTargetIdentity
|
|
1293
|
+
);
|
|
1294
|
+
const reusePendingTarget = pendingTargetPolicy === "reuse" && pendingTargetMatchesCurrentSession;
|
|
1295
|
+
|
|
1296
|
+
const desiredBindingBeforeRecovery = reusePendingTarget
|
|
1297
|
+
? pendingTransitionState?.targetBinding ?? getBinding()
|
|
1298
|
+
: getBinding();
|
|
1299
|
+
const desiredStateBeforeRecovery = reusePendingTarget
|
|
1300
|
+
? pendingTransitionState?.targetState ?? null
|
|
1301
|
+
: config.autoSelectReadSlices === true && desiredBindingBeforeRecovery?.tab
|
|
1302
|
+
? getAutoSelectionStateFromBranch(ctx, desiredBindingBeforeRecovery)
|
|
1303
|
+
: null;
|
|
1304
|
+
|
|
1305
|
+
if (!reusePendingTarget) {
|
|
1306
|
+
setPendingTransitionTargetState(
|
|
1307
|
+
transitionTargetIdentity,
|
|
1308
|
+
desiredBindingBeforeRecovery,
|
|
1309
|
+
desiredStateBeforeRecovery,
|
|
1310
|
+
autoSelectionRetryModeForSyncOptions(options)
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1158
1313
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1314
|
+
const recoveryPaths = desiredStateBeforeRecovery ? autoSelectionManagedPaths(desiredStateBeforeRecovery) : [];
|
|
1315
|
+
const hasRecoverableState = recoveryPaths.length > 0;
|
|
1316
|
+
const liveBinding = await ensureBindingTargetsLiveWindow(ctx, {
|
|
1317
|
+
...options,
|
|
1318
|
+
hasRecoverableState,
|
|
1319
|
+
recoveryPaths,
|
|
1320
|
+
});
|
|
1161
1321
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1322
|
+
if (config.autoSelectReadSlices !== true) {
|
|
1323
|
+
clearPendingTransitionSelectionState();
|
|
1324
|
+
activeAutoSelectionState = null;
|
|
1325
|
+
return liveBinding;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const sourceState =
|
|
1329
|
+
pendingTransitionState?.sourceState ??
|
|
1330
|
+
activeAutoSelectionState ??
|
|
1331
|
+
(options.allowSyntheticSource === true ? desiredStateBeforeRecovery : null);
|
|
1332
|
+
|
|
1333
|
+
let desiredState = liveBinding?.tab ? getAutoSelectionStateFromBranch(ctx, liveBinding) : null;
|
|
1334
|
+
let recoveredState = false;
|
|
1335
|
+
|
|
1336
|
+
if (
|
|
1337
|
+
liveBinding?.tab &&
|
|
1338
|
+
desiredState &&
|
|
1339
|
+
desiredStateBeforeRecovery &&
|
|
1340
|
+
autoSelectionManagedPaths(desiredState).length === 0
|
|
1341
|
+
) {
|
|
1342
|
+
const recovered = recoverAutoSelectionStateForTabRecovery(
|
|
1343
|
+
desiredStateBeforeRecovery,
|
|
1344
|
+
desiredBindingBeforeRecovery,
|
|
1345
|
+
liveBinding
|
|
1346
|
+
);
|
|
1347
|
+
if (recovered) {
|
|
1348
|
+
desiredState = recovered;
|
|
1349
|
+
recoveredState = true;
|
|
1350
|
+
}
|
|
1167
1351
|
}
|
|
1168
|
-
}
|
|
1169
1352
|
|
|
1170
|
-
|
|
1171
|
-
activeAutoSelectionState = desiredState;
|
|
1172
|
-
return binding;
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
let reconcileSucceeded = true;
|
|
1176
|
-
try {
|
|
1177
|
-
await reconcileAutoSelectionStates(activeAutoSelectionState, desiredState);
|
|
1178
|
-
} catch {
|
|
1179
|
-
reconcileSucceeded = false;
|
|
1180
|
-
}
|
|
1353
|
+
await reconcileAutoSelectionStates(sourceState, desiredState);
|
|
1181
1354
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1355
|
+
if (recoveredState && desiredState) {
|
|
1356
|
+
persistAutoSelectionState(desiredState);
|
|
1357
|
+
} else {
|
|
1358
|
+
commitLiveAutoSelectionState(desiredState);
|
|
1359
|
+
}
|
|
1186
1360
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1361
|
+
clearPendingTransitionSelectionState();
|
|
1362
|
+
return liveBinding;
|
|
1363
|
+
});
|
|
1189
1364
|
}
|
|
1190
1365
|
|
|
1191
1366
|
function getBaseAutoSelectionState(
|
|
@@ -1224,20 +1399,19 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1224
1399
|
// Lifecycle Events
|
|
1225
1400
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1226
1401
|
|
|
1227
|
-
pi.on("session_start", async (
|
|
1402
|
+
pi.on("session_start", async (event, ctx) => {
|
|
1228
1403
|
shutdownRequested = false;
|
|
1229
1404
|
extensionPaused = false;
|
|
1405
|
+
clearReadcacheCaches();
|
|
1406
|
+
clearRootsCache();
|
|
1407
|
+
resetAutoSelectionRuntimeState();
|
|
1230
1408
|
|
|
1231
1409
|
if (ctx.hasUI) {
|
|
1232
1410
|
// This extension used to set a status bar item; clear it to avoid persisting stale UI state
|
|
1233
1411
|
ctx.ui.setStatus("rp", undefined);
|
|
1234
1412
|
}
|
|
1235
1413
|
|
|
1236
|
-
|
|
1237
|
-
activeAutoSelectionState =
|
|
1238
|
-
config.autoSelectReadSlices === true && restoredBinding
|
|
1239
|
-
? getAutoSelectionStateFromBranch(ctx, restoredBinding)
|
|
1240
|
-
: null;
|
|
1414
|
+
restoreBinding(ctx, config);
|
|
1241
1415
|
|
|
1242
1416
|
// Best-effort stale cache pruning (only when readcache is enabled)
|
|
1243
1417
|
if (config.readcacheReadFile === true) {
|
|
@@ -1246,6 +1420,9 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1246
1420
|
});
|
|
1247
1421
|
}
|
|
1248
1422
|
|
|
1423
|
+
const syncOptions = autoSelectionSyncOptionsForSessionStartReason(event.reason);
|
|
1424
|
+
seedPendingTransitionTargetForSessionStart(ctx, syncOptions);
|
|
1425
|
+
|
|
1249
1426
|
// Non-blocking initialization
|
|
1250
1427
|
const pendingInit = initializeExtension(pi, ctx, config);
|
|
1251
1428
|
initPromise = pendingInit;
|
|
@@ -1257,8 +1434,8 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1257
1434
|
if (shutdownRequested) {
|
|
1258
1435
|
return;
|
|
1259
1436
|
}
|
|
1260
|
-
await syncAutoSelectionToCurrentBranch(ctx);
|
|
1261
|
-
}).catch(async (
|
|
1437
|
+
await syncAutoSelectionToCurrentBranch(ctx, syncOptions, "refresh");
|
|
1438
|
+
}).catch(async () => {
|
|
1262
1439
|
if (initPromise === pendingInit) {
|
|
1263
1440
|
initPromise = null;
|
|
1264
1441
|
}
|
|
@@ -1273,8 +1450,9 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1273
1450
|
if (launched && !shutdownRequested) {
|
|
1274
1451
|
try {
|
|
1275
1452
|
await resetRpClient();
|
|
1453
|
+
clearRootsCache();
|
|
1276
1454
|
await initializeExtension(pi, ctx, config);
|
|
1277
|
-
await syncAutoSelectionToCurrentBranch(ctx);
|
|
1455
|
+
await syncAutoSelectionToCurrentBranch(ctx, syncOptions, "refresh");
|
|
1278
1456
|
return;
|
|
1279
1457
|
} catch {
|
|
1280
1458
|
// Fall through to pause
|
|
@@ -1297,62 +1475,21 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1297
1475
|
pi.on("session_shutdown", async () => {
|
|
1298
1476
|
shutdownRequested = true;
|
|
1299
1477
|
initPromise = null;
|
|
1478
|
+
updatePendingTransitionSelectionFromLiveState();
|
|
1300
1479
|
|
|
1301
1480
|
// Never block Pi shutdown on an MCP startup handshake that may be stuck waiting on the app
|
|
1481
|
+
clearBinding();
|
|
1302
1482
|
clearReadcacheCaches();
|
|
1303
|
-
|
|
1483
|
+
clearRootsCache();
|
|
1484
|
+
resetAutoSelectionRuntimeState();
|
|
1304
1485
|
await resetRpClient();
|
|
1305
1486
|
});
|
|
1306
1487
|
|
|
1307
|
-
pi.on("
|
|
1488
|
+
pi.on("session_tree", async (_event, ctx) => {
|
|
1308
1489
|
clearReadcacheCaches();
|
|
1490
|
+
clearRootsCache();
|
|
1309
1491
|
restoreBinding(ctx, config);
|
|
1310
|
-
await syncAutoSelectionToCurrentBranch(ctx,
|
|
1311
|
-
provisionTab: false,
|
|
1312
|
-
recoverClosedTab: true,
|
|
1313
|
-
reuseSoleEmptyTab: true,
|
|
1314
|
-
});
|
|
1315
|
-
if (ctx.hasUI) {
|
|
1316
|
-
ctx.ui.setStatus("rp", undefined);
|
|
1317
|
-
}
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
// Restore binding from the current branch on /fork navigation
|
|
1321
|
-
pi.on("session_fork", async (_event: unknown, ctx: ExtensionContext) => {
|
|
1322
|
-
clearReadcacheCaches();
|
|
1323
|
-
restoreBinding(ctx, config);
|
|
1324
|
-
await syncAutoSelectionToCurrentBranch(ctx, {
|
|
1325
|
-
provisionTab: false,
|
|
1326
|
-
recoverClosedTab: true,
|
|
1327
|
-
reuseSoleEmptyTab: true,
|
|
1328
|
-
});
|
|
1329
|
-
if (ctx.hasUI) {
|
|
1330
|
-
ctx.ui.setStatus("rp", undefined);
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
|
|
1334
|
-
// Backwards compatibility (older pi versions)
|
|
1335
|
-
(pi as any).on("session_branch", async (_event: unknown, ctx: ExtensionContext) => {
|
|
1336
|
-
clearReadcacheCaches();
|
|
1337
|
-
restoreBinding(ctx, config);
|
|
1338
|
-
await syncAutoSelectionToCurrentBranch(ctx, {
|
|
1339
|
-
provisionTab: false,
|
|
1340
|
-
recoverClosedTab: true,
|
|
1341
|
-
reuseSoleEmptyTab: true,
|
|
1342
|
-
});
|
|
1343
|
-
if (ctx.hasUI) {
|
|
1344
|
-
ctx.ui.setStatus("rp", undefined);
|
|
1345
|
-
}
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
pi.on("session_tree", async (_event: unknown, ctx: ExtensionContext) => {
|
|
1349
|
-
clearReadcacheCaches();
|
|
1350
|
-
restoreBinding(ctx, config);
|
|
1351
|
-
await syncAutoSelectionToCurrentBranch(ctx, {
|
|
1352
|
-
provisionTab: false,
|
|
1353
|
-
recoverClosedTab: true,
|
|
1354
|
-
reuseSoleEmptyTab: true,
|
|
1355
|
-
});
|
|
1492
|
+
await syncAutoSelectionToCurrentBranch(ctx, TRANSITION_AUTO_SELECTION_SYNC_OPTIONS, "refresh");
|
|
1356
1493
|
if (ctx.hasUI) {
|
|
1357
1494
|
ctx.ui.setStatus("rp", undefined);
|
|
1358
1495
|
}
|
|
@@ -1619,9 +1756,10 @@ export default function repopromptMcp(pi: ExtensionAPI) {
|
|
|
1619
1756
|
const wasPaused = extensionPaused;
|
|
1620
1757
|
try {
|
|
1621
1758
|
await resetRpClient();
|
|
1759
|
+
clearRootsCache();
|
|
1622
1760
|
extensionPaused = false;
|
|
1623
1761
|
await initializeExtension(pi, ctx, config);
|
|
1624
|
-
await syncAutoSelectionToCurrentBranch(ctx);
|
|
1762
|
+
await syncAutoSelectionToCurrentBranch(ctx, reconnectAutoSelectionSyncOptions());
|
|
1625
1763
|
ctx.ui.notify("RepoPrompt reconnected", "info");
|
|
1626
1764
|
|
|
1627
1765
|
if (wasPaused) {
|
|
@@ -1890,7 +2028,7 @@ Mode priority: call > describe > search > windows > bind > status`,
|
|
|
1890
2028
|
|
|
1891
2029
|
if (ctx) {
|
|
1892
2030
|
try {
|
|
1893
|
-
await syncAutoSelectionToCurrentBranch(ctx);
|
|
2031
|
+
await syncAutoSelectionToCurrentBranch(ctx, reconnectAutoSelectionSyncOptions());
|
|
1894
2032
|
} catch {
|
|
1895
2033
|
// Fail-open
|
|
1896
2034
|
}
|
|
@@ -2141,7 +2279,7 @@ Mode priority: call > describe > search > windows > bind > status`,
|
|
|
2141
2279
|
);
|
|
2142
2280
|
|
|
2143
2281
|
if (autoSelectionStatesEqual(baseState, nextState)) {
|
|
2144
|
-
|
|
2282
|
+
commitLiveAutoSelectionState(nextState);
|
|
2145
2283
|
return;
|
|
2146
2284
|
}
|
|
2147
2285
|
|
|
@@ -2170,7 +2308,7 @@ Mode priority: call > describe > search > windows > bind > status`,
|
|
|
2170
2308
|
}
|
|
2171
2309
|
|
|
2172
2310
|
if (!plan.desiredSlice) {
|
|
2173
|
-
|
|
2311
|
+
commitLiveAutoSelectionState(plan.nextState);
|
|
2174
2312
|
return;
|
|
2175
2313
|
}
|
|
2176
2314
|
|
|
@@ -2203,7 +2341,7 @@ Mode priority: call > describe > search > windows > bind > status`,
|
|
|
2203
2341
|
);
|
|
2204
2342
|
|
|
2205
2343
|
if (autoSelectionStatesEqual(baseState, nextState)) {
|
|
2206
|
-
|
|
2344
|
+
commitLiveAutoSelectionState(nextState);
|
|
2207
2345
|
return;
|
|
2208
2346
|
}
|
|
2209
2347
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { AutoSelectionEntryData, RpBinding } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export type PendingTransitionRetryMode = "startup" | "transition";
|
|
4
|
+
|
|
5
|
+
export interface PendingTransitionTargetIdentity {
|
|
6
|
+
sessionFile: string | null;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PendingTransitionSelectionState {
|
|
11
|
+
retryMode: PendingTransitionRetryMode | null;
|
|
12
|
+
sourceState: AutoSelectionEntryData | null;
|
|
13
|
+
targetIdentity: PendingTransitionTargetIdentity | null;
|
|
14
|
+
targetBinding: RpBinding | null;
|
|
15
|
+
targetState: AutoSelectionEntryData | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let pendingTransitionSelectionState: PendingTransitionSelectionState | null = null;
|
|
19
|
+
|
|
20
|
+
function cloneBinding(binding: RpBinding | null): RpBinding | null {
|
|
21
|
+
if (!binding) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
windowId: binding.windowId,
|
|
27
|
+
tab: binding.tab,
|
|
28
|
+
workspace: binding.workspace,
|
|
29
|
+
autoDetected: binding.autoDetected,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cloneIdentity(identity: PendingTransitionTargetIdentity | null): PendingTransitionTargetIdentity | null {
|
|
34
|
+
if (!identity) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
sessionFile: identity.sessionFile,
|
|
40
|
+
sessionId: identity.sessionId,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function cloneRange(range: { start_line: number; end_line: number }) {
|
|
45
|
+
return {
|
|
46
|
+
start_line: range.start_line,
|
|
47
|
+
end_line: range.end_line,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cloneState(state: AutoSelectionEntryData | null): AutoSelectionEntryData | null {
|
|
52
|
+
if (!state) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
windowId: state.windowId,
|
|
58
|
+
tab: state.tab,
|
|
59
|
+
workspace: state.workspace,
|
|
60
|
+
fullPaths: [...state.fullPaths],
|
|
61
|
+
slicePaths: state.slicePaths.map((slice) => ({
|
|
62
|
+
path: slice.path,
|
|
63
|
+
ranges: slice.ranges.map(cloneRange),
|
|
64
|
+
})),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function clonePendingState(
|
|
69
|
+
state: PendingTransitionSelectionState | null
|
|
70
|
+
): PendingTransitionSelectionState | null {
|
|
71
|
+
if (!state) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
retryMode: state.retryMode,
|
|
77
|
+
sourceState: cloneState(state.sourceState),
|
|
78
|
+
targetIdentity: cloneIdentity(state.targetIdentity),
|
|
79
|
+
targetBinding: cloneBinding(state.targetBinding),
|
|
80
|
+
targetState: cloneState(state.targetState),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function setPendingState(state: PendingTransitionSelectionState | null): void {
|
|
85
|
+
const cloned = clonePendingState(state);
|
|
86
|
+
if (!cloned) {
|
|
87
|
+
pendingTransitionSelectionState = null;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!cloned.sourceState && !cloned.targetBinding && !cloned.targetState) {
|
|
92
|
+
pendingTransitionSelectionState = null;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pendingTransitionSelectionState = cloned;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getPendingTransitionState(): PendingTransitionSelectionState | null {
|
|
100
|
+
return clonePendingState(pendingTransitionSelectionState);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getPendingTransitionSelectionState(): AutoSelectionEntryData | null {
|
|
104
|
+
return cloneState(pendingTransitionSelectionState?.sourceState ?? null);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function setPendingTransitionSelectionState(
|
|
108
|
+
state: AutoSelectionEntryData | null,
|
|
109
|
+
retryMode: PendingTransitionRetryMode | null = state
|
|
110
|
+
? (pendingTransitionSelectionState?.retryMode ?? "transition")
|
|
111
|
+
: pendingTransitionSelectionState?.retryMode ?? null
|
|
112
|
+
): void {
|
|
113
|
+
setPendingState({
|
|
114
|
+
retryMode,
|
|
115
|
+
sourceState: state,
|
|
116
|
+
targetIdentity: pendingTransitionSelectionState?.targetIdentity ?? null,
|
|
117
|
+
targetBinding: pendingTransitionSelectionState?.targetBinding ?? null,
|
|
118
|
+
targetState: pendingTransitionSelectionState?.targetState ?? null,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function setPendingTransitionTargetState(
|
|
123
|
+
identity: PendingTransitionTargetIdentity | null,
|
|
124
|
+
binding: RpBinding | null,
|
|
125
|
+
state: AutoSelectionEntryData | null,
|
|
126
|
+
retryMode: PendingTransitionRetryMode | null = pendingTransitionSelectionState?.retryMode ?? null
|
|
127
|
+
): void {
|
|
128
|
+
setPendingState({
|
|
129
|
+
retryMode,
|
|
130
|
+
sourceState: pendingTransitionSelectionState?.sourceState ?? null,
|
|
131
|
+
targetIdentity: identity,
|
|
132
|
+
targetBinding: binding,
|
|
133
|
+
targetState: state,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function clearPendingTransitionSelectionState(): void {
|
|
138
|
+
pendingTransitionSelectionState = null;
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-repoprompt-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "A token-efficient RepoPrompt integration for Pi with automated and branch-safe workspace management",
|
|
5
5
|
"keywords": ["pi-package", "pi", "pi-coding-agent", "repoprompt", "mcp"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"diff": "^7.0.0"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@mariozechner/pi-coding-agent": "0.
|
|
25
|
+
"@mariozechner/pi-coding-agent": "^0.65.0",
|
|
26
26
|
"@mariozechner/pi-tui": "*",
|
|
27
27
|
"@sinclair/typebox": "*"
|
|
28
28
|
},
|