next-term
v0.1.2
Published
Build CLI applications using Next.js-style file-based routing.
Readme
next-term
Build CLI applications using Next.js-style file-based routing.
Features
- File-based routing: Organize CLI commands using Next.js-style directory structure
- React/Ink support: Build beautiful CLI UIs with React and Ink
- Automatic bundling: All dependencies are bundled into a single distributable CLI
- Easy distribution: Generate install scripts for easy CLI distribution
- TypeScript support: Full TypeScript support out of the box
- Watch mode: Development mode with hot reloading
Installation
npm install next-term
# or
pnpm add next-term
# or
yarn add next-termQuick Start
0. Initialize your project
Run the interactive setup:
npx next-term initThis will:
- Ask about your preferred distribution method (bundle or registry)
- Create a
next-term.config.jsonfile with appropriate defaults - Generate a JSON schema for IDE autocomplete support
- Skip unnecessary prompts based on your chosen build type
- Automatically add
.next-termand build output to.gitignore
1. Create command files
Organize your CLI commands using Next.js-style routing:
src/app/
command.ts # Default command (runs when no subcommand is provided)
clock/
command.tsx # "my-cli clock" command
users/
command.ts # "my-cli users" command
[id]/
command.ts # "my-cli users <id>" command2. Write command handlers
Each command.ts or command.tsx file exports a default handler function:
// src/app/command.ts
export default function handler() {
console.log("Hello from my CLI!");
}// src/app/clock/command.tsx
import React from 'react';
import { render, Text } from 'ink';
export default async function handler() {
const Clock = () => {
return <Text color="blue">Clock</Text>;
};
render(<Clock />);
}3. Build your CLI
Bundle Mode (Self-Hosted Distribution)
npx next-term buildThis will:
- Find all
command.tsandcommand.tsxfiles - Bundle them with all dependencies
- Generate an install script at
public/cli/install - Create a distributable CLI in
public/cli/
Users can install your CLI by running:
curl https://your-domain.com/cli/install | shRegistry Mode (npm Distribution)
npx next-term build --packageThis will:
- Find all
command.tsandcommand.tsxfiles - Bundle them with all dependencies
- Generate a
package.jsonwith automatic versioning - Create an npm-publishable package in
.next-term/dist/
Publish to npm:
cd .next-term/dist
npm publishUsers can install your CLI from npm:
npm install -g your-cli-nameBuild Options
--package- Create npm-publishable package in.next-term/dist--backend-url <url>- Backend URL for CLI distribution (overrides config)--x-deployment-id <id>- Deployment ID for versioning (overrides config)
Priority Resolution:
- CLI flags (highest priority)
- Config file values
- Vercel environment variables (
VERCEL_URL,VERCEL_DEPLOYMENT_ID) - Default values
Configuration
Create a next-term.config.json file to configure your CLI:
Bundle Mode Configuration
{
"$schema": "./.next-term/next-term.config.schema.json",
"cliName": "my-cli",
"outputDir": "cli",
"buildType": "bundle",
"backendUrl": "https://my-site.com",
"deploymentId": "production",
"installPath": ".my-cli",
"localBaseUrl": "http://localhost:3000"
}Registry Mode Configuration
{
"$schema": "./.next-term/next-term.config.schema.json",
"cliName": "my-cli",
"buildType": "registry",
"localBaseUrl": "http://localhost:3000",
"npmInfo": {
"author": "Your Name <[email protected]>",
"description": "My awesome CLI tool",
"keywords": ["cli", "terminal", "automation"],
"repository": {
"type": "git",
"url": "https://github.com/user/repo"
},
"homepage": "https://example.com",
"bugs": {
"url": "https://github.com/user/repo/issues"
},
"license": "MIT"
}
}Configuration Options
$schema- Path to JSON schema for IDE autocomplete (automatically added bynpx next-term init)cliName- Name of your CLI command (required)buildType- Distribution mode:"bundle"(self-hosted) or"registry"(npm) (default: "bundle")localBaseUrl- Local base URL for development (default: "http://localhost:3000")
Bundle mode only:
outputDir- Output directory relative to public/ (default: "cli")installPath- Installation path on users' machines (default: ".demo-cli")backendUrl- Backend URL for CLI distribution (optional)deploymentId- Deployment ID for versioning (optional)
Registry mode only:
npmInfo- npm package metadata (optional object):author- Package author (name )description- Package descriptionkeywords- Array of keywords for npm search (merged with defaults: "cli", "next-term")repository- Source repository info (typeandurl)homepage- Package homepage URLbugs- Bug tracker info (url)license- SPDX license identifier (default: "MIT")
JSON Schema Support
Running npx next-term init automatically creates a JSON schema file at .next-term/next-term.config.schema.json and adds a $schema reference to your config. This provides:
- IDE autocomplete for all configuration options
- Inline documentation as you type
- Validation for config structure and values
- Works with VS Code, WebStorm, and other JSON-schema-aware editors
The schema file is preserved during builds, so you retain IDE autocomplete when editing your config after building.
Build Types
Bundle Mode ("bundle"):
- Outputs to
public/{outputDir}/ - Generates install script for curl-based installation
- Users install via:
curl https://your-domain.com/cli/install | sh - Auto-updates enabled: CLI checks for updates from your backend on startup
- Best for self-hosted distribution
Registry Mode ("registry"):
- Outputs to
.next-term/dist/ - Generates
package.jsonwith date-based versioning - Ready for npm publishing
- Users install via:
npm install -g your-cli-name - Auto-updates disabled: Relies on npm/yarn/pnpm for version management
- Best for npm/yarn/pnpm distribution
README Documentation
For registry builds, next-term automatically includes README documentation in your npm package:
- README-CLI.md (priority): If this file exists in your project root, it will be copied to
.next-term/dist/README.md - README.md (fallback): If README-CLI.md doesn't exist, the main README.md is used instead
This allows you to maintain separate documentation:
- README.md: For developers working on the codebase
- README-CLI.md: For end-users installing your CLI from npm
Bundle mode doesn't copy README files (not needed for curl-based installation).
Gitignore Management
Running npx next-term init automatically updates your .gitignore file:
Always added:
.next-term- Internal build directory
Bundle mode only:
public/<outputDir>- Your CLI distribution files (e.g.,public/cli)
If .gitignore doesn't exist, it will be created with additional essentials like node_modules and .env files.
Entries are added with a # next-term comment header for clarity. Existing formatting and comments in your .gitignore are preserved.
API
compile(options)
Compile your CLI application.
import { compile } from 'next-term';
await compile({
folderPath: 'src/app', // Path to your command files
outputDir: 'cli', // Output directory (relative to public/)
watch: false, // Enable watch mode
internalBuildPath: '.next-term', // Internal build directory
baseUrl: 'https://example.com', // Base URL for CLI distribution
cliName: 'my-cli', // Name of your CLI
deploymentId: 'deploy-123', // Optional deployment ID
installPath: '.my-cli', // Installation path on user's machine
buildType: 'registry', // Build type: 'bundle' or 'registry'
npmInfo: { // Optional npm metadata (registry mode only)
author: 'Your Name',
description: 'My CLI tool',
keywords: ['cli', 'tool'],
license: 'MIT',
},
});Command Routing
Commands are automatically routed based on file structure:
src/app/command.ts→my-cli(default command)src/app/clock/command.tsx→my-cli clocksrc/app/users/[id]/command.ts→my-cli users <id>
Route paths map directly to CLI commands:
/→ default command/clock→clocksubcommand/clock/settings→clock settingsnested command
Using React/Ink
You can use React and Ink to build interactive CLI UIs:
import React, { useState, useEffect } from 'react';
import { render, Text } from 'ink';
export default function handler() {
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 100);
return () => clearInterval(timer);
}, []);
return <Text color="green">{count} tests passed</Text>;
};
render(<Counter />);
}Vercel Deployment
When building on Vercel, next-term automatically detects environment variables:
# On Vercel, these are automatically used:
# - VERCEL_URL → backend URL
# - VERCEL_DEPLOYMENT_ID → deployment ID
npx next-term build --packageYou can still override with CLI flags if needed:
npx next-term build --package \
--backend-url https://custom.com \
--x-deployment-id custom-idAutomatic Versioning
Registry builds use date-based versioning with build hashes:
Format: YYYY.M.D-{buildHash}
Example: 2026.1.24-a1b2c3d4e5f6This ensures:
- Unique versions for every build
- Chronological ordering
- npm semver compatibility
Development
Run in watch mode during development:
npx next-term devThis will watch for changes and rebuild automatically.
Troubleshooting
Config validation errors
If you're getting validation errors in your next-term.config.json:
- Make sure you've run
npx next-term initto generate the JSON schema - Check that the
$schemafield points to./.next-term/next-term.config.schema.json - Verify your IDE supports JSON schema validation (most modern editors do)
Registry build missing npm metadata
If your npm package is missing author, description, or other metadata:
- Add an
npmInfoobject to your config file (see Registry Mode Configuration above) - Fill in the fields you want to include in package.json
- Rebuild with
npx next-term build --package
Bundle mode CLI not auto-updating
Auto-updates only work in bundle mode. If you're using registry mode:
- Updates are managed by npm/yarn/pnpm
- Users update by running
npm update -g your-cli-name - The CLI will not check for updates on startup
README not included in npm package
For registry builds:
- Create a
README-CLI.mdin your project root (recommended for CLI-specific docs) - Or ensure
README.mdexists in your project root (used as fallback) - The file will be automatically copied to
.next-term/dist/README.mdduring build
Migration Notes
Breaking Changes
Output file renamed from runtime.js to cli.js
The compiled CLI output is now named cli.js instead of runtime.js. This affects:
- Bundle mode:
public/<outputDir>/cli.js - Registry mode:
.next-term/dist/cli.js - Manifest field renamed from
runtimeHashtocliHash
No migration needed - simply rebuild your CLI to use the new naming. Existing deployed CLIs continue working until rebuilt.
Requirements
- Node.js 18+
- TypeScript 5+
License
MIT
