中文

Quick Start

Create, develop, and bundle a func CLI project with the default TypeScript template.

Updated 2 weeks ago
  1. Create the project

    Install Node.js 24.0 or newer first.

    The command passes ship as the project name. The creator makes a new directory with that name and copies the TypeScript template into it. It will not overwrite an existing directory. If you use an agent to create it automatically, follow the agent setup guide.

    Terminal
    npm init func@latest ship
  2. Install and inspect the generated CLI

    Enter the new directory, install dependencies, and run its help handler.

    Terminal
    cd ship
    npm install
    npm run dev -- --help

Understand the generated project

In the default template, the src directory contains all application code and tests contains the test cases. A dist directory will also appear after you run build. In most cases, only the contents of dist are ultimately published.

Project structure
.
|-- src
|   |-- app.module.ts          root module
|   |-- config.ts              package and runtime settings
|   |-- commands
|   |   |-- error.command.ts   global input-error output
|   |   |-- greet.command.ts   named command example
|   |   |-- major.command.ts   empty input, help, and version
|   |   |-- missing.command.ts unknown-command response
|   |   +-- index.ts           command list
|   |-- services
|   |   +-- project.service.ts injectable service example
|   +-- index.ts               executable entry
|-- tests                      executable behavior tests
|-- package.json
|-- tsconfig.json
+-- README.md

The executable entry calls run(AppModule). The root module gathers the command and service classes that func is allowed to use:

src/app.module.ts
import { FuncModule } from 'func'
import { commands } from './commands'
import { services } from './services'

@FuncModule({
  commands,
  services,
})
export class AppModule {}

Commands describe the user-facing CLI. Services hold reusable work that should not be tied to parsing or printing. Tests spawn the generated executable so dispatch, input parsing, and output are checked together.

Run commands during development

The template exposes funcgo through ordinary npm scripts:

package.json
{
  "scripts": {
    "dev": "funcgo dev --",
    "build": "funcgo build"
  }
}

In the npm tab, npm run dev -- <arguments> follows npm’s argument-passthrough convention. The first -- tells npm to stop reading its own options and append everything that follows to the script. The script’s trailing -- then tells funcgo dev that those tokens belong to your CLI rather than funcgo. You do not need to parse or remove either delimiter in application code.

Terminal
npm run dev -- greet
npm run dev -- greet --name Ada
npm run dev -- greet shout --name Ada

Each invocation executes the TypeScript entry once. This is the fastest way to test a command while editing because no production bundle is required. Use the switcher in the terminal header for the equivalent commands from other package managers.

Add your first command

Create a class with a stable command name and one default handler. The class name is only for TypeScript; users type the name from @Command .

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

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

Export it from the command list so AppModule can register it, then run npm run dev -- status.

src/commands/index.ts
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]
Click terminal to focus

See Commands before adding aliases or several actions, and Field Options before accepting flags or values.

Optional: Map the command globally

This step is not required to use funcgo. Create a global link only if you want to type the final command name directly during development, such as ship; otherwise, keep using npm run dev -- <command>.

During development, your package manager can map the current package into its global command directory. Build first because package.json#bin points to the generated dist/bin.js, then create the link from the project root:

Terminal
npm run build
npm link

# Use the key from package.json#bin

ship --help
Click terminal to focus

The command name here is ship, so you can run ship --help above. The mapping uses this project’s build output. Rebuild the bundle after source changes and rerun the mapping command when needed.

To remove the mapping, use the corresponding command for the active package manager:

Terminal
npm uninstall --global ship

Rebuild while files change

funcgo build --watch performs an initial build, watches src/**/*.ts by default, and rebuilds after matching changes. Keep it running in one terminal and invoke the globally linked command in another. Press Ctrl+C to stop watching.

Terminal
npm run build -- --watch

# Watch additional files or custom globs
npm run build -- --watch --watch-path 'src/**/*.ts' --watch-path config.json

Each --watch-path can be a file, directory, or positive glob. Use it for inputs outside src, such as a JSON configuration file. Generated output, node_modules, and .git are ignored.

Troubleshoot the first run

The shell cannot find the global command

Confirm that the build produced dist/bin.js, run npm link from the package root, and invoke the key from package.json#bin rather than the package name.

func reports an unknown command

Export the command class from the registered command list. Creating the file and adding @Command() does not register the class by itself.

npm consumes an option intended for the CLI

Keep the passthrough separator: use npm run dev -- status --json. The tokens after -- belong to your CLI.

Bundle and prepare to publish

The build script bundles the configured TypeScript entry into func.outDir (the template uses dist) and creates an executable bin.js. The package’s bin field exposes that file under the command name users will install.

Terminal
npm run build
npm pack --dry-run

The dry-run pack command in the terminal shows which files would be published without publishing them. Confirm that the bundle, package metadata, README, and license are present, then run npm publish to publish the package to npm. For custom entry, output, external dependency, and watch settings, see Tooling.