中文

Quick start

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

Updated
  1. Create the project

    Install Node.js 24.15 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
|   |-- commands
|   |   |-- deploy
|   |   |   |-- deploy.command.ts  deploy command example
|   |   |   |-- deploy.module.ts   deploy feature module
|   |   |   +-- deploy.service.ts  deploy business service
|   |   +-- greet
|   |       |-- greet.command.ts   greeting command example
|   |       |-- greet.module.ts    greeting feature module
|   |       +-- greet.service.ts   greeting business service
|   |-- decorators
|   |   +-- url.decorator.ts       custom URL value decorator
|   |-- shared
|   |   |-- services
|   |   |   +-- output.service.ts  shared output service
|   |   +-- shared.module.ts       shared feature module
|   +-- index.ts                   executable entry
|-- tests
|   |-- cli
|   |   +-- smoke.test.ts          built-artifact smoke test
|   |-- commands
|   |   |-- catch.test.ts          error-filter test
|   |   |-- deploy.test.ts         deploy command test
|   |   |-- greet.test.ts          greeting command test
|   |   +-- help.test.ts           help output test
|   +-- utils
|       +-- cli.ts                 CLI test utility
|-- .gitignore                     Git ignore rules
|-- LICENSE                        open-source license
|-- README.md                      project documentation
|-- package.json
|-- tsconfig.json
|-- vitest.cli.config.mts
+-- vitest.config.mts

Installing dependencies also creates the lockfile for the selected package manager. Running build later creates dist. Neither generated artifact appears in the initial tree above.

The root Module (app.module.ts) gathers and registers the Commands, options, and dependencies that func may call. The following excerpt omits optional feature configuration already enabled by the template so the core structure stays visible:

src/app.module.ts
import { Module } from 'func'
import { DeployModule } from './commands/deploy/deploy.module'
import { GreetModule } from './commands/greet/greet.module'

@Module({
  imports: [GreetModule, DeployModule],
})
export class AppModule {}

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 path and one default Handler. The class name exists only in TypeScript; users type the path declared by @Command.

src/commands/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')
  }
}

Add it to the command list registered by AppModule, then run npm run dev -- status.

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.