# How to Use the design.md CLI Programmatically: JavaScript and TypeScript API Guide

> Learn how to use the design.md CLI programmatically with JavaScript and TypeScript via its Linter API. Integrate linting directly into your projects for seamless code quality checks.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-30

---

**Yes, you can use the design.md CLI programmatically** by importing functions from the `@google/design.md/linter` module, which exposes the same `lint()` and `runLinter()` APIs that power the command-line interface.

The **google-labs-code/design.md** repository provides both a command-line interface built on the citty framework and a fully-featured JavaScript/TypeScript API. While the [`design.md`](https://github.com/google-labs-code/design.md/blob/main/design.md) command handles file I/O and user interaction, all core functionality—including linting, diffing, and report generation—is exported from source modules that you can integrate directly into build pipelines, CI checks, or custom Node.js scripts.

## Public API Architecture

The package exposes its programmatic interface through the **exports** field in [`packages/cli/package.json`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/package.json). The `"./linter"` conditional export maps to [`dist/linter/index.js`](https://github.com/google-labs-code/design.md/blob/main/dist/linter/index.js), making the underlying engine accessible without spawning a child process.

In [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts), the library re-exports the primary public entry points:

- **`lint`** – Validates a DESIGN.md document string and returns a `LintReport` (lines 15‑18).
- **`runLinter`** – Executes the complete linting pipeline with file resolution (line 35).
- **`DEFAULT_RULES`** – The rule set configuration used by the CLI.
- **Emitter handlers** – For customizing report output streams.

The CLI implementation in [`packages/cli/src/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/index.ts) (lines 16‑35) defines the command structure using citty, but delegates all actual processing to these same exported functions.

## Core Programmatic Functions

### lint()

The `lint()` function is the foundational API for validating DESIGN.md content. According to [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts) (lines 15‑18), it accepts a document string and returns a `LintReport` object containing findings and summary statistics.

The CLI command in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts) (lines 36‑40) uses `readInput` to read the file or stdin, then passes the content string directly to `lint()`. You can call the same function in your code to validate dynamically generated content without writing to disk.

### runLinter()

For file-based workflows, `runLinter()` defined in [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts) (line 35) provides a higher-level interface that handles file resolution and runs the complete pipeline. This function mirrors the internal logic of the CLI's lint command but accepts a file path and options object rather than parsing command-line arguments.

### Utility Helpers

The [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) module exports formatting and diffing utilities used by both the CLI and programmatic consumers:

- **`formatOutput`** (lines 46‑52) – Serializes a `LintReport` into JSON or Markdown formats matching the CLI's `--format` options.
- **`diffMaps`** (lines 58‑84) – Compares two design system states for the diff functionality.
- **`serializeDesignSystem`** (lines 58‑84) – Converts design system objects into comparable Map structures.

## Programmatic Usage Examples

### Lint a DESIGN.md String in Memory

Import `lint` from the linter module and `formatOutput` from utils to replicate the CLI's behavior without file I/O:

```typescript
import { lint } from '@google/design.md/linter';
import { formatOutput } from '@google/design.md/utils';

const designMd = `

# My Design System

## Colors

- primary: #ff0000
`;

const report = lint(designMd);

// JSON output (equivalent to design.md lint --format json)
console.log(formatOutput(report, { format: 'json' }));

// Markdown output (equivalent to design.md lint --format markdown)
console.log(formatOutput(report, { format: 'markdown' }));

```

This example uses the same `lint` function called by the CLI in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts), but operates directly on a string variable.

### Compare Design System Versions Programmatically

Reproduce the `design.md diff` command using the serialization and comparison utilities:

```typescript
import { diffMaps, serializeDesignSystem } from '@google/design.md/utils';
import { parseDesignSystem } from '@google/design.md/linter';
import { readFileSync } from 'node:fs';

const before = parseDesignSystem(readFileSync('v1/DESIGN.md', 'utf-8'));
const after = parseDesignSystem(readFileSync('v2/DESIGN.md', 'utf-8'));

const diff = diffMaps(
  new Map(Object.entries(serializeDesignSystem(before))),
  new Map(Object.entries(serializeDesignSystem(after)))
);

console.log('Added:', diff.added);
console.log('Removed:', diff.removed);
console.log('Modified:', diff.modified);

```

This leverages `diffMaps` and `serializeDesignSystem` from [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) (lines 58‑84) to compute differences between design system versions without invoking the CLI process.

### Run the Full Linter Pipeline

For scenarios requiring the complete rule engine with custom configuration:

```typescript
import { runLinter, DEFAULT_RULES } from '@google/design.md/linter';

const options = {
  rules: DEFAULT_RULES,
  // additional configuration options
};

runLinter('path/to/DESIGN.md', options).then(report => {
  console.log('Error count:', report.summary.errors);
  console.dir(report.findings, { depth: null });
});

```

The `runLinter` function exported from [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts) (line 35) executes the same pipeline as the CLI's `lint` command, returning a Promise that resolves with the complete report object.

## How the CLI Consumes the API

The command-line interface in [`packages/cli/src/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/index.ts) (lines 16‑35) defines the citty command structure, but each command delegates to the public API. For example, the lint command implementation in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts) (lines 36‑40) performs the following:

1. Resolves input via `readInput` (handling file paths or stdin).
2. Calls `lint(content)` with the resolved string.
3. Passes the result to `formatOutput` for serialization.

This architecture ensures that the programmatic API and CLI are functionally identical—any update to the linting logic immediately benefits both interfaces.

## Summary

- **The design.md package exports a public API** from `@google/design.md/linter` that includes `lint()`, `runLinter()`, and `DEFAULT_RULES`.
- **All CLI functionality is available programmatically** through functions defined in [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts) and [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts).
- **The CLI is a thin wrapper** around these exports, using citty for argument parsing in [`packages/cli/src/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/index.ts) but delegating execution to the same functions you can import directly.
- **Utility functions** like `formatOutput` and `diffMaps` allow you to replicate CLI output formats and comparison logic in your own code.

## Frequently Asked Questions

### Do I need to install the CLI separately to use the programmatic API?

No. Installing the `npm` package `@google/design.md` provides both the binary and the library exports. The [`package.json`](https://github.com/google-labs-code/design.md/blob/main/package.json) declares conditional exports (`"./linter"`) that map to [`dist/linter/index.js`](https://github.com/google-labs-code/design.md/blob/main/dist/linter/index.js), allowing you to import the API directly while the CLI binary remains available for command-line usage.

### What is the difference between `lint()` and `runLinter()`?

The **`lint()`** function accepts a DESIGN.md content string and returns a validation report immediately, making it ideal for testing generated content or in-memory strings. The **`runLinter()`** function accepts a file path and options object, handling file I/O and running the complete pipeline—effectively mirroring the CLI's `lint` command but returning a Promise instead of writing to stdout.

### How do I format the output to match the CLI exactly?

Import **`formatOutput`** from `@google/design.md/utils` (defined in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts), lines 46‑52). Pass your `LintReport` object and specify the format option (`'json'` or `'markdown'`) to generate strings identical to the CLI's `--format` output. This function is what the CLI uses internally to serialize results before printing.

### Can I customize which rules run when using the API programmatically?

Yes. When calling `runLinter()`, pass a `rules` array in the options object. You can import **`DEFAULT_RULES`** from `@google/design.md/linter` to use the standard configuration, or construct a custom array of rule objects to enable only specific validations. The `lint()` function also accepts options for rule configuration, allowing fine-grained control over the validation logic.