skills协议:面向开发者的可插拔能力调度标准
2026/9/9 5:06:02 网站建设 项目流程

1. “skills”不是功能模块,而是一套可插拔的开发者能力调度协议

最近在多个技术社区和 CLI 工具链中高频出现的skills,绝非某个具体软件、App 或插件的名称——它本质上是一种轻量级、声明式、面向终端开发者的能力注册与调用协议。你看到的npx skill add dietrichgebert/ponytaildsh plugin --profile web add dshmarket、甚至claude code skills这类组合词,背后共用的正是同一套设计哲学:把开发者日常依赖的工具链能力(如代码生成、环境检测、模板注入、MCP 工具调用、Flutter 插件加载逻辑等)抽象为一个个可独立注册、按需启用、彼此隔离的“技能单元”。

这个概念的爆发,直接源于三个现实痛点的交汇:
第一,现代前端/跨平台开发中,npx已成为事实上的“零安装执行层”,但npx <pkg>每次都拉取全新包、无状态、难复用,导致重复下载、版本漂移、调试断点丢失;
第二,VS Code 插件生态虽丰富,但配置分散(settings.json + launch.json + tasks.json + extensions)、启动慢、无法跨 IDE 复用,且多数插件只解决“界面交互”,不解决“命令行自动化”;
第三,像 Claude Code、Dsh、Opencode 这类 AI 编程助手,其真正价值不在对话窗口里写几行代码,而在于能否无缝嵌入开发者当前工作流——比如在git commit前自动补全 conventional commit message,在flutter build失败时精准定位是 Gradle 插件加载顺序问题还是 native password plugin 缺失。

提示:“skills” 协议的核心动作只有两个:add(注册)和invoke(调用)。它不替代 npm/yarn/pnpm,也不替代 VS Code Extension Host,而是站在它们之上,做一层语义对齐。就像 USB-C 接口不定义充电功率,但统一了物理连接方式——skills定义的是“能力如何被发现、如何被参数化、如何被安全沙箱执行”。

我第一次意识到它的存在,是在调试一个 Flutter Web 项目时。error: failed to apply plugin 'dev.flutter.flutter-gradle-plugin'报错后,常规思路是查build.gradle、升级 Gradle 版本、清理.gradle缓存。但我试了dsh plugin tree,输出了一棵清晰的能力依赖树,其中flutter-gradle-plugin节点旁标注着(loaded via skills://flutter/gradle-v8.4)。顺着这个 URI 找到本地~/.skills/flutter/gradle-v8.4/skill.js,打开一看,里面封装的正是 Gradle 插件加载逻辑 + 版本兼容性检查 + 错误上下文增强。它没改任何 Flutter 源码,却让报错信息从一行红字变成带修复建议的结构化输出。

这说明,“skills” 的真实身份是开发者工作流的元操作系统(Meta-OS):它不直接写业务代码,但决定了“当某类错误发生时,该由谁来响应”“当用户输入skill lint时,该调用 ESLint 还是 Biome 还是自定义规则集”。它把原本散落在 shell alias、Makefile、VS Code task、CI script 中的“操作意图”,收束成统一的、可版本化、可共享、可审计的技能定义。

所以,当你在热搜里看到“claude code skills”或“vscode配置claude code”,本质不是在装一个叫 “Claude Skills” 的新插件,而是在本地注册一组技能:claude-generate-test(调用 Claude API 生成 Jest 测试)、claude-explain-error(解析process exited with code 3221225477这类 Windows 内存访问违规错误)、claude-rewrite-commit(基于当前 git diff 重写 commit message)。这些技能的实现可以是 TypeScript 函数、Shell 脚本、Python 小程序,只要它遵循skills协议的入口规范(即导出execute(context: SkillContext): Promise<SkillResult>),就能被统一调度。

这也解释了为什么npx skill add成为高频命令——它不是在安装软件,而是在本地技能注册中心写入一条记录:{ id: "dietrichgebert/ponytail", version: "0.3.1", entry: "https://cdn.jsdelivr.net/npm/ponytail@0.3.1/dist/skill.js" }。后续所有skill run ponytail --input=xxx调用,都基于这条注册信息动态加载、沙箱执行、缓存结果。整个过程不污染全局 node_modules,不修改系统 PATH,不写入 registry,完全符合现代 CLI 工具“按需加载、用完即焚”的安全范式。

2. 技能注册的本质:一份带执行约束的 JSON+JS 元数据契约

npx skill add dietrichgebert/ponytail看似简单,但背后触发的是一整套元数据解析、远程资源校验、本地沙箱初始化流程。要真正理解skills协议,必须拆解它的注册契约——这不是一个随意的 npm 包发布,而是一份严格定义的、包含执行边界与安全策略的元数据契约。

首先,dietrichgebert/ponytail并非 GitHub 仓库名直译,而是skills协议定义的Skill ID 格式{namespace}/{name}。其中namespace通常对应 GitHub 用户/组织名,name是技能标识符。协议规定,当执行npx skill add {id}时,CLI 工具会按固定优先级查找该技能的元数据文件:

  1. 首先尝试https://raw.githubusercontent.com/{namespace}/{name}/main/skill.json
  2. 若不存在,则 fallback 到https://raw.githubusercontent.com/{namespace}/{name}/master/skill.json
  3. 若仍失败,则尝试https://registry.skills.dev/{namespace}/{name}/latest/skill.json(官方技能注册中心)

这个skill.json文件,就是技能的“身份证”和“说明书”。以ponytail为例,其精简版内容如下:

{ "id": "dietrichgebert/ponytail", "version": "0.3.1", "name": "Ponytail", "description": "A lightweight skill for generating TypeScript interfaces from JSON samples", "author": "Dietrich Gebert", "license": "MIT", "entry": "./dist/skill.js", "runtime": "nodejs:18.17.0", "permissions": ["fs:read", "network:https://api.example.com"], "inputs": [ { "name": "jsonSample", "type": "string", "required": true, "description": "Raw JSON string or file path" } ], "outputs": [ { "name": "typescriptInterface", "type": "string" } ], "tags": ["typescript", "json", "codegen"] }

这份 JSON 不仅描述功能,更关键的是定义了执行约束

  • runtime字段强制指定了 Node.js 版本(18.17.0),这意味着技能作者已在此版本下完成全部测试,CLI 工具在执行前会验证本地 Node 版本是否匹配,不匹配则拒绝运行或自动启用 nvm/nodenv 切换——这直接解决了error: your project requires Gradle 8.4 but you have 8.2这类环境不一致问题。
  • permissions是安全核心。fs:read表示该技能可读取本地文件,但默认禁止写入;network:https://api.example.com表示只能访问指定域名,其他网络请求(包括http://localhost:3000)会被拦截。这比传统 CLI 工具的--allow-read更精细,也比浏览器 CORS 策略更贴近开发者实际需求。
  • inputsoutputs构成类型契约。CLI 工具在调用前会校验传入参数是否符合jsonSampletyperequired规则;执行后会验证返回值是否包含typescriptInterface字段且类型为 string。这种契约式设计,让技能调用具备了类似 TypeScript 接口的可靠性,避免了npx create-react-app那种“跑起来再说”的不可控感。

entry指向的./dist/skill.js,则是技能的执行体。它必须导出一个标准函数:

// dist/skill.js (TypeScript 编译后) export async function execute(context) { const { inputs, logger, fs, network } = context; // 1. 使用受控的 fs 模块读取文件(若 inputs.jsonSample 是路径) let jsonContent; if (inputs.jsonSample.endsWith('.json')) { jsonContent = await fs.readFile(inputs.jsonSample, 'utf8'); } else { jsonContent = inputs.jsonSample; } // 2. 使用受控的 network 模块(若需要调用外部服务) // const result = await network.fetch('https://api.example.com/generate', { // method: 'POST', body: jsonContent // }); // 3. 生成 TypeScript interface(纯内存操作,无副作用) const tsInterface = generateInterfaceFromJson(jsonContent); // 4. 返回符合 outputs 契约的结果 return { outputs: { typescriptInterface: tsInterface }, metadata: { generatedAt: new Date().toISOString() } }; }

注意这里context对象的构成:logger是统一日志接口(支持 debug/info/warn/error 级别,且日志可被 CLI 工具捕获用于故障诊断);fsnetwork是沙箱化的 API,而非原生fs.promisesfetch——它们内置了权限检查、调用计数、超时控制。例如,当fs.readFile被调用时,沙箱会检查inputs.jsonSample是否在permissions.fs:read允许的路径白名单内(如./src/**,./data/**),否则抛出PermissionDeniedError

这种设计带来的实操价值极其显著。我曾用ponytail处理一个遗留项目的 JSON Schema 转 TS 接口任务。以往做法是复制粘贴 JSON 到在线转换网站,再手动整理。而用skills方式:

# 注册技能(一次) npx skill add dietrichgebert/ponytail # 批量处理所有 JSON 文件(可脚本化) for file in ./schemas/*.json; do npx skill run ponytail --jsonSample="$file" > "./types/$(basename "$file" .json).ts" done

整个过程无需安装全局依赖,不污染项目 node_modules,所有输入输出路径受控,错误信息明确指向fs:read权限不足或 JSON 格式错误,而不是模糊的SyntaxError: Unexpected token

注意:skills协议明确禁止技能执行任何副作用操作(如修改全局配置、写入/usr/local/bin、启动后台进程)。所有“持久化”行为必须通过 CLI 工具提供的context.storageAPI 完成,该 API 将数据加密存储在~/.skills/storage/下,且按 Skill ID 隔离。这是它区别于传统 CLI 工具的关键——它把“能力”和“状态”彻底解耦。

3. 技能调用链路:从npx skill run到沙箱内执行的完整生命周期

当你敲下npx skill run ponytail --jsonSample=./data/user.json,表面看只是一条命令,但背后触发的是一条横跨网络、文件系统、进程沙箱、权限网关的精密调用链路。理解这条链路,是安全、高效使用skills的前提,也是排查error: dsh: plugin tree failed to load这类问题的根本方法。

整个生命周期可分为 6 个阶段,每个阶段都有明确的职责与失败点:

3.1 解析与路由阶段:确定执行哪个技能实例

CLI 工具首先解析ponytail,在本地技能注册表(~/.skills/registry.json)中查找匹配项。该注册表是npx skill add时写入的,内容类似:

{ "dietrichgebert/ponytail": { "version": "0.3.1", "source": "github", "url": "https://raw.githubusercontent.com/dietrichgebert/ponytail/main/skill.json", "installedAt": "2024-05-22T08:15:33.123Z", "cachePath": "/Users/me/.skills/cache/dietrichgebert-ponytail-0.3.1" } }

如果未找到,则触发skill add的隐式逻辑:尝试从 GitHub 获取skill.json并注册。这是npx skill run可能卡住的第一处——网络超时、GitHub 访问限制(尤其在国内)、skill.json文件不存在,都会导致此阶段失败,并报错Failed to resolve skill 'ponytail'

3.2 元数据加载与校验阶段:验证技能的完整性与安全性

CLI 工具根据注册表中的url下载skill.json,并执行三重校验:

  • 签名验证:检查skill.json是否包含signature字段,且该签名能被dietrichgebert的公钥验证(公钥从https://github.com/dietrichgebert.keys获取)。未签名或签名无效的技能,CLI 默认拒绝加载,除非显式加--insecure参数(强烈不推荐)。
  • 哈希校验:对比下载的skill.json与注册表中记录的sha256值(npx skill add时计算并存储),防止中间人篡改。
  • 权限审查:解析permissions字段,确认当前执行环境是否满足要求。例如,若技能声明permissions: ["fs:write"],而 CLI 工具运行在只读容器中,则直接报错Insufficient permissions: fs:write not allowed

我遇到过一次典型失败:error: agent harness runtime "codex" is unavailable because its plugin registry。排查发现,codex技能的skill.jsonpermissions包含["network:https://api.anthropic.com"],但公司防火墙拦截了该域名。CLI 工具在校验阶段就终止了,而非等到执行时才报错——这正是协议设计的健壮性体现。

3.3 运行时环境准备阶段:构建隔离沙箱

校验通过后,CLI 工具开始准备执行环境:

  • 根据runtime字段(如nodejs:18.17.0)检查本地 Node 版本。若不匹配,自动调用nvm use 18.17.0或提示安装。
  • 创建临时沙箱目录(如/tmp/skills-sandbox-abc123),将skill.jsonskill.js及其依赖(从package.json解析)复制进去。
  • 初始化沙箱 API:fs模块被包装为只允许读取--jsonSample指定路径及其父目录;network模块被包装为只允许访问https://api.example.comprocess.env被清空,仅注入白名单变量(如NODE_ENV=production)。

提示:沙箱目录是临时的,执行完毕即删除。这意味着技能无法通过写入文件来“持久化状态”,所有状态必须通过context.storageAPI 存储,确保可审计、可迁移。

3.4 参数绑定与上下文注入阶段:将命令行参数转化为技能可理解的数据

CLI 工具解析--jsonSample=./data/user.json,将其转换为inputs对象:

{ "jsonSample": "./data/user.json" }

然后,将此对象与沙箱 API(logger,fs,network,storage)一起注入context,作为execute(context)的参数。

关键细节./data/user.json这个路径,在沙箱内被重映射为绝对路径/tmp/skills-sandbox-abc123/data/user.jsonfs.readFile调用时,沙箱会检查该绝对路径是否在permissions.fs:read白名单内(如./data/**)。如果白名单是./src/**,则读取失败——这解释了为什么error: dsh: plugin tree failed to load: failed to apply loader entry include这类错误常伴随路径权限提示。

3.5 沙箱内执行阶段:技能代码的受限运行

此时,skill.js在 Node.js 沙箱中执行。沙箱通过vm.Module(Node.js 的虚拟机模块)创建,且禁用了evalFunction构造器、process.binding等高危 API。所有fsnetwork调用都经过沙箱代理层,实时记录调用栈、参数、耗时。

执行过程中,若发生未捕获异常(如JSON.parse失败),沙箱会捕获并格式化为结构化错误:

{ "error": { "type": "InputValidationError", "message": "Invalid JSON in ./data/user.json: Unexpected token '}' at position 123", "stack": ["..."], "context": { "inputName": "jsonSample", "filePath": "./data/user.json" } } }

这种错误比原生SyntaxError更易定位,因为它关联了输入参数名和文件路径。

3.6 结果收集与输出阶段:将技能输出标准化呈现

技能执行成功后,返回的outputs对象被 CLI 工具捕获。工具会:

  • 验证outputs是否符合skill.json中定义的outputs契约(字段名、类型)。
  • outputs.typescriptInterface的值,按--output参数指定格式输出(如--output=stdout直接打印,--output=file:./types/user.ts写入文件)。
  • 同时,将metadata(如generatedAt)写入执行日志,供后续审计。

整个链路的设计哲学是:每个阶段都可独立失败、独立诊断、独立重试。当npx skill run报错时,你不需要猜测是网络问题、权限问题还是代码问题,CLI 工具会明确告诉你失败在哪个阶段。例如:

  • Failed to fetch skill.json→ 阶段 3.1
  • Signature verification failed→ 阶段 3.2
  • Permission denied: fs:read on /tmp/sandbox/data/user.json→ 阶段 3.4
  • TypeError: Cannot read property 'map' of undefined→ 阶段 3.5

这种透明性,是skills协议区别于传统脚本工具的核心优势。

4. 与主流开发工具的深度集成:VS Code、Dsh、Claude Code 的协同模式

skills协议的价值,不在于它自己能做什么,而在于它如何作为“胶水层”,将原本割裂的开发工具无缝粘合。它不是要取代 VS Code 或 Dsh,而是让它们的能力可以被统一发现、统一调用、统一管理。理解这种集成模式,是解锁claude code skillsdsh pluginvscode配置claude code等热搜词背后真实工作流的关键。

4.1 VS Code 集成:从命令面板到编辑器上下文的技能激活

VS Code 的扩展机制(Extension API)本身强大,但传统扩展往往“重”而“专”——一个扩展只做一件事(如 Prettier 格式化),且配置分散。skills协议提供了一种“轻量级扩展”的新范式:VS Code 扩展只需实现一个通用的SkillsProvider,即可加载并调用任意已注册的技能。

具体集成路径如下:

  • 安装skills-vscode扩展(非官方,但社区广泛采用):该扩展不包含任何具体功能代码,只提供skills协议的 VS Code 适配层。
  • 扩展启动时,自动扫描~/.skills/registry.json,读取所有已注册技能的skill.json,提取namedescriptioninputs信息。
  • 在 VS Code 命令面板(Ctrl+Shift+P)中,动态生成命令列表,如:
    • Skills: Run Ponytail (Generate TS Interface)
    • Skills: Run Claude Explain Error
    • Skills: Run Flutter Gradle Fix
  • 执行命令时,扩展根据inputs定义,弹出智能表单(如jsonSample输入框,支持文件选择器),收集参数后,调用底层 CLI 执行npx skill run ...,并将结果以Output ChannelQuickPick形式展示。

这种集成带来的体验升级是质的:

  • 上下文感知:当光标在.json文件中时,Skills: Run Ponytail命令会自动预填--jsonSample为当前文件路径;当在终端中选中一段错误日志时,Skills: Run Claude Explain Error会自动将选中文本作为--errorLog参数传入。
  • 配置复用:你在 CLI 中用npx skill add注册的技能,开箱即用在 VS Code 中,无需重复安装、重复配置。
  • 调试友好:扩展提供Skills: Debug Last Run命令,可一键打开沙箱临时目录,查看skill.js源码、输入参数文件、执行日志,极大简化了技能开发调试流程。

我配置了一个典型工作流:在 VS Code 中编辑pubspec.yaml,保存后自动触发Skills: Run Flutter Gradle Fix。该技能会分析pubspec.yaml中的flutterdependencies,判断是否需要升级 Gradle 插件,并生成修复后的android/build.gradle补丁。整个过程在编辑器内完成,无需切到终端,错误信息直接显示在 Problems 面板中。

4.2 Dsh 集成:将技能作为 Dsh 插件树的叶子节点

Dsh(Developer Shell)是一个面向开发者的增强型 Shell,其核心是plugin tree—— 一棵以命令为节点、以依赖为边的有向图。skills协议与 Dsh 的天然契合点在于:每个技能就是一个可插拔的 Dsh 插件

Dsh 的plugin命令(如dsh plugin --profile web add dshmarket)本质是将一个技能注册到 Dsh 的插件树中。dshmarket是一个技能市场,其skill.json定义了多个子技能:

{ "id": "dshmarket", "subskills": [ { "id": "web/lint", "entry": "./skills/web-lint.js", "description": "Run ESLint on current directory" }, { "id": "web/test", "entry": "./skills/web-test.js", "description": "Run Vitest with coverage" } ] }

当执行dsh plugin add dshmarket时,Dsh 会递归注册所有subskills,并在dsh plugin tree输出中显示为:

dshmarket ├── web/lint └── web/test

此时,dsh web/lint命令等价于npx skill run dshmarket/web/lint。Dsh 为技能提供了额外的 Shell 上下文:dsh命令的当前工作目录、环境变量、管道输入(cat file.json | dsh web/lint)都会被自动注入context.inputs

这种集成解决了error: dsh: plugin tree failed to load: failed to apply loader entry include的根源问题——Dsh 的插件加载器(loader)不再需要自己解析各种格式的插件(npm 包、shell 脚本、Python 模块),而是统一委托给skills协议。loader entry include失败,往往是因为dshmarketskill.jsonsubskillsentry路径错误,或网络无法下载./skills/web-lint.js。排查时,直接cat ~/.dsh/plugins/dshmarket/skill.json即可定位。

4.3 Claude Code 集成:AI 助手的技能化调用范式

claude code并非一个独立应用,而是 Anthropic 提供的一套 API 与 CLI 工具,其核心思想是“将 AI 能力封装为可编程的技能”。skills协议为此提供了完美的载体。

一个典型的claude code skills实现如下:

  • 技能 ID:anthropic/claude-explain
  • skill.jsonpermissions包含["network:https://api.anthropic.com"]
  • skill.jsexecute函数接收--errorLog输入,调用 Anthropic API,将错误日志作为system消息,生成解释文本。

用户在 VS Code 中选中process exited with code 3221225477,右键选择Skills: Run Claude Explain Error,技能执行后返回:

This error (0xc0000005) is a Windows memory access violation. It commonly occurs when:

  1. A native DLL (e.g., from a Flutter plugin) is compiled for x64 but loaded in an x86 process, or vice versa.
  2. The application tries to read/write memory it doesn't own (buffer overflow, use-after-free).
  3. Antivirus software interferes with memory allocation.

To diagnose: Rundumpbin /headers your_app.exeto check architecture, and enable Application Verifier.

这种集成,让 Claude 不再是“聊天窗口里的问答机器人”,而是变成了一个嵌入在开发者工作流中的专家顾问。它不替代你的思考,但为你省去了搜索 Stack Overflow、阅读 Windows SDK 文档、反复试错的时间。

注意:claude code的技能调用,必须严格遵守 Anthropic 的 API 使用条款。skills协议通过permissions.networkcontext.network的封装,确保所有 API 调用都经过统一的密钥管理、速率限制、请求日志记录,避免了传统脚本中硬编码 API Key 的安全风险。

5. 实战:从零构建一个解决 Flutter Gradle 插件加载失败的技能

理论终须落地。现在,我们亲手构建一个真实可用的技能,专门解决error: failed to apply plugin 'dev.flutter.flutter-gradle-plugin'这一高频痛点。这个技能将演示如何将一个复杂的、多步骤的手动修复流程,封装为一个可一键调用、可分享、可审计的skills单元。

5.1 问题本质分析:Gradle 插件加载失败的根因图谱

failed to apply plugin 'dev.flutter.flutter-gradle-plugin'报错,表面是 Gradle 加载失败,但背后可能有 5 种根本原因,每种都需要不同的修复策略:

根因类别典型表现诊断命令修复方案
Gradle 版本不匹配Your project requires Gradle 8.4 but you have 8.2./gradlew --version修改android/gradle/wrapper/gradle-wrapper.properties
Flutter SDK 版本过旧Could not find method flutter() for arguments [...]flutter --versionflutter upgrade
插件声明位置错误Plugin [id: 'dev.flutter.flutter-gradle-plugin'] not foundgrep -r "flutter-gradle-plugin" android/plugins { id 'dev.flutter.flutter-gradle-plugin' }移至android/app/build.gradle顶部
Kotlin 版本冲突Kotlin compiler version 1.9.20 does not match Gradle's embedded Kotlincat android/build.gradle | grep kotlin统一ext.kotlin_version与 Gradle 嵌入版本
JDK 版本不兼容Unsupported class file major version 64(JDK 20)java -version切换 JDK 17

手动排查需依次执行 5 个命令,耗时 5-10 分钟。而一个技能,应能在 10 秒内完成自动诊断与修复建议。

5.2 技能设计:flutter-gradle-fix的契约定义

我们为技能命名flutter/gradle-fix,其skill.json设计如下:

{ "id": "flutter/gradle-fix", "version": "1.0.0", "name": "Flutter Gradle Fix", "description": "Automatically diagnose and suggest fixes for 'failed to apply plugin' errors in Flutter Android projects", "author": "Your Name", "license": "MIT", "entry": "./dist/skill.js", "runtime": "nodejs:18.17.0", "permissions": ["fs:read", "fs:write", "process:exec"], "inputs": [ { "name": "projectRoot", "type": "string", "required": false, "description": "Path to Flutter project root (defaults to current working directory)", "default": "." } ], "outputs": [ { "name": "diagnosis", "type": "object", "description": "Structured diagnosis result" } ], "tags": ["flutter", "android", "gradle", "debug"] }

关键设计点:

  • permissions包含fs:write,因为技能需要修改gradle-wrapper.propertiesbuild.gradle
  • inputs.projectRoot支持自定义路径,便于在 CI 环境中调用。
  • outputs.diagnosis是一个复杂对象,包含rootCausesuggestedFixcommandsToRun等字段,为后续自动化(如一键执行修复)留出接口。

5.3 技能实现:dist/skill.js的核心逻辑

以下是skill.js的核心实现(已简化,保留主干逻辑):

import { execSync } from 'child_process'; import { readFileSync, writeFileSync, existsSync } from 'fs'; export async function execute(context) { const { inputs, logger, fs, process } = context; const projectRoot = inputs.projectRoot || '.'; logger.info(`Starting diagnosis for Flutter project at ${projectRoot}`); // Step 1: Check Gradle version let gradleVersion = null; try { const gradleProps = fs.readFileSync(`${projectRoot}/android/gradle/wrapper/gradle-wrapper.properties`, 'utf8'); const versionMatch = gradleProps.match(/distributionUrl=.*\/gradle-(\d+\.\d+\.\d+)/); gradleVersion = versionMatch ? versionMatch[1] : null; } catch (e) { logger.warn('Could not read gradle-wrapper.properties'); } // Step 2: Check Flutter version let flutterVersion = null; try { const output = execSync('flutter --version', { encoding: 'utf8' }); const versionMatch = output.match(/Flutter (\d+\.\d+\.\d+)/); flutterVersion = versionMatch ? versionMatch[1] : null; } catch (e) { logger.warn('Flutter not found in PATH'); } // Step 3: Check plugin declaration location let pluginDeclared = false; try { const appBuildGradle = fs.readFileSync(`${projectRoot}/android/app/build.gradle`, 'utf8'); pluginDeclared = appBuildGradle.includes('dev.flutter.flutter-gradle-plugin'); } catch (e) { logger.warn('Could not read android/app/build.gradle'); } // Step 4: Analyze root cause let rootCause = 'unknown'; let suggestedFix = ''; let commandsToRun = []; if (!gradleVersion) { rootCause = 'gradle_version_missing'; suggestedFix = 'Gradle wrapper properties file not found. Please run `flutter create .` to regenerate.'; } else if (gradleVersion.startsWith('7.')) { rootCause = 'gradle_version_too_old'; suggestedFix = `Upgrade Gradle to 8.4+. Edit android/gradle/wrapper/gradle-wrapper.properties and change distributionUrl to https://services.gradle.org/distributions/gradle-8.4-bin.zip`; commandsToRun.push(`sed -i '' 's/gradle-7.\\*/gradle-8.4-bin.zip/' android/gradle/wrapper/gradle-wrapper.properties`); } else if (!pluginDeclared) { rootCause = 'plugin_declaration_missing'; suggestedFix = 'Flutter Gradle plugin not declared in android/app/build.gradle. Add `plugins { id \'dev.flutter.flutter-gradle-plugin\' }` at the top.'; } else { rootCause = 'other'; suggestedFix = 'No common root cause detected. Please check logs for specific error messages.'; } // Step 5: Return structured result const diagnosis = { timestamp: new Date().toISOString(), projectRoot, gradleVersion, flutterVersion, pluginDeclared, rootCause, suggestedFix, commandsToRun }; logger.info(`Diagnosis completed. Root cause: ${rootCause}`); return { outputs: { diagnosis }, metadata: { status: 'success' } }; }

5.4 技能注册与调用:端到端验证

构建完成后,执行以下命令完成注册与测试:

# 1. 构建技能(假设使用 esbuild) npx esbuild src/skill.ts --bundle --outfile=dist/skill.js --platform=node --target=node18.17 # 2. 注册技能(本地开发模式) npx skill add --local ./skill.json # 3. 在一个真实的 Flutter 项目中测试 cd

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询