feat: add cursor local adapter across server ui and cli
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
||||
DEFAULT_CODEX_LOCAL_MODEL,
|
||||
} from "@paperclipai/adapter-codex-local";
|
||||
import { DEFAULT_CURSOR_LOCAL_MODEL } from "@paperclipai/adapter-cursor-local";
|
||||
import { DEFAULT_OPENCODE_LOCAL_MODEL } from "@paperclipai/adapter-opencode-local";
|
||||
import {
|
||||
Popover,
|
||||
@@ -140,6 +141,12 @@ const opencodeVariantOptions = [
|
||||
{ id: "max", label: "Max" },
|
||||
] as const;
|
||||
|
||||
const cursorModeOptions = [
|
||||
{ id: "", label: "Auto" },
|
||||
{ id: "plan", label: "Plan" },
|
||||
{ id: "ask", label: "Ask" },
|
||||
] as const;
|
||||
|
||||
const claudeThinkingEffortOptions = [
|
||||
{ id: "", label: "Auto" },
|
||||
{ id: "low", label: "Low" },
|
||||
@@ -267,7 +274,8 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
const isLocal =
|
||||
adapterType === "claude_local" ||
|
||||
adapterType === "codex_local" ||
|
||||
adapterType === "opencode_local";
|
||||
adapterType === "opencode_local" ||
|
||||
adapterType === "cursor";
|
||||
const uiAdapter = useMemo(() => getUIAdapter(adapterType), [adapterType]);
|
||||
|
||||
// Fetch adapter models for the effective adapter type
|
||||
@@ -329,12 +337,16 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
const thinkingEffortKey =
|
||||
adapterType === "codex_local"
|
||||
? "modelReasoningEffort"
|
||||
: adapterType === "cursor"
|
||||
? "mode"
|
||||
: adapterType === "opencode_local"
|
||||
? "variant"
|
||||
: "effort";
|
||||
const thinkingEffortOptions =
|
||||
adapterType === "codex_local"
|
||||
? codexThinkingEffortOptions
|
||||
: adapterType === "cursor"
|
||||
? cursorModeOptions
|
||||
: adapterType === "opencode_local"
|
||||
? opencodeVariantOptions
|
||||
: claudeThinkingEffortOptions;
|
||||
@@ -346,6 +358,8 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
"modelReasoningEffort",
|
||||
String(config.modelReasoningEffort ?? config.reasoningEffort ?? ""),
|
||||
)
|
||||
: adapterType === "cursor"
|
||||
? eff("adapterConfig", "mode", String(config.mode ?? ""))
|
||||
: adapterType === "opencode_local"
|
||||
? eff("adapterConfig", "variant", String(config.variant ?? ""))
|
||||
: eff("adapterConfig", "effort", String(config.effort ?? ""));
|
||||
@@ -466,12 +480,14 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
nextValues.model = DEFAULT_CODEX_LOCAL_MODEL;
|
||||
nextValues.dangerouslyBypassSandbox =
|
||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX;
|
||||
} else if (t === "cursor") {
|
||||
nextValues.model = DEFAULT_CURSOR_LOCAL_MODEL;
|
||||
} else if (t === "opencode_local") {
|
||||
nextValues.model = DEFAULT_OPENCODE_LOCAL_MODEL;
|
||||
}
|
||||
set!(nextValues);
|
||||
} else {
|
||||
// Clear all adapter config and explicitly blank out model + both effort keys
|
||||
// Clear all adapter config and explicitly blank out model + effort/mode keys
|
||||
// so the old adapter's values don't bleed through via eff()
|
||||
setOverlay((prev) => ({
|
||||
...prev,
|
||||
@@ -480,12 +496,15 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
model:
|
||||
t === "codex_local"
|
||||
? DEFAULT_CODEX_LOCAL_MODEL
|
||||
: t === "cursor"
|
||||
? DEFAULT_CURSOR_LOCAL_MODEL
|
||||
: t === "opencode_local"
|
||||
? DEFAULT_OPENCODE_LOCAL_MODEL
|
||||
: "",
|
||||
effort: "",
|
||||
modelReasoningEffort: "",
|
||||
variant: "",
|
||||
mode: "",
|
||||
...(t === "codex_local"
|
||||
? {
|
||||
dangerouslyBypassApprovalsAndSandbox:
|
||||
@@ -584,6 +603,8 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
placeholder={
|
||||
adapterType === "codex_local"
|
||||
? "codex"
|
||||
: adapterType === "cursor"
|
||||
? "agent"
|
||||
: adapterType === "opencode_local"
|
||||
? "opencode"
|
||||
: "claude"
|
||||
@@ -855,7 +876,7 @@ function AdapterEnvironmentResult({ result }: { result: AdapterEnvironmentTestRe
|
||||
|
||||
/* ---- Internal sub-components ---- */
|
||||
|
||||
const ENABLED_ADAPTER_TYPES = new Set(["claude_local", "codex_local", "opencode_local"]);
|
||||
const ENABLED_ADAPTER_TYPES = new Set(["claude_local", "codex_local", "opencode_local", "cursor"]);
|
||||
|
||||
/** Display list includes all real adapter types plus UI-only coming-soon entries. */
|
||||
const ADAPTER_DISPLAY_LIST: { value: string; label: string; comingSoon: boolean }[] = [
|
||||
@@ -864,7 +885,6 @@ const ADAPTER_DISPLAY_LIST: { value: string; label: string; comingSoon: boolean
|
||||
label: adapterLabels[t] ?? t,
|
||||
comingSoon: !ENABLED_ADAPTER_TYPES.has(t),
|
||||
})),
|
||||
{ value: "cursor", label: "Cursor", comingSoon: true },
|
||||
];
|
||||
|
||||
function AdapterTypeDropdown({
|
||||
|
||||
@@ -19,7 +19,7 @@ const adapterLabels: Record<string, string> = {
|
||||
codex_local: "Codex (local)",
|
||||
opencode_local: "OpenCode (local)",
|
||||
openclaw: "OpenClaw",
|
||||
cursor: "Cursor",
|
||||
cursor: "Cursor (local)",
|
||||
process: "Process",
|
||||
http: "HTTP",
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
||||
DEFAULT_CODEX_LOCAL_MODEL
|
||||
} from "@paperclipai/adapter-codex-local";
|
||||
import { DEFAULT_CURSOR_LOCAL_MODEL } from "@paperclipai/adapter-cursor-local";
|
||||
import { DEFAULT_OPENCODE_LOCAL_MODEL } from "@paperclipai/adapter-opencode-local";
|
||||
import { AsciiArtAnimation } from "./AsciiArtAnimation";
|
||||
import { ChoosePathButton } from "./PathInstructionsModal";
|
||||
@@ -51,6 +52,7 @@ type AdapterType =
|
||||
| "claude_local"
|
||||
| "codex_local"
|
||||
| "opencode_local"
|
||||
| "cursor"
|
||||
| "process"
|
||||
| "http"
|
||||
| "openclaw";
|
||||
@@ -153,11 +155,13 @@ export function OnboardingWizard() {
|
||||
enabled: onboardingOpen && step === 2
|
||||
});
|
||||
const isLocalAdapter =
|
||||
adapterType === "claude_local" || adapterType === "codex_local" || adapterType === "opencode_local";
|
||||
adapterType === "claude_local" || adapterType === "codex_local" || adapterType === "opencode_local" || adapterType === "cursor";
|
||||
const effectiveAdapterCommand =
|
||||
command.trim() ||
|
||||
(adapterType === "codex_local"
|
||||
? "codex"
|
||||
: adapterType === "cursor"
|
||||
? "agent"
|
||||
: adapterType === "opencode_local"
|
||||
? "opencode"
|
||||
: "claude");
|
||||
@@ -219,6 +223,8 @@ export function OnboardingWizard() {
|
||||
model:
|
||||
adapterType === "codex_local"
|
||||
? model || DEFAULT_CODEX_LOCAL_MODEL
|
||||
: adapterType === "cursor"
|
||||
? model || DEFAULT_CURSOR_LOCAL_MODEL
|
||||
: adapterType === "opencode_local"
|
||||
? model || DEFAULT_OPENCODE_LOCAL_MODEL
|
||||
: model,
|
||||
@@ -598,8 +604,7 @@ export function OnboardingWizard() {
|
||||
value: "cursor" as const,
|
||||
label: "Cursor",
|
||||
icon: MousePointer2,
|
||||
desc: "Cursor AI agent",
|
||||
comingSoon: true
|
||||
desc: "Local Cursor agent"
|
||||
},
|
||||
{
|
||||
value: "process" as const,
|
||||
@@ -633,6 +638,8 @@ export function OnboardingWizard() {
|
||||
setAdapterType(nextType);
|
||||
if (nextType === "codex_local" && !model) {
|
||||
setModel(DEFAULT_CODEX_LOCAL_MODEL);
|
||||
} else if (nextType === "cursor" && !model) {
|
||||
setModel(DEFAULT_CURSOR_LOCAL_MODEL);
|
||||
} else if (nextType === "opencode_local" && !model) {
|
||||
setModel(DEFAULT_OPENCODE_LOCAL_MODEL);
|
||||
}
|
||||
@@ -656,7 +663,8 @@ export function OnboardingWizard() {
|
||||
{/* Conditional adapter fields */}
|
||||
{(adapterType === "claude_local" ||
|
||||
adapterType === "codex_local" ||
|
||||
adapterType === "opencode_local") && (
|
||||
adapterType === "opencode_local" ||
|
||||
adapterType === "cursor") && (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
@@ -789,7 +797,9 @@ export function OnboardingWizard() {
|
||||
<div className="rounded-md border border-border/70 bg-muted/20 px-2.5 py-2 text-[11px] space-y-1.5">
|
||||
<p className="font-medium">Manual debug</p>
|
||||
<p className="text-muted-foreground font-mono break-all">
|
||||
{adapterType === "codex_local"
|
||||
{adapterType === "cursor"
|
||||
? `${effectiveAdapterCommand} -p --mode ask --output-format json \"Respond with hello.\"`
|
||||
: adapterType === "codex_local"
|
||||
? `${effectiveAdapterCommand} exec --json -`
|
||||
: adapterType === "opencode_local"
|
||||
? `${effectiveAdapterCommand} run --format json \"Respond with hello.\"`
|
||||
@@ -799,13 +809,20 @@ export function OnboardingWizard() {
|
||||
Prompt:{" "}
|
||||
<span className="font-mono">Respond with hello.</span>
|
||||
</p>
|
||||
{adapterType === "codex_local" || adapterType === "opencode_local" ? (
|
||||
{adapterType === "cursor" || adapterType === "codex_local" || adapterType === "opencode_local" ? (
|
||||
<p className="text-muted-foreground">
|
||||
If auth fails, set{" "}
|
||||
<span className="font-mono">OPENAI_API_KEY</span> in
|
||||
<span className="font-mono">
|
||||
{adapterType === "cursor" ? "CURSOR_API_KEY" : "OPENAI_API_KEY"}
|
||||
</span>{" "}
|
||||
in
|
||||
env or run{" "}
|
||||
<span className="font-mono">
|
||||
{adapterType === "codex_local" ? "codex login" : "opencode auth login"}
|
||||
{adapterType === "cursor"
|
||||
? "agent login"
|
||||
: adapterType === "codex_local"
|
||||
? "codex login"
|
||||
: "opencode auth login"}
|
||||
</span>.
|
||||
</p>
|
||||
) : (
|
||||
|
||||
@@ -54,7 +54,7 @@ export const adapterLabels: Record<string, string> = {
|
||||
codex_local: "Codex (local)",
|
||||
opencode_local: "OpenCode (local)",
|
||||
openclaw: "OpenClaw",
|
||||
cursor: "Cursor",
|
||||
cursor: "Cursor (local)",
|
||||
process: "Process",
|
||||
http: "HTTP",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user