A command is a stable word a user types after the executable name to choose a business area or action. In git commit, git is the executable and commit is the command. In func, that command is modeled by a class decorated with @Command({ name: 'commit' }).
func reads only the first input token when choosing a named command. If it matches a name or alias, that command owns the invocation; if it begins with a hyphen, the major scope owns it. After the scope is selected, the command’s handlers and fields decide how to interpret the remaining tokens.
Recognize each part of an invocation
| User input | Command scope | Remaining meaning |
|---|---|---|
ship status | status | No additional input; the default handler runs. |
ship config get | config | get is a handler path inside the config command. |
ship deploy --env prod | deploy | —env prod is a value option owned by deploy. |
ship --help | Major command | —help can select a handler on the major command. |
The major command is a func-specific scope for invocation without a named command. It is explained with missing-command behavior in Core Concepts.
Choose the public shape before the decorator
Write the invocation a user should remember first. The stable and variable parts of that input determine which func concept should own them:
| Category | Performance | Score |
|---|---|---|
@Command | A stable top-level word selects a business area or action. | ship deploy |
@Handler({ path }) | Fixed nested words select one action inside the chosen command. | ship config profile get |
@Flag / @Value | Named data needs a type, default, alias, description, or validation. | ship deploy --env prod |
@Args().inputs | Remaining positional tokens are variable user data rather than fixed grammar. | ship search alice team-a |
Add the smallest useful command
Start with one class and one default handler. The name is the public word users type; description is metadata that your help handler can read through @Regs().
import { Command, Handler } from 'func'
@Command({
name: 'status',
description: 'Print service status',
})
export class StatusCommand {
@Handler()
run() {
console.log('All systems operational')
}
}A decorated class does not become reachable merely because its file exists. Add it to the command list registered by your root module:
import { ErrorHandler } from './error.command'
import { Major } from './major.command'
import { Missing } from './missing.command'
import { StatusCommand } from './status.command'
export const commands = [Major, StatusCommand, Missing, ErrorHandler]func selects StatusCommand, creates an instance, and invokes run() because it is the default @Handler(). Each command must have at least one handler, and a command can have at most one default handler.
Add an alias when it saves real typing
An alias is a second token that selects the same command. It does not create another command and does not run a different handler:
@Command({
name: 'status',
alias: 's',
description: 'Print service status',
})
export class StatusCommand {}Both ship status and ship s now select StatusCommand. Prefer recognizable abbreviations for frequent commands, such as g for greet. Avoid aliases for rarely used commands or abbreviations that could plausibly belong to another command. Names and aliases must be unique across all registered named commands.
Command aliases may contain more than one character, while option aliases such as -h must be a single character. Neither form should include a leading hyphen in the decorator value; func adds the hyphen for options.
Put related actions in one command
Use a command for a stable top-level verb or resource. When several actions belong to the same resource, keep one command scope and select methods with handler paths. This produces a predictable family such as project, project create, and project member add:
import { Command, Handler } from 'func'
@Command({
name: 'project',
alias: 'p',
description: 'Manage projects and members',
})
export class ProjectCommand {
@Handler()
list() {
console.log('List projects')
}
@Handler({ path: ['create'] })
create() {
console.log('Create project')
}
@Handler({ path: ['member', 'add'] })
addMember() {
console.log('Add project member')
}
@Handler({ flag: 'help', alias: 'h', description: 'Print project help' })
help() {
console.log('Project usage')
}
}ship projectruns the defaultlist()handler.ship project createruns thecreate()path handler.ship p member adduses the command alias, then runs the longest matching path.ship project --helpruns the help handler selected by a handler flag.
A handler flag chooses one mutually exclusive action. This is different from a field @Flag(), which supplies boolean data to whichever handler was selected. Path handlers are checked first, then handler flags, then the default handler. A path cannot declare an alias or be combined with a handler flag.
Organize several commands
An application can register as many named commands as it needs, normally one class per top-level business area. Group larger areas in feature @FuncModule classes and import those modules from the root instead of putting unrelated operations into one large command class.
One CLI invocation selects only one top-level command. Do not expect ship build deploy to run the build command and then the deploy command: build owns the scope and deploy is remaining input. For a fixed nested action, use a handler path. For a workflow that intentionally performs several operations, create one orchestration command and call shared services from its handler.
Next, use Field Options for flags and values, or see Examples to choose a command shape from user behavior.