Custom Commands
KickJS lets you extend the CLI with project-specific commands defined in kick.config.ts. These appear alongside built-in commands in kick --help.
Distributing commands as a package
Adopter commands in kick.config.ts are the simplest path for project-local shell wrappers. If you want to ship commands (plus generators or typegens) inside a published package, write a KickCliPlugin and expose them via plugins[] instead — see CLI Plugins.
Configuration
Create a kick.config.ts (or .js, .mjs, .json) in your project root:
import { defineConfig } from '@forinda/kickjs-cli'
export default defineConfig({
commands: [
{
name: 'db:migrate',
description: 'Run database migrations',
steps: 'kick db migrate',
},
{
name: 'db:seed',
description: 'Run seed files',
steps: 'npx tsx src/db/seed.ts',
},
{
name: 'proto:gen',
description: 'Generate TypeScript from protobuf definitions',
steps: ['npx buf generate', 'echo "Protobuf types generated"'],
},
],
})defineConfig()
The defineConfig helper provides type safety for your configuration:
import { defineConfig } from '@forinda/kickjs-cli'
export default defineConfig({
modules: {
dir: 'src/modules',
repo: { name: 'postgres' },
schemaDir: 'src/db/schema',
},
style: {
semicolons: false,
quotes: 'single',
trailingComma: 'all',
indent: 2,
},
commands: [
/* ... */
],
})Command Definition
Each command in the commands array has this shape:
interface KickCommandDefinition {
name: string // Command name (e.g. 'db:migrate')
description: string // Shown in --help
steps: string | string[] // Shell command(s) to execute
aliases?: string[] // Optional aliases (e.g. ['migrate'])
}Single Step
{
name: 'db:push',
description: 'Push schema directly (dev only)',
steps: 'kick db push',
}Multiple Steps
When steps is an array, commands run sequentially. If any step fails, execution stops:
{
name: 'db:reset',
description: 'Drop and recreate the database',
steps: [
'kick db drop',
'kick db migrate',
'npx tsx src/db/seed.ts',
],
}Aliases
{
name: 'db:migrate',
description: 'Run database migrations',
steps: 'kick db migrate',
aliases: ['migrate'],
}Now both kick db:migrate and kick migrate work.
Passing Arguments
Custom commands accept trailing arguments. Any extra arguments are appended to the shell command:
kick db:migrate --verbose
# Runs: kick db migrate --verboseFull Example
A production kick.config.ts with Drizzle ORM commands:
import { defineConfig } from '@forinda/kickjs-cli'
export default defineConfig({
modules: {
dir: 'src/modules',
repo: { name: 'postgres' },
},
commands: [
{
name: 'db:generate',
description: 'Generate Drizzle migrations from schema',
steps: 'kick db generate',
},
{
name: 'db:migrate',
description: 'Run database migrations',
steps: 'kick db migrate',
},
{
name: 'db:push',
description: 'Push schema directly (dev only)',
steps: 'kick db push',
},
{
name: 'db:studio',
description: 'Open Drizzle Studio GUI',
steps: 'kick db studio',
},
{
name: 'db:seed',
description: 'Run seed files',
steps: 'npx tsx src/db/seed.ts',
},
],
})Copying Static Assets (copyDirs)
When you run kick build, Vite bundles your TypeScript into dist/index.js. But static assets like EJS templates, HTML files, or public CSS/JS need to be copied alongside the bundle for runtime access.
The copyDirs config handles this automatically after vite build completes:
import { defineConfig } from '@forinda/kickjs-cli'
export default defineConfig({
copyDirs: [
'views', // views/ → dist/views/
'public', // public/ → dist/public/
{ src: 'templates', dest: 'dist/email' }, // templates/ → dist/email/
],
})Each entry can be:
- A string — copies
<name>/todist/<name>/ - An object with
srcand optionaldest— copiessrcto the specified destination
Build output
kick build
Building for production...
vite v8.0.2 building ssr environment for production...
✓ 83 modules transformed.
dist/index.js 429.46 kB
Copying directories to dist...
✓ views → dist/views
✓ public → dist/public
Build complete.When to use
- EJS/Pug/Handlebars templates served by the
ViewAdapter - Public assets (CSS, JS, images) served via
express.static() - Email templates loaded at runtime by the mailer
- Migration files or seed scripts that need to ship with the build
Config File Resolution
The CLI searches for configuration in this order:
kick.config.tskick.config.jskick.config.mjskick.config.json
The first file found is loaded. TypeScript and ESM files are imported dynamically. JSON files are parsed directly. If no config file is found, custom commands are simply not registered.