环境搭建与调试

本文档带你从零开始搭建 Pi 的本地开发环境,并学会使用断点调试追踪代码执行。

第一步:克隆与安装

# 1. 克隆仓库
git clone https://github.com/earendil-works/pi-mono.git
cd pi-mono

# 2. 安装依赖(--ignore-scripts 不执行依赖的生命周期脚本,安全)
npm install --ignore-scripts

# 3. 验证安装
npm run check

npm run check 执行完整的质量检查(TypeScript 类型检查 + Biome lint),不会运行测试。这是每次修改代码后的标准验证命令。

第二步:理解目录结构

pi-mono/
├── packages/
│   ├── ai/                    # pi-ai:统一 LLM API
│   │   ├── src/
│   │   │   ├── types.ts       # 核心类型定义(Model, Context, Tool)
│   │   │   ├── stream.ts      # 流式调用入口
│   │   │   ├── complete.ts    # 非流式调用入口
│   │   │   ├── providers/     # 各 LLM 提供商实现
│   │   │   ├── api-registry.ts# 提供商注册表
│   │   │   └── utils/         # 事件流、工具校验等工具
│   │   └── scripts/
│   │       └── generate-models.ts  # 模型元数据生成
│   │
│   ├── agent/                 # pi-agent-core:智能体运行时
│   │   └── src/
│   │       ├── agent.ts       # Agent 类
│   │       ├── agent-loop.ts  # ★ 核心循环 runAgentLoop
│   │       ├── types.ts       # Agent 类型
│   │       └── proxy.ts       # 工具代理层
│   │
│   ├── coding-agent/          # pi-coding-agent:CLI 应用层
│   │   └── src/
│   │       ├── cli.ts         # ★ CLI 入口(第 17 行调用 main)
│   │       ├── main.ts        # ★ 主流程:参数解析 → 模式选择 → 启动
│   │       ├── config.ts      # 配置系统
│   │       ├── core/
│   │       │   ├── agent-session.ts       # AgentSession:业务逻辑中枢
│   │       │   ├── agent-session-runtime.ts  # 服务组装
│   │       │   ├── session-manager.ts     # 会话持久化
│   │       │   ├── extensions/            # 扩展系统
│   │       │   └── tools/                 # 内置工具
│   │       └── modes/
│   │           ├── interactive/           # ★ 交互模式
│   │           │   └── interactive-mode.ts # TUI 主循环
│   │           ├── print-mode.ts          # 非交互输出
│   │           └── rpc/                   # RPC 模式
│   │
│   └── tui/                   # pi-tui:终端 UI 库
│       └── src/
│           ├── tui.ts         # TUI 主类(差分渲染)
│           ├── terminal.ts    # 终端抽象层
│           ├── components/    # UI 组件
│           │   ├── editor.ts  # 编辑器组件
│           │   └── markdown.ts# Markdown 渲染
│           ├── keys.ts        # 按键解码(43K 行,最大的文件)
│           └── autocomplete.ts# 模糊搜索/补全

├── tsconfig.json              # TypeScript 配置
├── package.json               # Monorepo 根配置
└── AGENTS.md                  # 开发规则

第三步:直接运行源码(无需构建)

Pi 的源码使用 TypeScript,但不需要先编译。使用 tsx 直接运行:

# 方法一:使用 npx tsx
npx tsx packages/coding-agent/src/cli.ts --help

# 方法二:如果安装了 tsx 全局
tsx packages/coding-agent/src/cli.ts -p "Say hello"

不要运行 npm run build。Pi 的设计是开发时直接运行 .ts 源码,发布时通过打包脚本处理。

第四步:VS Code 断点调试

配置 launch.json

在项目根目录创建 .vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Pi Interactive",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "tsx",
      "program": "${workspaceFolder}/packages/coding-agent/src/cli.ts",
      "args": [],
      "console": "integratedTerminal",
      "sourceMaps": true,
      "env": {
        "ANTHROPIC_API_KEY": "你的key(可选)"
      }
    },
    {
      "name": "Debug Pi Print Mode",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "tsx",
      "program": "${workspaceFolder}/packages/coding-agent/src/cli.ts",
      "args": ["-p", "Summarize this repo"],
      "console": "integratedTerminal"
    }
  ]
}

关键断点位置

断点位置文件行号观察什么
CLI 入口packages/coding-agent/src/cli.ts第 17 行process.argv 参数
主流程开始packages/coding-agent/src/main.tsmain() 函数参数解析结果、appMode 选择
TUI 创建packages/coding-agent/src/modes/interactive/interactive-mode.ts构造函数TUI 初始化参数
TUI 启动同上init() 方法中 ui.start()UI 布局构建
主循环同上run() 方法中 while(true)getUserInput() 等待输入
消息发送packages/coding-agent/src/core/agent-session.tsprompt() 方法消息预处理、skill 展开
Agent 循环packages/agent/src/agent-loop.tsrunLoop() 函数★ 核心循环入口
LLM 调用同上streamAssistantResponse()LLM 请求参数和流式响应

调试技巧

// 在任意位置添加临时日志(调试完后删除)
console.log('[DEBUG]', JSON.stringify(obj, null, 2));

// 使用 Node.js 内置 util 查看大对象
import { inspect } from 'node:util';
console.log('[DEBUG]', inspect(obj, { depth: 3, colors: true }));

第五步:理解 TypeScript 执行方式

Pi 使用 Node.js strip-only 模式(通过 tsx),这意味着:

  1. TypeScript 类型注解被直接擦除,不生成 JS
  2. 不能使用该模式的 TS 特性(如 enumnamespace、参数属性)
  3. 所有类必须用显式字段 + 构造函数赋值,而非参数属性

示例(正确的写法):

// ✅ 正确:显式字段 + 构造函数赋值
class MyComponent {
  private width: number;
  private title: string;

  constructor(width: number, title: string) {
    this.width = width;
    this.title = title;
  }
}

// ❌ 错误:参数属性(需要 JS emit)
class MyComponent {
  constructor(
    private width: number,
    private title: string,
  ) {}
}

第六步:运行测试

# 运行非 E2E 测试套件
./test.sh

# 运行单个测试
node ../../node_modules/vitest/dist/cli.js --run test/specific.test.ts

不要直接运行 npm test。完整套件包含 E2E 测试,需要 API key 且会消耗 token。

下一步

从终端到 TUI — 理解输入 pi 后发生了什么