A robust TypeScript library for configuration management in command-line applications. Cardigantime provides type-safe configuration loading, validation, and CLI integration with Commander.js and Zod schemas.
Cardigantime is a configuration management library designed to solve the common problem of handling configuration in CLI applications. It provides a unified way to:
- Read configuration from multiple formats (YAML, JSON, JavaScript, TypeScript) with intelligent file discovery
- Validate configuration using Zod schemas for type safety
- Integrate with CLI frameworks like Commander.js seamlessly
- Merge configuration sources (files, CLI args, defaults) with proper precedence
- Handle errors gracefully with comprehensive logging and user-friendly error messages
Building CLI applications with proper configuration management is harder than it should be. Cardigantime was created specifically to solve the complex problem of supporting sophisticated configuration systems that seamlessly merge command-line arguments, configuration files, and default values.
Without Cardigantime, you need to manually handle:
- Multi-layered configuration sources with proper precedence
- Nested configuration objects with deep validation
- Type safety throughout the configuration pipeline
- Graceful error handling with actionable messages
Cardigantime provides a complete, battle-tested solution for all of this complexity.
Define your configuration schema once with Zod, and Cardigantime automatically supports:
- YAML (
.yaml,.yml) - Human-readable, great for hand-edited configs - JSON (
.json) - Strict syntax, ideal for programmatic generation - JavaScript (
.js,.mjs,.cjs) - Dynamic configs with environment logic - TypeScript (
.ts,.mts,.cts) - Type-safe configs with IDE support
No additional code or schema definitions needed per format. Your users choose their preferred format, and Cardigantime handles parsing, validation, and merging automatically.
Cardigantime serves two distinct audiences:
Developers building CLI applications who want robust configuration management without the boilerplate. You integrate Cardigantime once, define your Zod schema, and get:
- Multi-format config file support out of the box
- Automatic CLI option generation
- Deep merging with proper precedence
- Hierarchical config discovery (like
.eslintrcor.gitignore) - Type safety throughout your codebase
Users of tools built with Cardigantime benefit from a consistent, powerful configuration experience without needing to know Cardigantime exists. They get:
- Freedom to use their preferred config format (YAML, JSON, JS, or TS)
- Consistent CLI options across tools
- Clear, actionable error messages
- Built-in config generation (
--init-config) and analysis (--check-config)
npm install @utilarium/cardigantime
# or
yarn add @utilarium/cardigantime
# or
pnpm add @utilarium/cardigantimeHere's a complete example of building a CLI tool with Cardigantime:
import { Command } from 'commander';
import { create } from '@utilarium/cardigantime';
import { z } from 'zod';
// Define your configuration schema using Zod
const MyConfigSchema = z.object({
apiKey: z.string().min(1, "API key is required"),
timeout: z.number().min(1000).default(5000),
retries: z.number().min(0).max(10).default(3),
debug: z.boolean().default(false),
});
// Create a Cardigantime instance
const cardigantime = create({
defaults: {
configDirectory: './config', // Required: where to look for config files
configFile: 'myapp.yaml', // Optional: defaults to 'config.yaml'
isRequired: false, // Optional: whether config directory must exist
},
configShape: MyConfigSchema.shape, // Your Zod schema
features: ['config'], // Optional: enabled features
});
// Set up your CLI with Commander.js
async function main() {
const program = new Command();
program
.name('myapp')
.description('My awesome CLI application')
.version('1.0.0');
// Let Cardigantime add its CLI options (like --config-directory)
await cardigantime.configure(program);
// Add your own CLI options
program
.option('-k, --api-key <key>', 'API key for authentication')
.option('-t, --timeout <ms>', 'Request timeout in milliseconds', parseInt)
.option('--debug', 'Enable debug mode');
program.parse();
const args = program.opts();
try {
// Read and validate configuration
const config = await cardigantime.read(args);
await cardigantime.validate(config);
console.log('Configuration loaded successfully:', config);
// Your application logic here
await runMyApp(config);
} catch (error) {
console.error('Configuration error:', error.message);
process.exit(1);
}
}
main().catch(console.error);Cardigantime exports its own version information including git commit details. You can also set up the same pattern in your own project.
import { VERSION, PROGRAM_NAME } from '@utilarium/cardigantime';
console.log(`Using ${PROGRAM_NAME}: ${VERSION}`);
// Output: Using cardigantime: 0.0.22-dev.0 (working/a1b2c3d 2026-01-27 11:11:46 -0800) darwin arm64 v24.8.0To add the same detailed version format to your own CLI application, add this to your vite.config.ts:
import { defineConfig } from 'vite';
import replace from '@rollup/plugin-replace';
import { execSync } from 'node:child_process';
// Extract git information at build time
let gitInfo = {
branch: '',
commit: '',
tags: '',
commitDate: '',
};
try {
gitInfo = {
branch: execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
commit: execSync('git rev-parse --short HEAD').toString().trim(),
tags: '',
commitDate: execSync('git log -1 --format=%cd --date=iso').toString().trim(),
};
try {
gitInfo.tags = execSync('git tag --points-at HEAD | paste -sd "," -').toString().trim();
} catch {
gitInfo.tags = '';
}
} catch {
console.log('Directory does not have a Git repository, skipping git info');
}
export default defineConfig({
plugins: [
replace({
'__VERSION__': process.env.npm_package_version,
'__GIT_BRANCH__': gitInfo.branch,
'__GIT_COMMIT__': gitInfo.commit,
'__GIT_TAGS__': gitInfo.tags === '' ? '' : `T:${gitInfo.tags}`,
'__GIT_COMMIT_DATE__': gitInfo.commitDate,
'__SYSTEM_INFO__': `${process.platform} ${process.arch} ${process.version}`,
preventAssignment: true,
}),
// ... your other plugins
],
// ... rest of your config
});Then in your constants file:
export const VERSION = '__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__';
export const PROGRAM_NAME = 'myapp';The placeholders will be replaced at build time with actual values. This is particularly useful for CLI applications where you need visibility into exactly which build is running.
Cardigantime supports multiple configuration formats. Choose the one that best fits your workflow:
apiKey: "your-secret-api-key"
timeout: 10000
retries: 5
debug: true{
"apiKey": "your-secret-api-key",
"timeout": 10000,
"retries": 5,
"debug": true
}module.exports = {
apiKey: process.env.API_KEY || "your-secret-api-key",
timeout: 10000,
retries: 5,
debug: process.env.NODE_ENV === 'development'
};export default {
apiKey: process.env.API_KEY || "your-secret-api-key",
timeout: 10000,
retries: 5,
debug: process.env.NODE_ENV === 'development'
} as const;Format Priority: When multiple config files exist, Cardigantime uses this priority order:
- TypeScript (
.ts,.mts,.cts) - Highest priority - JavaScript (
.js,.mjs,.cjs) - JSON (
.json) - YAML (
.yaml,.yml) - Lowest priority
You can override automatic detection with --config-format:
./myapp --config-format yaml # Force YAML even if JSON exists# Use config from file
./myapp
# Override config with CLI arguments
./myapp --api-key "different-key" --timeout 15000
# Use different config directory
./myapp --config-directory /etc/myapp
# Generate initial configuration file
./myapp --init-config
# Analyze configuration with source tracking
./myapp --check-configMerges configuration from multiple sources in order of precedence:
- MCP invocation config (highest priority - for AI assistant tools)
- Configuration file(s) (medium priority)
- Environment variables (low priority - system-wide defaults)
- Default values (lowest priority)
Automatic environment variable support for all configuration fields:
// Define your schema
const schema = z.object({
planDirectory: z.string(),
port: z.number().default(3000),
});
// Set environment variables
process.env.MYAPP_PLAN_DIRECTORY = '/plans';
process.env.MYAPP_PORT = '8080';
// Resolve config - env vars automatically discovered
const config = await resolveConfig({
schema,
appName: 'myapp',
});Features:
- Automatic naming:
planDirectoryβMYAPP_PLAN_DIRECTORY - Type coercion: Strings parsed to numbers, booleans, arrays
- Custom names: Map to standard env vars like
OPENAI_API_KEY - Nested fields:
api.keyβMYAPP_API_KEY
Supports YAML (.yaml, .yml), JSON (.json), JavaScript (.js, .mjs, .cjs), and TypeScript (.ts, .mts, .cts) configuration files. When multiple formats exist, Cardigantime uses automatic format detection with configurable priority.
Cardigantime automatically searches for configuration files using multiple naming conventions, similar to how tools like Vite, ESLint, and TypeScript work:
| Priority | Pattern | Example |
|---|---|---|
| 1 | {app}.config.{ext} |
myapp.config.yaml |
| 2 | {app}.conf.{ext} |
myapp.conf.yaml |
| 3 | .{app}/config.{ext} |
.myapp/config.yaml |
| 4 | .{app}rc.{ext} |
.myapprc.yaml |
| 5 | .{app}rc |
.myapprc |
Modern visible config files (like myapp.config.yaml) are checked first for better discoverability.
Supports hierarchical configuration discovery, similar to how .gitignore, .eslintrc, or package.json work - searching up the directory tree for configuration directories.
Hierarchical Modes:
enabled(default) - Merge configs from parent directoriesdisabled- Use only the config in the starting directoryroot-only- Find first config, no mergingexplicit- Only merge explicitly referenced configs
# Disable hierarchical for isolated projects
hierarchical:
mode: disabledFirst-class support for Model Context Protocol (MCP), enabling AI assistants to configure tools directly through MCP invocations. Includes:
- MCP Configuration Priority - MCP config takes exclusive precedence when provided
- File-Based Fallback - Automatic discovery from target file or working directory
- CheckConfig Tool - Built-in diagnostic tool for all MCP tools
- Integration Helpers - Simple APIs for adding MCP support to your tools
import { createMCPIntegration } from '@utilarium/cardigantime/mcp';
const integration = createMCPIntegration({
appName: 'myapp',
configSchema: myConfigSchema,
});
// CheckConfig tool is automatically available
// Config resolution handles MCP and file-based sourcesFull TypeScript support with Zod schema validation for robust, type-safe configuration management.
Comprehensive error handling with detailed, actionable error messages to help users fix configuration issues quickly.
π Complete Documentation - Full documentation site
Quick Links:
- Getting Started Guide - Detailed setup and basic concepts
- Core Concepts - Configuration sources, hierarchical discovery
- MCP Integration - Model Context Protocol support for AI assistants
- CheckConfig Tool - Built-in diagnostic tool
- API Reference - Complete API documentation
- Configuration Options - All available options
- Debugging & Analysis - Tools for analyzing config
- Advanced Usage - Complex examples and scenarios
- Error Handling - Comprehensive error handling guide
We welcome contributions! Please see our Contributing Guide for details.
Apache-2.0 - see LICENSE file for details.
Because configuration management should be as comfortable and reliable as your favorite cardigan. Just like a good cardigan keeps you warm and comfortable, Cardigantime keeps your application configuration cozy and well-organized.