Skill 开发踩坑:相对路径解析问题

Skill 开发踩坑:相对路径解析问题

在这里分享一下我在 Skill 开发过程中遇到的问题以及解决方案。

我经验有限,这个方案也远非完美,只是希望能为佬友们提供一点参考方向。若有更好的方法,也请佬友不吝赐教,一起交流进步。

问题描述

Claude Code 和 Qoder Cli 中,Skill 无法感知自身目录,导致 Skill 脚本的第一次调用都会出错,然后才会搜索正确目录,影响 scripts/reference/ 的使用。

测试环境为:

  1. Claude Code + Qwen3.5-Plus
  2. Qoder Cli + Auto / Lite 模式

不管 Skill 在用户级目录 (~/.agents/skills/xxx/scripts) 还是项目目录都会出现问题。

[!NOTE] GitHub Issue
直到 2026-04-04 仍未解决:[Bug] Skill plugin scripts fail on first execution with relative path resolution · Issue #11011 · anthropics/claude-code · GitHub

解决思路

在 Skill 调用时,注入 Skill 目录的绝对路径,如 “Base directory for this skill: /xxx/xxx/”。

注入方法通过 PreToolUse Hook 实现。

解决方案

  1. 修改 ~/.claude/settings.json

    {
      "hooks": {
        "PreToolUse": [
          {   
            "matcher": "Skill",
            "hooks": [
              {   
                "type": "command",
                "command": "bash ~/.claude/hooks/inject-skill-dir.sh"
              }
            ]
          }
        ]
      }
    }
    
  2. 新增 Hook 脚本 ~/.claude/hooks/inject-skill-dir.sh

    #!/bin/bash
    # inject skill's base directory when invoke Skill. Aim to solve the relative path resolution issue.
    
    set -euo pipefail
    
    # resolve the director and find cli name (handle symlinks)
    HOME_DIR="$(cd ~ && pwd)"
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
    CLI_DIR_NAME="$(basename "$(cd "$SCRIPT_DIR/.." && pwd -P)")"
    if [[ ! "$CLI_DIR_NAME" =~ ^(\.claude|\.codex|\.qoder)$ ]]; then
      echo "Unknown cli name detected: '$CLI_DIR_NAME'."
      exit 2
    fi
    
    # resolve /dev/stdin
    INPUT=$(cat)
    TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
    SKILL_NAME=$(echo "$INPUT" | jq -r '.tool_input.skill')
    CWD=$(echo "$INPUT" | jq -r '.cwd')
    # echo "$INPUT" >> $HOME_DIR/$CLI_DIR_NAME/hooks/hook.log
    # echo "$TOOL_NAME: $CURRENT_DIR / $SKILL_NAME" >> $HOME_DIR/$CLI_DIR_NAME/hooks/hook.log
    
    if [ "$TOOL_NAME" != 'Skill' ]; then
      exit 0
    fi
    
    # define the directory map
    # Structure: [CLI_DIR_NAME] -> {local_config_dir, global_dir}
    case "$CLI_DIR_NAME" in
        ".claude") 
            LOCAL_DIRS=("$CWD/.claude/skills/$SKILL_NAME")
            GLOBAL_DIRS=("$HOME_DIR/.claude/skills/$SKILL_NAME")
            ;;
        ".codex"|".qoder")
            LOCAL_DIRS=("$CWD/$CLI_DIR_NAME/skills/$SKILL_NAME" "$CWD/.agents/skills/$SKILL_NAME")
            GLOBAL_DIRS=("$HOME_DIR/$CLI_DIR_NAME/skills/$SKILL_NAME" "$HOME_DIR/.agents/skills/$SKILL_NAME")
            ;;
        *)
            exit 2
            ;;
    esac
    
    # Function to find the directory. Keep it simple.
    find_dir() {
      for dir in "$@"; do
        [ -d "$dir" ] && echo "$dir" && return 0
      done
      return 1
    }
    
    # Combine lists and resolve.
    # Order: Project Dir (CWD) first, then Global.
    CANDIDATES=("${LOCAL_DIRS[@]}" "${GLOBAL_DIRS[@]}")
    FOUND_DIR=$(find_dir "${CANDIDATES[@]}")
    echo "Skill dir: $FOUND_DIR" >> $HOME_DIR/$CLI_DIR_NAME/hooks/hook.log
    
    
    if [ -z "$FOUND_DIR" ]; then
        exit 2
    fi
    
    # return
    jq -n '{
      "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "allow",
        "additionalContext": "<IMPORTANT>\nBase directory for this skill: '"$FOUND_DIR"'\nYou MUST find scripts in base directory.\n<IMPORTANT>"
    }
    exit 0
    

测试结果

评估环境:

  1. Claude Code + Qwen3.5-Plus
  2. Qoder Cli + Auto / Lite Mode

Prompt:用 xxxx skill 完成 xxx 任务,显式调用
评估指标:路径出错次数

Claude Code 结果:

  1. 无 Hook:出错次数 9 / 10
  2. 有 Hook:出错次数 0 / 10

Qoder Cli 结果:

  1. 无 Hook:出错次数 9 / 10
  2. 有 Hook:出错次数 6 / 10

[!Note] 结果分析

  1. 这个问题大概率和模型能力有关。Claude 和 Codex 系列模型不需要显式提示 Skill 目录就会自己主动搜索,但 Qwen 系列模型目前做不到;
  2. Qoder Cli 通过 Hook 注入上下文的效果很差,没找到原因;
  3. Agent Cli 仍然以 Claude Code 和 Codex 领先;
8 个赞

谢佬友分享

感谢分享

我是SAA的项目贡献者,这个问题和Skill实现的方式有关,在框架注册Skill的时候需要在prompt里面去注入这个Skill的绝对路径,并告知模型执行Skill的任何东西首先应该切换到激活Skill的目录,这样模型才能完成执行Skill的脚本,因为市场上分发的Skill是非标准化的,不能够要求开发者写脚本的时候还去写一个特定路径。这个问题大概率和Qoder cli实现skill的时候没有这样做导致的,类似的问题在Trae里面也有,包括还有嵌套skill,比如父skill里面依赖子skill,子skill需要执行他自己的脚本,也会有问题,不过他们项目都没开源只能通过社区反馈来尝试催促解决了

1 个赞