现在位置: 首页 > Pi Agent > 正文

Pi Agent 命令与快捷键注册

通过扩展注册自定义命令、键盘快捷键和 CLI 标志,让 Pi Agent 更好地适应你的工作流。


注册自定义命令

使用 pi.registerCommand() 注册用户可通过 / 调用的命令:

实例

// 基础命令
pi.registerCommand("stats", {
  description: "显示当前会话的统计信息",
  handler: async (args, ctx) => {
    const count = ctx.sessionManager.getEntries().length;
    ctx.ui.notify(`共 ${count} 条消息`, "info");
  }
});

// 带参数自动补全的命令
pi.registerCommand("deploy", {
  description: "部署到指定环境",
  // 定义参数自动补全
  getArgumentCompletions: (prefix) => {
    const envs = ["dev", "staging", "prod"];
    const items = envs
      .filter(e => e.startsWith(prefix))
      .map(e => ({ value: e, label: e }));
    return items.length > 0 ? items : null;
  },
  handler: async (args, ctx) => {
    if (!args) {
      ctx.ui.notify("用法: /deploy <环境名>", "warning");
      return;
    }
    ctx.ui.notify(`正在部署到 ${args} 环境...`, "info");
  },
});

如果多个扩展注册了同名的命令,Pi Agent 会保留所有注册并按加载顺序添加数字后缀,例如 /review:1 和 /review:2。这避免了扩展之间的命名冲突。


命令上下文 (ExtensionCommandContext)

命令的 handler 接收的是 ExtensionCommandContext,它继承自 ExtensionContext,并额外提供以下方法:

方法说明
ctx.waitForIdle()等待 AI 完全空闲(包括重试、压缩、跟进消息全部完成)
ctx.newSession(options)创建新会话,可指定父会话和初始化逻辑
ctx.fork(entryId, options)从指定条目分叉出新会话
ctx.navigateTree(targetId, options)导航到会话树中的指定位置
ctx.switchSession(sessionPath, options)切换到另一个会话文件
ctx.reload()执行与 /reload 相同的热重载流程

会话切换命令示例:

实例

import { SessionManager } from "@earendil-works/pi-coding-agent";

pi.registerCommand("switch", {
  description: "切换到另一个会话",
  handler: async (args, ctx) => {
    // 列出当前项目的所有会话
    const sessions = await SessionManager.list(ctx.cwd);
    if (sessions.length === 0) {
      ctx.ui.notify("没有可用的会话", "warning");
      return;
    }

    // 弹出选择列表
    const choice = await ctx.ui.select(
      "选择要切换的会话:",
      sessions.map(s => s.file),
    );

    if (choice) {
      // 切换到选中的会话
      await ctx.switchSession(choice, {
        withSession: async (ctx) => {
          ctx.ui.notify("已切换会话", "info");
        },
      });
    }
  },
});

会话替换的注意事项:withSession 回调中的 ctx 是一个全新的上下文。不要使用旧的 pi 对象或命令回调中捕获的 ctx,它们在会话替换后会变成无效状态。


注册快捷键

使用 pi.registerShortcut() 注册自定义键盘快捷键:

实例

pi.registerShortcut("ctrl+shift+p", {
  description: "切换 Plan 模式",
  handler: async (ctx) => {
    ctx.ui.notify("已切换 Plan 模式!", "info");
  },
});

快捷键格式为 修饰键+按键,例如 ctrl+shift+p、alt+x 等。


注册 CLI 标志

使用 pi.registerFlag() 为 pi 命令添加自定义 CLI 参数:

实例

pi.registerFlag("plan", {
  description: "以 Plan 模式启动",
  type: "boolean",
  default: false,
});

// 在扩展代码中检查标志值
if (pi.getFlag("plan")) {
  // Plan 模式已启用
  pi.on("before_agent_start", async (event, ctx) => {
    return {
      systemPrompt: event.systemPrompt
        + "\n\nPlan Mode:先提出计划,经用户确认后再执行。",
    };
  });
}

使用方式:

$ pi --plan "帮我实现用户认证功能"

扩展间通信

使用 pi.events 在扩展之间共享事件:

实例

// 扩展 A:发送事件
pi.events.emit("deploy:completed", {
  env: "production",
  timestamp: Date.now(),
});

// 扩展 B:监听事件
pi.events.on("deploy:completed", (data) => {
  console.log(`部署完成:${data.env}`);
});

获取已注册的命令

使用 pi.getCommands() 获取当前会话中所有可用的命令:

实例

const commands = pi.getCommands();

// 按来源分类
const extCommands = commands.filter(
  c => c.source === "extension"
);
const templates = commands.filter(
  c => c.source === "prompt"
);
const skills = commands.filter(
  c => c.source === "skill"
);

每个命令条目包含 name(命令名)、description(描述)、source(来源类型)和 sourceInfo(详细的来源元数据)。


动态工具管理

运行时管理工具的启用和禁用:

实例

// 获取当前启用的工具列表
const active = pi.getActiveTools();
// 例如:["read", "bash", "edit", "write"]

// 获取所有已注册工具的元数据
const all = pi.getAllTools();

// 筛选内置工具
const builtinTools = all.filter(
  t => t.sourceInfo.source === "builtin"
);

// 筛选扩展注册的工具
const extTools = all.filter(
  t => t.sourceInfo.source !== "builtin"
    && t.sourceInfo.source !== "sdk"
);

// 动态启用自定义工具(保留现有工具)
pi.setActiveTools([...new Set([...active, "my_tool"])]);

// 切换到只读模式
pi.setActiveTools(["read", "bash"]);