This guide helps AI assistants work efficiently with the CALM CLI codebase.
- Language: TypeScript 5.8+
- CLI Framework: Commander.js 14
- Build Tool: tsup (esbuild-based)
- Test Framework: Vitest
- Package Manager: npm (workspace package)
- Dependencies:
@finos/calm-shared- Shared utilities@finos/calm-widgets- Widget framework for visualizations- JSON Schema validation via
@apidevtools/json-schema-ref-parser
IMPORTANT: Always run npm commands from the repository root using workspaces, not from within this package directory.
# Development (from repository root)
npm run build --workspace cli # Build CLI + copy schemas/templates/widgets/AI tools
npm run watch --workspace cli # Watch mode with live reload (uses watch.mjs)
npm test --workspace cli # Run Vitest tests
npm run lint --workspace cli # ESLint check
npm run lint-fix --workspace cli # Auto-fix linting issues
# If you want to test just one file run this. Make sure you're in the cli directory or below so it can resolve vitest.config.ts.
npx vitest run ${TEST FILE}
# Local testing (from repository root)
npm run link:cli # Link CLI globally for testing
calm --help # Test globally linked CLI
# Build steps (executed by npm run build)
npm run copy-calm-schema # Copy CALM JSON schemas from ../calm/
npm run copy-docify-templates # Copy docify templates from ../shared/
npm run copy-widgets # Copy widget files from ../calm-widgets/
npm run copy-ai-tools # Copy AI agent files from ../calm-ai/
npm run copy-workspace-templates # Copy `workspace new` templates from src/command-helpers/workspace/templates/- Entry:
src/index.ts- Thin (~13-line) bin bootstrap. Imports Commander'sprogram, callssetupCLI(program), thenprogram.parseAsync(...). No command definitions live here. - Commands:
src/cli.ts- ExportssetupCLI(program), which registers every command and its options/actions. This is where all command wiring lives. - Pattern: Each command is registered inline in
setupCLI; the heavier action logic is dynamically imported fromsrc/command-helpers/(see below).
- generate - Generate architecture from CALM pattern
- validate - Validate a CALM document (architecture, pattern, and/or timeline) against schemas
- init-ai - Install AI Assistant support for CALM
- template - Generate files from Handlebars templates
- docify - Generate documentation websites (supports
--scaffoldfor two-stage workflow) - diff - Compare two CALM documents (architectures or patterns), or adjacent/explicit moments of a CALM timeline, and report what changed. Supports
--exit-codeto gate CI on detected changes. - timeline - Synthesise an implied CALM timeline from a set of local versioned architecture files (one moment per input).
- init-config - Create or update the CLI configuration file (
~/.calm.json), e.g. allowed remote hosts and CALM Hub URL. - hub - Command group for interacting with CALM Hub. Subcommands:
hub push <resource> <file>/hub pull <resource>/hub list <resources>/hub create <resource>- Resources span architectures, patterns, standards, interfaces, control-requirements, control-configurations, namespaces, and domains (which subcommands exist varies per verb).
hub pushauto-bumps by default (creates a new version off the latest). The--fail-if-modifiedflag (supported onarchitecture,pattern,standard,control-requirementandcontrol-configuration) switches to a strict, non-bumping mode: a brand-new mapping is still created at1.0.0, an unchanged document is skipped, and a document that differs from the latest published version fails the push. The local document is normalised the same way Hub stores it (viaupdateDocumentMetadata/updateControlDocumentMetadata) before comparing withcanonicalEqualfrom@finos/calm-shared, so a version-only or defaulted-field difference is not mistaken for a content change.
- workspace - Command group for a local, git-rooted bundle of CALM documents (
.calm-workspace/). Subcommands includeinit,add,rm,new,tree,list,show,switch,clean, and the CalmHub sync trio:workspace push— pushes the exact version each document's$iddeclares (no auto-bump). An existing version with unchanged content is skipped;--fail-if-modified(orpush.failIfModified: truein.calm-workspace/config.json) fails when a document already published at its declared version has changed on disk, for strict merge-time CI.workspace check— CI/PR gate: exits non-zero if any tracked document changed on disk relative to CalmHub but wasn't version-bumped, or if any architecture/pattern in the workspace fails validation. Prints a pass/fail validation summary alongside the bump check.workspace bump— prompts interactively for the bump type (MAJOR/MINOR/PATCH) for each directly-changed document (default from config or MINOR). Cascade-bumped dependents are also prompted with the trigger's increment as the default;--inherit-change-typesuppresses cascade prompts and auto-inherits silently.--major/--minor/--patchskip all prompts. After bumping, repoints all references to the new$ids and prints a post-bump validation summary (informational only). Idempotent: re-bumping an already-bumped doc is a no-op until it's pushed.- Central config
.calm-workspace/config.jsonholdspush.failIfModified(true|false) andbump.defaultIncrement(MAJOR|MINOR|PATCH).
src/
├── cli.ts # setupCLI: registers all commands, options, and actions
├── cli-config.ts # Configuration helpers (~/.calm.json loading/saving)
├── index.ts # Thin bin bootstrap (calls setupCLI + parseAsync)
├── command-helpers/ # Action logic for commands (see below)
└── test_helpers/ # Test utilities
The command-helpers/ directory now holds the substantial per-command logic that
cli.ts dynamically imports:
diff.ts- document and timeline diffingtimeline.ts- timeline synthesishub-commands.ts- CALM Hub push/pull/list/create implementationshub-output.ts- formatting of Hub command outputtemplate.ts- template processing helpers (e.g. URL-to-local-file mapping)validate.ts- validation option checks and executiongenerate-options.ts- interactive/option-choice handling forgenerateai-tools.ts-init-aiprovider setup
After npm run build, the dist/ directory contains:
dist/
├── index.js # Compiled CLI entry point (bin)
├── calm/ # Copied CALM meta schemas only (release + draft **/meta/* files)
├── calm-ai/ # Copied AI agent files
├── template-bundles/ # Copied docify templates
└── widgets/ # Copied widget files (copy-widgets uses --up 4)
Note: copy-calm-schema only bundles **/meta/* files (not every CALM schema)
into dist/calm/.
- CALM schemas live in
../calm/release/and../calm/draft/ - Default schema directory:
../calm/release - CLI accepts
-s, --schema-directoryto override - Schemas are copied during build, not at runtime
- CLI can load architectures/patterns from CALM Hub via
-c, --calm-hub-url - Supports both file paths and URLs for pattern/architecture files
- Most top-level commands support
-v, --verbose(generate, validate, template, docify, init-ai, diff, timeline).init-configand thehubsubcommands do not. - Use for debugging command execution
cli-config.tshandles loading CLI configuration- Supports reading from multiple locations
*.spec.ts- Unit tests alongside source files*.e2e.spec.ts- End-to-end CLI teststest_fixtures/- Test data (patterns, architectures, schemas)
# From repository root (preferred)
npm test --workspace cli # All tests
npm test --workspace cli -- --watch # Watch mode
npm test --workspace cli -- <file> # Specific test filecli/smoke/*.smoke.spec.ts drive the packed CLI against a real CalmHub
(Docker + MongoDB, no-auth). They are excluded from the default npm test.
# One-time (per change to calm-hub): build the hub image
bash scripts/build-hub-smoke-image.sh # -> calm-hub:smoke
# Run the smoke suite (starts/stops the hub via docker compose)
npm run build:cli
npm run test:smoke --workspace cliThe suite starts one shared hub via cli/smoke/global-setup.ts; each flow uses
its own namespace (smoke-crud, smoke-genval, smoke-workspace).
- Use
test_helpers/for shared test utilities - Mock external dependencies (file system, HTTP)
- Test both success and error cases
- Add command definition in
src/cli.ts - Implement command function (follow existing patterns)
- Add tests in
src/cli.spec.ts - Add E2E test in
src/cli.e2e.spec.ts - Update README.md with command documentation
- Schemas are in
../calm/directory (outside CLI) - Update copy script in
package.jsonif schema structure changes - Test with
npm run buildto ensure schemas copy correctly
- Widget files come from
../calm-widgets/dist/cli/widgets/ - Built by calm-widgets package first
- CLI copies pre-built widgets during build
calm-cli depends on:
├── calm-models (built first)
├── calm-widgets (built first)
├── shared (built first)
└── calm-ai (copied during build)
Important: When working across packages, rebuild dependencies:
# From repository root (always use workspaces)
npm run build:cli # Builds models, widgets, shared, then CLI
# Or build individual packages:
npm run build --workspace calm-models
npm run build --workspace calm-widgets
npm run build --workspace shared
npm run build --workspace cli- Missing Schemas: If tests fail due to missing schemas, run
npm run build - Widget Errors: Ensure
calm-widgetsis built before building CLI - Watch Mode: Use
npm run watch(nottsc --watch) - includes file copying - Global Linking: After changes, run
npm run link:clifrom root to update global install
tsconfig.json- TypeScript compiler optionstsconfig.build.json- Production build configtsup.config.ts- Build configuration (entry points, formats)vitest.config.mts- Test configurationeslint.config.mjs- Linting rules.releaserc.json- Semantic release configuration
- Uses semantic-release for automated releases
- Versioning follows semantic versioning
- CHANGELOG.md auto-generated
- Published to npm as
@finos/calm-cli
- README.md - User-facing documentation
- DEVELOPER_GUIDE.md - Detailed development guide
- Root README - Monorepo overview