@slats/claude-assets-sync
CLI tool to sync Claude commands and skills from npm packages to your project's .claude/ directory.
yarn add @slats/claude-assets-syncAllows npm package authors to distribute Claude Code commands and skills alongside their packages.
When users install these packages, they can sync the assets to their local .claude/ directory for immediate use.
- Multi-package sync in a single command
- Version tracking — skips re-sync when version is unchanged
- Flat file structure with prefixed filenames (default)
- Interactive add/list UI (ink-based terminal UI)
- Status monitoring with npm registry update checks
- Dry-run mode to preview changes before applying
- Legacy nested structure support and migration tool
Installation
# One-time use (recommended)
npx @slats/claude-assets-sync -p @canard/schema-form
# Global install
npm install -g @slats/claude-assets-sync
Commands
sync (default)
Sync Claude assets from one or more npm packages.
npx @slats/claude-assets-sync [options] -p <package>
| Option | Description |
|---|---|
-p, --package <name> | Package to sync (repeatable) |
-f, --force | Force sync even if version matches |
--dry-run | Preview changes without writing files |
-l, --local | Read from local workspace instead of node_modules |
-r, --ref <ref> | Git ref (branch, tag, commit) to fetch from |
--no-flat | Use legacy nested directory structure |
# Sync a single package
npx @slats/claude-assets-sync -p @canard/schema-form
# Sync multiple packages
npx @slats/claude-assets-sync -p @canard/schema-form -p @lerx/promise-modal
# Preview without writing
npx @slats/claude-assets-sync -p @canard/schema-form --dry-run
# Force re-sync
npx @slats/claude-assets-sync -p @canard/schema-form --force
# Sync from a specific branch
npx @slats/claude-assets-sync -p @canard/schema-form -r main
add
Add a package with interactive file selection (TTY).
npx @slats/claude-assets-sync add -p <package> [options]
| Option | Description |
|---|---|
-p, --package <name> | Package to add (required) |
-l, --local | Read from local workspace |
-r, --ref <ref> | Git ref to fetch from |
Keyboard controls in interactive mode:
| Key | Action |
|---|---|
↑ / ↓ | Navigate items |
Space | Toggle selection |
→ / ← | Expand / collapse directory |
Enter | Confirm and sync |
a | Select all |
n | Deselect all |
q | Cancel |
npx @slats/claude-assets-sync add -p @lerx/promise-modal
list
List all synced packages with an interactive tree view.
npx @slats/claude-assets-sync list [--json]
View mode keys: e edit, r refresh, q quit
Edit mode keys: d delete file, a add file, Esc exit edit
# Interactive tree view
npx @slats/claude-assets-sync list
# JSON output for scripting
npx @slats/claude-assets-sync list --json
┌─ Synced Packages ─────────────────────────────┐
│ │
│ ▼ @canard/schema-form@1.0.0 │
│ ├─ commands/ │
│ │ └─ @canard-schema-form-my-command.md │
│ └─ skills/ │
│ └─ @canard-schema-form-my-skill.md │
│ │
│ [e] Edit [r] Refresh [q] Quit │
└───────────────────────────────────────────────┘
status
Show sync status and check for available updates.
npx @slats/claude-assets-sync status [--no-remote]
| Option | Description |
|---|---|
--no-remote | Skip npm registry check |
Status indicators: ✓ up-to-date, ⚠ update available, ✗ error
┌─ Package Status ──────────────────────────────┐
│ ✓ @canard/schema-form │
│ Local: 1.0.0 Synced: 1.0.0 Assets: 2 │
│ ⚠ @lerx/promise-modal │
│ Local: 0.6.0 Synced: 0.5.0 │
├─ Summary ────────────────────────────────────┤
│ Total: 2 ✓ 1 ⚠ 1 │
└───────────────────────────────────────────────┘
remove
Remove a synced package and its assets.
npx @slats/claude-assets-sync remove -p <package> [--yes] [--dry-run]
npx @slats/claude-assets-sync remove -p @canard/schema-form --yes
migrate
Migrate from legacy nested structure to flat structure.
npx @slats/claude-assets-sync migrate [--dry-run]
Directory Structure
Flat (default)
your-project/
└── .claude/
├── commands/
│ ├── @canard-schema-form-my-command.md
│ └── @lerx-promise-modal-another-command.md
└── skills/
├── @canard-schema-form-my-skill.md
└── @lerx-promise-modal-another-skill.md
A single .sync-meta.json tracks all packages in each asset directory.
Nested (legacy, --no-flat)
your-project/
└── .claude/
├── commands/
│ └── @canard/schema-form/
│ ├── my-command.md
│ └── .sync-meta.json
└── skills/
└── @lerx/promise-modal/
├── my-skill.md
└── .sync-meta.json
.sync-meta.json
{
"version": "0.0.1",
"syncedAt": "2025-02-05T10:30:00.000Z",
"packages": {
"@canard-schema-form": {
"originalName": "@canard/schema-form",
"version": "1.0.0",
"files": {
"commands": [
{ "original": "my-command.md", "transformed": "@canard-schema-form-my-command.md" }
],
"skills": [
{ "original": "my-skill.md", "transformed": "@canard-schema-form-my-skill.md" }
]
}
}
}
}
Environment Variables
| Variable | Description | Default |
|---|---|---|
GITHUB_TOKEN | GitHub personal access token (raises rate limit to 5,000 req/hr) | unauthenticated (60 req/hr) |
VERBOSE | Enable debug logging | disabled |
GITHUB_TOKEN=ghp_xxxxxxxxxxxx npx @slats/claude-assets-sync -p @package1 -p @package2
For Package Authors
Add a claude field to your package.json and place assets under the declared path:
{
"name": "@your-scope/your-package",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "https://github.com/your-org/your-repo.git",
"directory": "packages/your-package"
},
"claude": {
"assetPath": "docs/claude"
}
}
your-package/
└── docs/claude/
├── commands/
│ └── your-command.md
└── skills/
└── your-skill.md
- Asset files must be Markdown (
.md) repository.typeandrepository.urlare requiredassetPathis relative to the package root
Sync Flow
1. Read package.json from node_modules (or local workspace)
2. Extract claude.assetPath
3. Parse repository URL → GitHub owner/repo
4. Compare version against .sync-meta.json (skip if unchanged, unless --force)
5. Fetch file list from GitHub API (commands/ and skills/ under assetPath)
6. Download each file from raw.githubusercontent.com
7. Write to .claude/{type}/{prefixed-name}.md
8. Update .sync-meta.json
Programmatic API
import { syncPackage, syncPackages } from '@slats/claude-assets-sync';
import type { CliOptions, SyncResult } from '@slats/claude-assets-sync';
// Sync a single package
const result: SyncResult = await syncPackage('@canard/schema-form', {
force: false,
dryRun: false,
flat: true,
});
// Sync multiple packages
const results = await syncPackages(['@canard/schema-form', '@lerx/promise-modal'], {
force: true,
});
AI Agent Reference
AI Reference
Package: @slats/claude-assets-sync v0.0.6
Purpose: Claude Code asset synchronization CLI tool.
Exports
import { syncPackage } from '@slats/claude-assets-sync';
import { syncPackages } from '@slats/claude-assets-sync';
import { createProgram } from '@slats/claude-assets-sync';
import { run } from '@slats/claude-assets-sync';
import { migrateToFlat } from '@slats/claude-assets-sync';
import { needsMigration } from '@slats/claude-assets-sync';
import type { MigrationResult } from '@slats/claude-assets-sync';
import type { AssetType } from '@slats/claude-assets-sync';
import type { CliOptions } from '@slats/claude-assets-sync';
import type { ClaudeConfig } from '@slats/claude-assets-sync';
import type { GitHubRepoInfo } from '@slats/claude-assets-sync';
import type { PackageInfo } from '@slats/claude-assets-sync';
import type { SyncMeta } from '@slats/claude-assets-sync';
import type { SyncResult } from '@slats/claude-assets-sync';
import type { UnifiedSyncMeta } from '@slats/claude-assets-sync';
import type { PackageSyncInfo } from '@slats/claude-assets-sync';
import type { FileMapping } from '@slats/claude-assets-sync';