CLI (cac)
The cac adapter wraps a DevframeDefinition in a cac-powered command-line interface. From one entry it spins up an h3 dev server with WebSocket RPC, builds static snapshots, builds SPA bundles, or starts an MCP server.
cac is an optional peer dependency, pulled in only through this adapter — install it alongside devframe to opt into createCac:
npm install devframe cacTools that assemble their own command-line shell from the lower-level factories never import this adapter, so they run without cac.
import { defineDevframe } from 'devframe'
import { createCac } from 'devframe/adapters/cac'
const devframe = defineDevframe({
id: 'my-devframe',
name: 'My Devframe',
cli: { distDir: './client/dist' },
setup(ctx) { /* register docks, RPC, etc. */ },
})
await createCac(devframe).parse()The devframe/adapters/cli entry (createCli) remains as a deprecated alias for this module — new code should import createCac from devframe/adapters/cac.
Running the resulting binary:
my-devframe # dev server at http://localhost:9999/
my-devframe --port 8080
my-devframe build --out-dir dist-static
my-devframe build --out-dir dist-static --base /devframe/
my-devframe mcp # stdio MCP server (experimental)Standalone CLI serves the SPA at / by default. The /__devframe/ prefix is for hosted adapters where devframe mounts alongside an existing app — see Mount paths.
Options
createCac(def, options?) accepts:
| Option | Default | Description |
|---|---|---|
defaultPort | 9999 (or def.cli?.port) | Port used by the dev command when --port isn't provided. |
configureCli | — | (cli: CAC) => void — final hook to add commands/flags at the assembly stage, after the definition's cli.configure runs. |
onReady | — | (info: { origin, port, app }) => void | Promise<void> — called once the dev server is listening. Use this to print your own startup banner. |
createCac returns a CacHandle:
interface CacHandle {
cli: CAC // raw cac instance — mutate before calling parse()
parse: (argv?: string[]) => Promise<void>
}The cli property lets the caller add ad-hoc commands and flags right before parse() when a configureCli callback is inconvenient.
Definition-level cli fields
defineDevframe({
id: 'my-devframe',
cli: {
command: 'my-devframe', // binary name; default: the id
distDir: './client/dist', // required for dev/build/spa
port: 7777, // preferred port
portRange: [7777, 9000], // passed through to get-port-please
random: false, // passed through to get-port-please
host: '127.0.0.1', // default host; --host overrides
open: true, // auto-open the browser on dev start
auth: false, // skip the trust handshake (single-user localhost)
configure(cli) { // contribute capability flags/commands
cli.option('--config <file>', 'Custom config file')
.option('--no-files', 'Skip file matching')
},
},
setup(ctx, { flags }) {
// `flags` is the parsed cac flag bag — includes both devframe's
// built-ins (`--port`, `--host`, `--open`) and anything declared in
// `cli.configure` or `configureCli`.
},
})distDir is the only required field; everything else has sensible defaults. The configure hook runs before the configureCli option passed to createCac, so the final tool author always has the last word on flags.
Headless logging
Devframe leaves startup output to the application. Wire onReady to print your own banner:
await createCac(devframe, {
onReady({ origin }) {
console.log(`ESLint Config Inspector ready at ${origin}`)
},
}).parse()Structured diagnostics (via nostics) continue to surface through their normal reporters.
Use your own CLI framework
To integrate devframe into an existing commander / yargs program — or to expose a different command structure than createCac's dev / build / mcp triplet — drop down to the peer factories. Same DevframeDefinition, different shell:
| Building block | Entry | Purpose |
|---|---|---|
createDevServer(def, opts?) | devframe/adapters/dev | h3 + WebSocket RPC + SPA mount |
createBuild(def, opts?) | devframe/adapters/build | Static deploy |
createMcpServer(def, opts?) | devframe/adapters/mcp | stdio MCP server |
parseCliFlags(schema, raw) | devframe/adapters/cac | Validate a flag bag against a CliFlagsSchema |
See the Standalone CLI guide for a worked commander example.