Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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];
Expand All @@ -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(
Expand All @@ -44,5 +58,5 @@ export function nextDefaultConversationTitle(
let next = 1;
while (used.has(next)) next += 1;

return `${capitalizeProviderId(providerId)} (${next})`;
return `${getDefaultTitlePrefix(providerId)} (${next})`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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,
},
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 26 additions & 0 deletions packages/plugins/src/agents/impl/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
});
});
});
12 changes: 6 additions & 6 deletions packages/plugins/src/agents/impl/kimi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -50,21 +50,21 @@ 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',
},
],
},
updates: {
kind: 'supported',
releaseSource: {
kind: 'github',
repo: 'moonshotai/kimi-cli',
repo: 'moonshotai/kimi-code',
},
update: {
kind: 'package-manager',
Expand Down
15 changes: 8 additions & 7 deletions packages/plugins/src/agents/impl/rovo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
],
},
Expand All @@ -37,8 +38,7 @@ export const plugin = definePlugin(
},
},
prompt: {
kind: 'argv',
flag: '',
kind: 'keystroke',
},
sessions: {
kind: 'stateless',
Expand All @@ -51,6 +51,7 @@ export const provider = registerPluginBehavior(plugin, {
prompt: {
buildCommand: (ctx) =>
buildStandardCommand(ctx, {
defaultArgs: ['rovodev', 'run'],
autoApproveFlag: '--yolo',
}),
},
Expand Down
Loading