# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

- `npm run build` - 编译 TypeScript 并复制脚本文件到 dist/ 目录
- `npm run dev` - 开发模式，监听文件变化并实时编译
- `npm start` - 运行编译后的服务器
- `npm run copy-files` - 复制 OmniFocus 脚本文件到 dist 目录
- `npm test` - 运行测试（当前只是占位符，需要手动测试）

## Architecture Overview

这是一个基于 TypeScript 的 Model Context Protocol (MCP) 服务器，专门用于 OmniFocus 集成。
你给用户输出的东西,不要包含表情符号,这是一个严肃的项目

### 核心架构层次：
1. **服务器层** (`src/server.ts`) - MCP 服务器主入口，注册所有工具
2. **工具定义层** (`src/tools/definitions/`) - 定义工具的 schema 和处理器
3. **基础功能层** (`src/tools/primitives/`) - 实际的业务逻辑实现
4. **脚本执行层** (`src/utils/scriptExecution.ts`) - JXA/OmniJS 脚本执行引擎
5. **OmniFocus 脚本层** (`src/utils/omnifocusScripts/`) - 具体的 OmniFocus 操作脚本


### 工具分类：
- **数据库管理**: dump_database
- **任务管理**: add_omnifocus_task, remove_item, edit_item, get_task_by_id
- **项目管理**: add_project
- **批量操作**: batch_add_items, batch_remove_items
- **透视图**: get_inbox_tasks, get_flagged_tasks, get_forecast_tasks, get_tasks_by_tag
- **高级过滤**: filter_tasks
- **自定义透视图**: list_custom_perspectives (基于 OmniJS Perspective.Custom.all API)
- **完成任务**: get_today_completed_tasks

## 重要约束

- 不要使用 AppleScript 来解决问题，使用 JXA (JavaScript for Automation) 或 OmniJS
- 仅支持 macOS 平台（依赖 OmniFocus 应用）
- 需要 Node.js 18+ 环境
- 所有脚本文件必须在构建时复制到 dist/ 目录
- **CLAUDE.md 是私人约定文件，不要提交到 git**

## 代码结构规范

### 添加新工具的步骤：
1. 在 `src/tools/definitions/` 创建工具定义文件
2. 在 `src/tools/primitives/` 实现具体功能
3. 在 `src/server.ts` 中注册新工具
4. 如需要，在 `src/utils/omnifocusScripts/` 添加相应的 JXA 脚本

### 工具定义模式：
```typescript
// 在 definitions/ 文件中
export const schema = z.object({...});
export const handler = async (args: any) => {
  // 调用 primitives/ 中的实现
};
```

## 脚本执行机制

使用 `src/utils/scriptExecution.ts` 来执行 JXA/OmniJS 脚本：
- JXA 脚本通过 `osascript -l JavaScript` 执行
- OmniJS 脚本通过 OmniFocus 的脚本引擎执行
- 脚本文件必须保存在 `src/utils/omnifocusScripts/` 目录

## 类型定义

所有类型定义在 `src/types.ts` 中，包括：
- OmniFocus 对象类型（Task, Project, Context等）
- 工具参数和返回值类型
- 脚本执行结果类型

## 踩坑记录和解决方案

### ❌ 重大错误：executeOmniFocusScript 返回值处理

**错误现象**: 工具返回 "脚本执行返回了无效的结果"

**问题根源**: `executeOmniFocusScript` 函数可能返回两种类型：
1. **JSON 对象** - 当脚本执行成功且 JSON.parse 成功时
2. **字符串** - 当 JSON.parse 失败时，返回原始 stdout

**我犯的错误**:
```typescript
// ❌ 只处理了字符串情况，忽略了对象类型
if (typeof result === 'string') {
  const data = JSON.parse(result);
  // 处理...
}
throw new Error('脚本执行返回了无效的结果'); // 对象类型会跳到这里！
```

**正确处理方式**:
```typescript
// ✅ 同时处理字符串和对象两种情况
let data: any;

if (typeof result === 'string') {
  try {
    data = JSON.parse(result);
  } catch (parseError) {
    throw new Error(`解析字符串结果失败: ${result}`);
  }
} else if (typeof result === 'object' && result !== null) {
  data = result; // 直接使用已解析的对象
} else {
  throw new Error(`脚本执行返回了无效的结果类型: ${typeof result}, 值: ${result}`);
}
```

**调试技巧**:
- 先用 console.log 打印 result 的类型和值
- 添加详细的错误信息，包含实际的返回值
- 不要假设返回类型，要做完整的类型检查

### ✅ OmniJS 脚本最佳实践

**统一的脚本格式模板**:
```javascript
(() => {
  try {
    // 获取数据的业务逻辑
    const customPerspectives = Perspective.Custom.all;
    
    // 格式化结果
    const perspectives = customPerspectives.map(p => ({
      name: p.name,
      identifier: p.identifier
    }));
    
    // 返回统一格式
    const result = {
      success: true,
      count: perspectives.length,
      perspectives: perspectives
    };
    
    return JSON.stringify(result);
    
  } catch (error) {
    // 错误处理
    const errorResult = {
      success: false,
      error: error.message || String(error),
      count: 0,
      perspectives: []
    };
    
    return JSON.stringify(errorResult);
  }
})();
```

**关键要点**:
- 用 IIFE `(() => {})()` 包装避免全局污染
- 统一返回 JSON 字符串格式
- 必须包含 `success` 字段指示成功/失败
- 错误处理要完整，包含错误信息
- 数据结构要一致，便于 TypeScript 处理

### 🔧 工具开发流程教训

**正确的开发顺序**:
1. 先写 OmniJS 脚本并单独测试
2. 实现 primitive 函数，处理返回值
3. 创建工具定义，定义 schema
4. 在 server.ts 中注册新工具
5. 编译测试，逐步调试

**重要提醒**:
- 遇到错误先加详细的 console.log 调试日志
- 不要积累太多问题，每一步都要测试
- 错误信息要包含实际的数据类型和值
- primitive 函数要处理所有可能的返回类型

### 🎯 成功案例：list_custom_perspectives

基于 `Perspective.Custom.all` API 成功实现的工具：
- OmniJS 脚本：使用原生 API 获取透视列表
- 支持 simple 和 detailed 两种格式
- 完整的错误处理和类型检查
- 编译和运行都正常