F4: propose/accept diff flow — pending proposals, ✓/✗ review, seam-applied acceptance (Feature #12) #13
+24
-10
@@ -138,8 +138,9 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// F3 SLICE-5: the live turn (PUC-2) — selection + instruction → one
|
// F3 SLICE-5 → F4 SLICE-4: the live turn — selection + instruction → one
|
||||||
// claude-code SDK turn (liveTurn.ts, INV-8) → the applyAgentEdit seam (INV-9).
|
// claude-code SDK turn (liveTurn.ts, INV-8) → a PENDING PROPOSAL (F4,
|
||||||
|
// INV-10); the applyAgentEdit seam (INV-9) now fires only on accept.
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
vscode.commands.registerCommand("cowriting.editSelection", async () => {
|
vscode.commands.registerCommand("cowriting.editSelection", async () => {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
@@ -163,8 +164,13 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
|||||||
}
|
}
|
||||||
const document = editor.document;
|
const document = editor.document;
|
||||||
const selection = editor.selection;
|
const selection = editor.selection;
|
||||||
const expectedVersion = document.version;
|
|
||||||
const selectedText = document.getText(selection);
|
const selectedText = document.getText(selection);
|
||||||
|
// Capture the anchor BEFORE the turn (spec §6.5 PUC-1): mid-turn edits
|
||||||
|
// can't skew it — the proposal renders wherever the target re-resolves.
|
||||||
|
const fp = buildFingerprint(document.getText(), {
|
||||||
|
start: document.offsetAt(selection.start),
|
||||||
|
end: document.offsetAt(selection.end),
|
||||||
|
});
|
||||||
const turnId = `turn-${Date.now().toString(36)}`;
|
const turnId = `turn-${Date.now().toString(36)}`;
|
||||||
try {
|
try {
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
@@ -174,24 +180,32 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
|||||||
const turn = await runEditTurn(instruction, selectedText);
|
const turn = await runEditTurn(instruction, selectedText);
|
||||||
if (turn.replacement === "") {
|
if (turn.replacement === "") {
|
||||||
void vscode.window.showWarningMessage(
|
void vscode.window.showWarningMessage(
|
||||||
"Cowriting: Claude returned an empty replacement — nothing was applied.",
|
"Cowriting: Claude returned an empty replacement — nothing was proposed.",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ok = await attributionController.applyAgentEdit(
|
if (turn.replacement === selectedText) {
|
||||||
|
void vscode.window.showInformationMessage(
|
||||||
|
"Cowriting: Claude proposed no change to the selection.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// F4 (INV-10): the turn ends in a PROPOSAL, not a buffer mutation.
|
||||||
|
// The seam now fires only on accept (ProposalController, INV-9).
|
||||||
|
const id = await proposalController.propose(
|
||||||
document,
|
document,
|
||||||
new vscode.Range(selection.start, selection.end),
|
fp,
|
||||||
turn.replacement,
|
turn.replacement,
|
||||||
{
|
{
|
||||||
kind: "agent",
|
kind: "agent",
|
||||||
id: "claude",
|
id: "claude",
|
||||||
agent: { sdk: "@cline/sdk", model: turn.model, sessionId: turn.sessionId },
|
agent: { sdk: "@cline/sdk", model: turn.model, sessionId: turn.sessionId },
|
||||||
},
|
},
|
||||||
{ expectedVersion, turnId },
|
{ turnId, instruction },
|
||||||
);
|
);
|
||||||
if (!ok) {
|
if (id) {
|
||||||
void vscode.window.showWarningMessage(
|
void vscode.window.showInformationMessage(
|
||||||
"Cowriting: the edit could not be applied (the document changed during the turn, or the editor rejected the edit) — nothing was changed.",
|
"Cowriting: Claude proposed an edit — review the diff at the highlighted range (✓ accept / ✗ reject).",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user