refactor(cli): split oversized command modules (#9775)

This commit is contained in:
Brennan Benson 2026-07-21 13:50:17 -07:00 committed by GitHub
parent 7ca3e670c5
commit f1c84d3858
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 285 additions and 217 deletions

View File

@ -22,7 +22,7 @@
{
"name": "linear-tickets",
"sourcePath": "skills/linear-tickets",
"releaseRevision": 5,
"releaseRevision": 6,
"packageDigest": "ff9f085631f753f059c631d874177ddd4fa847c5eca85a420dc85fb2bece6ff6",
"gitTreeSha": "e35ac3c0c583661983d3fc1352ff3aec74e67e8c",
"files": [
@ -94,7 +94,7 @@
{
"name": "orca-linear",
"sourcePath": "skills/orca-linear",
"releaseRevision": 3,
"releaseRevision": 4,
"packageDigest": "5e9622bd3883c0f53e6bd349758096deafceebd2fa260d3e90d677e64d06416d",
"gitTreeSha": "f3727995a4719fd522119eca6d1b57542cb5fe23",
"files": [

View File

@ -548,6 +548,19 @@
"orca-per-workspace-env": 2,
"orchestration": 25
}
},
{
"appVersion": "1.4.149-rc.1.issue7936",
"skills": {
"computer-use": 5,
"linear-tickets": 5,
"orca-cli": 35,
"orca-emulator": 4,
"orca-emulator-android": 2,
"orca-linear": 3,
"orca-per-workspace-env": 2,
"orchestration": 25
}
}
]
}

View File

@ -1198,6 +1198,22 @@
},
{
"releaseRevision": 5,
"packageDigest": "30f836d836df611c0c974ac0ee48d817fbe6711a49465d139f0981cbd75f828e",
"gitTreeSha": "8e862333bdeb9970e11dcd6634e493ecc6ff6698",
"files": [
{
"path": "SKILL.md",
"size": 11514,
"executable": false,
"classification": "text",
"exactSha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9",
"textNormalizedSha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9",
"identitySha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9"
}
]
},
{
"releaseRevision": 6,
"packageDigest": "ff9f085631f753f059c631d874177ddd4fa847c5eca85a420dc85fb2bece6ff6",
"gitTreeSha": "e35ac3c0c583661983d3fc1352ff3aec74e67e8c",
"files": [
@ -1248,6 +1264,22 @@
},
{
"releaseRevision": 3,
"packageDigest": "2fcccfe7c3166ca0b78a50ccdfd7acad4c6e6eeffa14e48ed1edee53c796e1f0",
"gitTreeSha": "761d4427e9f330607664da5d6ff55400be8c690b",
"files": [
{
"path": "SKILL.md",
"size": 11238,
"executable": false,
"classification": "text",
"exactSha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6",
"textNormalizedSha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6",
"identitySha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6"
}
]
},
{
"releaseRevision": 4,
"packageDigest": "5e9622bd3883c0f53e6bd349758096deafceebd2fa260d3e90d677e64d06416d",
"gitTreeSha": "f3727995a4719fd522119eca6d1b57542cb5fe23",
"files": [

View File

@ -0,0 +1,96 @@
import type { CommandSpec } from '../args'
import { GLOBAL_FLAGS } from '../args'
export const LINEAR_MCP_COMMAND_SPECS: CommandSpec[] = [
{
path: ['linear', 'save-issue'],
summary: 'Create or update a Linear issue',
usage:
'orca linear save-issue [<id>] [--current] [--team <key|id>] [--title <title>] [--description <text> | --body-file <path|->] [--state <state>] [--assignee me|<user>|null] [--priority none|low|medium|high|urgent] [--estimate <number>|null] [--due-date <yyyy-mm-dd>|null] [--label <label>...] [--project <project>|null] [--parent-id <issue>|null] [--write-id <uuid>] [--workspace <id>] [--json]',
allowedFlags: [
...GLOBAL_FLAGS,
'current',
'team',
'title',
'description',
'body',
'body-file',
'state',
'assignee',
'priority',
'estimate',
'due-date',
'label',
'project',
'parent-id',
'write-id',
'workspace',
'id'
],
positionalArgs: ['id'],
examples: [
'orca linear save-issue --team ENG --title "Fix auth" --priority high --json',
'orca linear save-issue ENG-123 --title "Fix OAuth callback" --assignee me --json',
'orca linear save-issue --current --project null --due-date null --json'
],
notes: [
'Without <id> or --current, creates an issue and requires --team and --title.',
'Labels replace the complete label set, matching Linear MCP save_issue semantics.',
'Use the literal null to clear assignee, estimate, due date, project, or parent.'
]
},
{
path: ['linear', 'list-issues'],
summary: 'List Linear issues with MCP-compatible filters',
usage:
'orca linear list-issues [--team <team>] [--cycle <cycle>] [--label <label>] [--limit <n>] [--query <text>] [--state <state>] [--cursor <cursor>] [--order-by createdAt|updatedAt] [--project <project>] [--release <release>] [--assignee <user|me|null>] [--delegate <user|me|null>] [--parent-id <issue|null>] [--priority <0-4>] [--created-at <datetime|duration>] [--updated-at <datetime|duration>] [--include-archived] [--workspace <id>|all] [--json]',
allowedFlags: [
...GLOBAL_FLAGS,
'team',
'cycle',
'label',
'limit',
'query',
'state',
'cursor',
'order-by',
'project',
'release',
'assignee',
'delegate',
'parent-id',
'priority',
'created-at',
'updated-at',
'include-archived',
'workspace'
],
examples: [
'orca linear list-issues --team ENG --state started --assignee me --json',
'orca linear list-issues --query auth --updated-at -P7D --limit 100 --json',
'orca linear list-issues --cursor <cursor> --workspace <id> --json'
]
},
{
path: ['linear', 'relation', 'add'],
summary: 'Add a Linear issue relation',
usage:
'orca linear relation add [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
positionalArgs: ['id'],
examples: [
'orca linear relation add ENG-1 --related ENG-2 --type blocks --json',
'orca linear relation add --current --related ENG-2 --type blocked-by --json'
]
},
{
path: ['linear', 'relation', 'remove'],
aliases: [['linear', 'relation', 'rm']],
summary: 'Remove a Linear issue relation',
usage:
'orca linear relation remove [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
positionalArgs: ['id'],
examples: ['orca linear relation remove ENG-1 --related ENG-2 --type related --json']
}
]

View File

@ -1,98 +1,9 @@
import type { CommandSpec } from '../args'
import { GLOBAL_FLAGS } from '../args'
import { LINEAR_MCP_COMMAND_SPECS } from './linear-mcp'
export const LINEAR_COMMAND_SPECS: CommandSpec[] = [
{
path: ['linear', 'save-issue'],
summary: 'Create or update a Linear issue',
usage:
'orca linear save-issue [<id>] [--current] [--team <key|id>] [--title <title>] [--description <text> | --body-file <path|->] [--state <state>] [--assignee me|<user>|null] [--priority none|low|medium|high|urgent] [--estimate <number>|null] [--due-date <yyyy-mm-dd>|null] [--label <label>...] [--project <project>|null] [--parent-id <issue>|null] [--write-id <uuid>] [--workspace <id>] [--json]',
allowedFlags: [
...GLOBAL_FLAGS,
'current',
'team',
'title',
'description',
'body',
'body-file',
'state',
'assignee',
'priority',
'estimate',
'due-date',
'label',
'project',
'parent-id',
'write-id',
'workspace',
'id'
],
positionalArgs: ['id'],
examples: [
'orca linear save-issue --team ENG --title "Fix auth" --priority high --json',
'orca linear save-issue ENG-123 --title "Fix OAuth callback" --assignee me --json',
'orca linear save-issue --current --project null --due-date null --json'
],
notes: [
'Without <id> or --current, creates an issue and requires --team and --title.',
'Labels replace the complete label set, matching Linear MCP save_issue semantics.',
'Use the literal null to clear assignee, estimate, due date, project, or parent.'
]
},
{
path: ['linear', 'list-issues'],
summary: 'List Linear issues with MCP-compatible filters',
usage:
'orca linear list-issues [--team <team>] [--cycle <cycle>] [--label <label>] [--limit <n>] [--query <text>] [--state <state>] [--cursor <cursor>] [--order-by createdAt|updatedAt] [--project <project>] [--release <release>] [--assignee <user|me|null>] [--delegate <user|me|null>] [--parent-id <issue|null>] [--priority <0-4>] [--created-at <datetime|duration>] [--updated-at <datetime|duration>] [--include-archived] [--workspace <id>|all] [--json]',
allowedFlags: [
...GLOBAL_FLAGS,
'team',
'cycle',
'label',
'limit',
'query',
'state',
'cursor',
'order-by',
'project',
'release',
'assignee',
'delegate',
'parent-id',
'priority',
'created-at',
'updated-at',
'include-archived',
'workspace'
],
examples: [
'orca linear list-issues --team ENG --state started --assignee me --json',
'orca linear list-issues --query auth --updated-at -P7D --limit 100 --json',
'orca linear list-issues --cursor <cursor> --workspace <id> --json'
]
},
{
path: ['linear', 'relation', 'add'],
summary: 'Add a Linear issue relation',
usage:
'orca linear relation add [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
positionalArgs: ['id'],
examples: [
'orca linear relation add ENG-1 --related ENG-2 --type blocks --json',
'orca linear relation add --current --related ENG-2 --type blocked-by --json'
]
},
{
path: ['linear', 'relation', 'remove'],
aliases: [['linear', 'relation', 'rm']],
summary: 'Remove a Linear issue relation',
usage:
'orca linear relation remove [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
positionalArgs: ['id'],
examples: ['orca linear relation remove ENG-1 --related ENG-2 --type related --json']
},
...LINEAR_MCP_COMMAND_SPECS,
{
path: ['linear', 'issue'],
summary: 'Read Linear issue context for agents',

View File

@ -0,0 +1,117 @@
import { RemoteCliArgumentError, type ParsedRemoteCli } from './ssh-remote-cli-argument-error'
const REMOTE_BOOLEAN_FLAGS = new Set([
'all',
'attachments',
'children',
'comments',
'current',
'full',
'help',
'inject',
'include-archived',
'json',
'me',
'relations',
'parent-current',
'unread',
'wait'
])
const REPEATED_FLAG_SEPARATOR = '\u0000'
const REPEATABLE_REMOTE_STRING_FLAGS = new Set(['label'])
export function parseRemoteCliArgs(argv: string[]): ParsedRemoteCli {
const commandPath: string[] = []
const flags = new Map<string, string | boolean>()
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i]
if (!token.startsWith('--')) {
commandPath.push(token)
continue
}
const assignment = token.slice(2)
// Why: the SSH relay-backed shim should accept values beginning with `--` via `--flag=value`.
const equalsIndex = assignment.indexOf('=')
if (equalsIndex !== -1) {
setRemoteFlag(flags, assignment.slice(0, equalsIndex), assignment.slice(equalsIndex + 1))
continue
}
const flag = assignment
const next = argv[i + 1]
if (!isRemoteBooleanFlag(flag, commandPath) && next && !next.startsWith('--')) {
setRemoteFlag(flags, flag, next)
i += 1
} else {
setRemoteFlag(flags, flag, true)
}
}
return { commandPath, flags }
}
export function resolveRemoteCliHandle(
flags: Map<string, string | boolean>,
env: Record<string, string>,
flagName: string
): string {
return optionalRemoteCliString(flags, flagName) ?? env.ORCA_TERMINAL_HANDLE ?? 'unknown'
}
export function requiredRemoteCliString(
flags: Map<string, string | boolean>,
name: string
): string {
const value = optionalRemoteCliString(flags, name)
if (!value) {
throw new Error(`Missing --${name}`)
}
return value
}
export function optionalRemoteCliString(
flags: Map<string, string | boolean>,
name: string
): string | undefined {
const value = flags.get(name)
return typeof value === 'string' && value.length > 0 ? value : undefined
}
export function optionalRemoteCliNumber(
flags: Map<string, string | boolean>,
name: string
): number | undefined {
const value = optionalRemoteCliString(flags, name)
if (value === undefined) {
return undefined
}
const parsed = Number(value)
if (!Number.isFinite(parsed)) {
throw new RemoteCliArgumentError('invalid_argument', `Invalid numeric value for --${name}`)
}
return parsed
}
function isRemoteBooleanFlag(flag: string, commandPath: string[]): boolean {
// Why: Android launch already uses --activity <name>; only Linear issue reads use it as a boolean.
return (
REMOTE_BOOLEAN_FLAGS.has(flag) ||
(flag === 'activity' && commandPath[0] === 'linear' && commandPath[1] === 'issue')
)
}
function setRemoteFlag(
flags: Map<string, string | boolean>,
name: string,
value: string | boolean
): void {
const previous = flags.get(name)
if (
typeof previous === 'string' &&
typeof value === 'string' &&
REPEATABLE_REMOTE_STRING_FLAGS.has(name)
) {
flags.set(name, `${previous}${REPEATED_FLAG_SEPARATOR}${value}`)
return
}
flags.set(name, value)
}

View File

@ -11,6 +11,13 @@ import {
type RemoteOrcaCliResult
} from './ssh-remote-cli-host-passthrough'
import { RemoteCliArgumentError, type ParsedRemoteCli } from './ssh-remote-cli-argument-error'
import {
optionalRemoteCliNumber,
optionalRemoteCliString,
parseRemoteCliArgs,
requiredRemoteCliString,
resolveRemoteCliHandle
} from './ssh-remote-cli-args'
import { getRemoteLinearHelp, tryDispatchRemoteLinearCli } from './ssh-remote-linear-cli'
import {
getRemoteOrchestrationPayload,
@ -32,26 +39,6 @@ const HOST_INTERACTIVE_COMMANDS: Record<string, string> = {
'orca agent-teams-tmux is a tmux pane shim for the Orca host machine and cannot run through the SSH relay bridge.'
}
const REMOTE_BOOLEAN_FLAGS = new Set([
'all',
'attachments',
'children',
'comments',
'current',
'full',
'help',
'inject',
'include-archived',
'json',
'me',
'relations',
'parent-current',
'unread',
'wait'
])
const REPEATED_FLAG_SEPARATOR = '\u0000'
const REPEATABLE_REMOTE_STRING_FLAGS = new Set(['label'])
export async function runRemoteOrcaCli(
runtime: OrcaRuntimeService,
request: RemoteOrcaCliRequest,
@ -175,19 +162,19 @@ async function dispatchRemoteCli(
}
case 'terminal list':
return await call(dispatcher, 'terminal.list', {
worktree: optionalString(parsed.flags, 'worktree'),
limit: optionalNumber(parsed.flags, 'limit')
worktree: optionalRemoteCliString(parsed.flags, 'worktree'),
limit: optionalRemoteCliNumber(parsed.flags, 'limit')
})
case 'orchestration send': {
const type = optionalString(parsed.flags, 'type')
const type = optionalRemoteCliString(parsed.flags, 'type')
return await call(dispatcher, 'orchestration.send', {
from: resolveRemoteOrchestrationSender(parsed.flags, env, type),
to: requiredString(parsed.flags, 'to'),
subject: requiredString(parsed.flags, 'subject'),
body: optionalString(parsed.flags, 'body'),
to: requiredRemoteCliString(parsed.flags, 'to'),
subject: requiredRemoteCliString(parsed.flags, 'subject'),
body: optionalRemoteCliString(parsed.flags, 'body'),
type,
priority: optionalString(parsed.flags, 'priority'),
threadId: optionalString(parsed.flags, 'thread-id'),
priority: optionalRemoteCliString(parsed.flags, 'priority'),
threadId: optionalRemoteCliString(parsed.flags, 'thread-id'),
payload: getRemoteOrchestrationPayload(parsed.flags),
// Why: the legacy in-process bridge must preserve the same pane
// authority as the full host CLI passthrough.
@ -196,24 +183,24 @@ async function dispatchRemoteCli(
}
case 'orchestration check':
return await call(dispatcher, 'orchestration.check', {
terminal: resolveHandle(parsed.flags, env, 'terminal'),
terminal: resolveRemoteCliHandle(parsed.flags, env, 'terminal'),
unread: parsed.flags.has('unread') ? true : undefined,
all: parsed.flags.has('all') ? true : undefined,
types: optionalString(parsed.flags, 'types'),
types: optionalRemoteCliString(parsed.flags, 'types'),
inject: parsed.flags.has('inject') ? true : undefined,
wait: parsed.flags.has('wait') ? true : undefined,
timeoutMs: optionalNumber(parsed.flags, 'timeout-ms')
timeoutMs: optionalRemoteCliNumber(parsed.flags, 'timeout-ms')
})
case 'orchestration reply':
return await call(dispatcher, 'orchestration.reply', {
id: requiredString(parsed.flags, 'id'),
body: requiredString(parsed.flags, 'body'),
from: resolveHandle(parsed.flags, env, 'from')
id: requiredRemoteCliString(parsed.flags, 'id'),
body: requiredRemoteCliString(parsed.flags, 'body'),
from: resolveRemoteCliHandle(parsed.flags, env, 'from')
})
case 'orchestration inbox':
return await call(dispatcher, 'orchestration.inbox', {
limit: optionalNumber(parsed.flags, 'limit'),
terminal: optionalString(parsed.flags, 'terminal')
limit: optionalRemoteCliNumber(parsed.flags, 'limit'),
terminal: optionalRemoteCliString(parsed.flags, 'terminal')
})
default:
// Why: only reachable when the full host CLI could not be launched;
@ -238,94 +225,6 @@ async function call(
})
}
function parseRemoteCliArgs(argv: string[]): ParsedRemoteCli {
const commandPath: string[] = []
const flags = new Map<string, string | boolean>()
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i]
if (!token.startsWith('--')) {
commandPath.push(token)
continue
}
const assignment = token.slice(2)
// Why: the SSH relay-backed shim should accept the same `--flag=value`
// form as the local CLI, including values that themselves start with `--`.
const equalsIndex = assignment.indexOf('=')
if (equalsIndex !== -1) {
setRemoteFlag(flags, assignment.slice(0, equalsIndex), assignment.slice(equalsIndex + 1))
continue
}
const flag = assignment
const next = argv[i + 1]
if (!isRemoteBooleanFlag(flag, commandPath) && next && !next.startsWith('--')) {
setRemoteFlag(flags, flag, next)
i += 1
} else {
setRemoteFlag(flags, flag, true)
}
}
return { commandPath, flags }
}
function isRemoteBooleanFlag(flag: string, commandPath: string[]): boolean {
// Why: Android launch already uses --activity <name>; only Linear issue reads use it as a boolean.
return (
REMOTE_BOOLEAN_FLAGS.has(flag) ||
(flag === 'activity' && commandPath[0] === 'linear' && commandPath[1] === 'issue')
)
}
function setRemoteFlag(
flags: Map<string, string | boolean>,
name: string,
value: string | boolean
): void {
const previous = flags.get(name)
if (
typeof previous === 'string' &&
typeof value === 'string' &&
REPEATABLE_REMOTE_STRING_FLAGS.has(name)
) {
flags.set(name, `${previous}${REPEATED_FLAG_SEPARATOR}${value}`)
return
}
flags.set(name, value)
}
function resolveHandle(
flags: Map<string, string | boolean>,
env: Record<string, string>,
flagName: string
): string {
return optionalString(flags, flagName) ?? env.ORCA_TERMINAL_HANDLE ?? 'unknown'
}
function requiredString(flags: Map<string, string | boolean>, name: string): string {
const value = optionalString(flags, name)
if (!value) {
throw new Error(`Missing --${name}`)
}
return value
}
function optionalString(flags: Map<string, string | boolean>, name: string): string | undefined {
const value = flags.get(name)
return typeof value === 'string' && value.length > 0 ? value : undefined
}
function optionalNumber(flags: Map<string, string | boolean>, name: string): number | undefined {
const value = optionalString(flags, name)
if (value === undefined) {
return undefined
}
const parsed = Number(value)
if (!Number.isFinite(parsed)) {
throw new RemoteCliArgumentError('invalid_argument', `Invalid numeric value for --${name}`)
}
return parsed
}
function buildLocalError(message: string, code = 'runtime_error'): RpcResponse {
return {
id: 'remote-cli-local',