跳到主内容
创建时间:最后更新:作者:白川

在技能中调用脚本和现有工具

为代码检查技能添加只读 Node 脚本,处理参数、工作目录、退出状态和输出边界。

在技能中调用脚本和现有工具

需要确定文件大小、函数声明清单等事实时,让模型自己数容易出错。可以把这部分交给脚本,再让模型结合源码解释结果。

本篇实现完整技能目录中引用的脚本。脚本只读取用户指定文件,不执行源码、不修改文件、不安装依赖。

用脚本提供可核对事实

创建 skills/code-inspection/scripts/inspect-source.mjs:

javascript
import { readFile, stat } from 'node:fs/promises'
import { basename, extname, resolve } from 'node:path'

const args = process.argv.slice(2)
if (args.length !== 1) {
  console.error('用法:node inspect-source.mjs <目标文件>')
  process.exitCode = 2
} else {
  try {
    const path = resolve(args[0])
    if (!['.js', '.jsx', '.ts', '.tsx'].includes(extname(path).toLowerCase())) {
      throw new Error('只接受 JavaScript 或 TypeScript 文件')
    }
    const info = await stat(path)
    if (!info.isFile() || info.size > 64 * 1024) {
      throw new Error('输入必须是最多 64 KiB 的普通文件')
    }
    const source = await readFile(path, 'utf8')
    if (Buffer.byteLength(source, 'utf8') > 64 * 1024) {
      throw new Error('文件读取期间超出大小限制')
    }
    const declarations = [...source.matchAll(/function\s+([A-Za-z_$][\w$]*)\s*\(/g)].map((match) => ({
      name: match[1],
      line: source.slice(0, match.index).split('\n').length,
    }))
    console.log(JSON.stringify({
      file: basename(path), bytes: Buffer.byteLength(source, 'utf8'), declarations,
      limitation: '仅扫描普通命名函数文本,不是编译器或完整语义检查',
    }, null, 2))
  } catch (error) {
    console.error(error instanceof Error ? error.message : String(error))
    process.exitCode = 1
  }
}

脚本接受一个路径参数,支持几种 JavaScript/TypeScript 扩展名,限制文件大小,并输出普通命名函数文本的名字与行号。大文件或错误输入失败时,不生成假的成功对象。

这不是编译器。箭头函数不会被当前表达式识别,注释或字符串中的相似文本也可能被误识别,因此模型还要读实际源码核对。扩展名检查不构成文件系统沙箱。

先脱离模型单独运行

在练习项目根目录执行:

bash
node ./skills/code-inspection/scripts/inspect-source.mjs ./fixtures/sample.ts

检查 file、bytes 和声明行号是否与实际文件一致。输出的 limitation 保留能力限制,不把清单写成质量结论。

测试失败路径:

bash
node ./skills/code-inspection/scripts/inspect-source.mjs
node ./skills/code-inspection/scripts/inspect-source.mjs ./fixtures/missing.ts

缺参数退出码为 2,文件等执行错误为 1;成功为 0。错误写到标准错误,结构化结果写到标准输出。宿主必须检查退出状态,不把一段错误文字当作成功数据交给模型。

再让技能组织工具调用

使用完整技能目录,启动允许读取和执行脚本的宿主:

bash
npx --no-install pi --no-extensions --no-skills --skill ./skills/code-inspection --tools read,bash

在交互中调用:

text
/skill:code-inspection 检查 fixtures/sample.ts,先取得脚本观测,再按模板生成报告。

观察是否读取规则、以正确脚本路径执行、保留真实退出状态,再生成报告。技能说明与宿主工具选择是两层:正文中的命令不是已经执行的事实,目录里有脚本也不等于宿主自动注册了新工具。

不把路径拼成任意命令

用户文件名应作为独立参数传递,含空格时正确引用。可使用固定的 Node 入口和脚本路径,不根据源码里的文字追加 shell 命令。

相对用户路径从工作目录解释,脚本本身从技能位置定位。复制技能到另一个项目后再次验证,不能把个人电脑绝对路径写死在 Skill 中。

用户限制与执行失败怎样处理

用户要求不运行脚本时,应说明本次仅做源码阅读;宿主没有 Node 或执行权限时也应报告限制。不能为了满足模板而写“脚本检查通过”,更不能把没有运行的项目测试写成通过。

脚本只读取一份已明确指定的练习文件,不保证任意第三方 Skill 同样只读。实际动作仍由宿主权限、工具实现和任务授权控制。

脚本的正常与错误路径已实际检查,模型能否正确组织这些动作则进入技能测试。

参考资料