diff --git a/CLAUDE.md b/CLAUDE.md index 8e4f477..1819e9c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,12 +12,17 @@ A CLI tool for OmniFocus on macOS that uses JavaScript for Automation (JXA) to i ```bash bun install # Install dependencies bun run build # Build TypeScript to dist/ -bun run dev # Watch mode for development -bun link # Link binary for local testing (creates `of` command) +bun run dev # Run CLI directly via bun (no build required) +bun run link # Build + link binary globally (creates `of` command) +bun run lint # Lint with oxlint +bun run format # Format with Biome (writes changes) +bun run typecheck # Type-check without emitting +bun run test # Run all tests with vitest +bun run vitest run # Run a single test file ``` ### Testing the CLI -After `bun link`, use `of` command globally: +After `bun run link`, use `of` command globally: ```bash of task list # List tasks of project list # List projects @@ -50,6 +55,10 @@ Files: - `src/commands/perspective.ts` - Perspective switching and viewing - `src/commands/search.ts` - Task search - `src/commands/tag.ts` - Tag management and statistics +- `src/commands/folder.ts` - Folder hierarchy (list, view) +- `src/commands/mcp.ts` + `src/mcp/server.ts` - MCP server (`of mcp`) exposing all OmniFocus operations as MCP tools via stdio transport +- `src/lib/display.ts` - Formatting helpers (estimates, dates, tags) +- `src/lib/output.ts` - `outputJson()` for all command output (supports compact mode) ### Type Definitions diff --git a/src/commands/task.ts b/src/commands/task.ts index fa39205..4014be5 100644 --- a/src/commands/task.ts +++ b/src/commands/task.ts @@ -39,6 +39,7 @@ export function createTaskCommand(): Command { .option('-t, --tag ', 'Add tags') .option('-d, --due ', 'Set due date') .option('-D, --defer ', 'Set defer date') + .option('-P, --planned ', 'Set planned date') .option('-f, --flagged', 'Flag the task') .option('-e, --estimate ', 'Estimated time in minutes', parseInt) .action( @@ -51,6 +52,7 @@ export function createTaskCommand(): Command { tags: options.tag, due: options.due ? parseDateTime(options.due) : undefined, defer: options.defer ? parseDateTime(options.defer) : undefined, + planned: options.planned ? parseDateTime(options.planned) : undefined, flagged: options.flagged, estimatedMinutes: options.estimate, }); @@ -67,6 +69,7 @@ export function createTaskCommand(): Command { .option('-t, --tag ', 'Replace tags') .option('-d, --due ', 'Set due date') .option('-D, --defer ', 'Set defer date') + .option('-P, --planned ', 'Set planned date') .option('-f, --flag', 'Flag the task') .option('-F, --unflag', 'Unflag the task') .option('-c, --complete', 'Mark as completed') @@ -86,6 +89,9 @@ export function createTaskCommand(): Command { ...(options.defer !== undefined && { defer: options.defer ? parseDateTime(options.defer) : null, }), + ...(options.planned !== undefined && { + planned: options.planned ? parseDateTime(options.planned) : null, + }), ...(options.flag && { flagged: true }), ...(options.unflag && { flagged: false }), ...(options.complete && { completed: true }), diff --git a/src/lib/omnifocus.ts b/src/lib/omnifocus.ts index 0eea0c7..8c83f40 100644 --- a/src/lib/omnifocus.ts +++ b/src/lib/omnifocus.ts @@ -50,6 +50,7 @@ export class OmniFocus { tags: tagNames, defer: task.deferDate ? task.deferDate.toISOString() : null, due: task.dueDate ? task.dueDate.toISOString() : null, + planned: task.plannedDate ? task.plannedDate.toISOString() : null, estimatedMinutes: task.estimatedMinutes || null, completionDate: task.completionDate ? task.completionDate.toISOString() : null, added: task.added ? task.added.toISOString() : null, @@ -371,6 +372,13 @@ export class OmniFocus { : 'task.dueDate = null;' ); } + if (options.planned !== undefined) { + updates.push( + options.planned + ? `task.plannedDate = new Date(${JSON.stringify(options.planned)});` + : 'task.plannedDate = null;' + ); + } if (options.project !== undefined && options.project) { updates.push(` const targetProject = findByName(flattenedProjects, "${this.escapeString(options.project)}", "Project"); @@ -458,6 +466,7 @@ export class OmniFocus { ${options.estimatedMinutes ? `task.estimatedMinutes = ${options.estimatedMinutes};` : ''} ${options.defer ? `task.deferDate = new Date(${JSON.stringify(options.defer)});` : ''} ${options.due ? `task.dueDate = new Date(${JSON.stringify(options.due)});` : ''} + ${options.planned ? `task.plannedDate = new Date(${JSON.stringify(options.planned)});` : ''} ${options.tags && options.tags.length > 0 ? `assignTags(task, ${JSON.stringify(options.tags)});` : ''} return JSON.stringify(serializeTask(task)); diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 56f6720..4d98174 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -72,6 +72,7 @@ server.tool( tags: z.array(z.string()).optional().describe('Tags to assign'), defer: z.string().optional().describe('Defer date (ISO 8601)'), due: z.string().optional().describe('Due date (ISO 8601)'), + planned: z.string().optional().describe('Planned date (ISO 8601)'), flagged: z.boolean().optional().describe('Flag the task'), estimatedMinutes: z.number().optional().describe('Estimated duration in minutes'), }, @@ -89,6 +90,7 @@ server.tool( tags: z.array(z.string()).optional().describe('Replace tags'), defer: z.string().optional().describe('New defer date (ISO 8601)'), due: z.string().optional().describe('New due date (ISO 8601)'), + planned: z.string().optional().describe('New planned date (ISO 8601)'), flagged: z.boolean().optional().describe('Flag/unflag the task'), estimatedMinutes: z.number().optional().describe('New estimated duration'), completed: z.boolean().optional().describe('Mark complete/incomplete'), diff --git a/src/types.ts b/src/types.ts index c058dd8..06f316a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,7 @@ export interface Task { tags: string[]; defer: string | null; due: string | null; + planned: string | null; estimatedMinutes: number | null; completionDate: string | null; added: string | null; @@ -49,6 +50,7 @@ export interface CreateTaskOptions { tags?: string[]; defer?: string; due?: string; + planned?: string; flagged?: boolean; estimatedMinutes?: number; } @@ -58,8 +60,9 @@ export interface UpdateTaskOptions { note?: string; project?: string; tags?: string[]; - defer?: string; - due?: string; + defer?: string | null; + due?: string | null; + planned?: string | null; flagged?: boolean; estimatedMinutes?: number; completed?: boolean;