# How CLI-Anything Merges CLI-Provided Tokens with Spec-Defined Tokens

> Learn how CLI-Anything merges CLI provided tokens with spec defined tokens using a strict precedence hierarchy and directory-based security validation.

- Repository: [✨Data Intelligence Lab@HKU✨/CLI-Anything](https://github.com/HKUDS/CLI-Anything)
- Tags: how-to-guide
- Published: 2026-08-16

---

**CLI-Anything resolves token files through a strict precedence hierarchy where CLI-provided paths override spec-defined ones, with both subjected to directory-based security validation before falling back to bundled defaults.**

The HKUDS/CLI-Anything repository provides a sketch generation framework that relies on JSON design tokens to maintain visual consistency across outputs. When executing a build, the system must determine which token definition file to load, balancing user customization with security constraints. Understanding how the tool merges CLI-provided tokens with spec-defined tokens ensures you can safely override defaults without triggering path-traversal protections.

## The Three-Stage Token Resolution Process

In [`sketch/agent-harness/src/builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/sketch/agent-harness/src/builder.js), the `loadTokens` function implements a deterministic resolution algorithm that selects exactly one token source. The process follows a strict precedence chain: CLI arguments take priority over spec configurations, which in turn supersede built-in defaults.

### Stage 1: Loading the Default Token Set

The builder always initializes with the bundled default tokens as a failsafe. At lines 62-64, the system resolves the path to [`tokens/default.json`](https://github.com/HKUDS/CLI-Anything/blob/main/tokens/default.json) relative to the builder's location.

```javascript
const defaultTokensPath = path.resolve(__dirname, '..', 'tokens', 'default.json');

```

This default set serves as the foundation that remains active unless overridden by subsequent validation stages.

### Stage 2: Evaluating CLI-Provided Tokens

When the user supplies the `--tokens <path>` flag, the CLI passes this path to `loadTokens`. The builder subjects the path to rigorous validation through the `isSafePath` helper before accepting it.

A CLI-provided path is considered safe only if it resides in one of three locations:
- The repository's global `tokens` directory
- The specification file's directory (`specDir`)
- The current working directory (CWD)—the only location where explicit CLI overrides are permitted

If the path passes validation, it immediately becomes the sole token source, short-circuiting spec-level lookups. If validation fails, the system emits a warning and proceeds to evaluate spec-defined tokens.

The validation logic appears at lines 90-101:

```javascript
if (cliTokensPath) {
  const resolved = path.resolve(cliTokensPath);
  if (
    isSafePath(resolved, tokensDir) ||
    (specDir && isSafePath(resolved, path.resolve(specDir))) ||
    isSafePath(resolved, process.cwd())
  ) {
    tokensPath = resolved;
  } else {
    console.warn(`Unsafe tokens path ignored: ${cliTokensPath}`);
  }
}

```

### Stage 3: Falling Back to Spec-Defined Tokens

When no CLI token file is provided (or the provided path was rejected), the builder examines the design spec JSON for a `tokens` entry. The system resolves this path relative to the spec's directory and validates it against the global `tokens` folder or the spec's own directory—note that CWD is intentionally excluded from spec-defined token validation for security reasons.

The fallback logic at lines 103-110 demonstrates this behavior:

```javascript
else if (specTokensPath) {
  const resolved = path.resolve(specDir, specTokensPath);
  if (isSafePath(resolved, tokensDir) || isSafePath(resolved, path.resolve(specDir))) {
    tokensPath = resolved;
  } else {
    console.warn(`Unsafe spec tokens path ignored: ${specTokensPath}`);
  }
}

```

If this check also fails, the builder retains the default token set loaded in Stage 1.

## Security Validation with isSafePath

The `isSafePath` function (lines 67-85 in [`builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/builder.js)) prevents directory traversal attacks by canonicalizing both the candidate path and the allowed base directory. It ensures the relative path does not start with `../` (or equal `..`), effectively blocking escapes from permitted directories. Notably, the validation permits directory names that begin with dots (such as `..brand`), only rejecting actual parent-directory references.

## Practical Usage Examples

To override tokens via CLI, specify the path using the `-t` or `--tokens` flag:

```bash
sketch-cli build -i design.json -o output.sketch -t ./my-tokens.json

```

To define tokens within your design specification, add a `tokens` field to your spec JSON:

```json
{
  "tokens": "./tokens/my-spec-tokens.json",
  "pages": [ /* … */ ]
}

```

If you attempt to load a token file from an unsafe location, the CLI warns you and falls back to the spec-defined or default tokens:

```bash
sketch-cli build -i design.json -o out.sketch -t ../../secret.json

# → Unsafe tokens path ignored: ../../secret.json

```

## Summary

- **CLI tokens override spec tokens**: The presence of a valid `--tokens` argument short-circuits spec-level token resolution according to the source code in [`sketch/agent-harness/src/builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/sketch/agent-harness/src/builder.js).
- **Security-first validation**: Both CLI and spec token paths must pass `isSafePath` checks to prevent directory traversal attacks.
- **Directory constraints differ**: CLI tokens may reside in the CWD, global tokens directory, or spec directory; spec-defined tokens are restricted to the global tokens directory or spec directory only.
- **Deterministic fallback**: The system always loads default tokens first, then conditionally replaces them with validated CLI or spec tokens, ensuring the build pipeline always has a valid token object to process.

## Frequently Asked Questions

### What happens if I provide both CLI and spec token files?

The CLI-provided token file takes precedence exclusively. If the CLI path passes security validation, the builder uses it immediately and never checks the spec's `tokens` field. Only if the CLI path is missing or rejected does the builder evaluate spec-defined tokens.

### Why does my token file path get rejected as unsafe?

The `isSafePath` function rejects paths that resolve outside permitted boundaries. For CLI tokens, the file must reside in the global `tokens` directory, the spec's directory, or the current working directory. For spec-defined tokens, only the global `tokens` directory or the spec's own directory are allowed. Paths containing `../` or absolute paths outside these locations trigger the safety warning.

### Can I use a token file from outside the project directory?

Only when invoking the override through the CLI `--tokens` flag with a path in your current working directory. Spec-defined tokens cannot reference files outside the project's token directory or the spec's location. This restriction prevents malicious specs from accessing arbitrary files on the filesystem.

### How do I know which token file was actually loaded?

The builder emits a `console.warn` message when ignoring an unsafe path, helping you diagnose configuration issues. You can also check the resolution logic: if you provided a CLI path and didn't see a warning, that file was used; otherwise, check for warnings about spec paths or confirm that defaults were loaded.