78 lines
4.1 KiB
TypeScript
78 lines
4.1 KiB
TypeScript
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
import * as vscode from "vscode";
|
|
import type { CowritingApi } from "../../../src/extension";
|
|
import { settleUntil } from "../suite/helpers";
|
|
|
|
/**
|
|
* Task 9 (spec §7.1 rung 3 — the real @cline/sdk, manual gate). Drives the
|
|
* SAME comments-first ask -> reply -> offer -> proposal loop as
|
|
* test/e2e/suite/commentLoop.test.ts / fullLoop.test.ts, but with BOTH turns
|
|
* left UNSTUBBED: ThreadController's reply loop and EditFlow's edit turn hit
|
|
* the real local `claude` login (INV-8 -- the extension itself holds no
|
|
* credentials). Not part of `npm run test:e2e` / CI -- driven only by
|
|
* `scripts/smoke-native-loop.mjs` (`npm run smoke:native`), mirroring
|
|
* `scripts/smoke-live-turn.mjs`'s "manual gate, real SDK" role for F3.
|
|
*
|
|
* There is no VS Code API to drive the native comment-widget's OWN submit
|
|
* gesture from outside the UI (see commentLoop.test.ts's "Finding 1" note) --
|
|
* this calls the exact seam a real comment submission reaches
|
|
* (ThreadController.createThreadOnSelection / .makeThreadEdit), which is the
|
|
* closest a headless script gets to "post a real comment" on a live doc.
|
|
*/
|
|
export async function run(): Promise<void> {
|
|
const ext = vscode.extensions.getExtension("benstull.vscode-cowriting-plugin");
|
|
if (!ext) throw new Error("extension not found -- is extensionDevelopmentPath wired correctly?");
|
|
const api = (await ext.activate()) as CowritingApi;
|
|
if (!api?.threadController || !api?.editFlow || !api?.proposalController || !api?.sidecarRouter) {
|
|
throw new Error("extension did not export the expected controllers");
|
|
}
|
|
|
|
const folder = vscode.workspace.workspaceFolders?.[0];
|
|
if (!folder) throw new Error("no workspace folder -- launch with the sandbox/ folder open");
|
|
const target = path.join(folder.uri.fsPath, `.smoke-native-loop-${process.pid}.md`);
|
|
const ORIGINAL = "The quick brown fox jumps over the lazy dog paragraph.";
|
|
fs.writeFileSync(target, `# Native-loop smoke\n\n${ORIGINAL}\n`, "utf8");
|
|
|
|
try {
|
|
const uri = vscode.Uri.file(target);
|
|
const doc = await vscode.workspace.openTextDocument(uri);
|
|
const ed = await vscode.window.showTextDocument(doc);
|
|
await vscode.commands.executeCommand("cowriting.coeditDocument");
|
|
|
|
const docKey = api.proposalController.keyFor(doc);
|
|
const start = doc.getText().indexOf(ORIGINAL);
|
|
ed.selection = new vscode.Selection(doc.positionAt(start), doc.positionAt(start + ORIGINAL.length));
|
|
|
|
console.log("[smoke-native-loop] posting a real comment (createThreadOnSelection)...");
|
|
const threadId = await api.threadController.createThreadOnSelection(
|
|
"Rewrite this sentence to be more concise.",
|
|
);
|
|
if (!threadId) throw new Error("createThreadOnSelection refused (not coediting, or no selection)");
|
|
console.log(`[smoke-native-loop] thread created: ${threadId} -- waiting for the real @cline/sdk reply (this calls the live SDK; may take a while)...`);
|
|
|
|
await settleUntil(() => {
|
|
const t = api.sidecarRouter.load(docKey)?.threads.find((x) => x.id === threadId);
|
|
return (t?.messages.length ?? 0) >= 2 && t!.messages[1].author.kind === "agent";
|
|
}, 180000);
|
|
console.log("[smoke-native-loop] reply landed -- thread flipped to \"offer\". Making the edit (real SDK)...");
|
|
|
|
const ids = await api.threadController.makeThreadEdit(threadId, docKey);
|
|
if (ids.length === 0) throw new Error("makeThreadEdit produced no proposals (the turn returned an empty/no-op replacement)");
|
|
console.log(`[smoke-native-loop] proposal id(s) created: ${ids.join(", ")}`);
|
|
|
|
await settleUntil(() => ids.every((id) => api.proposalController.isApplied(docKey, id)), 10000);
|
|
for (const id of ids) {
|
|
const ok = await api.proposalController.finalizeInPlace(docKey, id);
|
|
console.log(`[smoke-native-loop] accepted proposal ${id}: ${ok}`);
|
|
}
|
|
console.log(`[smoke-native-loop] PASS -- ${ids.length} proposal(s) created and accepted: ${ids.join(", ")}`);
|
|
} finally {
|
|
try {
|
|
fs.rmSync(target, { force: true });
|
|
} catch {
|
|
// best-effort cleanup -- never let cleanup mask the real result
|
|
}
|
|
}
|
|
}
|