npm package: @felipeubidots/clickup-shortcut-migrator
Description: Migrate tasks from ClickUp to Shortcut with full data preservation. Preserves descriptions, comments, subtasks, custom fields, and more.
Author: Felipe Ubidots
Latest version: 1.0.0

# ClickUp → Shortcut Task Migration Tool

A comprehensive task migration tool that transfers tasks from ClickUp to Shortcut while preserving all relevant data including descriptions, comments, subtasks, and custom fields.

## Features

✅ **Complete Data Migration**
- Task names and descriptions (with inline images/videos)
- Story points (Sprint Points custom field)
- Status mapping (Open → Ready for Development, In Progress → In Development, Done → Done)
- Assignees with email/username mapping
- Tags → Labels (with auto-creation of missing labels)
- Comments with author attribution
- Subtasks as nested Shortcut stories
- External link back-reference for traceability

✅ **Safety & Reliability**
- Duplicate detection (checks if task already migrated)
- User ID mapping with fallback handling
- Error handling per task (failures don't stop batch migrations)
- Detailed reporting with migration status

✅ **Flexible Batch Operations**
- Migrate single tasks: `node migrate.js abc123`
- Migrate multiple tasks: `node migrate.js abc123 def456 ghi789`
- Automatic workflow state mapping
- Label deduplication and creation on-demand

---

## Quick Start

### Prerequisites

- Node.js v16+
- Access to both ClickUp and Shortcut workspaces via Claude Code MCP tools

### Installation

```bash
cd /path/to/task-migrator
npm install
```

### Basic Usage

Migrate a single ClickUp task:
```bash
node migrate.js <clickup_task_id>
```

Migrate multiple tasks:
```bash
node migrate.js task_id_1 task_id_2 task_id_3
```

Example:
```bash
node migrate.js abc123def456 ghi789jkl012
```

---

## How It Works

### Phase 0: Setup (automatic, one-time per session)

The tool automatically initializes the Shortcut environment:
- Fetches the default workflow and available states
- Loads all Shortcut users and builds an email/username → ID map
- Loads existing labels to avoid duplicates

```
📋 Phase 0: Initializing Shortcut environment...
  → Fetching default workflow...
    ✓ Workflow ID: 123456
    ✓ Available states: Ready for Development, In Development, Done
  → Fetching Shortcut users...
    ✓ 12 users loaded
  → Fetching Shortcut labels...
    ✓ 25 labels found
✅ Shortcut environment initialized
```

### Phase 1: Read from ClickUp (per task)

Fetches complete task data:
- Task name, description, status, assignees, tags
- Sprint Points custom field (if present)
- All attachments (images, videos, files)
- All comments with author info
- Subtasks
- Original task URL (for back-reference)

### Phase 2: Transform Data

Converts ClickUp data to Shortcut format:
- **Description**: Converts to Markdown with embedded image/video links
  - Images: `![alt](url)`
  - Videos: `[▶ See video](url)`
  - Files: `[📎 filename](url)`
  - Migration footer: `---\n*Migrated from ClickUp*`

- **Status Mapping**:
  - `open` → "Ready for Development"
  - `in progress` → "In Development"
  - `done`, `closed` → "Done"

- **Estimate**: Extracts "Sprint Points" custom field → story `estimate`

- **Assignees**: Maps ClickUp assignees to Shortcut users by email/username

- **Tags → Labels**:
  - Reuses existing labels (case-insensitive)
  - Creates missing labels automatically with random colors

### Phase 3: Write to Shortcut (per task)

1. **Duplicate Check**: Verifies task hasn't been migrated already (by external link)
2. **Create Story**: Creates main story in Shortcut with all metadata
3. **Add External Link**: Links back to original ClickUp task
4. **Migrate Comments**: Adds all comments with `[ClickUp - Author]` attribution
5. **Migrate Subtasks**: Creates subtasks as nested Shortcut stories and links them

### Phase 4: Report

Generates a summary table showing:
- ClickUp ID
- Task name
- Shortcut story ID (if migrated)
- Status (✅ Migrated, ⚠️ Skipped, ❌ Failed)

Example output:
```
📊 MIGRATION REPORT
==============================================================================

| ClickUp ID | Name | Shortcut ID | Status |
|---|---|---|---|
| abc123 | Fix login bug | sc-4521 | ✅ Migrated |
| def456 | Update docs | sc-4522 | ✅ Migrated |
| ghi789 | Old task | — | ⚠️ Already exists |

📈 Summary:
  • Total: 3
  • ✅ Migrated: 2
  • ⚠️ Skipped: 1
  • ❌ Failed: 0
```

---

## Field Mapping Reference

| ClickUp | Shortcut | Notes |
|---------|----------|-------|
| `name` | `name` | Direct copy |
| `description` + `attachments` | `description` | Markdown formatted with embedded links |
| `custom_fields` (Sprint Points) | `estimate` | Parsed as integer for story points |
| `status` | `workflow_state_id` | Status mapping applied (see Phase 2) |
| `assignees` | `owner_ids` | Mapped by email/username |
| `tags` | `labels` | Reused or created as needed |
| `comments` | Story comments | Formatted as `**[ClickUp - Author]:** text` |
| `subtasks` | Nested stories | Created as subtasks via `stories-add-subtask` |
| `url` | External link | Added for back-reference |

---

## Advanced Usage

### Environment Variables

```bash
# Optional: Specify custom workflow (defaults to workspace default)
export SHORTCUT_WORKFLOW_ID=12345

# Optional: Specify team (defaults to workspace)
export SHORTCUT_TEAM_ID=abc-def-ghi

node migrate.js task_id_1 task_id_2
```

### Dry Run (Planned Feature)

```bash
node migrate.js task_id --dry-run
# Shows what would be migrated without actually creating stories
```

### Batch Migration from File

Create `tasks.txt`:
```
abc123def456
ghi789jkl012
mno345pqr678
```

Then:
```bash
node migrate.js $(cat tasks.txt)
```

---

## Error Handling

The tool is designed to be resilient:

- **Per-task isolation**: If one task fails, migration continues with others
- **Graceful user mapping**: If a ClickUp assignee can't be mapped, the story is created without that owner
- **Label creation fallback**: If label creation fails, the story is created without that label
- **Comment/subtask failures**: Individual comment/subtask failures don't stop story creation

Each result is reported in the final summary table with specific error messages.

---

## Troubleshooting

### "Story already migrated"
This means the tool detected an external link pointing to the ClickUp task. The story already exists in Shortcut. If you need to re-migrate, delete the Shortcut story first.

### "Failed to map user: email@example.com"
The Shortcut workspace doesn't have a user with this email. Check:
1. User exists in Shortcut workspace
2. Email matches exactly in Shortcut
3. Or add the user to Shortcut and re-run

### "Failed to create label"
This usually happens if a label with the same name already exists but wasn't found (case sensitivity). The story is created without that label. You can manually add the label afterward.

---

## Verification Checklist

After migration, verify each story:

- [ ] Story visible in Shortcut with correct name
- [ ] Description contains all content from ClickUp (text + embedded images/videos)
- [ ] Estimate matches Sprint Points (if set)
- [ ] Status matches ClickUp status (via workflow state)
- [ ] Assignees/owners correct
- [ ] Labels present
- [ ] External link points to original ClickUp task
- [ ] All comments migrated with author attribution
- [ ] Subtasks created and linked correctly

---

## Technical Details

### Architecture

```
migrate.js
├─ Phase 0: Initialize Shortcut Environment
│  ├─ mcp__shortcut__workflows-get-default
│  ├─ mcp__shortcut__users-list
│  └─ mcp__shortcut__labels-list
│
├─ Phase 1-3: For each task
│  ├─ Read ClickUp
│  │  ├─ mcp__clickup__clickup_get_task
│  │  └─ mcp__clickup__clickup_get_task_comments
│  │
│  ├─ Transform
│  │  ├─ markdownify description
│  │  ├─ map status
│  │  ├─ parse estimate
│  │  ├─ map users
│  │  └─ map/create labels
│  │
│  └─ Write Shortcut
│     ├─ Check for duplicates (mcp__shortcut__stories-get-by-external-link)
│     ├─ Create story (mcp__shortcut__stories-create)
│     ├─ Add external link (mcp__shortcut__stories-add-external-link)
│     ├─ Migrate comments (mcp__shortcut__stories-create-comment)
│     └─ Migrate subtasks (recursively)
│
└─ Phase 4: Generate Report
```

### MCP Tools Used

**ClickUp (Reading):**
- `mcp__clickup__clickup_get_task` - Get task with subtasks
- `mcp__clickup__clickup_get_task_comments` - Get all comments

**Shortcut (Writing):**
- `mcp__shortcut__workflows-get-default` - Get available workflow states
- `mcp__shortcut__users-list` - Get user ID mappings
- `mcp__shortcut__labels-list` - Get existing labels
- `mcp__shortcut__stories-create` - Create main story
- `mcp__shortcut__stories-add-external-link` - Add back-reference
- `mcp__shortcut__stories-create-comment` - Migrate comments
- `mcp__shortcut__stories-create-subtask` or `mcp__shortcut__stories-add-subtask` - Link subtasks
- `mcp__shortcut__labels-create` - Create missing labels
- `mcp__shortcut__stories-get-by-external-link` - Check for duplicates

---

## Limitations

- **Image/Video URLs**: URLs remain as links; images aren't re-uploaded to Shortcut
- **Custom Fields**: Only "Sprint Points" is migrated. Other ClickUp custom fields don't have equivalents
- **Time Tracking**: ClickUp time entries aren't migrated (no equivalent in Shortcut)
- **Change History**: ClickUp activity history isn't preserved
- **Permissions**: Migrated stories inherit workspace defaults; ClickUp-specific permissions aren't replicated
- **Attachments**: Embedded as links in description; not re-hosted

---

## Performance

Typical migration times (per task):
- Single task: ~2-5 seconds
- Task + 5 comments: ~3-7 seconds
- Task + 3 subtasks: ~8-15 seconds
- Batch of 10 tasks: ~30-60 seconds

Times depend on ClickUp/Shortcut API latency.

---

## Support

For issues or feature requests:
1. Check the Troubleshooting section above
2. Review error messages in the final report
3. Verify MCP tools are accessible: `/help` in Claude Code

---

## License

MIT