Skip to content

Commit 5d7cd2f

Browse files
committed
refactor: centralize cli command schema catalog
1 parent 353d979 commit 5d7cd2f

4 files changed

Lines changed: 38 additions & 20 deletions

File tree

src/command-catalog.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,32 @@ export const INTERNAL_COMMANDS = {
5454
sessionList: 'session_list',
5555
} as const;
5656

57+
export const LOCAL_CLI_COMMANDS = {
58+
auth: 'auth',
59+
connect: 'connect',
60+
connection: 'connection',
61+
disconnect: 'disconnect',
62+
mcp: 'mcp',
63+
metro: 'metro',
64+
reactDevtools: 'react-devtools',
65+
session: 'session',
66+
} as const;
67+
5768
const GESTURE_SUBCOMMANDS = ['pan', 'fling', 'pinch', 'rotate', 'transform'] as const;
5869
export const GESTURE_SUBCOMMAND_ERROR = `gesture requires one of: ${GESTURE_SUBCOMMANDS.join(', ')}`;
5970

6071
export type PublicCommandName = (typeof PUBLIC_COMMANDS)[keyof typeof PUBLIC_COMMANDS];
61-
export type CliCommandName =
62-
| PublicCommandName
63-
| 'auth'
64-
| 'connect'
65-
| 'connection'
66-
| 'disconnect'
67-
| 'metro'
68-
| 'session';
72+
export type LocalCliCommandName = (typeof LOCAL_CLI_COMMANDS)[keyof typeof LOCAL_CLI_COMMANDS];
73+
export type CliCommandName = PublicCommandName | LocalCliCommandName;
74+
75+
export const MCP_EXCLUDED_CLI_COMMANDS = commandSet(
76+
LOCAL_CLI_COMMANDS.auth,
77+
LOCAL_CLI_COMMANDS.connect,
78+
LOCAL_CLI_COMMANDS.connection,
79+
LOCAL_CLI_COMMANDS.disconnect,
80+
LOCAL_CLI_COMMANDS.mcp,
81+
LOCAL_CLI_COMMANDS.reactDevtools,
82+
);
6983

7084
export const DAEMON_COMMAND_GROUPS = {
7185
inventory: commandSet(

src/mcp/__tests__/router.test.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import assert from 'node:assert/strict';
22
import { test } from 'vitest';
3+
import { MCP_EXCLUDED_CLI_COMMANDS } from '../../command-catalog.ts';
34
import { getCliCommandNames } from '../../utils/command-schema.ts';
45
import { handleMcpMessage } from '../router.ts';
56

6-
const LOCAL_ONLY_CLI_COMMANDS = new Set([
7-
'auth',
8-
'connect',
9-
'connection',
10-
'disconnect',
11-
'react-devtools',
12-
]);
13-
147
test('MCP exposes every automatable CLI command as a semantic direct tool', async () => {
158
const response = await handleMcpMessage({
169
jsonrpc: '2.0',
@@ -23,7 +16,7 @@ test('MCP exposes every automatable CLI command as a semantic direct tool', asyn
2316
(tool) => tool.name,
2417
);
2518
const expectedToolNames = getCliCommandNames()
26-
.filter((command) => command !== 'mcp' && !LOCAL_ONLY_CLI_COMMANDS.has(command))
19+
.filter((command) => !MCP_EXCLUDED_CLI_COMMANDS.has(command))
2720
.sort();
2821

2922
assert.deepEqual(tools.filter((name) => name !== 'status').sort(), expectedToolNames);

src/utils/__tests__/args.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { parseArgs, usage, usageForCommand } from '../args.ts';
44
import { AppError } from '../errors.ts';
55
import { getCliCommandNames, getSchemaCapabilityKeys } from '../command-schema.ts';
66
import { listCapabilityCommands } from '../../core/capabilities.ts';
7+
import { LOCAL_CLI_COMMANDS, PUBLIC_COMMANDS } from '../../command-catalog.ts';
78

89
test('parseArgs recognizes command-specific flag combinations', async () => {
910
const scenarios: Array<{
@@ -136,6 +137,13 @@ test('parseArgs recognizes command-specific flag combinations', async () => {
136137
}
137138
});
138139

140+
test('command parser schemas cover the centralized CLI command catalog', () => {
141+
assert.deepEqual(
142+
new Set(getCliCommandNames()),
143+
new Set([...Object.values(PUBLIC_COMMANDS), ...Object.values(LOCAL_CLI_COMMANDS)]),
144+
);
145+
});
146+
139147
test('parseArgs recognizes device isolation flags', () => {
140148
const parsed = parseArgs(
141149
[

src/utils/command-schema.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SCREENSHOT_SPECIFIC_FLAG_DEFINITIONS,
99
type ScreenshotRequestFlags,
1010
} from '../commands/capture-screenshot-options.ts';
11+
import type { CliCommandName } from '../command-catalog.ts';
1112

1213
export type CliFlags = RemoteConfigMetroOptions &
1314
ScreenshotRequestFlags & {
@@ -1481,7 +1482,7 @@ export const GLOBAL_FLAG_KEYS = new Set<FlagKey>([
14811482
'noRecord',
14821483
]);
14831484

1484-
const COMMAND_SCHEMAS: Record<string, CommandSchema> = {
1485+
const COMMAND_SCHEMAS: Record<CliCommandName, CommandSchema> = {
14851486
boot: {
14861487
helpDescription: 'Ensure target device/simulator is booted and ready',
14871488
summary: 'Boot target device/simulator',
@@ -1944,6 +1945,8 @@ const COMMAND_SCHEMAS: Record<string, CommandSchema> = {
19441945
},
19451946
};
19461947

1948+
const COMMAND_SCHEMA_MAP: Readonly<Record<string, CommandSchema>> = COMMAND_SCHEMAS;
1949+
19471950
const flagDefinitionByName = new Map<string, FlagDefinition>();
19481951
const flagDefinitionsByKey = new Map<FlagKey, FlagDefinition[]>();
19491952
for (const definition of FLAG_DEFINITIONS) {
@@ -1965,7 +1968,7 @@ export function getFlagDefinitions(): readonly FlagDefinition[] {
19651968

19661969
export function getCommandSchema(command: string | null): CommandSchema | undefined {
19671970
if (!command) return undefined;
1968-
return COMMAND_SCHEMAS[command];
1971+
return COMMAND_SCHEMA_MAP[command];
19691972
}
19701973

19711974
export function applyCommandDefaults(
@@ -2044,7 +2047,7 @@ CLI to control iOS and Android devices for AI agents.
20442047
`;
20452048

20462049
const commands = getCliCommandNames().map((name) => {
2047-
const schema = COMMAND_SCHEMAS[name];
2050+
const schema = COMMAND_SCHEMA_MAP[name];
20482051
if (!schema) throw new Error(`Missing command schema for ${name}`);
20492052
return { name, schema, usage: buildCommandListUsage(name, schema) };
20502053
});

0 commit comments

Comments
 (0)