diff --git a/apps/emdash-desktop/src/renderer/features/tasks/conversations/conversation-title-utils.ts b/apps/emdash-desktop/src/renderer/features/tasks/conversations/conversation-title-utils.ts index e4d24999f4..91df9771ac 100644 --- a/apps/emdash-desktop/src/renderer/features/tasks/conversations/conversation-title-utils.ts +++ b/apps/emdash-desktop/src/renderer/features/tasks/conversations/conversation-title-utils.ts @@ -1,16 +1,30 @@ -import { type AgentProviderId } from '@shared/core/agents/agent-provider-registry'; +import { getProvider, type AgentProviderId } from '@shared/core/agents/agent-provider-registry'; type ConversationTitleInput = { providerId: AgentProviderId; title: string; }; -function capitalizeProviderId(providerId: AgentProviderId): string { - return `${providerId.charAt(0).toUpperCase()}${providerId.slice(1)}`; +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function getDefaultTitlePrefix(providerId: AgentProviderId): string { + const provider = getProvider(providerId); + if (!provider) { + throw new Error(`Missing provider definition for "${providerId}"`); + } + return provider.name; } function parseDefaultTitleIndex(title: string, providerId: AgentProviderId): number | null { - const match = title.match(new RegExp(`^${providerId} \\(([1-9]\\d*)\\)$`, 'i')); + const defaultTitlePrefix = getDefaultTitlePrefix(providerId); + const prefixes = + defaultTitlePrefix.toLowerCase() === providerId.toLowerCase() + ? [providerId] + : [providerId, defaultTitlePrefix]; + const pattern = prefixes.map(escapeRegExp).join('|'); + const match = title.match(new RegExp(`^(?:${pattern}) \\(([1-9]\\d*)\\)$`, 'i')); if (!match) return null; const rawIndex = match[1]; @@ -26,7 +40,7 @@ export function formatConversationTitleForDisplay( ): string { const index = parseDefaultTitleIndex(title, providerId); if (index === null) return title; - return `${capitalizeProviderId(providerId)} (${index})`; + return `${getDefaultTitlePrefix(providerId)} (${index})`; } export function nextDefaultConversationTitle( @@ -44,5 +58,5 @@ export function nextDefaultConversationTitle( let next = 1; while (used.has(next)) next += 1; - return `${capitalizeProviderId(providerId)} (${next})`; + return `${getDefaultTitlePrefix(providerId)} (${next})`; } diff --git a/apps/emdash-desktop/src/renderer/tests/conversation-title-utils.test.ts b/apps/emdash-desktop/src/renderer/tests/conversation-title-utils.test.ts index b0ac8d1e61..e2f99c8b2c 100644 --- a/apps/emdash-desktop/src/renderer/tests/conversation-title-utils.test.ts +++ b/apps/emdash-desktop/src/renderer/tests/conversation-title-utils.test.ts @@ -39,6 +39,13 @@ describe('nextDefaultConversationTitle', () => { expect(formatConversationTitleForDisplay('gemini', 'gemini (1)')).toBe('Gemini (1)'); }); + it('uses provider display names for default titles', () => { + expect(formatConversationTitleForDisplay('kimi', 'Kimi (1)')).toBe('Kimi Code (1)'); + expect(nextDefaultConversationTitle('kimi', [{ providerId: 'kimi', title: 'Kimi (1)' }])).toBe( + 'Kimi Code (2)' + ); + }); + it('leaves custom conversation titles unchanged', () => { expect(formatConversationTitleForDisplay('codex', 'release-triage')).toBe('release-triage'); }); diff --git a/apps/emdash-desktop/src/shared/core/agents/agent-provider-registry.ts b/apps/emdash-desktop/src/shared/core/agents/agent-provider-registry.ts index 0ccd250192..85b5c6310a 100644 --- a/apps/emdash-desktop/src/shared/core/agents/agent-provider-registry.ts +++ b/apps/emdash-desktop/src/shared/core/agents/agent-provider-registry.ts @@ -437,11 +437,11 @@ export const AGENT_PROVIDERS: AgentProviderDefinition[] = [ }, { id: 'kimi', - name: 'Kimi', + name: 'Kimi Code', description: - 'Kimi CLI by Moonshot AI, with shell execution, Zsh integration, ACP, and MCP support.', - docUrl: 'https://moonshotai.github.io/kimi-cli/en/guides/getting-started.html', - installCommand: 'curl -LsSf https://code.kimi.com/install.sh | bash', + 'Kimi Code CLI by Moonshot AI, with shell execution, ACP, MCP, plugins, subagents, and lifecycle hooks.', + docUrl: 'https://moonshotai.github.io/kimi-code/en/guides/getting-started.html', + installCommand: 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash', commands: ['kimi'], versionArgs: ['--version'], cli: 'kimi', @@ -452,7 +452,7 @@ export const AGENT_PROVIDERS: AgentProviderDefinition[] = [ sessionIdOnResumeOnly: true, resumeWithoutSessionFlag: '-C', icon: 'kimi.svg', - alt: 'Kimi CLI', + alt: 'Kimi Code CLI', terminalOnly: true, supportsHooks: true, }, @@ -502,11 +502,13 @@ export const AGENT_PROVIDERS: AgentProviderDefinition[] = [ description: 'Atlassian Rovo Dev CLI integrates terminal assistance with Jira, Confluence, and Bitbucket workflows.', docUrl: 'https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/', - installCommand: 'acli rovodev auth login', - commands: ['rovodev', 'acli'], + installCommand: 'brew tap atlassian/homebrew-acli && brew install acli', + commands: ['acli'], versionArgs: ['--version'], + cli: 'acli', + defaultArgs: ['rovodev', 'run'], autoApproveFlag: '--yolo', - autoStartCommand: 'acli rovodev run', + useKeystrokeInjection: true, icon: 'atlassian.png', alt: 'Rovo Dev CLI', terminalOnly: true, diff --git a/packages/plugins/src/agents/impl/index.test.ts b/packages/plugins/src/agents/impl/index.test.ts index 414172432d..357da1453d 100644 --- a/packages/plugins/src/agents/impl/index.test.ts +++ b/packages/plugins/src/agents/impl/index.test.ts @@ -154,4 +154,30 @@ describe('pluginRegistry', () => { releaseSource: { kind: 'npm', package: '@ampcode/cli' }, }); }); + + it('uses current Rovo Dev ACLI install and interactive launch metadata', () => { + const rovo = pluginRegistry.get('rovo')!; + + expect(rovo.capabilities.hostDependency.binaryNames).toEqual(['acli']); + expect(rovo.capabilities.hostDependency.installCommands.macos?.[0]).toMatchObject({ + method: 'homebrew', + command: 'brew tap atlassian/homebrew-acli && brew install acli', + }); + expect(rovo.capabilities.prompt.kind).toBe('keystroke'); + + const command = rovo.behavior.prompt!.buildCommand({ + cli: 'acli', + autoApprove: true, + initialPrompt: 'Fix the bug', + sessionId: '', + isResuming: false, + model: '', + }); + + expect(command).toEqual({ + command: 'acli', + args: ['rovodev', 'run', '--yolo'], + env: {}, + }); + }); }); diff --git a/packages/plugins/src/agents/impl/kimi/index.ts b/packages/plugins/src/agents/impl/kimi/index.ts index 9245595c88..2e45c141d1 100644 --- a/packages/plugins/src/agents/impl/kimi/index.ts +++ b/packages/plugins/src/agents/impl/kimi/index.ts @@ -29,10 +29,10 @@ import { icon } from './icon'; export const plugin = definePlugin( { id: 'kimi', - name: 'Kimi', + name: 'Kimi Code', description: - 'Kimi CLI by Moonshot AI, with shell execution, Zsh integration, ACP, and MCP support.', - websiteUrl: 'https://moonshotai.github.io/kimi-cli/en/guides/getting-started.html', + 'Kimi Code CLI by Moonshot AI, with shell execution, ACP, MCP, plugins, subagents, and lifecycle hooks.', + websiteUrl: 'https://moonshotai.github.io/kimi-code/en/guides/getting-started.html', }, { autoApprove: { @@ -50,13 +50,13 @@ export const plugin = definePlugin( macos: [ { method: 'curl', - command: 'curl -LsSf https://code.kimi.com/install.sh | bash', + command: 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash', }, ], linux: [ { method: 'curl', - command: 'curl -LsSf https://code.kimi.com/install.sh | bash', + command: 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash', }, ], }, @@ -64,7 +64,7 @@ export const plugin = definePlugin( kind: 'supported', releaseSource: { kind: 'github', - repo: 'moonshotai/kimi-cli', + repo: 'moonshotai/kimi-code', }, update: { kind: 'package-manager', diff --git a/packages/plugins/src/agents/impl/rovo/index.ts b/packages/plugins/src/agents/impl/rovo/index.ts index 77850bcf11..94e2810f64 100644 --- a/packages/plugins/src/agents/impl/rovo/index.ts +++ b/packages/plugins/src/agents/impl/rovo/index.ts @@ -17,18 +17,19 @@ export const plugin = definePlugin( }, hostDependency: { id: 'rovo', - binaryNames: ['rovodev', 'acli'], + binaryNames: ['acli'], installCommands: { macos: [ { - method: 'other', - command: 'acli rovodev auth login', + method: 'homebrew', + command: 'brew tap atlassian/homebrew-acli && brew install acli', }, ], linux: [ { - method: 'other', - command: 'acli rovodev auth login', + method: 'apt', + command: + 'sudo apt-get install -y wget gnupg2 && sudo mkdir -p -m 755 /etc/apt/keyrings && wget -nv -O- https://acli.atlassian.com/gpg/public-key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/acli-archive-keyring.gpg && sudo chmod go+r /etc/apt/keyrings/acli-archive-keyring.gpg && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/acli-archive-keyring.gpg] https://acli.atlassian.com/linux/deb stable main" | sudo tee /etc/apt/sources.list.d/acli.list > /dev/null && sudo apt update && sudo apt install -y acli', }, ], }, @@ -37,8 +38,7 @@ export const plugin = definePlugin( }, }, prompt: { - kind: 'argv', - flag: '', + kind: 'keystroke', }, sessions: { kind: 'stateless', @@ -51,6 +51,7 @@ export const provider = registerPluginBehavior(plugin, { prompt: { buildCommand: (ctx) => buildStandardCommand(ctx, { + defaultArgs: ['rovodev', 'run'], autoApproveFlag: '--yolo', }), },