浏览 AI 使用教程
操作教程

Codex Skill调试:定位脚本路径、输入校验与触发问题

用一个可复现的ENOENT路径错误调试Codex Skill,提供完整Node脚本、资源文件、跨目录命令与正常和异常输入回归。

Codex项目开发与自动化工作流主视觉

先看重点:快速结论

  1. 用完整错误与目录定位资源路径,不同时改触发说明。
  2. 从两个工作目录执行同一脚本,验证资源与输入路径分别解析。
本文目录 · 跳到当前步骤

Skill 已被调用,却报 ENOENT,不应该先重写 description。本例接续任务状态汇总 Skill,新增 Node.js 20+ 脚本,把资源与输入路径的来源分开。错误与数据都是教学案例。

固定目录与失败证据

在原练习项目添加:

text
skill-demo/
  .agents/skills/task-status-summary/
    SKILL.md
    references/statuses.json
    scripts/run.mjs
  input/tasks.json

references/statuses.json 的完整内容:

json
["done","todo"]

如果 scripts/run.mjs 用以下写法读取资源:

js
fs.readFileSync('references/statuses.json', 'utf8');

从项目根目录执行时,它会找项目根下的 references,而不是 Skill 内的 references。记录实际工作目录、执行命令和错误路径,就能验证这个假设。文件确实存在但路径算错,与 Skill 没触发是不同问题。

完整修复脚本

将下面保存为该 Skill 的 scripts/run.mjs。资源相对于脚本定位,用户输入则相对于调用时的工作目录解析;两者不能混用。

js
import fs from 'node:fs';
import path from 'node:path';

try {
  if (process.argv.length !== 3) throw new Error('用法:node run.mjs <输入JSON>');
  const allowed = JSON.parse(fs.readFileSync(
    new URL('../references/statuses.json', import.meta.url), 'utf8'));
  if (!Array.isArray(allowed) || allowed.join(',') !== 'done,todo') {
    throw new Error('资源必须声明 done,todo');
  }
  const inputPath = path.resolve(process.argv[2]);
  const rows = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
  if (!Array.isArray(rows)) throw new Error('输入顶层必须为数组');
  const ids = new Set();
  const result = { total: rows.length, done: 0, todo: 0, todoIds: [] };
  for (const [index, row] of rows.entries()) {
    if (!row || typeof row.id !== 'string' || !row.id.trim() ||
        typeof row.title !== 'string' || !row.title.trim()) {
      throw new Error('记录 ' + (index + 1) + ' 缺少有效 id/title');
    }
    if (ids.has(row.id)) throw new Error('重复 id: ' + row.id);
    if (!allowed.includes(row.status)) throw new Error('未知 status: ' + row.id);
    ids.add(row.id);
    result[row.status]++;
    if (row.status === 'todo') result.todoIds.push(row.id);
  }
  console.log(JSON.stringify(result));
} catch (error) {
  console.error(error.message);
  process.exitCode = 2;
}

SKILL.md 中增加脚本调用要求:从用户指定的项目目录执行该 Skill 脚本,传入用户指定输入文件;脚本退出码非零时报告错误,不继续生成统计。保留第一篇的输入和用途边界,不改宽触发描述来掩盖路径问题。

用同一样本从两个目录运行

input/tasks.json 使用第一篇三条任务样本。先在项目根运行:

powershell
node .agents/skills/task-status-summary/scripts/run.mjs input/tasks.json

再进入 input 目录运行:

powershell
Set-Location input
node ../.agents/skills/task-status-summary/scripts/run.mjs tasks.json
Set-Location ..

两次预期输出相同:

json
{"total":3,"done":1,"todo":2,"todoIds":["T-02","T-03"]}

同一个脚本、同一资源没有随当前目录变化,输入路径则由调用者正确给出。不要把输入硬编码为个人电脑绝对路径,否则“在我这里能跑”仍可能掩盖可移植性问题。

失败分支也要回归

输入或环境 预期
三条正常任务 输出 3/1/2,退出码 0
[] 输出 0/0/0 和空 todoIds
T-02 的 status 为 doing 标准错误提示未知 status,退出码 2,无成功统计
重复 id 指出重复 id,退出码 2
输入文件不存在 文件读取错误,退出码 2
statuses.json 被移走 资源路径错误,退出码 2;不能默认放行所有状态

PowerShell 在运行后用 $LASTEXITCODE 查看 Node 退出码。先确认脚本层满足这些预期,再回到 Codex 显式调用 Skill,检查它真的用了正确脚本并保留错误信息。

如果显式调用成功、普通表达不触发,再单独检查 name/description 与任务是否匹配;如果输入未指定,应先索取路径。将每次修改限制在证据指向的一层,才能知道到底修好了什么。

资料来源

  • OpenAI Skills

    核对 Skill 的说明、脚本和资源组织。

  • Node.js URL

    使用 import.meta.url 解析脚本相邻资源。

下一步

继续阅读与你当前问题相关的教程: