Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/split-optional-entry-points.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'padrone': major
---

Optional integrations are now exported from dedicated subpath entry points so bundlers that don't tree-shake re-exports keep their dependencies out of the main bundle. Update imports as follows:

- `padroneInk`, `isReactElement`, `InkOptions` → `'padrone/ink'`
- `padroneMcp`, `WithMcp`, `PadroneMcpPreferences` → `'padrone/mcp'`
- `padroneServe`, `WithServe` → `'padrone/serve'`
- `padroneTracing`, `WithTracing`, `PadroneTracer`, `PadroneTracingConfig`, `OtelSpan`, `OtelTracer`, `OtelTracerProvider` → `'padrone/tracing'`
- `padroneCompletion`, `WithCompletion` → `'padrone/completion'`
- `padroneMan`, `WithMan` → `'padrone/man'`
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ When asked to commit with a changeset, create a concise changeset suitable for a

**Interceptor system**: Onion model with 7 phases: start → parse → route → validate → execute → (error) → shutdown. `collectInterceptors()` in `exec.ts` walks the parent chain (root outermost, subcommand innermost). Start/parse use root interceptors only. Route/validate/execute use the full collected chain (root + command). Error/shutdown run in two layers: command-level interceptors first (for validate/execute failures), then root-level interceptors (for all failures including parse). All interceptor phase contexts include `caller` (the invocation method: 'cli', 'eval', 'run', etc.) and `signal` (AbortSignal for cancellation). `defineInterceptor(meta, factory)` is the recommended API — the factory is called fresh per execution, enabling cross-phase state sharing via closures. Supports `.provides<T>()` and `.requires<T>()` for typed context (type-level only). Interceptors with the same `id` are deduplicated (last wins). Signal handling is implemented as a start-phase interceptor (`padroneSignalHandling`) that creates an AbortController and propagates it via `next({ signal })`. Error help display (routing/validation errors in CLI mode) is handled by the help extension's error phase, not hardcoded in exec.ts.

**Extension system**: Build-time composition via `.extend(extension)`. A `PadroneExtension` is a function that receives the builder and returns a modified builder, enabling reusable command/config bundles. Unlike interceptors (which hook into runtime phases), extensions operate at definition time to compose commands, arguments, and configuration. Built-in extensions are included by default via `createPadrone()`: help (-1000), version (-1000), repl (-1000), color (-1001), suggestions (-500), signal (-2000), autoOutput (-1100), stdin (-1001), interactive (-999). Numbers are interceptor `order` values (lower = outermost). User-facing builtins (help, version, repl) can be individually disabled via `{ builtins: { help: false } }`. Advanced opt-in extensions: completion, logger, timing, progress, man, mcp, serve, update-check, tracing, ink, env, config.
**Extension system**: Build-time composition via `.extend(extension)`. A `PadroneExtension` is a function that receives the builder and returns a modified builder, enabling reusable command/config bundles. Unlike interceptors (which hook into runtime phases), extensions operate at definition time to compose commands, arguments, and configuration. Built-in extensions are included by default via `createPadrone()`: help (-1000), version (-1000), repl (-1000), color (-1001), suggestions (-500), signal (-2000), autoOutput (-1100), stdin (-1001), interactive (-999). Numbers are interceptor `order` values (lower = outermost). User-facing builtins (help, version, repl) can be individually disabled via `{ builtins: { help: false } }`. Advanced opt-in extensions imported from `'padrone'`: logger, timing, progress, update-check, env, config. Optional integrations with heavier transitive surfaces live in their own subpath entry points so the main bundle stays lean: `padrone/ink`, `padrone/mcp`, `padrone/serve`, `padrone/tracing`, `padrone/completion`, `padrone/man`.

**Flags vs aliases vs negatives**: `flags` = single-char short flags (`-v`), stackable. `alias` = multi-char alternative long names (`--dry-run`). `autoAlias` (default: true) auto-generates kebab-case aliases for camelCase option names. `negative` = custom negation keyword(s) for booleans (`negative: 'remote'` makes `--remote` set the arg to `false` and disables `--no-` prefix). Set to `''` or `[]` to only disable the prefix.

Expand Down
12 changes: 6 additions & 6 deletions docs/src/content/docs/guides/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ Additional opt-in extensions are available for advanced features:
| `padroneEnv(schema)` | `'padrone'` | Parse environment variables into args |
| `padroneConfig(options)` | `'padrone'` | Load args from config files |
| `padroneProgress(config)` | `'padrone'` | Auto-managed progress indicators |
| `padroneCompletion()` | `'padrone'` | Shell completion generation |
| `padroneLogger(options)` | `'padrone'` | Structured logging with levels |
| `padroneTiming()` | `'padrone'` | Execution timing |
| `padroneMan()` | `'padrone'` | Man page generation |
| `padroneUpdateCheck(config)` | `'padrone'` | Background version checking |
| `padroneMcp()` | `'padrone'` | MCP server integration |
| `padroneServe()` | `'padrone'` | REST server integration |
| `padroneTracing(config)` | `'padrone'` | OpenTelemetry tracing |
| `padroneInk()` | `'padrone'` | React (Ink) rendering support |
| `padroneInk()` | `'padrone/ink'` | React (Ink) rendering support |
| `padroneMcp()` | `'padrone/mcp'` | MCP server integration |
| `padroneServe()` | `'padrone/serve'` | REST server integration |
| `padroneTracing(config)` | `'padrone/tracing'` | OpenTelemetry tracing |
| `padroneCompletion()` | `'padrone/completion'` | Shell completion generation |
| `padroneMan()` | `'padrone/man'` | Man page generation |

## Extensions

Expand Down
17 changes: 11 additions & 6 deletions docs/src/content/docs/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1187,15 +1187,20 @@ These extensions are available as named exports from `'padrone'`:
| `padroneEnv(schema)` | Parse environment variables into args |
| `padroneConfig(options)` | Load args from config files |
| `padroneProgress(config)` | Auto-managed progress indicators |
| `padroneCompletion()` | Shell completion generation |
| `padroneLogger(options)` | Structured logging with levels |
| `padroneTiming()` | Execution timing |
| `padroneMan()` | Man page generation |
| `padroneUpdateCheck(config)` | Background version checking |
| `padroneMcp()` | MCP server integration |
| `padroneServe()` | REST server integration |
| `padroneTracing(config)` | OpenTelemetry tracing |
| `padroneInk()` | React (Ink) rendering support |

The following extensions live in their own subpath imports to keep optional dependencies and large transitive surfaces out of the main bundle:

| Export | Import from | Purpose |
|--------|-------------|---------|
| `padroneInk()` | `'padrone/ink'` | React (Ink) rendering support |
| `padroneMcp()` | `'padrone/mcp'` | MCP server integration |
| `padroneServe()` | `'padrone/serve'` | REST server integration |
| `padroneTracing(config)` | `'padrone/tracing'` | OpenTelemetry tracing |
| `padroneCompletion()` | `'padrone/completion'` | Shell completion generation |
| `padroneMan()` | `'padrone/man'` | Man page generation |

The following extensions are applied automatically by `createPadrone()` and can be disabled via `builtins`:

Expand Down
3 changes: 2 additions & 1 deletion examples/padrone-example/src/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createPadrone, defineInterceptor, padroneInk, padroneLogger, padroneProgress, padroneTiming } from 'padrone';
import { createPadrone, defineInterceptor, padroneLogger, padroneProgress, padroneTiming } from 'padrone';
import { padroneInk } from 'padrone/ink';
import { zodAsyncStream } from 'padrone/zod';
import * as z from 'zod/v4';
import { addTask, getTask, getTasks, removeTask, setTaskStatus, updateTask } from './tasks-store.ts';
Expand Down
58 changes: 53 additions & 5 deletions packages/padrone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@
"./codegen": {
"padrone@dev": "./src/codegen/index.ts",
"import": {
"types": "./dist/codegen.d.mts",
"default": "./dist/codegen.mjs"
"types": "./dist/codegen/index.d.mts",
"default": "./dist/codegen/index.mjs"
},
"source": "./src/codegen/index.ts"
},
"./docs": {
"padrone@dev": "./src/docs/index.ts",
"import": {
"types": "./dist/docs.d.mts",
"default": "./dist/docs.mjs"
"types": "./dist/docs/index.d.mts",
"default": "./dist/docs/index.mjs"
},
"source": "./src/docs/index.ts"
},
Expand All @@ -89,10 +89,58 @@
},
"source": "./src/zod.ts"
},
"./ink": {
"padrone@dev": "./src/ink.ts",
"import": {
"types": "./dist/ink.d.mts",
"default": "./dist/ink.mjs"
},
"source": "./src/ink.ts"
},
"./mcp": {
"padrone@dev": "./src/mcp.ts",
"import": {
"types": "./dist/mcp.d.mts",
"default": "./dist/mcp.mjs"
},
"source": "./src/mcp.ts"
},
"./serve": {
"padrone@dev": "./src/serve.ts",
"import": {
"types": "./dist/serve.d.mts",
"default": "./dist/serve.mjs"
},
"source": "./src/serve.ts"
},
"./tracing": {
"padrone@dev": "./src/tracing.ts",
"import": {
"types": "./dist/tracing.d.mts",
"default": "./dist/tracing.mjs"
},
"source": "./src/tracing.ts"
},
"./completion": {
"padrone@dev": "./src/completion.ts",
"import": {
"types": "./dist/completion.d.mts",
"default": "./dist/completion.mjs"
},
"source": "./src/completion.ts"
},
"./man": {
"padrone@dev": "./src/man.ts",
"import": {
"types": "./dist/man.d.mts",
"default": "./dist/man.mjs"
},
"source": "./src/man.ts"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "tsdown src/index.ts src/test.ts src/zod.ts src/codegen/index.ts src/docs/index.ts --dts --sourcemap",
"build": "tsdown src/index.ts src/test.ts src/zod.ts src/ink.ts src/mcp.ts src/serve.ts src/tracing.ts src/completion.ts src/man.ts src/codegen/index.ts src/docs/index.ts --dts --sourcemap --unbundle",
"start": "bun --conditions=padrone@dev src/cli/index.ts",
"dev": "bun --conditions=padrone@dev --watch src/cli/index.ts",
"test": "bun test --conditions=padrone@dev",
Expand Down
2 changes: 2 additions & 0 deletions packages/padrone/src/completion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { WithCompletion } from './extension/completion.ts';
export { padroneCompletion } from './extension/completion.ts';
3 changes: 3 additions & 0 deletions packages/padrone/src/extension/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export type WithCompletion<T> = WithCommand<T, 'completion', CompletionCommand>;
*
* Usage:
* ```ts
* import { createPadrone } from 'padrone';
* import { padroneCompletion } from 'padrone/completion';
*
* createPadrone('my-cli').extend(padroneCompletion())
* ```
*/
Expand Down
12 changes: 0 additions & 12 deletions packages/padrone/src/extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,15 @@ export type { WithAsync } from '../util/type-utils.ts';
export type { PadroneAutoOutputOptions } from './auto-output.ts';
export { padroneAutoOutput } from './auto-output.ts';
export { padroneColor } from './color.ts';
export type { WithCompletion } from './completion.ts';
export { padroneCompletion } from './completion.ts';
export type { PadroneConfigOptions } from './config.ts';
export { padroneConfig } from './config.ts';
export type { PadroneEnvOptions } from './env.ts';
export { padroneEnv } from './env.ts';
export type { HelpCommand, WithHelp } from './help.ts';
export { padroneHelp } from './help.ts';
export type { InkOptions } from './ink.ts';
export { isReactElement, padroneInk } from './ink.ts';
export { padroneInteractive } from './interactive.ts';
export type { PadroneLogger, PadroneLoggerConfig, PadroneLogLevel, WithLogger } from './logger.ts';
export { padroneLogger } from './logger.ts';
export type { WithMan } from './man.ts';
export { padroneMan } from './man.ts';
export type { WithMcp } from './mcp.ts';
export { padroneMcp } from './mcp.ts';
export type {
PadroneProgressConfig,
PadroneProgressDefaults,
Expand All @@ -31,15 +23,11 @@ export type { PadroneProgressRenderer } from './progress-renderer.ts';
export { createTerminalProgress } from './progress-renderer.ts';
export type { WithRepl } from './repl.ts';
export { padroneRepl } from './repl.ts';
export type { WithServe } from './serve.ts';
export { padroneServe } from './serve.ts';
export { padroneSignalHandling } from './signal.ts';
export { padroneStdin } from './stdin.ts';
export { padroneSuggestions } from './suggestions.ts';
export type { PadroneTimingOptions } from './timing.ts';
export { padroneTiming } from './timing.ts';
export type { OtelSpan, OtelTracer, OtelTracerProvider, PadroneTracer, PadroneTracingConfig, WithTracing } from './tracing.ts';
export { padroneTracing } from './tracing.ts';
export { padroneUpdateCheck } from './update-check.ts';
export type { VersionCommand, WithVersion } from './version.ts';
export { padroneVersion } from './version.ts';
3 changes: 2 additions & 1 deletion packages/padrone/src/extension/ink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ function createInkInterceptor(rawOptions?: InkOptions) {
* Requires `ink` and `react` as peer dependencies.
*
* ```ts
* import { createPadrone, padroneInk } from 'padrone';
* import { createPadrone } from 'padrone';
* import { padroneInk } from 'padrone/ink';
*
* const program = createPadrone('my-tui')
* .extend(padroneInk())
Expand Down
3 changes: 3 additions & 0 deletions packages/padrone/src/extension/man.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export type WithMan<T> = WithCommand<T, 'man', ManCommand>;
*
* Usage:
* ```ts
* import { createPadrone } from 'padrone';
* import { padroneMan } from 'padrone/man';
*
* createPadrone('my-cli').extend(padroneMan())
* ```
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/padrone/src/extension/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export type WithMcp<T> = WithCommand<T, 'mcp', McpCommand>;
*
* Usage:
* ```ts
* import { createPadrone } from 'padrone';
* import { padroneMcp } from 'padrone/mcp';
*
* createPadrone('my-cli').extend(padroneMcp())
* ```
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/padrone/src/extension/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export type WithServe<T> = WithCommand<T, 'serve', ServeCommand>;
*
* Usage:
* ```ts
* import { createPadrone } from 'padrone';
* import { padroneServe } from 'padrone/serve';
*
* createPadrone('my-cli').extend(padroneServe())
* ```
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/padrone/src/extension/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ function tracingInterceptor(config: ResolvedTracingConfig) {
* Usage:
* ```ts
* import { trace } from '@opentelemetry/api';
* import { createPadrone, padroneLogger } from 'padrone';
* import { padroneTracing } from 'padrone/tracing';
*
* createPadrone('my-cli')
* .extend(padroneTracing({ provider: trace.getTracerProvider() }))
Expand Down
19 changes: 0 additions & 19 deletions packages/padrone/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ export type {
export { REPL_SIGINT } from './core/runtime.ts';
export type {
HelpCommand,
InkOptions,
OtelSpan,
OtelTracer,
OtelTracerProvider,
PadroneLogger,
PadroneLoggerConfig,
PadroneLogLevel,
Expand All @@ -35,47 +31,32 @@ export type {
PadroneProgressMessage,
PadroneProgressMessages,
PadroneProgressRenderer,
PadroneTracer,
PadroneTracingConfig,
VersionCommand,
WithAsync,
WithCompletion,
WithHelp,
WithLogger,
WithMan,
WithMcp,
WithProgress,
WithRepl,
WithServe,
WithTracing,
WithVersion,
} from './extension/index.ts';
export {
createTerminalProgress,
isReactElement,
padroneAutoOutput,
padroneColor,
padroneCompletion,
padroneConfig,
padroneEnv,
padroneHelp,
padroneInk,
padroneInteractive,
padroneLogger,
padroneMan,
padroneMcp,
padroneProgress,
padroneRepl,
padroneServe,
padroneSignalHandling,
padroneStdin,
padroneSuggestions,
padroneTiming,
padroneTracing,
padroneUpdateCheck,
padroneVersion,
} from './extension/index.ts';
export type { PadroneMcpPreferences } from './feature/mcp.ts';
export type { UpdateCheckConfig } from './feature/update-check.ts';
export type { WrapConfig, WrapResult } from './feature/wrap.ts';
export type { AnsiStyle, ColorConfig, ColorTheme } from './output/colorizer.ts';
Expand Down
2 changes: 2 additions & 0 deletions packages/padrone/src/ink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { InkOptions } from './extension/ink.ts';
export { isReactElement, padroneInk } from './extension/ink.ts';
2 changes: 2 additions & 0 deletions packages/padrone/src/man.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { WithMan } from './extension/man.ts';
export { padroneMan } from './extension/man.ts';
3 changes: 3 additions & 0 deletions packages/padrone/src/mcp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type { WithMcp } from './extension/mcp.ts';
export { padroneMcp } from './extension/mcp.ts';
export type { PadroneMcpPreferences } from './feature/mcp.ts';
2 changes: 2 additions & 0 deletions packages/padrone/src/serve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { WithServe } from './extension/serve.ts';
export { padroneServe } from './extension/serve.ts';
9 changes: 9 additions & 0 deletions packages/padrone/src/tracing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type {
OtelSpan,
OtelTracer,
OtelTracerProvider,
PadroneTracer,
PadroneTracingConfig,
WithTracing,
} from './extension/tracing.ts';
export { padroneTracing } from './extension/tracing.ts';
2 changes: 1 addition & 1 deletion packages/padrone/tests/ink-detect.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from 'bun:test';
import { isReactElement } from 'padrone';
import { isReactElement } from 'padrone/ink';
import React from 'react';

describe('isReactElement', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/padrone/tests/ink-extension.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'bun:test';
import { Text } from 'ink';
import { createPadrone, padroneInk } from 'padrone';
import { createPadrone } from 'padrone';
import { padroneInk } from 'padrone/ink';
import React from 'react';

describe('padroneInk extension', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/padrone/tests/runtime.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, mock } from 'bun:test';
import { createPadrone, padroneCompletion, padroneConfig, padroneEnv } from 'padrone';
import { createPadrone, padroneConfig, padroneEnv } from 'padrone';
import { padroneCompletion } from 'padrone/completion';
import * as z from 'zod/v4';

describe('runtime', () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/padrone/tests/tracing.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'bun:test';
import type { OtelSpan, OtelTracer, OtelTracerProvider } from 'padrone';
import { createPadrone, padroneLogger, padroneTracing } from 'padrone';
import { createPadrone, padroneLogger } from 'padrone';
import type { OtelSpan, OtelTracer, OtelTracerProvider } from 'padrone/tracing';
import { padroneTracing } from 'padrone/tracing';

// ---------------------------------------------------------------------------
// Mock OTEL primitives
Expand Down
11 changes: 2 additions & 9 deletions packages/padrone/tests/type.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
// biome-ignore-all lint/correctness/noUnusedVariables: This file is for testing TypeScript types, so unused variables are intentional.

import { expectTypeOf, test } from 'bun:test';
import type {
DefineCommand,
DefineCommandContext,
PadroneBuilder,
PadroneLogger,
PadroneProgram,
PadroneProgress,
PadroneTracer,
} from 'padrone';
import type { DefineCommand, DefineCommandContext, PadroneBuilder, PadroneLogger, PadroneProgram, PadroneProgress } from 'padrone';
import { asyncSchema, createPadrone, defineCommand, defineInterceptor, padroneProgress } from 'padrone';
import type { PadroneTracer } from 'padrone/tracing';
import * as z from 'zod/v4';
import { createTasksProgram } from './common.ts';

Expand Down
Loading