A unified command-line interface for managing tasks across multiple project management tools.
Currently supports:
- Asana (fully implemented)
- Notion (fully implemented)
- Aggregate tasks from multiple PM tools in one place
- Create, update, and complete tasks from the command line
- Morning dashboard and summary views
- Filter by status/priority and sort results
- Search across all connected providers
- Create git branches from tasks
- Add comments to tasks
- Switch between workspaces
- Cached responses for fast repeat queries
- Multiple output modes: table, JSON, plain, ids-only
pnpm add -g @jogi47/pm-cliThe pm command is now available globally:
pm --helpYou can also use npm or yarn:
npm install -g @jogi47/pm-cli
# or
yarn global add @jogi47/pm-cli# Clone the repository
git clone <repo-url>
cd pm-cli
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Link the CLI globally
pnpm link --global --filter @jogi47/pm-cliThe pm command is now available system-wide:
pm --help# If installed from npm
pnpm remove -g @jogi47/pm-cli
# If installed from source
cd pm-cli
pnpm unlink --global --filter @jogi47/pm-cliPM-CLI ships with an agent skill that teaches AI coding agents (Claude Code, Cursor, etc.) how to use the pm CLI. Once installed, your agent can run pm-cli commands to manage tasks on your behalf.
Browse the skill at skills.sh or install directly:
npx skills add jogi47/pm-cli -g -y- Complete command reference for all
pmcommands - Task ID format conventions (
PROVIDER-externalId) - Flag and argument details for every subcommand
- Common workflows (check overdue, search, force-refresh, switch workspace)
- Caching behavior and output modes (table vs JSON)
# Search for related skills
npx skills find pm-cli
# Check for updates to installed skills
npx skills check
# Update all installed skills
npx skills updateLearn more about the agent skills ecosystem at skills.sh.
# 1. Connect to Asana
pm connect asana
# Enter your Personal Access Token from https://app.asana.com/0/my-apps
# 2. View your tasks
pm tasks assigned
# 3. Search for tasks
pm tasks search "bug"# Connect to a provider
pm connect asana
pm connect notion
# Disconnect from a provider
pm disconnect asana
# List all providers and their status
pm providers
pm providers --json# List available workspaces
pm workspace
# Switch to a different workspace
pm workspace switch
# Specify provider (default: asana)
pm workspace -s asana# Morning dashboard — overdue, due today, and in-progress tasks
pm today
pm today --source=asana --json
# Provider status and task count statistics
pm summary
pm summary --json# List tasks assigned to you
pm tasks assigned
pm tasks assigned --source=asana # Filter by provider
pm tasks assigned --limit=10 # Limit results
pm tasks assigned --refresh # Bypass cache
pm tasks assigned --json # JSON output
pm tasks assigned --status=todo # Filter by status
pm tasks assigned --sort=priority # Sort by field
pm tasks assigned --plain # Tab-separated, no colors
pm tasks assigned --ids-only # Just task IDs
# List overdue tasks
pm tasks overdue
pm tasks overdue --source=asana
# Search for tasks
pm tasks search "login bug"
pm tasks search "api" --limit=5
# Show task details
pm tasks show ASANA-1234567890
pm tasks show ASANA-123 --json # JSON output
pm tasks show ASANA-123 --open # Open in browser
# Create a task
pm tasks create "Fix login bug"
pm tasks create "Update docs" --source=asana --due=2026-03-01
# Update a task
pm tasks update ASANA-123456 --status in_progress
pm tasks update ASANA-123456 --due 2026-03-15 --title "New title"
# Mark tasks as done
pm done ASANA-123456
pm done ASANA-123456 ASANA-789012 # Batch complete
# Open a task in browser
pm open ASANA-123456# Create a git branch from a task
pm branch ASANA-123456 --prefix feat --checkout
pm branch ASANA-123456 --no-id
# Add a comment to a task
pm comment ASANA-123456 "Fixed in commit abc"pm-cli/
├── packages/
│ ├── core/ # @jogi47/pm-cli-core
│ │ └── src/
│ │ ├── models/ # Task, Plugin interfaces
│ │ ├── managers/ # Auth, Cache, Plugin managers
│ │ └── utils/ # Date, Error, Output utilities
│ │
│ ├── cli/ # @jogi47/pm-cli (main CLI)
│ │ └── src/
│ │ └── commands/ # Oclif commands
│ │
│ ├── plugin-asana/ # @jogi47/pm-cli-plugin-asana
│ │ └── src/
│ │ ├── client.ts # Asana API client
│ │ ├── mapper.ts # Asana → Task mapping
│ │ └── index.ts # Plugin implementation
│ │
│ └── plugin-notion/ # @jogi47/pm-cli-plugin-notion
│
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.base.json
├── vitest.config.ts
└── PUBLISHING.md # npm publishing guide
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Build specific package
pnpm --filter @jogi47/pm-cli-core build
# Run in development mode (watches for changes)
pnpm dev
# Run tests
pnpm test
# Run CLI in dev mode
pnpm pm <command>For publishing to npm, see jogi-docs/PUBLISHING.md.
PM-CLI uses a plugin architecture where each provider (Asana, Notion, etc.) is a separate package that implements the PMPlugin interface:
interface PMPlugin {
name: ProviderType;
displayName: string;
// Lifecycle
initialize(): Promise<void>;
authenticate(credentials: ProviderCredentials): Promise<void>;
disconnect(): Promise<void>;
isAuthenticated(): Promise<boolean>;
// Read Operations
getAssignedTasks(options?: TaskQueryOptions): Promise<Task[]>;
getOverdueTasks(options?: TaskQueryOptions): Promise<Task[]>;
searchTasks(query: string, options?: TaskQueryOptions): Promise<Task[]>;
getTask(externalId: string): Promise<Task | null>;
// Write Operations
createTask(input: CreateTaskInput): Promise<Task>;
updateTask(externalId: string, updates: UpdateTaskInput): Promise<Task>;
completeTask(externalId: string): Promise<Task>;
addComment?(externalId: string, body: string): Promise<void>;
// Optional: Workspace Support
supportsWorkspaces?(): boolean;
getWorkspaces?(): Workspace[];
setWorkspace?(workspaceId: string): void;
}All tasks from different providers are normalized to a common format:
interface Task {
id: string; // e.g., "ASANA-1234567890"
externalId: string; // Original provider ID
title: string;
description?: string;
status: 'todo' | 'in_progress' | 'done';
dueDate?: Date;
assignee?: string;
assigneeEmail?: string;
project?: string;
tags?: string[];
source: 'asana' | 'notion';
url: string;
priority?: 'low' | 'medium' | 'high' | 'urgent';
createdAt?: Date;
updatedAt?: Date;
}Credentials are stored encrypted in:
- macOS:
~/Library/Preferences/pm-cli/ - Linux:
~/.config/pm-cli/ - Windows:
%APPDATA%/pm-cli/
Cache is stored in:
- macOS/Linux:
~/.cache/pm-cli/ - Windows:
%LOCALAPPDATA%/pm-cli/
You can also set credentials via environment variables:
export ASANA_TOKEN=your_token_here
export NOTION_TOKEN=your_token_here- Create a new package:
packages/plugin-<provider>/ - Implement the
PMPlugininterface - Register in
packages/cli/src/init.ts - Add to
ProviderTypeinpackages/core/src/models/task.ts
See packages/plugin-asana/ for a complete example.
- CLI Framework: Oclif
- Language: TypeScript
- Package Manager: pnpm (workspaces)
- Auth Storage: conf (encrypted)
- Caching: lowdb
- Output: cli-table3 + chalk
- Testing: Vitest
MIT