agentsclimarketplace

Clickup local dev loop

Skill ComeOnOliver/skillshub/skills/jeremylongshore/claude-code-plugins-plus-skills/clickup-local-dev-loop

🧠 The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.

Install
npx -y skills add ComeOnOliver/skillshub --skill clickup-local-dev-loop

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Set up local development for ClickUp API integrations with testing, mocking, and hot reload. Trigger: "clickup dev setup", "clickup local development", "clickup dev environment", "develop with clickup", "clickup testing setup", "mock clickup API".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

5.8 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

ClickUp Local Dev Loop

Overview

Set up a fast local development workflow for ClickUp API v2 integrations with hot reload, mocking, and integration testing.

Project Setup

mkdir my-clickup-integration && cd $_
npm init -y
npm install -D typescript tsx vitest @types/node dotenv
npx tsc --init --target ES2022 --module nodenext --outDir dist
my-clickup-integration/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ clickup/
β”‚   β”‚   β”œβ”€β”€ client.ts       # ClickUp API client (see clickup-sdk-patterns)
β”‚   β”‚   β”œβ”€β”€ types.ts         # TypeScript interfaces
β”‚   β”‚   └── tasks.ts         # Task operations
β”‚   └── index.ts
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ mocks/
β”‚   β”‚   └── clickup.ts       # Mock ClickUp API responses
β”‚   β”œβ”€β”€ unit/
β”‚   β”‚   └── tasks.test.ts
β”‚   └── integration/
β”‚       └── clickup.test.ts
β”œβ”€β”€ .env.local                # Local secrets (git-ignored)
β”œβ”€β”€ .env.example              # Template for team
└── package.json

Environment Configuration

# .env.example (commit this)
CLICKUP_API_TOKEN=pk_your_token_here
CLICKUP_TEAM_ID=your_team_id
CLICKUP_TEST_LIST_ID=your_test_list_id

# .env.local (git-ignored, copy from .env.example)
cp .env.example .env.local
{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "test": "vitest",
    "test:watch": "vitest --watch",
    "test:integration": "CLICKUP_LIVE=1 vitest --run tests/integration/",
    "build": "tsc",
    "typecheck": "tsc --noEmit"
  }
}

Mock ClickUp API for Unit Tests

// tests/mocks/clickup.ts
export const mockTask = {
  id: 'test_task_001',
  name: 'Test Task',
  status: { status: 'to do', color: '#d3d3d3', type: 'open' },
  priority: { id: '3', priority: 'normal', color: '#6fddff' },
  date_created: '1695000000000',
  date_updated: '1695000000000',
  due_date: null,
  assignees: [],
  tags: [],
  url: 'https://app.clickup.com/t/test_task_001',
  list: { id: '900100200300', name: 'Test List' },
  folder: { id: '456', name: 'Test Folder' },
  space: { id: '789' },
  custom_fields: [],
};

export const mockTeam = {
  teams: [{
    id: '1234567',
    name: 'Test Workspace',
    members: [{ user: { id: 183, username: 'testuser', email: '[email protected]' } }],
  }],
};

// Mock fetch for ClickUp API calls
export function mockClickUpFetch() {
  return vi.fn(async (url: string, options?: RequestInit) => {
    const path = new URL(url).pathname.replace('/api/v2', '');

    const routes: Record<string, any> = {
      '/team': mockTeam,
      '/user': { user: { id: 183, username: 'testuser' } },
    };

    // Match dynamic routes
    if (path.match(/^\/task\/.+/)) return jsonResponse(mockTask);
    if (path.match(/^\/list\/.+\/task$/) && options?.method === 'POST') {
      return jsonResponse({ ...mockTask, ...JSON.parse(options.body as string) });
    }

    return jsonResponse(routes[path] ?? {}, routes[path] ? 200 : 404);
  });
}

function jsonResponse(data: any, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: {
      'Content-Type': 'application/json',
      'X-RateLimit-Remaining': '95',
      'X-RateLimit-Limit': '100',
      'X-RateLimit-Reset': String(Math.floor(Date.now() / 1000) + 60),
    },
  });
}

Unit Test Example

// tests/unit/tasks.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mockClickUpFetch, mockTask } from '../mocks/clickup';

describe('ClickUp Task Operations', () => {
  beforeEach(() => {
    vi.stubGlobal('fetch', mockClickUpFetch());
    vi.stubEnv('CLICKUP_API_TOKEN', 'pk_test_token');
  });

  it('creates a task with required fields', async () => {
    const { createTask } = await import('../../src/clickup/tasks');
    const task = await createTask('list123', { name: 'New Task' });
    expect(task.name).toBe('New Task');
    expect(fetch).toHaveBeenCalledWith(
      expect.stringContaining('/list/list123/task'),
      expect.objectContaining({ method: 'POST' }),
    );
  });

  it('gets a task by ID', async () => {
    const { getTask } = await import('../../src/clickup/tasks');
    const task = await getTask('test_task_001');
    expect(task.id).toBe(mockTask.id);
  });
});

Integration Test (Live API)

// tests/integration/clickup.test.ts
import { describe, it, expect } from 'vitest';
import 'dotenv/config';

const LIVE = process.env.CLICKUP_LIVE === '1';

describe.skipIf(!LIVE)('ClickUp Live API', () => {
  it('authenticates and lists workspaces', async () => {
    const response = await fetch('https://api.clickup.com/api/v2/team', {
      headers: { 'Authorization': process.env.CLICKUP_API_TOKEN! },
    });
    expect(response.ok).toBe(true);
    const data = await response.json();
    expect(data.teams.length).toBeGreaterThan(0);
  });
});

Error Handling

ErrorCauseSolution
CLICKUP_API_TOKEN undefinedMissing .env.localCopy from .env.example
Integration tests failNo live tokenSet CLICKUP_LIVE=1 and valid token
Mock not matchingRoute pattern wrongCheck URL path in mock router

Resources

Next Steps

See clickup-sdk-patterns for production-ready client patterns.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 327,132. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.