Skip to content

feat: add CommandName union type derived from COMMAND_SCHEMAS#237

Open
ken0nek wants to merge 3 commits intocallstackincubator:mainfrom
ken0nek:feat/command-name
Open

feat: add CommandName union type derived from COMMAND_SCHEMAS#237
ken0nek wants to merge 3 commits intocallstackincubator:mainfrom
ken0nek:feat/command-name

Conversation

@ken0nek
Copy link

@ken0nek ken0nek commented Mar 19, 2026

Summary

  • Derive CliCommandName type from keyof typeof COMMAND_SCHEMAS — no separate manually-maintained list
  • Define daemon-internal commands (install_source, lease_allocate, etc.) as a small separate union
  • CommandName = CliCommandName | DaemonInternalCommandName (46 CLI + 6 internal)
  • Apply satisfies checks to COMMAND_CAPABILITY_MATRIX, clientCommandHandlers, and LEASE_RPC_METHOD_TO_COMMAND to catch key typos at compile time
  • Type dispatchCommand, BatchStep.command, NormalizedBatchStep.command, and client execute() with CommandName
  • Centralize CLI aliases (long-presslongpress, metricsperf) into COMMAND_ALIASES record
  • DaemonRequest.command stays string — no wire-boundary changes

Zero runtime behavior change. Pure type addition.

Verification

  • pnpm typecheck — passes
  • pnpm test:unit — all 765 tests pass, 0 failures
  • pnpm build — succeeds

…cation

Introduce a CommandName union type derived from a const COMMAND_NAMES
array (52 entries: 46 CLI + 6 daemon-internal) and apply it to internal
function signatures and lookup tables via satisfies checks.

DaemonRequest.command stays string (untrusted wire boundary).
Copilot AI review requested due to automatic review settings March 19, 2026 08:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a centralized CommandName union type derived from a single COMMAND_NAMES source-of-truth and threads it through key command-related maps and APIs to catch typos at compile time (while keeping daemon wire inputs as string).

Changes:

  • Add src/core/command-names.ts with COMMAND_NAMES and the derived CommandName union type.
  • Apply satisfies constraints across command maps (schemas/capabilities/client handlers/RPC mapping) to enforce valid keys/values at compile time.
  • Centralize CLI command alias normalization via COMMAND_ALIASES and use it in argument parsing.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/core/command-names.ts Adds canonical command list, CommandName union, and centralized CLI alias mapping.
src/utils/args.ts Uses COMMAND_ALIASES to normalize CLI command aliases during parsing/usage.
src/utils/command-schema.ts Applies satisfies to schema map to catch invalid command keys at compile time.
src/core/capabilities.ts Applies satisfies to capability matrix to catch invalid command keys at compile time.
src/core/dispatch.ts Types dispatch/batch step command fields with CommandName.
src/core/batch.ts Types normalized batch steps/results with CommandName.
src/daemon/request-router.ts Adds CommandName typing/satisfies usage and casts command when dispatching.
src/daemon/http-server.ts Ensures lease RPC method mapping values are valid CommandNames via satisfies.
src/daemon/handlers/session.ts Narrows internal dispatch parameter types to CommandName.
src/daemon/handlers/session-close.ts Narrows dispatch parameter types to CommandName.
src/client.ts Types internal execute command parameter as CommandName.
src/cli-client-commands.ts Applies satisfies to client handler map to catch invalid command keys.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 85 to 88
normalized.push({
command,
command: command as CommandName,
positionals: positionals as string[],
flags: (step.flags ?? {}) as Partial<CommandFlags>,
Comment on lines +348 to 349
const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, {
...contextFromFlags(
@thymikee
Copy link
Contributor

I like the intent here, but I don’t think COMMAND_NAMES should land as a separate manually maintained list.

Right now we already have command-bearing structures that act as the practical source of truth, especially COMMAND_SCHEMAS. Adding src/core/command-names.ts creates another registry that has to stay in sync with those existing maps, so the PR ends up increasing duplication rather than reducing it. That makes the “single source of truth” claim hard to justify, and it means every new command now needs updates in multiple places to preserve the type guarantees.

I’d be more comfortable with this if the command union were derived from existing structures instead of introduced as a new top-level list. For example:

  • derive CLI command names from keyof typeof COMMAND_SCHEMAS
  • keep daemon-internal commands as a small separate union if needed
  • keep COMMAND_ALIASES centralized if you want, since that part seems like a clear cleanup

So overall: good goal, but I don’t think the extra COMMAND_NAMES registry is a net positive for the repo in its current form.

Instead of maintaining a separate COMMAND_NAMES array, derive
CliCommandName from keyof typeof COMMAND_SCHEMAS. Only the 6
daemon-internal commands remain as a small explicit union.
@ken0nek ken0nek requested a review from Copilot March 19, 2026 17:22
@ken0nek ken0nek changed the title feat: add CommandName union type feat: add CommandName union type derived from COMMAND_SCHEMAS Mar 19, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a canonical CommandName union type (derived from CLI command schema keys plus a small daemon-internal set) and threads it through dispatch/client/batch-related types to catch command-key typos at compile time without changing runtime behavior.

Changes:

  • Derive CliCommandName from the CLI command schema keys and introduce CommandName in a new src/core/command-names.ts.
  • Apply satisfies-based compile-time checks to several command-keyed maps and tighten types for dispatch, batch steps, and client execution.
  • Centralize CLI command alias normalization via COMMAND_ALIASES.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/utils/command-schema.ts Refactors schema object into commandSchemaEntries and exports CliCommandName derived from its keys.
src/utils/args.ts Uses centralized COMMAND_ALIASES for CLI alias normalization.
src/daemon/request-router.ts Adds CommandName typing to exempt-command lists and casts command for dispatch.
src/daemon/http-server.ts Applies satisfies to ensure RPC→command mapping values conform to CommandName.
src/daemon/handlers/session.ts Narrows runSessionOrSelectorDispatch’s command parameter to CommandName.
src/daemon/handlers/session-close.ts Narrows close handler dispatch signature to CommandName.
src/core/dispatch.ts Types dispatchCommand and BatchStep.command with CommandName.
src/core/command-names.ts Adds CommandName union type and COMMAND_ALIASES mapping.
src/core/capabilities.ts Adds satisfies check tying capability-matrix keys to CommandName.
src/core/batch.ts Types normalized batch command/result command with CommandName and adds casts.
src/client.ts Types internal client execute() command parameter as CommandName.
src/cli-client-commands.ts Applies satisfies to ensure handler-map keys are within CommandName.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 44 to 47
export async function dispatchCommand(
device: DeviceInfo,
command: string,
command: CommandName,
positionals: string[],
Comment on lines +348 to 349
const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, {
...contextFromFlags(
Comment on lines 85 to 88
normalized.push({
command,
command: command as CommandName,
positionals: positionals as string[],
flags: (step.flags ?? {}) as Partial<CommandFlags>,
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 19, 2026 17:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a canonical CommandName union type (derived from COMMAND_SCHEMAS keys + a small daemon-internal union) and threads it through dispatch/client/batch plumbing to catch command-key typos at compile time, while centralizing legacy CLI aliases.

Changes:

  • Derive CliCommandName from the command schema keys and define CommandName = CliCommandName | DaemonInternalCommandName.
  • Add compile-time key validation via satisfies for capability/handler/method maps.
  • Centralize CLI alias normalization via COMMAND_ALIASES and update call sites to use CommandName types.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/command-schema.ts Refactors command schema object to enable deriving CliCommandName from keys.
src/utils/args.ts Uses centralized COMMAND_ALIASES for legacy CLI alias normalization.
src/daemon/request-router.ts Tightens command-related typing and adds compile-time checking for exempt command sets; dispatch call now uses CommandName.
src/daemon/http-server.ts Adds satisfies check to ensure lease RPC method mapping values are valid CommandNames.
src/daemon/handlers/session.ts Narrows internal dispatch helper param command to CommandName.
src/daemon/handlers/session-close.ts Narrows close handler dispatch signature command to CommandName.
src/core/dispatch.ts Updates dispatchCommand and batch step typing to use CommandName.
src/core/command-names.ts Adds new canonical CommandName type and COMMAND_ALIASES record.
src/core/capabilities.ts Adds satisfies check to validate capability matrix keys against CommandName.
src/core/batch.ts Updates normalized batch step/result command typing to CommandName.
src/client.ts Narrows internal client execute() command parameter to CommandName.
src/cli-client-commands.ts Adds satisfies check to validate client handler keys against CommandName.
Comments suppressed due to low confidence (1)

src/core/dispatch.ts:38

  • BatchStep.command is now typed as CommandName, but batch steps originate from untrusted JSON (parseBatchStepsJson returns parsed as BatchStep[]). This makes the type unsound: callers can treat command as validated even though it isn't. Consider keeping BatchStep.command as string (untrusted) and only narrowing to CommandName after explicit validation/normalization.
export type BatchStep = {
  command: CommandName;
  positionals?: string[];
  flags?: Partial<CommandFlags>;
  runtime?: unknown;
};

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 84 to 88
}
normalized.push({
command,
command: command as CommandName,
positionals: positionals as string[],
flags: (step.flags ?? {}) as Partial<CommandFlags>,
Comment on lines 346 to 349
? { ...(lockedReq.flags ?? {}), out: resolvedOut }
: (lockedReq.flags ?? {});
const data = await dispatch(session.device, command, resolvedPositionals, resolvedOut, {
const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, {
...contextFromFlags(
]);

const COMMAND_SCHEMAS: Record<string, CommandSchema> = {
const commandSchemaEntries = {
@ken0nek
Copy link
Author

ken0nek commented Mar 19, 2026

@thymikee Thank you for your feedback! Addressed. Please take a look when you have a chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants