basecamp-cli
CLI and MCP server for Basecamp 4. Use when you need to interact with Basecamp projects, todos, messages, schedules, kanban cards, documents, or campfires. Provides 76 MCP tools for AI-driven project management workflows.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-basecamp-cli-mcp
Repository
Skill path: skills/drkraft/basecamp-cli-mcp
CLI and MCP server for Basecamp 4. Use when you need to interact with Basecamp projects, todos, messages, schedules, kanban cards, documents, or campfires. Provides 76 MCP tools for AI-driven project management workflows.
Open repositoryBest for
Primary workflow: Analyze Data & AI.
Technical facets: Full Stack, Backend, Data / AI, Integration.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install basecamp-cli into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding basecamp-cli to shared team environments
- Use basecamp-cli for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: basecamp-cli
description: CLI and MCP server for Basecamp 4. Use when you need to interact with Basecamp projects, todos, messages, schedules, kanban cards, documents, or campfires. Provides 76 MCP tools for AI-driven project management workflows.
mcp: true
metadata: {"openclaw":{"emoji":"šļø","homepage":"https://github.com/drkraft/basecamp-cli","primaryEnv":"BASECAMP_CLIENT_SECRET","requires":{"bins":["basecamp-mcp"],"env":["BASECAMP_CLIENT_ID","BASECAMP_CLIENT_SECRET"]},"install":[{"id":"npm","kind":"node","package":"@drkraft/basecamp-cli","bins":["basecamp","basecamp-mcp"],"label":"Install @drkraft/basecamp-cli (npm)","global":true}]}}
---
# Basecamp CLI
Full-featured CLI and MCP server for Basecamp 4 API.
## Features
- **21 CLI command groups** covering all Basecamp 4 domains
- **76 MCP tools** for AI assistant integration
- Automatic pagination and retry with exponential backoff
- OAuth 2.0 authentication with PKCE
## Installation
```bash
npm install -g @drkraft/basecamp-cli
```
## Requirements
- Node.js >= 20
## Authentication Setup
1. Create an OAuth app at https://launchpad.37signals.com/integrations
- Set redirect URI to `http://localhost:9292/callback`
2. Configure credentials:
```bash
basecamp auth configure --client-id <your-client-id>
export BASECAMP_CLIENT_SECRET="<your-client-secret>"
export BASECAMP_CLIENT_ID="<your-client-id>"
```
3. Login:
```bash
basecamp auth login
```
## MCP Server Configuration
Add to your MCP config (e.g., `~/.config/claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"basecamp": {
"command": "basecamp-mcp",
"env": {
"BASECAMP_CLIENT_ID": "<your-client-id>",
"BASECAMP_CLIENT_SECRET": "<your-client-secret>"
}
}
}
}
```
## Available MCP Tools (76)
| Category | Tools |
|----------|-------|
| Projects | `basecamp_list_projects`, `basecamp_get_project`, `basecamp_create_project`, `basecamp_archive_project` |
| Todo Lists | `basecamp_list_todolists`, `basecamp_get_todolist`, `basecamp_create_todolist`, `basecamp_delete_todolist` |
| Todo Groups | `basecamp_list_todolist_groups`, `basecamp_create_todolist_group` |
| Todos | `basecamp_list_todos`, `basecamp_get_todo`, `basecamp_create_todo`, `basecamp_update_todo`, `basecamp_complete_todo`, `basecamp_uncomplete_todo`, `basecamp_delete_todo`, `basecamp_move_todo` |
| Messages | `basecamp_list_messages`, `basecamp_get_message`, `basecamp_create_message` |
| People | `basecamp_list_people`, `basecamp_get_person`, `basecamp_get_me` |
| Comments | `basecamp_list_comments`, `basecamp_get_comment`, `basecamp_create_comment`, `basecamp_update_comment`, `basecamp_delete_comment` |
| Vaults | `basecamp_list_vaults`, `basecamp_get_vault`, `basecamp_create_vault`, `basecamp_update_vault` |
| Documents | `basecamp_list_documents`, `basecamp_get_document`, `basecamp_create_document`, `basecamp_update_document` |
| Uploads | `basecamp_list_uploads`, `basecamp_get_upload`, `basecamp_create_upload`, `basecamp_update_upload` |
| Schedules | `basecamp_get_schedule`, `basecamp_list_schedule_entries`, `basecamp_get_schedule_entry`, `basecamp_create_schedule_entry`, `basecamp_update_schedule_entry`, `basecamp_delete_schedule_entry` |
| Card Tables | `basecamp_get_card_table`, `basecamp_get_column`, `basecamp_create_column`, `basecamp_update_column`, `basecamp_delete_column`, `basecamp_list_cards`, `basecamp_get_card`, `basecamp_create_card`, `basecamp_update_card`, `basecamp_move_card`, `basecamp_delete_card` |
| Search | `basecamp_search` |
| Recordings | `basecamp_list_recordings`, `basecamp_archive_recording`, `basecamp_restore_recording`, `basecamp_trash_recording` |
| Subscriptions | `basecamp_list_subscriptions`, `basecamp_subscribe`, `basecamp_unsubscribe` |
| Webhooks | `basecamp_list_webhooks`, `basecamp_get_webhook`, `basecamp_create_webhook`, `basecamp_update_webhook`, `basecamp_delete_webhook`, `basecamp_test_webhook` |
| Events | `basecamp_list_events` |
| Campfires | `basecamp_list_campfires`, `basecamp_get_campfire_lines`, `basecamp_send_campfire_line` |
## CLI Quick Reference
```bash
# Projects
basecamp projects list
basecamp projects get <id>
# Todos
basecamp todolists list --project <id>
basecamp todos list --project <id> --list <list-id>
basecamp todos create --project <id> --list <list-id> --content "Task"
basecamp todos complete <id> --project <id>
basecamp todos delete <id> --project <id>
basecamp todos move <id> --project <id> --list <target-list-id>
# Messages
basecamp messages list --project <id>
basecamp messages create --project <id> --subject "Title" --content "<p>Body</p>"
# Kanban
basecamp cardtables get --project <id>
basecamp cardtables cards --project <id> --column <col-id>
basecamp cardtables create-card --project <id> --column <col-id> --title "Card"
# Search
basecamp search "keyword"
basecamp search "keyword" --type Todo --project <id>
```
All commands support `--format json` for JSON output.
## Links
- [Full Documentation](https://github.com/drkraft/basecamp-cli)
- [npm Package](https://www.npmjs.com/package/@drkraft/basecamp-cli)
- [Basecamp API Reference](https://github.com/basecamp/bc3-api)
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### README.md
```markdown
# @drkraft/basecamp-cli
A comprehensive command-line interface and MCP server for Basecamp 4. Manage projects, to-dos, messages, schedules, kanban boards, and more from your terminal or AI assistant.
[](https://www.npmjs.com/package/@drkraft/basecamp-cli)
[](https://opensource.org/licenses/MIT)
[](https://codecov.io/gh/drkraft/basecamp-cli)
## Features
- **Full CLI** - 21 command groups covering the complete Basecamp 4 API
- **MCP Server** - 76 tools for AI assistant integration (Claude, etc.)
- **Multiple Output Formats** - Table or JSON output for all commands
- **Pagination & Retry** - Automatic handling of large datasets and rate limits
- **OAuth 2.0** - Secure authentication via browser
## Installation
```bash
npm install -g @drkraft/basecamp-cli
```
Or with bun:
```bash
bun add -g @drkraft/basecamp-cli
```
## Requirements
- Node.js >= 20
## Quick Start
### 1. Create a Basecamp Integration
1. Go to [Basecamp Integrations](https://launchpad.37signals.com/integrations)
2. Click "Register another application"
3. Fill in the details:
- **Name**: Your app name
- **Company**: Your company
- **Website**: Your website
- **Redirect URI**: `http://localhost:9292/callback`
4. Note your **Client ID** and **Client Secret**
### 2. Configure Credentials
```bash
export BASECAMP_CLIENT_ID="your-client-id"
export BASECAMP_CLIENT_SECRET="your-client-secret"
```
Or configure via CLI:
```bash
basecamp auth configure --client-id "your-client-id"
```
### 3. Login
```bash
basecamp auth login
```
This opens your browser for OAuth authentication.
## CLI Reference
### Authentication
```bash
basecamp auth login # Login via OAuth
basecamp auth status # Check auth status
basecamp auth logout # Logout
```
### Accounts
```bash
basecamp accounts # List available accounts
basecamp account set <id> # Set current account
basecamp account current # Show current account
```
### Projects
```bash
basecamp projects list # List all projects
basecamp projects get <id> # Get project details
basecamp projects create --name "Project" --description "Desc" # Create project
basecamp projects archive <id> # Archive project
```
### To-do Lists & To-dos
```bash
# To-do lists
basecamp todolists list --project <id>
basecamp todolists create --project <id> --name "Tasks"
basecamp todolists delete <id> --project <project-id>
# To-dos
basecamp todos list --project <id> --list <list-id>
basecamp todos list --project <id> --list <list-id> --completed
basecamp todos get <id> --project <project-id>
basecamp todos create --project <id> --list <list-id> --content "Task"
basecamp todos create --project <id> --list <list-id> --content "Task" \
--due "2025-12-31" --assignees "123,456"
basecamp todos update <id> --project <project-id> --content "Updated"
basecamp todos complete <id> --project <project-id>
basecamp todos uncomplete <id> --project <project-id>
basecamp todos delete <id> --project <project-id>
basecamp todos move <id> --project <project-id> --list <target-list-id>
# To-do groups
basecamp todogroups list --project <id>
basecamp todogroups create --project <id> --name "Sprint 1"
```
### Messages
```bash
basecamp messages list --project <id>
basecamp messages get <id> --project <project-id>
basecamp messages create --project <id> --subject "Subject" --content "<p>HTML</p>"
```
### Campfires (Chat)
```bash
basecamp campfires list --project <id>
basecamp campfires lines --project <id> --campfire <campfire-id>
basecamp campfires send --project <id> --campfire <campfire-id> --message "Hello!"
```
### Comments
```bash
basecamp comments list --project <id> --recording <recording-id>
basecamp comments get <id> --project <project-id>
basecamp comments create --project <id> --recording <recording-id> --content "<p>Comment</p>"
basecamp comments update <id> --project <project-id> --content "<p>Updated</p>"
basecamp comments delete <id> --project <project-id>
```
### Documents & Vaults
```bash
# Vaults (folders)
basecamp vaults list --project <id>
basecamp vaults get <id> --project <project-id>
basecamp vaults create --project <id> --vault <parent-vault-id> --title "Folder Name"
# Documents
basecamp documents list --project <id> --vault <vault-id>
basecamp documents get <id> --project <project-id>
basecamp documents create --project <id> --vault <vault-id> --title "Doc" --content "<p>...</p>"
basecamp documents update <id> --project <project-id> --title "New Title"
# Uploads
basecamp uploads list --project <id> --vault <vault-id>
basecamp uploads get <id> --project <project-id>
```
### Schedules
```bash
basecamp schedules get --project <id>
basecamp schedules entries --project <id>
basecamp schedules entries --project <id> --status upcoming
basecamp schedules create-entry --project <id> --summary "Meeting" \
--starts-at "2025-02-15T10:00:00" --ends-at "2025-02-15T11:00:00"
basecamp schedules update-entry <id> --project <project-id> --summary "Updated"
basecamp schedules delete-entry <id> --project <project-id>
```
### Card Tables (Kanban)
```bash
basecamp cardtables get --project <id>
basecamp cardtables columns --project <id>
basecamp cardtables create-column --project <id> --title "In Progress"
basecamp cardtables cards --project <id> --column <column-id>
basecamp cardtables create-card --project <id> --column <column-id> --title "Card"
basecamp cardtables move-card <card-id> --project <id> --column <new-column-id>
```
### Webhooks
```bash
basecamp webhooks list --project <id>
basecamp webhooks get <id> --project <project-id>
basecamp webhooks create --project <id> --payload-url "https://..."
basecamp webhooks update <id> --project <project-id> --active false
basecamp webhooks delete <id> --project <project-id>
```
### Recordings & Events
```bash
# Recordings (cross-project content)
basecamp recordings list --type Todo
basecamp recordings list --type Message --status archived
basecamp recordings archive <id> --project <project-id>
basecamp recordings restore <id> --project <project-id>
basecamp recordings trash <id> --project <project-id>
# Events (activity feed)
basecamp events list --project <id> --recording <recording-id>
```
### Subscriptions
```bash
basecamp subscriptions list --project <id> --recording <recording-id>
basecamp subscriptions subscribe --project <id> --recording <recording-id>
basecamp subscriptions unsubscribe --project <id> --recording <recording-id>
```
### Search
```bash
basecamp search "keyword"
basecamp search "keyword" --type Todo
basecamp search "keyword" --project <id>
```
### People
```bash
basecamp people list
basecamp people list --project <id>
basecamp people get <id>
basecamp people me
```
## Output Formats
All commands support `--format` flag:
```bash
basecamp projects list --format table # Default, human-readable
basecamp projects list --format json # JSON for scripting
```
## Global Options
```bash
basecamp --verbose projects list # Enable debug output
basecamp -v people me # Short form
```
## MCP Server
The CLI includes an MCP (Model Context Protocol) server for AI assistant integration.
### Starting the Server
```bash
basecamp-mcp
# Or
bun run mcp
```
### Available Tools (76)
| Category | Tools |
|----------|-------|
| Projects | `basecamp_list_projects`, `basecamp_get_project`, `basecamp_create_project`, `basecamp_archive_project` |
| Todo Lists | `basecamp_list_todolists`, `basecamp_get_todolist`, `basecamp_create_todolist`, `basecamp_delete_todolist` |
| Todo Groups | `basecamp_list_todolist_groups`, `basecamp_create_todolist_group` |
| Todos | `basecamp_list_todos`, `basecamp_get_todo`, `basecamp_create_todo`, `basecamp_update_todo`, `basecamp_complete_todo`, `basecamp_uncomplete_todo`, `basecamp_delete_todo`, `basecamp_move_todo` |
| Messages | `basecamp_list_messages`, `basecamp_get_message`, `basecamp_create_message` |
| People | `basecamp_list_people`, `basecamp_get_person`, `basecamp_get_me` |
| Comments | `basecamp_list_comments`, `basecamp_get_comment`, `basecamp_create_comment`, `basecamp_update_comment`, `basecamp_delete_comment` |
| Vaults | `basecamp_list_vaults`, `basecamp_get_vault`, `basecamp_create_vault`, `basecamp_update_vault` |
| Documents | `basecamp_list_documents`, `basecamp_get_document`, `basecamp_create_document`, `basecamp_update_document` |
| Uploads | `basecamp_list_uploads`, `basecamp_get_upload`, `basecamp_create_upload`, `basecamp_update_upload` |
| Schedules | `basecamp_get_schedule`, `basecamp_list_schedule_entries`, `basecamp_get_schedule_entry`, `basecamp_create_schedule_entry`, `basecamp_update_schedule_entry`, `basecamp_delete_schedule_entry` |
| Card Tables | `basecamp_get_card_table`, `basecamp_get_column`, `basecamp_create_column`, `basecamp_update_column`, `basecamp_delete_column`, `basecamp_list_cards`, `basecamp_get_card`, `basecamp_create_card`, `basecamp_update_card`, `basecamp_move_card`, `basecamp_delete_card` |
| Search | `basecamp_search` |
| Recordings | `basecamp_list_recordings`, `basecamp_archive_recording`, `basecamp_restore_recording`, `basecamp_trash_recording` |
| Subscriptions | `basecamp_list_subscriptions`, `basecamp_subscribe`, `basecamp_unsubscribe` |
| Webhooks | `basecamp_list_webhooks`, `basecamp_get_webhook`, `basecamp_create_webhook`, `basecamp_update_webhook`, `basecamp_delete_webhook`, `basecamp_test_webhook` |
| Events | `basecamp_list_events` |
| Campfires | `basecamp_list_campfires`, `basecamp_get_campfire_lines`, `basecamp_send_campfire_line` |
### Using with OpenCode/Claude
Add to your MCP configuration:
```json
{
"mcpServers": {
"basecamp": {
"command": "basecamp-mcp",
"env": {
"BASECAMP_CLIENT_ID": "<your-client-id>",
"BASECAMP_CLIENT_SECRET": "<your-client-secret>"
}
}
}
}
```
Or with explicit path:
```json
{
"mcpServers": {
"basecamp": {
"command": "node",
"args": ["/path/to/node_modules/@drkraft/basecamp-cli/dist/mcp.js"]
}
}
}
```
## Environment Variables
| Variable | Description |
|----------|-------------|
| `BASECAMP_CLIENT_ID` | OAuth Client ID (required) |
| `BASECAMP_CLIENT_SECRET` | OAuth Client Secret (required) |
| `BASECAMP_REDIRECT_URI` | OAuth Redirect URI (default: `http://localhost:9292/callback`) |
## API Coverage
This CLI covers the complete Basecamp 4 API Tier 1 domains:
| Domain | Status |
|--------|--------|
| Projects | Complete |
| Todolists | Complete |
| Todos | Complete |
| Todolist Groups | Complete |
| Messages | Complete |
| Campfires | Complete |
| Comments | Complete |
| Vaults | Complete |
| Documents | Complete |
| Uploads | Complete |
| Schedules | Complete |
| Card Tables | Complete |
| Webhooks | Complete |
| Recordings | Complete |
| Events | Complete |
| Search | Complete |
| Subscriptions | Complete |
| People | Complete |
## Development
```bash
# Clone the repo
git clone https://github.com/drkraft/basecamp-cli
cd basecamp-cli
# Install dependencies
bun install
# Build
bun run build
# Run tests
bun test
# Run validation against real Basecamp
bun run scripts/validate.ts
```
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## License
MIT - see [LICENSE](LICENSE) for details.
## Credits
Originally forked from [@emredoganer/basecamp-cli](https://github.com/emredoganer/basecamp-cli).
```
### _meta.json
```json
{
"owner": "drkraft",
"slug": "basecamp-cli-mcp",
"displayName": "Basecamp CLI",
"latest": {
"version": "2.0.0",
"publishedAt": 1770126605252,
"commit": "https://github.com/clawdbot/skills/commit/84685087293e1665e2a6db2ad42dba7539876584"
},
"history": []
}
```
### scripts/release.sh
```bash
#!/bin/bash
set -e
echo "=== Basecamp CLI v2.0.0 Release ==="
echo ""
# Check prerequisites
echo "Checking prerequisites..."
if ! command -v npm &>/dev/null; then
echo "ERROR: npm not found"
exit 1
fi
if ! command -v gh &>/dev/null; then
echo "WARNING: gh (GitHub CLI) not found - manual release creation needed"
fi
# Verify build
echo "Building..."
bun run build
# Verify tests (if configured)
echo "Running tests..."
bun test || echo "Tests skipped or failed - review before publishing"
# Show package contents
echo ""
echo "Package contents:"
npm pack --dry-run
# Confirm
echo ""
read -p "Ready to publish? (y/N) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
# Push to GitHub
echo ""
echo "Pushing to GitHub..."
git push origin main
# Publish to npm
echo ""
echo "Publishing to npm..."
npm publish --access public
# Create GitHub release
echo ""
echo "Creating GitHub release..."
if command -v gh &>/dev/null; then
gh release create v2.0.0 --title "v2.0.0" --notes-file CHANGELOG.md
else
echo "Create release manually at: https://github.com/drkraft/basecamp-cli/releases/new"
fi
echo ""
echo "=== Release complete! ==="
echo ""
echo "Next steps:"
echo "1. Verify on npm: https://www.npmjs.com/package/@drkraft/basecamp-cli"
echo "2. Verify on GitHub: https://github.com/drkraft/basecamp-cli"
echo "3. Submit to ClawHub (manual)"
```
### scripts/validate.ts
```typescript
#!/usr/bin/env bun
/**
* Manual Validation Script for Basecamp CLI
*
* This script runs through all CLI commands against a real Basecamp account
* to verify everything works correctly before release.
*
* Prerequisites:
* - BASECAMP_CLIENT_ID and BASECAMP_CLIENT_SECRET environment variables set
* - Authenticated via `basecamp auth login`
* - At least one project in Basecamp with todos, messages, etc.
*
* Usage:
* bun run scripts/validate.ts
* bun run scripts/validate.ts --project <PROJECT_ID>
*/
import { execSync } from 'child_process';
const COLORS = {
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
gray: '\x1b[90m',
};
interface TestResult {
name: string;
command: string;
passed: boolean;
output?: string;
error?: string;
}
const results: TestResult[] = [];
function log(color: string, prefix: string, message: string): void {
console.log(`${color}${prefix}${COLORS.reset} ${message}`);
}
function run(name: string, command: string): TestResult {
log(COLORS.blue, '[RUN]', `${name}: ${COLORS.gray}${command}`);
try {
const output = execSync(command, {
encoding: 'utf-8',
timeout: 60000,
maxBuffer: 50 * 1024 * 1024,
stdio: ['pipe', 'pipe', 'pipe'],
});
log(COLORS.green, '[PASS]', name);
return { name, command, passed: true, output: output.trim() };
} catch (error) {
const err = error as { stderr?: Buffer; message?: string };
const errorMsg = err.stderr?.toString() || err.message || 'Unknown error';
log(COLORS.red, '[FAIL]', `${name}: ${errorMsg.slice(0, 100)}`);
return { name, command, passed: false, error: errorMsg };
}
}
function section(title: string): void {
console.log(`\n${COLORS.yellow}=== ${title} ===${COLORS.reset}\n`);
}
async function main(): Promise<void> {
console.log(`${COLORS.blue}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Basecamp CLI Manual Validation Script ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
${COLORS.reset}`);
// Parse arguments
const args = process.argv.slice(2);
const projectIdIndex = args.indexOf('--project');
let projectId = projectIdIndex !== -1 ? args[projectIdIndex + 1] : null;
const CLI = './dist/index.js';
// ============ AUTH ============
section('Authentication');
results.push(run('Auth status', `${CLI} auth status`));
// ============ ACCOUNTS ============
section('Accounts');
results.push(run('List accounts', `${CLI} accounts`));
results.push(run('Current account', `${CLI} account current`));
// ============ PROJECTS ============
section('Projects');
const projectsResult = run('List projects', `${CLI} projects list --format json`);
results.push(projectsResult);
// Extract first project ID if not provided
if (!projectId && projectsResult.passed && projectsResult.output) {
try {
const projects = JSON.parse(projectsResult.output);
if (Array.isArray(projects) && projects.length > 0) {
projectId = String(projects[0].id);
log(COLORS.blue, '[INFO]', `Using project ID: ${projectId}`);
}
} catch {
log(COLORS.yellow, '[WARN]', 'Could not parse projects output');
}
}
if (!projectId) {
log(COLORS.red, '[ERROR]', 'No project ID available. Create a project or specify --project <ID>');
printSummary();
process.exit(1);
}
results.push(run('Get project', `${CLI} projects get ${projectId} --format json`));
// ============ PEOPLE ============
section('People');
results.push(run('Get me', `${CLI} people me --format json`));
results.push(run('List people', `${CLI} people list --format json`));
results.push(run('List project people', `${CLI} people list --project ${projectId} --format json`));
// ============ TODO LISTS ============
section('Todo Lists');
const todolistsResult = run('List todolists', `${CLI} todolists list --project ${projectId} --format json`);
results.push(todolistsResult);
let todolistId: string | null = null;
if (todolistsResult.passed && todolistsResult.output) {
try {
const todolists = JSON.parse(todolistsResult.output);
if (Array.isArray(todolists) && todolists.length > 0) {
todolistId = String(todolists[0].id);
log(COLORS.blue, '[INFO]', `Using todolist ID: ${todolistId}`);
}
} catch {
log(COLORS.yellow, '[WARN]', 'Could not parse todolists output');
}
}
// ============ TODOS ============
section('Todos');
if (todolistId) {
results.push(run('List todos', `${CLI} todos list --project ${projectId} --list ${todolistId} --format json`));
// Create a test todo
const createTodoResult = run(
'Create todo',
`${CLI} todos create --project ${projectId} --list ${todolistId} --content "Test todo from validation script" --format json`
);
results.push(createTodoResult);
if (createTodoResult.passed && createTodoResult.output) {
try {
const todo = JSON.parse(createTodoResult.output);
const todoId = todo.id;
if (todoId) {
results.push(run('Get todo', `${CLI} todos get ${todoId} --project ${projectId} --format json`));
results.push(run('Complete todo', `${CLI} todos complete ${todoId} --project ${projectId}`));
results.push(run('Uncomplete todo', `${CLI} todos uncomplete ${todoId} --project ${projectId}`));
}
} catch {
log(COLORS.yellow, '[WARN]', 'Could not parse created todo');
}
}
} else {
log(COLORS.yellow, '[SKIP]', 'No todolist found, skipping todo tests');
}
// ============ TODO GROUPS ============
section('Todo Groups');
if (todolistId) {
results.push(run('List todo groups', `${CLI} todogroups list --project ${projectId} --list ${todolistId} --format json`));
} else {
log(COLORS.yellow, '[SKIP]', 'No todolist found, skipping todo groups tests');
}
// ============ MESSAGES ============
section('Messages');
results.push(run('List messages', `${CLI} messages list --project ${projectId} --format json`));
// ============ CAMPFIRES ============
section('Campfires');
results.push(run('List campfires', `${CLI} campfires list --project ${projectId} --format json`));
// ============ VAULTS ============
section('Vaults (Docs & Files)');
const vaultsResult = run('List vaults', `${CLI} vaults list --project ${projectId} --format json`);
results.push(vaultsResult);
// ============ DOCUMENTS ============
section('Documents');
// Documents require a vault ID - skip if no vault
log(COLORS.gray, '[INFO]', 'Document tests require manual vault ID');
// ============ SCHEDULES ============
section('Schedules');
results.push(run('Get schedule', `${CLI} schedules get --project ${projectId} --format json`));
results.push(run('List schedule entries', `${CLI} schedules entries --project ${projectId} --format json`));
// ============ CARD TABLES ============
section('Card Tables (Kanban)');
results.push(run('Get card table', `${CLI} cardtables get --project ${projectId} --format json`));
// ============ WEBHOOKS ============
section('Webhooks');
results.push(run('List webhooks', `${CLI} webhooks list --project ${projectId} --format json`));
// ============ RECORDINGS ============
section('Recordings');
results.push(run('List recordings (Todo)', `${CLI} recordings list --type Todo --bucket ${projectId} --format json`));
// ============ EVENTS ============
section('Events');
log(COLORS.gray, '[INFO]', 'Events require a recording ID - skipping (tested via MCP)');
// ============ SUBSCRIPTIONS ============
section('Subscriptions');
log(COLORS.gray, '[INFO]', 'Subscription tests require a recording ID');
// ============ SEARCH ============
section('Search');
results.push(run('Search', `${CLI} search "test" --format json`));
// ============ MCP SERVER ============
section('MCP Server');
results.push(run('MCP tools/list', `echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | bun run mcp | grep -q '"tools"' && echo "MCP OK"`));
// ============ SUMMARY ============
printSummary();
}
function printSummary(): void {
console.log(`\n${COLORS.blue}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SUMMARY ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
${COLORS.reset}`);
const passed = results.filter((r) => r.passed).length;
const failed = results.filter((r) => !r.passed).length;
const total = results.length;
console.log(`Total: ${total}`);
console.log(`${COLORS.green}Passed: ${passed}${COLORS.reset}`);
console.log(`${COLORS.red}Failed: ${failed}${COLORS.reset}`);
if (failed > 0) {
console.log(`\n${COLORS.red}Failed tests:${COLORS.reset}`);
results
.filter((r) => !r.passed)
.forEach((r) => {
console.log(` - ${r.name}`);
if (r.error) {
console.log(` ${COLORS.gray}${r.error.slice(0, 200)}${COLORS.reset}`);
}
});
}
console.log(`\nPass rate: ${((passed / total) * 100).toFixed(1)}%`);
process.exit(failed > 0 ? 1 : 0);
}
main().catch(console.error);
```