中文

Commands

Define user-facing command paths, aliases, and multiple actions within one Func command class.

Updated

A command is a fixed word that follows the executable name and selects a business area or action. In git commit, git is the executable and commit is the command. In func, a class decorated with @Command('commit') models that command.

path accepts either one string or an array of strings. Several Commands may share a prefix. func compiles every Command path and Handler path into one command graph, then selects the unique entry with the longest matching path.

Common invocation shapes

User inputMatched pathMeaning of the remainder
ship statusCommand statusNo extra input; run the default Handler.
ship config profile set devCommand + Handler setdev is positional input for the Handler.
ship deploy --env prodCommand deploy--env prod is a value option for deploy.
ship --helpModule action --helpThe root Module supplies a global action.

Concepts explains the major and missing command types in detail. See Shared fields for Module-scoped actions and Help to add --help.

You do not need to absorb every concept at once. The simplest mental model is a one-to-one relationship: the status command is driven by a class decorated with @Command('status'). The next section follows that smallest useful example from source to execution.

Add the smallest useful command

A minimal command needs one class and one default Handler. path is the command path users type; description is command metadata that explains its purpose.

src/status.command.ts
import { Command, Handler } from 'func'

@Command({
  path: 'status',
  description: 'Print service status',
})
export class StatusCommand {
  @Handler()
  run() {
    console.log('All systems operational')
  }
}

Creating the file and adding decorators does not register it. Add the class to a Module’s command list:

src/app.module.ts
import { StatusCommand } from './status.command'

@Module({
  commands: [StatusCommand],
})
export class AppModule {}
Click terminal to focus

After matching StatusCommand, func creates the instance, finds the method marked with @Handler(), and runs StatusCommand.run(). In other words, @Handler() marks the command’s default execution entry. Every command needs at least one Handler, and a class may add more Handlers for related actions.

Command aliases

An alias is another way to reach the same command. It creates neither a new graph node nor a different Handler; it is simply a convenient entry point.

TypeScript
@Command({
  path: 'status',
  aliases: ['s', 'stat'],
  description: 'Print service status',
})
export class StatusCommand {}

ship s and ship stat are now equivalent to ship status because both are aliases for the status command.

Use aliases for several alternatives. The singular alias is merged with that list and duplicates are removed. A command alias may contain more than one character and only needs to be unique under the same parent path. Option aliases such as -h, by contrast, must be exactly one non-numeric character.

Command paths

When several commands share a fixed prefix, include that prefix in the Command path. The class becomes active only when its complete path matches. If config profile always belongs together, for example, model both words as the path.

An alias replaces only the final path segment, so ship config profile and ship config p are equivalent here:

src/profile.command.ts
import { Command, Handler } from 'func'

@Command({
  path: ['config', 'profile'],
  aliases: ['p'],
  description: 'Manage saved profiles',
})
export class ProfileCommand {
  @Handler()
  list() {
    console.log('List profiles')
  }

  @Handler('set')
  set() {
    console.log('Set profile')
  }
}

This class also extends the path with @Handler('set'), using separate Handlers for two related forms:

  • Default Handler @Handler(): ship config profile
  • Path Handler @Handler('set'): ship config profile set

Path Handlers and Command paths both describe fixed grammar, but at different levels. Prefer distinct Commands when the business areas or dependencies are substantial. Use a path Handler for a small branch at the end of one cohesive command.

Multiple Handlers

Closely related actions for one resource can share a Command class and use Handler paths to distinguish actions such as project, project create, and project member add:

src/project.command.ts
import { Command, Handler } from 'func'

@Command({
  path: 'project',
  aliases: ['p', 'proj'],
  description: 'Manage projects and members',
})
export class ProjectCommand {
  @Handler()
  list() {
    console.log('List projects')
  }

  @Handler('create')
  create() {
    console.log('Create project')
  }

  @Handler(['member', 'add'])
  addMember() {
    console.log('Add project member')
  }

  @Handler({ flag: 'version', alias: 'v', description: 'Print version' })
  version() {
    console.log('1.0.0')
  }
}
  • ship project runs the default list() Handler.
  • ship project create runs the create() path Handler.
  • ship p member add uses the Command alias and longest-path matching to run addMember().
  • ship project -v selects the version method through its short alias.

A Handler flag selects one mutually exclusive action. A field @Flag() merely supplies a boolean to the Handler already selected. Handler flags accept both alias and aliases, but every alias must be one non-numeric character. A path Handler cannot declare aliases or also act as a Handler flag.

Deprecate commands

Set deprecated: true on @Command to mark the whole command, or provide a string with migration guidance. The command remains executable, appears as deprecated in structured help, and prints a non-fatal notice to stderr when dispatched.

Use @Deprecated() on a method with @Handler({ flag }) to deprecate only that action option. Default and path Handlers cannot use @Deprecated(); deprecate their owning Command instead.

Organize multiple commands

Write the complete invocation first, then distinguish fixed syntax from variable data. That tells you which func construct owns each part:

Category Performance Score
@Command One or more fixed words form a reusable command path. ship deploy / ship config profile
@Handler(path) Fixed trailing words select one action inside the current command class. ship project member add
@Flag / @Value Named data needs a type, default, alias, description, or validation. ship deploy --env prod
@Args().inputs The remaining path is variable data rather than fixed grammar. ship search alice team-a

An application may register many Commands, usually one class per business area. A larger area can live in its own @Module, imported by the root Module. Sharing a path prefix does not require putting every action in one class; split Commands when they need different dependency graphs or team ownership.

One CLI invocation executes exactly one Command or action entry. To perform several business operations in sequence, define an orchestration command and call shared Providers from its Handler.

Continue with Field options for named flags and values, or browse Examples for common command shapes.