Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ describe('createMobileSlashCommandList', () => {
expect(list).toBe(SAMPLE_COMMANDS);
});

it('keeps a cloud-agent skill row in the mobile suggestion list and invokes it like any other command', () => {
// The mobile layer only has to pass a reported skill row through; the
// wrapper guarantee that skills survive `commands.available` is pinned by
// `services/cloud-agent-next/wrapper/src/kilo-api.test.ts`.
const skill: SlashCommandInfo = {
name: 'kilo-config',
description: 'Guide for Kilo configuration',
source: 'skill',
hints: [],
};
const list = createMobileSlashCommandList('cloud-agent', [COMPACT, skill], null);

expect(getSlashCommandSuggestions('/', list)).toEqual([COMPACT, skill]);
expect(
parseChatComposerSubmission('/kilo-config', list, {
hasAttachments: false,
sessionType: 'cloud-agent',
remoteCommandState: null,
})
).toEqual({ type: 'command', command: 'kilo-config', arguments: '' });
});

it('does not strip a CLI-reported /goal from a remote catalog', () => {
const list = createMobileSlashCommandList('remote', [GOAL], remoteState({ commands: [GOAL] }));
expect(list.map(command => command.name)).toEqual(['goal', 'new']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ function descriptionLines(root: TestRenderer.ReactTestInstance): string[] {
.map(node => node.props.children as string);
}

/** The skill badge of every row: the one Text that carries `uppercase`. */
function skillBadges(root: TestRenderer.ReactTestInstance): string[] {
return findHost(root, 'Text')
.filter(
node => typeof node.props.className === 'string' && node.props.className.includes('uppercase')
)
.map(node => node.props.children as string);
}

/**
* Sync-commit mount so the pre-resolution render is observable: the dynamic
* import cannot resolve inside a synchronous `act`, which is exactly the
Expand Down Expand Up @@ -171,6 +180,26 @@ beforeEach(() => {
setConfig({ enabled: false, model: MODEL });
});

describe('SlashCommandSuggestions skill rows', () => {
it('marks a skill row and leaves plain and MCP commands unmarked', () => {
const renderer = renderSuggestionsSync([REVIEW, MCP_TOOL, SKILL_TOOL]);

expect(skillBadges(renderer.root)).toEqual(['agentChat.slashCommands.skillBadge']);
act(() => {
renderer.unmount();
});
});

it('does not mark a local reserved command as a skill', () => {
const renderer = renderSuggestionsSync([LOCAL_NEW]);

expect(skillBadges(renderer.root)).toEqual([]);
act(() => {
renderer.unmount();
});
});
});

describe('SlashCommandSuggestions translation', () => {
it('translates runtime (MCP and skill) descriptions in place when enabled', async () => {
requestMock.mockImplementation(
Expand Down
18 changes: 16 additions & 2 deletions apps/mobile/src/components/agents/slash-command-suggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,19 @@ function SlashCommandSuggestionRow({
isRuntimeDescription,
SLASH_COMMAND_ITEM_PREFIX + command.name
);
// A skill row is marked so the user can tell it apart from a plain command.
const isSkill = command.source === 'skill';

return (
<Pressable
onPress={() => {
onSelect(command);
}}
accessibilityRole="button"
accessibilityLabel={t('agentChat.slashCommands.useCommand', { command: command.name })}
accessibilityLabel={t(
isSkill ? 'agentChat.slashCommands.useSkillCommand' : 'agentChat.slashCommands.useCommand',
{ command: command.name }
)}
accessibilityHint={sourceDescription ? description : undefined}
hitSlop={4}
className={cn(
Expand All @@ -80,7 +85,16 @@ function SlashCommandSuggestionRow({
)}
>
<View className="flex-1">
<Text className="text-sm font-semibold text-foreground">/{command.name}</Text>
<View className="flex-row items-center gap-2">
<Text className="text-sm font-semibold text-foreground">/{command.name}</Text>
{isSkill ? (
<View className="rounded-full bg-muted px-2 py-0.5">
<Text className="text-[10px] font-semibold uppercase tracking-[0.5px] text-muted-foreground">
{t('agentChat.slashCommands.skillBadge')}
</Text>
</View>
) : null}
</View>
{sourceDescription ? (
<Text className="mt-0.5 text-xs text-muted-foreground" numberOfLines={1}>
{description}
Expand Down
10 changes: 7 additions & 3 deletions apps/mobile/src/i18n/catalog-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ const PLURAL_SUFFIX = /_(?:zero|one|two|few|many|other)$/;
* cannot ship. Delete an entry the translation slice has landed in every
* catalog.
*
* Empty: the profile-editor validation copy the review-fix slice added (the
* duplicate-key refusal and the MCP bound messages) has landed in every one of
* the 87 catalogs, so the missing-key assertion is strict again.
* Empty: the `notifications.category.*Unavailable` reasons, the profile-editor
* validation copy (the duplicate-key refusal and the MCP bound messages), and
* the `/` suggestion list's skill-row keys
* (`agentChat.slashCommands.skillBadge` and
* `agentChat.slashCommands.useSkillCommand`, added by
* `slash-command-suggestions.tsx`) have all landed in every catalog, so the
* missing-key assertion is strict again.
*/
const PENDING_TRANSLATION_KEYS = new Set<string>();

Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/af.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/am.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/ar.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/az.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/be.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/bg.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/bn.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/bs.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/ca.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/ckb.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/cs.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/cy.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/da.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/de.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/el.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2629,6 +2629,8 @@
"resumeCodexDescription": "import an OpenAI Codex session transcript",
"reviewDescription": "review changes [uncommitted|staged|unpushed|branch|commit|pr]",
"useCommand": "Use /{{command}}",
"useSkillCommand": "Use skill /{{command}}",
"skillBadge": "Skill",
"insert": "Insert",
"upgradeRequiredFallback": "Please upgrade your CLI to use this command.",
"exitCapabilityUnavailable": "Update your CLI to exit the session.",
Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/es.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/et.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/eu.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/fa.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/fi.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/fil.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/fr.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/ga.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/mobile/src/i18n/locales/gl.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading