F4: propose/accept diff flow — pending proposals, ✓/✗ review, seam-applied acceptance (Feature #12) #13
+18
-2
@@ -30,11 +30,17 @@
|
||||
{ "command": "cowriting.reopenThread", "title": "Reopen Thread", "category": "Cowriting" },
|
||||
{ "command": "cowriting.toggleAttribution", "title": "Toggle Attribution", "category": "Cowriting" },
|
||||
{ "command": "cowriting.applyAgentEdit", "title": "Apply Agent Edit (internal seam)", "category": "Cowriting" },
|
||||
{ "command": "cowriting.editSelection", "title": "Ask Claude to Edit Selection", "category": "Cowriting" }
|
||||
{ "command": "cowriting.editSelection", "title": "Ask Claude to Edit Selection", "category": "Cowriting" },
|
||||
{ "command": "cowriting.acceptProposal", "title": "✓ Accept Proposal", "category": "Cowriting" },
|
||||
{ "command": "cowriting.rejectProposal", "title": "✗ Reject Proposal", "category": "Cowriting" },
|
||||
{ "command": "cowriting.proposeAgentEdit", "title": "Propose Agent Edit (internal seam)", "category": "Cowriting" }
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{ "command": "cowriting.applyAgentEdit", "when": "false" }
|
||||
{ "command": "cowriting.applyAgentEdit", "when": "false" },
|
||||
{ "command": "cowriting.proposeAgentEdit", "when": "false" },
|
||||
{ "command": "cowriting.acceptProposal", "when": "false" },
|
||||
{ "command": "cowriting.rejectProposal", "when": "false" }
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
@@ -61,6 +67,16 @@
|
||||
"command": "cowriting.reopenThread",
|
||||
"group": "inline",
|
||||
"when": "commentController == cowriting.threads && commentThread =~ /^resolved$/"
|
||||
},
|
||||
{
|
||||
"command": "cowriting.acceptProposal",
|
||||
"group": "inline@1",
|
||||
"when": "commentController == cowriting.proposals && commentThread =~ /^pending$/"
|
||||
},
|
||||
{
|
||||
"command": "cowriting.rejectProposal",
|
||||
"group": "inline@2",
|
||||
"when": "commentController == cowriting.proposals"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+38
-1
@@ -3,12 +3,15 @@ import { fetchSdkSummary } from "./cline";
|
||||
import { CoauthorStore } from "./store";
|
||||
import { ThreadController } from "./threadController";
|
||||
import { AttributionController } from "./attributionController";
|
||||
import { ProposalController } from "./proposalController";
|
||||
import { buildFingerprint } from "./anchorer";
|
||||
|
||||
const CHANNEL_NAME = "Cowriting (Cline SDK)";
|
||||
|
||||
export interface CowritingApi {
|
||||
threadController: ThreadController;
|
||||
attributionController: AttributionController;
|
||||
proposalController: ProposalController;
|
||||
}
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): CowritingApi | undefined {
|
||||
@@ -55,6 +58,9 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
||||
"cowriting.editSelection",
|
||||
"cowriting.toggleAttribution",
|
||||
"cowriting.applyAgentEdit",
|
||||
"cowriting.acceptProposal",
|
||||
"cowriting.rejectProposal",
|
||||
"cowriting.proposeAgentEdit",
|
||||
]) {
|
||||
context.subscriptions.push(vscode.commands.registerCommand(command, stub));
|
||||
}
|
||||
@@ -68,6 +74,10 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
||||
const attributionController = new AttributionController(store, root);
|
||||
context.subscriptions.push(attributionController);
|
||||
|
||||
// --- F4: propose/accept (Feature #12) ---
|
||||
const proposalController = new ProposalController(store, attributionController, root);
|
||||
context.subscriptions.push(proposalController);
|
||||
|
||||
// One SHARED sidecar watcher for both controllers; self-writes are
|
||||
// suppressed centrally in the store (the sidecar is co-owned).
|
||||
const watcher = vscode.workspace.createFileSystemWatcher("**/.threads/**/*.json");
|
||||
@@ -75,6 +85,7 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
||||
if (store.consumeSelfWrite(uri.fsPath)) return;
|
||||
threadController.handleExternalSidecarChange(uri);
|
||||
attributionController.handleExternalSidecarChange(uri);
|
||||
proposalController.handleExternalSidecarChange(uri);
|
||||
};
|
||||
watcher.onDidChange(onSidecar);
|
||||
watcher.onDidCreate(onSidecar);
|
||||
@@ -102,6 +113,31 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
||||
),
|
||||
);
|
||||
|
||||
// The propose ingress as a command (spec §6.4): records a pending proposal,
|
||||
// NEVER touches the document (INV-10) — for the host E2E harness.
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
"cowriting.proposeAgentEdit",
|
||||
(args: {
|
||||
uri: string; start: number; end: number; newText: string;
|
||||
model?: string; sessionId?: string; turnId?: string; instruction?: string;
|
||||
}) => {
|
||||
const doc = vscode.workspace.textDocuments.find((d) => d.uri.toString() === args.uri);
|
||||
if (!doc) return Promise.resolve(undefined);
|
||||
const fp = buildFingerprint(doc.getText(), { start: args.start, end: args.end });
|
||||
const provenance = {
|
||||
kind: "agent" as const,
|
||||
id: "claude",
|
||||
agent: { sdk: "@cline/sdk", model: args.model ?? "sonnet", sessionId: args.sessionId ?? "" },
|
||||
};
|
||||
return proposalController.propose(doc, fp, args.newText, provenance, {
|
||||
turnId: args.turnId,
|
||||
instruction: args.instruction,
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// F3 SLICE-5: the live turn (PUC-2) — selection + instruction → one
|
||||
// claude-code SDK turn (liveTurn.ts, INV-8) → the applyAgentEdit seam (INV-9).
|
||||
context.subscriptions.push(
|
||||
@@ -172,12 +208,13 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
|
||||
if (doc.uri.scheme === "file" && doc.uri.fsPath.startsWith(root)) {
|
||||
threadController.renderAll(doc);
|
||||
attributionController.loadAll(doc);
|
||||
proposalController.renderAll(doc);
|
||||
}
|
||||
};
|
||||
vscode.workspace.textDocuments.forEach(renderIfOpen);
|
||||
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(renderIfOpen));
|
||||
|
||||
return { threadController, attributionController };
|
||||
return { threadController, attributionController, proposalController };
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
|
||||
Reference in New Issue
Block a user