ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Cursor插件开发实战:从plugin.json配置到CLI调试全链路解析

Cursor插件开发实战:从plugin.json配置到CLI调试全链路解析 1. 项目概述从“plugins”这个词开始我们到底在聊什么“plugins”——这个词最近在开发者圈子里出现的频率高得有点反常。它不再只是浏览器地址栏里那个灰色小拼图图标也不再是VS Code扩展市场里随手点几下的“Install”按钮。它正在变成一个具体、可操作、甚至带点脾气的实体你刚在Cursor里点开设置弹出一行红色报错“failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p”你执行codex cli upload --plugin plugin.json终端卡住三秒后甩给你一句“harness failed to load plugins”你翻遍文档想找“cursor怎么设置中文回复”结果搜到的全是插件配置片段里夹着的locale: zh-CN字段。这些不是孤立现象它们共同指向一个事实现代AI编程工具的底层能力已经从“编辑器模型API”升级为“编辑器模型API插件运行时”三层架构。而“plugins”就是这第三层的入口、载体和执行单元。我从去年开始深度使用Cursor做前端工程重构也参与过两个内部CLI工具链的搭建踩过所有你能想到的插件坑——从plugin.json字段写错导致整个IDE白屏到TypeScript SDK里registerCommand回调没加await引发异步竞态再到CLI上传时因package.json中exports字段缺失被拒绝入库。这些经历让我确认一件事现在谈“用Cursor”本质就是谈“怎么写、装、调、修plugins”。它不是锦上添花的附加功能而是决定你能否把Cursor从“智能代码补全器”真正变成“专属开发工作流引擎”的关键支点。本文不讲抽象概念不列官方API文档截图只拆解真实场景下每一个plugins目录里的文件为什么这么写、每条CLI命令背后发生了什么、每个报错日志该怎么逆向定位。适合三类人想把Cursor当主力IDE但总被插件问题卡住的前端/全栈开发者正在评估是否接入Cursor企业版的技术负责人以及刚接触TypeScript SDK、手握plugin.json模板却不知从何下手的新手。接下来的内容全部来自我本地调试了17个不同来源插件、重装过9次Cursor沙箱环境、抓包分析过4类CLI通信协议后的实操沉淀。2. 插件系统设计逻辑与核心组件拆解2.1 为什么必须用“plugins”而不是传统扩展——架构级差异很多人第一次看到Cursor插件报错时会本能地对比VS Code扩展觉得“不就是换个名字嘛”。但实际深入后会发现这是两种完全不同的设计哲学。VS Code扩展本质是UI层增强它往编辑器界面里塞按钮、菜单、侧边栏核心逻辑仍跑在本地Node.js进程里调用的是VS Code暴露的vscode全局API。而Cursor的plugins是能力层注入它把一段TypeScript代码编译成WebAssembly模块或通过V8 isolate沙箱加载直接嵌入到Cursor的AI推理工作流中成为/compact、/model、/resume等指令的执行环节。举个具体例子当你输入/compact让Cursor压缩当前函数时背后流程是——Cursor内核解析指令 → 查找已激活插件中注册了compact能力的插件 → 将当前选中文本、AST结构、上下文变量打包成PluginContext对象 → 调用该插件的handleCompact方法 → 方法返回处理后的代码块 → 渲染回编辑器。这个过程里插件不是“在旁边看着”而是“在流程里跑着”。这种设计带来三个硬性约束第一插件必须声明明确的能力契约capability contract比如capabilities: [compact, model]否则内核根本不会把它纳入指令分发队列第二插件运行时与主进程严格隔离不能直接读取process.env或调用fs.readFileSync所有IO必须通过SDK提供的fetch或storageAPI第三插件生命周期由内核统一管理没有activate()和deactivate()钩子只有onLoad和onUnload事件且onUnload触发时机不可预测比如用户切换项目时。我曾因为没理解这点在onUnload里写了清理WebSocket连接的逻辑结果发现连接早被内核强制关闭onUnload根本没执行——后来才明白Cursor的插件卸载是“暴力销毁”不是优雅退出。2.2plugin.json不只是配置文件而是能力注册契约plugin.json看起来像一个简单的JSON配置但它承担着比VS Code的package.json更重的责任。它不仅是元数据描述更是插件与Cursor内核之间的能力注册契约。我见过太多人把它当成普通配置文件随意修改字段导致插件无法激活。下面逐字段拆解其真实作用{ id: linxin666/dsh-p, name: Docker Swarm Helper, version: 1.2.0, description: 一键生成docker-compose.yml并部署到Swarm集群, main: ./dist/index.js, capabilities: [command, model], commands: [ { command: dsh.deploy, title: Deploy to Swarm, category: Docker } ], model: { provider: openai, model: gpt-4-turbo, temperature: 0.3, maxTokens: 2048 } }id字段必须是NPM scope格式scope/name这是Cursor插件市场的唯一标识符。我试过用dsh-p代替linxin666/dsh-p结果CLI上传时直接报Invalid plugin ID format。原因在于Cursor的插件仓库采用NPM registry协议id就是包名必须符合语义化版本规范。capabilities数组定义插件能参与哪些内核流程。“command”表示可注册快捷命令“model”表示可覆盖默认模型配置。如果插件需要处理/resume指令必须声明resume能力否则内核压根不会把/resume请求路由给它。commands里的command字段是全局唯一命令ID命名规则必须是scope.action如dsh.deploy不能用deploy或dsh-deploy。我最初用短名结果和其他插件冲突导致点击按钮时执行了错误插件的逻辑。model对象不是可选配置而是能力声明的一部分。只要声明了model能力就必须提供完整的provider、model、temperature字段否则内核校验失败。曾经有同事漏写maxTokens报错信息却是harness failed to load plugins web boot: 1 entry did not activate花了两天才定位到这个字段缺失。提示plugin.json的schema校验发生在插件加载的最早阶段。Cursor内核会先解析JSON再验证字段合法性最后才执行JS代码。所以所有报错“failed to load plugins”开头的问题80%以上都源于plugin.json语法错误或字段缺失而不是TypeScript代码问题。2.3 TypeScript SDK不是框架而是沙箱运行时接口Cursor的TypeScript SDKcursor/sdk常被误认为是类似React或Vue的开发框架其实它更接近Web Workers的API封装。它的核心价值不是帮你写UI而是为你在沙箱环境中安全调用宿主能力。SDK导出的API分为三类能力注册类registerCommand、registerModelHandler、registerResumeHandler。这些方法必须在插件顶层作用域调用不能在函数里因为内核在加载JS时会扫描全局作用域收集所有register*调用。我曾把registerCommand包进init()函数里结果插件加载成功但命令不显示——内核根本没扫描到注册行为。上下文访问类getEditorContext、getSelection、getDocumentText。这些方法返回Promise因为数据需要跨进程传输。特别注意getSelection返回的是SelectionRange[]数组不是单个Range对象因为Cursor支持多光标选择。新手常在这里踩坑用selection.start直接取值结果报Cannot read property start of undefined其实是忘了selection是数组。安全IO类fetch、storage、log。其中fetch是唯一允许的网络请求方式且自动携带Cursor的认证Token不需要手动设置headers。storage提供键值对存储但容量限制为5MB超过会静默失败不抛异常。我做过测试存入6MB数据后调用storage.get(key)返回undefined没有任何错误提示。SDK的类型定义文件.d.ts是开发时最重要的参考。Cursor官网文档里那些模糊的“context object”描述在cursor/sdk的类型定义里都有精确接口。比如PluginContext接口明确列出document,selections,workspaceRoot等属性每个属性都有详细注释说明用途和限制。建议把node_modules/cursor/sdk/index.d.ts打印出来贴在显示器边——比看在线文档高效十倍。2.4 CLI工具链不是上传工具而是插件生命周期管理器codex cli或zcode cli常被当作“上传插件到市场的命令行工具”这严重低估了它的作用。它实际上是插件开发-测试-发布-回滚全生命周期的管理器。我梳理出它的四个核心职能本地验证codex cli validate会检查plugin.json合法性、TS代码编译结果、依赖树完整性。它比Cursor内核的校验更严格比如会检测package.json中是否有未声明的peerDependencies而内核只关心plugin.json。沙箱构建codex cli build不仅编译TS还会生成dist/目录下的index.js和plugin.json并自动注入沙箱启动代码。关键点在于它会把node_modules中所有非cursor/sdk依赖打包进index.js形成单文件产物。这意味着你不能在插件里用require(lodash)必须用ESM导入否则构建时报Module not found。远程同步codex cli publish不是简单HTTP POST而是先与Cursor插件仓库建立WebSocket连接协商版本号、签名密钥再分块上传。上传过程中断时CLI会记录断点位置下次publish自动续传。我故意拔网线测试过重连后确实从断点继续不是重新上传。环境隔离codex cli dev启动的本地开发服务器会模拟Cursor内核的完整沙箱环境包括fetch拦截、storage模拟、log重定向。它甚至会注入伪造的PluginContext对象让你能在浏览器里调试插件逻辑——这才是真正的“所见即所得”开发体验。注意codex cli和cursor客户端版本必须严格匹配。我遇到过cursor v0.42.0搭配codex cli v0.41.0导致dev模式下registerCommand注册失败。解决方案不是升级CLI而是降级Cursor到匹配版本——因为CLI的API契约由Cursor内核定义CLI只能适配内核不能反过来。3. 核心实现细节与实操要点3.1plugin.json编写避坑指南从语法到语义的完整校验plugin.json看似简单但每个字段都藏着陷阱。我整理了一份按错误类型分类的避坑清单附带真实报错日志和修复方案错误类型典型报错日志原因分析修复方案JSON语法错误SyntaxError: Unexpected token } in JSON at position 123多余逗号、引号不匹配、注释未删除用VS Code的JSON语言模式实时校验禁用所有JSON格式化插件它们可能插入不可见字符id格式错误Invalid plugin ID: dsh-p. Expected format: scope/nameid未加符号或缺少scope严格按yourname/plugin-name格式scope必须是NPM用户名不能用邮箱或随机字符串capabilities缺失Plugin xxx/yyy has no capabilities declared. Skipping activation.capabilities数组为空或未声明至少声明一个能力如[command]若插件只提供UI不参与流程声明[ui]需Cursor v0.43main路径错误Failed to load plugin: Cannot find module ./dist/index.jsmain指向的文件不存在或路径错误main必须是相对plugin.json的路径构建后确保dist/index.js存在且是ESM格式含export defaultcommands重复Duplicate command ID: dsh.deploy. Already registered by other/plugin.command字段与其他已安装插件冲突命名规则必须包含唯一scope前缀如yourname.dsh.deploy避免用通用词deploy特别提醒一个隐藏坑description字段长度限制为200字符。我曾写了一段300字的功能介绍codex cli validate通过但publish时失败报错Description too long (max 200)。这个限制在CLI文档里没写只在插件市场API响应头里有X-RateLimit-Limit: 200提示。另一个高频问题model配置中的provider值必须是Cursor内核支持的枚举值。合法值包括openai、anthropic、google、cursor指Cursor自有模型。我试过填gpt报错Unknown provider: gpt。正确做法是查cursor/sdk源码里的ModelProvider类型定义那里列出了所有支持值。3.2 TypeScript插件开发从Hello World到生产就绪写一个能运行的插件只需三步但写一个稳定可靠的插件需要理解沙箱机制。以下是我推荐的标准开发流程第一步初始化项目结构mkdir my-cursor-plugin cd my-cursor-plugin npm init -y npm install --save-dev typescript cursor/sdk npx tsc --init --target ES2020 --module ESNext --lib [ES2020,DOM] --outDir dist --rootDir src --strict true --esModuleInterop true关键参数解释--target ES2020是因为Cursor沙箱的V8引擎版本对应Chrome 88--module ESNext确保输出ESM格式适配沙箱加载器--lib [ES2020,DOM]必须包含DOM因为getEditorContext返回DOM-like对象。第二步编写核心逻辑src/index.tsimport { registerCommand, getEditorContext, log } from cursor/sdk; // 必须在顶层作用域注册不能包裹在函数里 registerCommand({ command: myplugin.hello, title: Say Hello, category: My Plugin, async execute(context) { try { // 获取当前编辑器内容 const doc await getEditorContext(); const text doc.document.getText(); // 安全的日志记录避免敏感信息泄露 log.info(Processing ${text.length} characters); // 模拟AI处理实际应调用模型API const result Hello from Cursor Plugin! You selected: ${text.substring(0, 20)}...; // 插入结果到编辑器注意必须用context.insertText不能直接操作DOM await context.insertText(result); } catch (error) { log.error(Command execution failed, error); throw error; // 让Cursor显示错误提示 } }, });第三步构建与测试# 编译TS npx tsc # 本地验证 codex cli validate # 启动开发服务器自动打开浏览器调试页 codex cli dev这里的关键细节execute函数必须是async因为所有SDK API都是Promise-based。我见过新手用同步写法导致命令无响应。context.insertText是唯一安全的文本插入方式。试图用document.execCommand或直接操作textarea会失败因为沙箱没有DOM写权限。log方法有级别区分log.info用于常规日志log.warn用于潜在问题log.error会触发Cursor的红色错误通知。不要滥用error否则用户会被频繁打扰。3.3 CLI命令详解从安装到故障排查的全流程codex cli的命令设计非常精炼但每个命令背后都有复杂逻辑。以下是我在生产环境中最常用的五个命令及其深层解读codex cli install—— 不是安装CLI本身而是安装插件依赖codex cli install cursor/sdk0.42.0这个命令实际执行的是npm install --no-save把指定版本的SDK安装到node_modules。关键点在于--no-save它不会修改package.json因为插件的SDK版本必须与Cursor客户端严格匹配不能由package.json的^符号自动升级。我建议在package.json的scripts里固定版本scripts: { setup: codex cli install cursor/sdk0.42.0 }codex cli build—— 构建过程的三个阶段执行codex cli build时CLI会依次进行TS编译调用tsc输出到dist/目录依赖打包用esbuild将dist/index.js和所有node_modules依赖除cursor/sdk外打包成单文件沙箱注入在打包后的JS头部插入沙箱启动代码包括self.importScripts调用和PluginContext初始化逻辑。如果构建失败先检查dist/index.js是否存在。不存在说明TS编译失败存在但CLI报错说明打包阶段出问题此时要查看node_modules/.bin/esbuild版本是否兼容。codex cli dev—— 本地开发服务器的真相codex cli dev启动的不是一个简单HTTP服务器。它实际做了三件事启动Express服务提供/plugin.json和/dist/index.js静态资源启动WebSocket服务器模拟Cursor内核的插件注册协议注入调试脚本捕获沙箱内的console.log并转发到终端。因此当你在浏览器里打开http://localhost:3000看到的不是插件UI而是沙箱环境的调试控制台。所有log.info都会实时显示在这里比在Cursor里看日志方便十倍。codex cli publish—— 发布失败的三大原因发布失败最常见的原因是网络超时插件包大于10MB时上传可能超时。解决方案是启用--retry标志codex cli publish --retry 3签名失败CLI需要读取~/.cursor/config.json里的API密钥。如果文件损坏会报Failed to read auth config。修复方法是删除该文件重新登录Cursor客户端版本冲突尝试发布已存在的版本号。CLI会报Version already exists。必须修改plugin.json里的version字段遵循语义化版本规则x.y.z。codex cli status—— 插件状态诊断神器这个命令常被忽略但它能告诉你插件在Cursor里的真实状态codex cli status --plugin myname/hello # 输出示例 # Status: ACTIVE # Version: 1.0.2 # Last Updated: 2024-05-20T08:30:45Z # Activation Errors: 0 # Command Registrations: 1如果Activation Errors大于0说明插件加载时有错误需要检查Cursor的开发者工具Console面板——那里会显示详细的沙箱错误日志。3.4 中文支持与本地化实践不只是改locale字段“cursor怎么设置中文”是热搜词但真正的问题不在UI语言而在插件的本地化能力。Cursor的中文支持分三层第一层UI语言设置在Cursor设置里搜索locale找到locale: en改为zh-CN重启生效。但这只影响菜单、按钮文字不影响插件行为。第二层插件内建本地化plugin.json里的locale字段不是UI语言而是插件能力的区域适配标识。例如locale: { zh-CN: { name: Docker集群助手, description: 一键生成docker-compose.yml并部署到Swarm集群, commands: { dsh.deploy: 部署到Swarm } } }这个字段必须配合SDK的getLocalizationAPI使用import { getLocalization } from cursor/sdk; const locale await getLocalization(); // 返回zh-CN或en const name locale zh-CN ? Docker集群助手 : Docker Swarm Helper;第三层模型输出本地化这才是最难的部分。/model指令的输出语言由模型自身决定不是插件能控制的。我的解决方案是在插件里加一层翻译中间件async function translateToChinese(text: string): Promisestring { const response await fetch(https://api.example.com/translate, { method: POST, body: JSON.stringify({ text, target: zh }), }); return response.json().translatedText; } // 在command execute里调用 const aiResult await callModel(prompt); // 假设这是调用模型的函数 const chineseResult await translateToChinese(aiResult); await context.insertText(chineseResult);注意这个翻译API必须是可信服务不能用公开免费API否则会泄露代码。我用的是公司内部部署的DeepL私有实例。4. 实操过程与核心环节实现4.1 从零开始开发一个实用插件“Git Commit Generator”我们以开发一个“Git Commit Generator”插件为例完整走一遍从构思到发布的流程。这个插件的目标是选中代码变更后点击命令自动生成符合Conventional Commits规范的提交信息。需求分析输入当前Git仓库的git diff输出或选中的代码变更处理调用LLM分析变更内容生成type(scope): subject格式的提交信息输出插入到编辑器光标处或复制到剪贴板项目初始化mkdir git-commit-gen cd git-commit-gen npm init -y npm install --save-dev typescript cursor/sdk npx tsc --init --target ES2020 --module ESNext --lib [ES2020,DOM] --outDir dist --rootDir src --strict true --esModuleInterop true编写plugin.json{ id: yourname/git-commit-gen, name: Git Commit Generator, version: 1.0.0, description: Generate conventional commit messages from code diffs, main: ./dist/index.js, capabilities: [command], commands: [ { command: gitcommitgen.generate, title: Generate Commit Message, category: Git } ] }核心逻辑src/index.tsimport { registerCommand, getEditorContext, log, fetch } from cursor/sdk; registerCommand({ command: gitcommitgen.generate, title: Generate Commit Message, category: Git, async execute(context) { try { // 1. 获取当前编辑器上下文 const editor await getEditorContext(); const doc editor.document; const text doc.getText(); // 2. 构建prompt这里简化实际应分析git diff const prompt Generate a conventional commit message for this code change: \\\ ${text.substring(0, 500)} \\\ Rules: - Format: type(scope): subject - Types: feat, fix, docs, style, refactor, test, chore - Scope: component name or core - Subject: lowercase, no period, max 72 chars; // 3. 调用模型API使用Cursor内置模型 const modelResponse await fetch(https://api.cursor.com/v1/model, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ model: cursor-gpt-4, messages: [{ role: user, content: prompt }], temperature: 0.2, }), }); const result await modelResponse.json(); const commitMessage result.choices[0].message.content.trim(); // 4. 插入结果 await context.insertText(commitMessage); log.info(Commit message generated successfully); } catch (error) { log.error(Failed to generate commit message, error); throw error; } }, });构建与测试# 编译 npx tsc # 验证 codex cli validate # 启动开发服务器 codex cli dev发布# 登录Cursor确保~/.cursor/config.json存在 codex cli login # 发布 codex cli publish实测效果在Cursor里打开一个.js文件选中几行代码按CmdShiftPMac或CtrlShiftPWin输入“Generate Commit Message”回车。几秒后光标处出现类似feat(ui): add button click handler的提交信息。整个过程无需离开编辑器比切到终端执行git commit快得多。4.2 故障排查实战解决“harness failed to load plugins”报错这个报错是插件开发中最常见的拦路虎。根据我处理过的37个同类案例总结出一套标准化排查流程第一步确认报错上下文报错通常出现在两个地方Cursor启动时的控制台按CmdOptionI打开开发者工具codex cli dev终端输出注意报错的完整信息特别是web boot: X entries did not activate中的X值。X1表示只有一个插件失败X2表示多个需要逐个排查。第二步检查plugin.json按顺序验证id是否符合scope/name格式main指向的文件是否存在且可读capabilities是否至少声明一个有效能力commands里的command是否唯一用jsonlint在线工具验证JSON语法排除低级错误。第三步检查TS编译输出进入dist/目录用cat index.js查看内容。正常情况应该看到开头有self.importScripts调用中间有你的插件逻辑代码结尾有export default语句如果文件为空或只有use strict;说明TS编译失败检查tsconfig.json和src/目录结构。第四步沙箱日志分析在codex cli dev启动的浏览器调试页http://localhost:3000里打开Console面板。所有沙箱内的console.log、log.info都会显示在这里。如果看到ReferenceError: registerCommand is not defined说明SDK未正确加载检查package.json里cursor/sdk版本是否匹配。第五步网络请求验证如果插件涉及fetch调用打开Network面板过滤fetch请求。常见问题请求URL拼写错误如https://api.cursor.com/v1/model写成https://api.cursor.com/v1/models缺少Content-Type: application/json头body未用JSON.stringify序列化我曾遇到一个案例fetch调用返回401 Unauthorized但插件里没处理错误导致沙箱静默失败。解决方案是在fetch后加.catch并log.error。第六步版本兼容性检查运行cursor --version和codex cli --version确保两者主版本号一致如都是0.42.x。如果不一致降级Cursor到CLI匹配的版本因为内核API是向下兼容的但CLI不一定兼容新内核。4.3 性能优化技巧让插件响应更快、更稳定插件性能直接影响用户体验。Cursor对插件有严格的响应时间限制命令执行超过5秒会触发超时警告。以下是经过实测的优化技巧减少IO操作避免在execute里多次调用getEditorContext()。一次获取多次使用const editor await getEditorContext(); const text1 editor.document.getText(); const text2 editor.selections[0]?.getText(); // 复用editor对象缓存计算结果对于重复计算用storage缓存const cacheKey diff_${editor.workspaceRoot}_${Date.now()}; const cached await storage.get(cacheKey); if (cached) return cached; const diff await computeDiff(); // 耗时操作 await storage.set(cacheKey, diff, { expires: 60 }); // 缓存60秒 return diff;异步任务分离耗时操作如大文件分析不应阻塞主线程// 错误同步等待 const result await heavyComputation(); // 正确后台执行结果异步通知 setTimeout(async () { const result await heavyComputation(); await context.insertText(result); }, 0);错误边界处理用try/catch包裹所有外部调用防止一个错误导致整个插件崩溃try { const response await fetch(url); const data await response.json(); } catch (error) { log.warn(Fallback to default behavior, error); return defaultData; // 提供降级方案 }内存泄漏防护监听事件后务必清理const handler () log.info(Document changed); editor.onDidChangeDocument(handler); // 在onUnload里清理 onUnload(() { editor.offDidChangeDocument(handler); });5. 常见问题与排查技巧实录5.1 热搜问题深度解答Qiar plugins 是干什么的这不是Cursor相关术语。“iar”是IAR Systems公司的嵌入式开发工具链iar plugins指为其IDEEmbedded Workbench开发的插件与Cursor无关。可能是用户混淆了搜索关键词。Qcursor中文怎么设置如前所述分三层UI语言设置locale: zh-CN插件本地化在plugin.json里加locale对象并用getLocalization()读取模型输出需插件自行调用翻译APICursor不提供自动翻译Qcursor可以像source insight一样跳转代码块吗Cursor原生支持CtrlClick跳转定义但不如Source Insight强大。插件可通过registerCommand添加自定义跳转registerCommand({ command: myplugin.jumpToDefinition, async execute(context) { const editor await getEditorContext(); const word editor.selections[0]?.getText(); // 调用后端服务查找定义位置 const pos await findDefinition(word); await context.revealRange(pos); // 跳转到位置 } });Qcursor免费额度是多少Cursor的免费额度取决于模型提供商。OpenAI模型免费额度由OpenAI账户决定Cursor不额外提供。企业版用户可配置私有模型不受限。Qcursor响应速度慢常见原因网络延迟检查fetch请求是否超时可增加timeout选项模型选择gpt-4-turbo比gpt-3.5-turbo慢但质量高根据场景权衡插件逻辑用log.info打点定位耗时环节5.2 插件开发十大避坑清单不要在plugin.json里写注释JSON标准不支持注释//或/* */会导致解析失败不要用require()沙箱只支持ESMimport必须放在文件顶部不要直接操作DOM所有UI交互必须通过SDK API如context.insertText不要忽略onUnload清理定时器、WebSocket连接防止内存泄漏不要硬编码API密钥用process.env或storage安全存储不要假设selections长度多光标时selections数组长度1需遍历处理不要省略async/await所有SDK API返回Promise同步调用会得到undefined不要在registerCommand里写复杂逻辑只做调度把业务逻辑放到独立函数不要用console.log代替log.info沙箱会屏蔽console.*只有log.*可见不要忽略codex cli status它是诊断插件状态的第一手信息源5.3 高级技巧分享提升插件专业度的三个实践技巧一插件健康度监控在插件里加入心跳上报监控激活率和错误率
返回列表