We recommend creating a new directory with its own package.json. The CLI can import public application modules or shared packages, while its runtime dependencies, tests, and generated bundle remain outside the host application’s package and build pipeline.
Recommended: create a separate CLI package
.
|-- src existing application
|-- package.json existing application package
+-- tools
+-- admin-cli independent func package
|-- src
|-- tests
|-- package.json
+-- dist independent bundle outputThis boundary prevents command files and CLI-only dependencies from being discovered by an application bundler that scans the host package. It also lets you publish, version, test, or remove the CLI independently. In a monorepo, add the new directory to the existing workspace configuration instead of installing it into the application package.
Pass the directory name to the creator from its parent directory. The following command creates the CLI package as tools/admin-cli:
cd tools
npm init func@latest admin-cli
cd admin-cli
npm install
npm run dev -- --helpShare business code through an existing workspace package or a stable public module. Avoid importing the application bootstrap merely to reach a service; bootstraps often start servers, connect to databases, or register process handlers as an import side effect.
Alternative: isolate func inside the existing package
A shared package is reasonable when the repository cannot add another workspace or the CLI is a private interface to the same application code. Install the runtime and development tool without replacing the application’s existing scripts:
npm install func
npm install --save-dev funcgoKeep every CLI-owned file under a dedicated directory. A small func application normally consists of one executable entry, one root @FuncModule, command classes, and optional service classes:
src/cli
|-- commands
| +-- hello.command.ts
|-- cli.module.ts
+-- index.tsimport { run } from 'func'
import { CliModule } from './cli.module'
run(CliModule)import { FuncModule } from 'func'
import { HelloCommand } from './commands/hello.command'
@FuncModule({
commands: [HelloCommand],
})
export class CliModule {}import { Command, Handler } from 'func'
@Command({
name: 'hello',
description: 'Print a greeting',
})
export class HelloCommand {
@Handler()
run() {
console.log('Hello from func')
}
}Why this does not start the host application
func decorators attach metadata to the classes that declare them; they do not parse arguments or execute handlers on import. The explicit run(CliModule) call starts dispatch. As long as only src/cli/index.ts calls run and the host application does not import that entry, func does not replace the application runtime or bootstrap.
Commands may safely import shared, side-effect-free business modules. If shared logic needs state or collaborators, wrap it in a @Service and register that service in the CLI module instead of initializing global state in a command file.
Run with explicit paths first
Run funcgo setup without --fix to inspect its suggestions. In an existing package, automatic setup may propose replacing generic dev and build scripts, so keep the first integration explicit:
npx funcgo setup
npx funcgo dev -f src/cli/index.ts -- --help
npx funcgo dev -f src/cli/index.ts -- helloOnce the boundary is proven, add dedicated scripts such as cli:dev and cli:build; do not take over names already owned by the application.
Build to an isolated output
Send the CLI bundle to its own output directory so the host build never packages it accidentally. Add generated output to .gitignore when it should not be committed.
npx funcgo build -f src/cli/index.ts -o dist/cliAdd permanent func configuration and a bin mapping only when the CLI should become a public package interface:
{
"func": {
"entry": "src/cli/index.ts",
"outDir": "dist/cli"
},
"bin": {
"admin-cli": "./dist/cli/bin.js"
}
}