coder(providers): remove retired cursor and copilot providers
Drop both retired providers from BooCoder's provider layer: acp-spawn argv cases, provider-manifest mode blocks + manifest keys, provider-commands maps, the provider-snapshot cursor model-CLI branch (+ orphaned exec/promisify imports), the agent-probe copilot ACP-detect branch, and the now-dead cursor-models module + its test. The PROVIDERS registry array already lacked both. Built-ins unchanged: claude, opencode, goose, qwen, native boocode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,47 +0,0 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import { parseCursorAgentModelsOutput } from '../cursor-models.js';
|
|
||||||
|
|
||||||
describe('parseCursorAgentModelsOutput', () => {
|
|
||||||
it('parses cursor-agent models output with default marker', () => {
|
|
||||||
const output = `
|
|
||||||
Available models
|
|
||||||
claude-4-sonnet - Claude 4 Sonnet (default)
|
|
||||||
gpt-4.1 - GPT-4.1
|
|
||||||
Tip: use cursor-agent models for full list
|
|
||||||
`.trim();
|
|
||||||
|
|
||||||
const models = parseCursorAgentModelsOutput(output);
|
|
||||||
|
|
||||||
expect(models).toEqual([
|
|
||||||
{ id: 'claude-4-sonnet', label: 'Claude 4 Sonnet', isDefault: true },
|
|
||||||
{ id: 'gpt-4.1', label: 'GPT-4.1', isDefault: false },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses current marker when no default', () => {
|
|
||||||
const output = `
|
|
||||||
model-a - Model A (current)
|
|
||||||
model-b - Model B
|
|
||||||
`.trim();
|
|
||||||
|
|
||||||
const models = parseCursorAgentModelsOutput(output);
|
|
||||||
|
|
||||||
expect(models.find((m) => m.id === 'model-a')?.isDefault).toBe(true);
|
|
||||||
expect(models.find((m) => m.id === 'model-b')?.isDefault).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('defaults to first model when no markers', () => {
|
|
||||||
const output = 'alpha - Alpha\nbeta - Beta';
|
|
||||||
const models = parseCursorAgentModelsOutput(output);
|
|
||||||
|
|
||||||
expect(models[0]?.isDefault).toBe(true);
|
|
||||||
expect(models[1]?.isDefault).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skips malformed lines', () => {
|
|
||||||
const output = 'no-separator\nvalid - Valid';
|
|
||||||
const models = parseCursorAgentModelsOutput(output);
|
|
||||||
|
|
||||||
expect(models).toEqual([{ id: 'valid', label: 'Valid', isDefault: true }]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -3,7 +3,7 @@ import { getManifestCommands, mergeCommands, PROVIDER_COMMANDS } from '../provid
|
|||||||
|
|
||||||
describe('provider-commands', () => {
|
describe('provider-commands', () => {
|
||||||
it('defines commands for every external harness', () => {
|
it('defines commands for every external harness', () => {
|
||||||
for (const name of ['claude', 'opencode', 'cursor', 'goose', 'qwen', 'copilot']) {
|
for (const name of ['claude', 'opencode', 'goose', 'qwen']) {
|
||||||
expect(getManifestCommands(name).length, name).toBeGreaterThan(0);
|
expect(getManifestCommands(name).length, name).toBeGreaterThan(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ export function resolveAcpSpawnArgs(agent: string): string[] | null {
|
|||||||
case 'opencode':
|
case 'opencode':
|
||||||
case 'goose':
|
case 'goose':
|
||||||
return ['acp'];
|
return ['acp'];
|
||||||
case 'cursor':
|
|
||||||
return ['acp'];
|
|
||||||
case 'copilot':
|
|
||||||
return ['--acp'];
|
|
||||||
case 'qwen':
|
case 'qwen':
|
||||||
return ['--acp'];
|
return ['--acp'];
|
||||||
default:
|
default:
|
||||||
@@ -18,12 +14,5 @@ export function resolveAcpSpawnArgs(agent: string): string[] | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveAcpProbeBinaries(agent: string): string[] {
|
export function resolveAcpProbeBinaries(agent: string): string[] {
|
||||||
switch (agent) {
|
return [agent];
|
||||||
case 'cursor':
|
|
||||||
return ['cursor-agent', 'agent'];
|
|
||||||
case 'copilot':
|
|
||||||
return ['copilot'];
|
|
||||||
default:
|
|
||||||
return [agent];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,6 @@ async function detectAcpSupport(agentName: string, installPath: string): Promise
|
|||||||
const transport = PROVIDERS_BY_NAME.get(agentName)?.transport;
|
const transport = PROVIDERS_BY_NAME.get(agentName)?.transport;
|
||||||
if (transport !== 'acp') return false;
|
if (transport !== 'acp') return false;
|
||||||
|
|
||||||
if (agentName === 'copilot') {
|
|
||||||
try {
|
|
||||||
const { stdout } = await exec(`"${installPath}" --help`, { timeout: 10_000 });
|
|
||||||
return stdout.includes('--acp');
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (agentName === 'qwen') {
|
if (agentName === 'qwen') {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await exec(`"${installPath}" --help`, { timeout: 10_000 });
|
const { stdout } = await exec(`"${installPath}" --help`, { timeout: 10_000 });
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Cursor model list parser — lifted from Paseo cursor-acp-agent.ts
|
|
||||||
*/
|
|
||||||
import type { ProviderModel } from './provider-types.js';
|
|
||||||
|
|
||||||
const CURSOR_MODEL_MARKER_PATTERN = /\s+\((?:default|current)\)$/;
|
|
||||||
|
|
||||||
export function parseCursorAgentModelsOutput(output: string): ProviderModel[] {
|
|
||||||
const parsed = output
|
|
||||||
.split(/\r?\n/)
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.filter((line) => line && line !== 'Available models' && !line.startsWith('Tip:'))
|
|
||||||
.map((line) => {
|
|
||||||
const separatorIndex = line.indexOf(' - ');
|
|
||||||
if (separatorIndex <= 0) return null;
|
|
||||||
|
|
||||||
const id = line.slice(0, separatorIndex).trim();
|
|
||||||
const rawLabel = line.slice(separatorIndex + 3).trim();
|
|
||||||
if (!id || !rawLabel) return null;
|
|
||||||
|
|
||||||
let marker: 'default' | 'current' | null = null;
|
|
||||||
if (rawLabel.endsWith(' (default)')) marker = 'default';
|
|
||||||
else if (rawLabel.endsWith(' (current)')) marker = 'current';
|
|
||||||
|
|
||||||
return { id, label: rawLabel.replace(CURSOR_MODEL_MARKER_PATTERN, ''), marker };
|
|
||||||
})
|
|
||||||
.filter((m): m is { id: string; label: string; marker: 'default' | 'current' | null } => m !== null);
|
|
||||||
|
|
||||||
const defaultModelId =
|
|
||||||
parsed.find((m) => m.marker === 'default')?.id ??
|
|
||||||
parsed.find((m) => m.marker === 'current')?.id ??
|
|
||||||
parsed[0]?.id;
|
|
||||||
|
|
||||||
return parsed.map((model) => ({
|
|
||||||
id: model.id,
|
|
||||||
label: model.label,
|
|
||||||
isDefault: model.id === defaultModelId,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -27,13 +27,6 @@ const OPENCODE_COMMANDS: AgentCommand[] = [
|
|||||||
{ name: 'export', description: 'Export session' },
|
{ name: 'export', description: 'Export session' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const CURSOR_COMMANDS: AgentCommand[] = [
|
|
||||||
{ name: 'help', description: 'Show available slash commands' },
|
|
||||||
{ name: 'clear', description: 'Clear conversation' },
|
|
||||||
{ name: 'compact', description: 'Compact context' },
|
|
||||||
{ name: 'resume', description: 'Resume a prior session' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const GOOSE_COMMANDS: AgentCommand[] = [
|
const GOOSE_COMMANDS: AgentCommand[] = [
|
||||||
{ name: 'help', description: 'Show available commands' },
|
{ name: 'help', description: 'Show available commands' },
|
||||||
{ name: 'clear', description: 'Clear conversation' },
|
{ name: 'clear', description: 'Clear conversation' },
|
||||||
@@ -49,23 +42,12 @@ const QWEN_COMMANDS: AgentCommand[] = [
|
|||||||
{ name: 'review', description: 'Review changes' },
|
{ name: 'review', description: 'Review changes' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const COPILOT_COMMANDS: AgentCommand[] = [
|
|
||||||
{ name: 'help', description: 'Show available commands' },
|
|
||||||
{ name: 'explain', description: 'Explain selected code' },
|
|
||||||
{ name: 'fix', description: 'Fix issues in context' },
|
|
||||||
{ name: 'tests', description: 'Generate or run tests' },
|
|
||||||
{ name: 'doc', description: 'Generate documentation' },
|
|
||||||
{ name: 'clear', description: 'Clear conversation' },
|
|
||||||
];
|
|
||||||
|
|
||||||
/** boocode harness uses /api/skills — merged on the frontend. */
|
/** boocode harness uses /api/skills — merged on the frontend. */
|
||||||
export const PROVIDER_COMMANDS: Record<string, AgentCommand[]> = {
|
export const PROVIDER_COMMANDS: Record<string, AgentCommand[]> = {
|
||||||
claude: CLAUDE_COMMANDS,
|
claude: CLAUDE_COMMANDS,
|
||||||
opencode: OPENCODE_COMMANDS,
|
opencode: OPENCODE_COMMANDS,
|
||||||
cursor: CURSOR_COMMANDS,
|
|
||||||
goose: GOOSE_COMMANDS,
|
goose: GOOSE_COMMANDS,
|
||||||
qwen: QWEN_COMMANDS,
|
qwen: QWEN_COMMANDS,
|
||||||
copilot: COPILOT_COMMANDS,
|
|
||||||
boocode: [],
|
boocode: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,31 +24,6 @@ const OPENCODE_MODES: ProviderMode[] = [
|
|||||||
{ id: 'full-access', label: 'Full Access', description: 'Auto-approves all tool prompts', isUnattended: true },
|
{ id: 'full-access', label: 'Full Access', description: 'Auto-approves all tool prompts', isUnattended: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const COPILOT_MODES: ProviderMode[] = [
|
|
||||||
{
|
|
||||||
id: 'https://agentclientprotocol.com/protocol/session-modes#agent',
|
|
||||||
label: 'Agent',
|
|
||||||
description: 'Default agent mode',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'https://agentclientprotocol.com/protocol/session-modes#plan',
|
|
||||||
label: 'Plan',
|
|
||||||
description: 'Plan mode for multi-step work',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'allow-all',
|
|
||||||
label: 'Allow All',
|
|
||||||
description: 'Automatically approves all tool, path, and URL requests',
|
|
||||||
isUnattended: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const CURSOR_CLI_MODES: ProviderMode[] = [
|
|
||||||
{ id: 'agent', label: 'Agent', description: 'Full agent capabilities with tool access' },
|
|
||||||
{ id: 'plan', label: 'Plan', description: 'Read-only planning mode' },
|
|
||||||
{ id: 'ask', label: 'Ask', description: 'Q&A read-only mode' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const QWEN_PTY_MODES: ProviderMode[] = [
|
const QWEN_PTY_MODES: ProviderMode[] = [
|
||||||
{ id: 'default', label: 'Default', description: 'Prompt for approval' },
|
{ id: 'default', label: 'Default', description: 'Prompt for approval' },
|
||||||
{ id: 'plan', label: 'Plan', description: 'Plan only — no edits' },
|
{ id: 'plan', label: 'Plan', description: 'Plan only — no edits' },
|
||||||
@@ -75,14 +50,6 @@ export const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry> = {
|
|||||||
defaultModeId: 'build',
|
defaultModeId: 'build',
|
||||||
modes: OPENCODE_MODES,
|
modes: OPENCODE_MODES,
|
||||||
},
|
},
|
||||||
copilot: {
|
|
||||||
defaultModeId: 'https://agentclientprotocol.com/protocol/session-modes#agent',
|
|
||||||
modes: COPILOT_MODES,
|
|
||||||
},
|
|
||||||
cursor: {
|
|
||||||
defaultModeId: 'agent',
|
|
||||||
modes: CURSOR_CLI_MODES,
|
|
||||||
},
|
|
||||||
goose: {
|
goose: {
|
||||||
defaultModeId: null,
|
defaultModeId: null,
|
||||||
modes: [],
|
modes: [],
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ export interface ProviderDef {
|
|||||||
* - boocode: llama-swap only
|
* - boocode: llama-swap only
|
||||||
* - opencode: ACP probe + mergeLlamaSwap (prefixed llama-swap/* ids)
|
* - opencode: ACP probe + mergeLlamaSwap (prefixed llama-swap/* ids)
|
||||||
* - qwen: ACP probe + merge ~/.qwen/settings.json; PTY fallback reads settings only
|
* - qwen: ACP probe + merge ~/.qwen/settings.json; PTY fallback reads settings only
|
||||||
* - cursor: ACP probe + cursor-agent models CLI fallback
|
* - goose: ACP probe only
|
||||||
* - goose / copilot: ACP probe only
|
|
||||||
* - claude: static manifest models + thinking options
|
* - claude: static manifest models + thinking options
|
||||||
*/
|
*/
|
||||||
export const PROVIDERS: ProviderDef[] = [
|
export const PROVIDERS: ProviderDef[] = [
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
* Provider snapshot cache — cold ACP probe per provider + static manifest merge.
|
* Provider snapshot cache — cold ACP probe per provider + static manifest merge.
|
||||||
*/
|
*/
|
||||||
import { homedir } from 'node:os';
|
import { homedir } from 'node:os';
|
||||||
import { exec as execCb } from 'node:child_process';
|
|
||||||
import { promisify } from 'node:util';
|
|
||||||
import type { FastifyBaseLogger } from 'fastify';
|
import type { FastifyBaseLogger } from 'fastify';
|
||||||
import type { Sql } from '../db.js';
|
import type { Sql } from '../db.js';
|
||||||
import type { Config } from '../config.js';
|
import type { Config } from '../config.js';
|
||||||
@@ -14,13 +12,10 @@ import {
|
|||||||
PROVIDER_MANIFEST,
|
PROVIDER_MANIFEST,
|
||||||
} from './provider-manifest.js';
|
} from './provider-manifest.js';
|
||||||
import { probeAcpProvider } from './acp-probe.js';
|
import { probeAcpProvider } from './acp-probe.js';
|
||||||
import { parseCursorAgentModelsOutput } from './cursor-models.js';
|
|
||||||
import type { ProviderModel, ProviderSnapshotEntry } from './provider-types.js';
|
import type { ProviderModel, ProviderSnapshotEntry } from './provider-types.js';
|
||||||
import { getManifestCommands, mergeCommands } from './provider-commands.js';
|
import { getManifestCommands, mergeCommands } from './provider-commands.js';
|
||||||
import { readQwenSettingsModels } from './qwen-settings.js';
|
import { readQwenSettingsModels } from './qwen-settings.js';
|
||||||
|
|
||||||
const exec = promisify(execCb);
|
|
||||||
|
|
||||||
interface AgentRow {
|
interface AgentRow {
|
||||||
name: string;
|
name: string;
|
||||||
install_path: string | null;
|
install_path: string | null;
|
||||||
@@ -41,15 +36,6 @@ async function fetchLlamaSwapModels(config: Config): Promise<ProviderModel[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchCursorModelsCli(installPath: string): Promise<ProviderModel[]> {
|
|
||||||
try {
|
|
||||||
const { stdout } = await exec(`"${installPath}" models`, { timeout: 15_000, maxBuffer: 1024 * 1024 });
|
|
||||||
return parseCursorAgentModelsOutput(stdout);
|
|
||||||
} catch {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Prefix llama-swap model ids so they don't collide with provider-native models. */
|
/** Prefix llama-swap model ids so they don't collide with provider-native models. */
|
||||||
export function prefixLlamaSwapModels(models: ProviderModel[]): ProviderModel[] {
|
export function prefixLlamaSwapModels(models: ProviderModel[]): ProviderModel[] {
|
||||||
return models.map((m) => ({
|
return models.map((m) => ({
|
||||||
@@ -141,8 +127,6 @@ async function buildProviderEntry(
|
|||||||
const probe = await probeAcpProvider(provider.name, agentRow.install_path, cwd);
|
const probe = await probeAcpProvider(provider.name, agentRow.install_path, cwd);
|
||||||
if (probe.models.length > 0) {
|
if (probe.models.length > 0) {
|
||||||
models = probe.models;
|
models = probe.models;
|
||||||
} else if (provider.name === 'cursor' && agentRow.install_path) {
|
|
||||||
models = await fetchCursorModelsCli(agentRow.install_path);
|
|
||||||
} else if (provider.modelSource === 'llama-swap') {
|
} else if (provider.modelSource === 'llama-swap') {
|
||||||
models = llamaModels;
|
models = llamaModels;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user