{"record":{"id":"71306f5ffb8e7c74","repo":"jackwener/OpenCLI","slug":"skill-file-not-found-name-relativepath","errorCode":null,"errorMessage":"Skill file not found: ${name}/${relativePath}","messagePattern":"Skill file not found: (.+?)/(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"src/skills.ts","lineNumber":63,"sourceCode":"  const { name, pathInSkill } = parseSkillTarget(target, relpath);\n  if (!name.startsWith('opencli-')) {\n    throw new ArgumentError(`Unknown OpenCLI skill: ${name}`, 'Run \"opencli skills list\" to see available OpenCLI skills.');\n  }\n\n  const skillsRoot = getSkillsRoot(packageRoot);\n  const skillRoot = path.join(skillsRoot, name);\n  if (!isDirectory(skillRoot) || !fs.existsSync(path.join(skillRoot, 'SKILL.md'))) {\n    throw new ArgumentError(`Unknown OpenCLI skill: ${name}`, 'Run \"opencli skills list\" to see available OpenCLI skills.');\n  }\n\n  const relativePath = normalizeSkillPath(pathInSkill || 'SKILL.md');\n  const absolutePath = path.resolve(skillRoot, relativePath);\n  const relativeToRoot = path.relative(skillRoot, absolutePath);\n  if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {\n    throw new ArgumentError(`Invalid skill path: ${relativePath}`, 'Skill paths must stay inside the selected OpenCLI skill.');\n  }\n  if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {\n    throw new ArgumentError(`Skill file not found: ${name}/${relativePath}`, 'Run \"opencli skills list <skill>\" is not supported yet; read SKILL.md or a known references/... file.');\n  }\n\n  return {\n    skill: name,\n    path: relativePath,\n    content: fs.readFileSync(absolutePath, 'utf8'),\n  };\n}\n\nfunction readSkillInfo(skillsRoot: string, name: string): OpenCliSkillInfo | null {\n  const skillMdPath = path.join(skillsRoot, name, 'SKILL.md');\n  if (!fs.existsSync(skillMdPath)) return null;\n  const content = fs.readFileSync(skillMdPath, 'utf8');\n  const fm = parseFrontmatter(content);\n  return {\n    name: typeof fm.name === 'string' && fm.name ? fm.name : name,\n    description: typeof fm.description === 'string' ? fm.description : firstBodyParagraph(content),\n    version: typeof fm.version === 'string' || typeof fm.version === 'number' ? String(fm.version) : '',","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/skills.ts#L45-L81","documentation":"readOpenCliSkill checks fs.existsSync and that the target is a regular file before reading. If the resolved path inside the skill does not exist or is a directory, it throws ArgumentError (exit code 2) telling you the skill file was not found and that 'opencli skills list <skill>' is not supported — read SKILL.md or a known references/... file.","triggerScenarios":"Requesting a file path inside a skill that does not exist on disk (wrong filename, wrong casing, file lives under references/ but path omits that), or passing a directory path instead of a file (e.g. just the skill name with no 'SKILL.md' when the default resolution misses).","commonSituations":"Case-sensitive filesystems (Linux) vs. skill docs written with different casing; skill versions that renamed reference files; typos like 'SKILL.md ' with trailing space; passing 'references' (a directory) instead of a file inside it.","solutions":["Verify the file exists: ls <skill-root>/<relativePath> and correct the path (typically 'SKILL.md' or 'references/<file>.md')","Check filename casing — paths are case-sensitive on Linux/macOS default filesystems","Make sure you pass a file, not a directory: append 'SKILL.md' when you just want the skill's main doc","Reinstall/update the skill if the file was removed in a newer version"],"exampleFix":"// before\nawait readOpenCliSkill('grok', 'api');\n// after\nawait readOpenCliSkill('grok', 'references/api.md');","handlingStrategy":"validation","validationCode":"const abs = path.resolve(skillRoot, relativePath);\nif (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) throw new Error(`skill file missing: ${relativePath}`);","typeGuard":null,"tryCatchPattern":"try {\n  const skill = await readOpenCliSkill(name, p);\n} catch (e) {\n  if (e instanceof ArgumentError && /not found/i.test(e.message)) {\n    console.error(`'${p}' not found in skill '${name}'. Read SKILL.md or a references/ file.`);\n  } else throw e;\n}","preventionTips":["Default to 'SKILL.md' when unsure which file to read","Check filename casing — Linux is case-sensitive","List the skill directory contents once before scripted reads"],"tags":["file-not-found","argument-validation","skills","filesystem"],"backgroundTag":"file-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}