# How to Handle Skill Names with Spaces During Installation in the Skills CLI

> Learn how the Skills CLI automatically handles skill names with spaces during installation, converting them to hyphens for safe filesystem storage without manual changes.

- Repository: [Vercel Labs/skills](https://github.com/vercel-labs/skills)
- Tags: how-to-guide
- Published: 2026-04-23

---

**The Skills CLI automatically converts spaces to hyphens using the `sanitizeName()` function in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts), allowing safe filesystem storage without manual intervention.**

Users can install skills with descriptive names containing spaces directly from the command line. The Skills CLI, developed by Vercel Labs, handles the conversion internally through a robust sanitization pipeline that ensures predictable, secure directory creation.

## How the Skills CLI Sanitizes Names with Spaces

### The Core Sanitization Function

The transformation happens in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) at lines 40-53. The `sanitizeName()` function processes every skill name before any filesystem operation:

```typescript
export function sanitizeName(name: string): string {
  const sanitized = name
    .toLowerCase()
    // Replace any sequence of characters that are NOT lowercase letters (a-z),
    // digits (0-9), dots (.) or underscores (_) with a single hyphen.
    // This converts spaces, special chars, and path-traversal attempts (../) into hyphens.
    .replace(/[^a-z0-9._]+/g, '-')
    // Remove leading/trailing dots and hyphens to prevent hidden files (.) and
    // ensure clean directory names.
    .replace(/^[.\-]+|[.\-]+$/g, '');
  // Limit to 255 chars (common filesystem limit), fallback to 'unnamed-skill' if empty
  return sanitized.substring(0, 255) || 'unnamed-skill';
}

```

### What Happens to Spaces

The regex `/[^a-z0-9._]+/g` treats spaces as disallowed characters, collapsing them into single hyphens:

| Input Name | Sanitized Output |
|------------|----------------|
| `my cool skill` | `my-cool-skill` |
| `My Cool Skill` | `my-cool-skill` |
| `my  cool   skill` | `my-cool-skill` |

### Space Handling in the Installation Pipeline

The `installSkillForAgent` function in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) demonstrates the complete flow:

```typescript
const sanitized = sanitizeName(skillName);
const skillDir = join(targetBase, sanitized);

```

This guarantees that skill names with spaces never reach the filesystem in their raw form.

## Installing Skills with Spaces from the Command Line

### Direct CLI Usage

Users can pass space-containing names directly via the `--skill` flag:

```bash
npx skills add vercel-labs/agent-skills --skill "my cool skill"

```

The CLI in [`src/add.ts`](https://github.com/vercel-labs/skills/blob/main/src/add.ts) (lines 172-186) parses the argument and forwards it to the installer unchanged. The raw name is preserved for display purposes while the sanitized version creates the directory.

### Expected Output

After installation, the CLI summarizes the result:

```

Installation Summary
/home/user/.agents/skills/my-cool-skill
  universal: Claude, CodeBuddy, ...

```

## Edge Cases and Safety Guarantees

### Character-Specific Transformations

| Situation | Result | Prevention Mechanism |
|-----------|--------|-------------------|
| `"  Leading and trailing  "` | `leading-and-trailing` | Leading/trailing hyphens stripped via `/^[.\-]+|[.\-]+$/g` |
| `"C++ Helper"` | `c-helper` | `+` characters collapsed to hyphens |
| `"../escape"` | `escape` | Path-traversal characters neutralized |
| `"🚀 rocket"` | `rocket` | Non-ASCII characters filtered out |

### Filesystem Safety Limits

The `sanitizeName()` function enforces a 255-character maximum and provides a fallback for empty results:

```typescript
return sanitized.substring(0, 255) || 'unnamed-skill';

```

## Programmatic Usage with Space-Containing Names

### Node.js API Example

```typescript
import { installSkillForAgent } from './src/installer.ts';
import { agents } from './src/agents.ts';

async function install() {
  const skillName = 'my cool skill'; // any user-provided string with spaces
  const result = await installSkillForAgent(
    { name: skillName, files: new Map() }, // minimal Skill object
    'claude-code',                         // target agent
    { global: false, mode: 'copy' }
  );

  console.log('Installed to:', result.path);
  // Output: Installed to: /home/user/.agents/skills/my-cool-skill
}

install();

```

### Verifying the Sanitized Directory

```bash

# After installation, confirm the space-to-hyphen conversion

ls ~/.agents/skills | grep my-cool-skill

# → my-cool-skill

```

## Key Source Files

| File | Purpose | Lines |
|------|---------|-------|
| [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) | Defines `sanitizeName()` and `installSkillForAgent()` | 40-53, 90-120 |
| [`src/add.ts`](https://github.com/vercel-labs/skills/blob/main/src/add.ts) | Parses CLI `--skill` arguments and forwards to installer | 172-186 |
| [`src/skill-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts) | Stores original unsanitized names for future updates | 1-30 |
| [`src/types.ts`](https://github.com/vercel-labs/skills/blob/main/src/types.ts) | `Skill` type definition with `name` field | 1-20 |

## Summary

- **Automatic transformation**: The `sanitizeName()` function in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) converts all spaces to hyphens before any filesystem operation.
- **Safe CLI usage**: Pass skill names with spaces directly to `npx skills add --skill "name with spaces"` — no manual escaping required.
- **Consistent output**: All sanitized names follow kebab-case, lowercase, with path-traversal protection and 255-character limits.
- **Original name preservation**: The unsanitized name is retained in [`src/skill-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts) for display and update purposes.

## Frequently Asked Questions

### What happens if I install two skills with similar names like "my skill" and "My Skill"?

Both names sanitize to `my-skill`, causing the second installation to overwrite or conflict with the first. The CLI's case normalization in `sanitizeName()` treats these as identical. Use distinct base names to avoid collisions.

### Can I prevent the automatic space-to-hyphen conversion?

No — the sanitization in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) is mandatory for all skill names. This design prevents filesystem errors, path traversal attacks, and cross-platform compatibility issues. The original name with spaces is preserved in the skill lock file for display purposes.

### How does the CLI handle skill names with emojis or special Unicode characters?

Emojis and non-ASCII characters are removed during sanitization. The regex `/[^a-z0-9._]+/g` only permits lowercase letters, digits, dots, and underscores. A name like `"🚀 rocket"` becomes `rocket`, and `"naïve"` becomes `na-ve` after the `ï` is replaced with a hyphen.

### Where can I see the original name with spaces after installation?

Check the skill lock file at [`.agents/skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/.agents/skills-lock.json) (or your configured lock file location). The [`src/skill-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts) module stores the unsanitized `name` property alongside the sanitized directory name, enabling accurate updates and user-friendly CLI output.