refactor: 重构 SKILL.md
- 所有涉及 .env 的代码块均添加 mkdir -p(或 New-Item)确保目录存在 - .env 改用标准 KEY=VALUE 格式(GITEA_TOKEN=<值>) - 写入:printf '%s' -> printf 'GITEA_TOKEN=...' - 读取:cat -> grep + cut 按 key 提取 - 检查:-s 大小检查 -> grep -q 按 key 检查 - 所有无标签代码块标注了语言(bash/powershell) - 零、路径定义首部添加 escalate 权限说明 - 每处需文件写入/网络访问的操作前标注需 escalate - 三、添加 3.1 fallback 网络不可用时的降级处理 - 限定范围:仅返回 PineSound 内部 Skills,不混入其他来源
This commit is contained in:
@@ -6,10 +6,14 @@ allowed-tools: Read, Grep, Edit, Write, Bash
|
||||
|
||||
# PineSound 内部 Skills 安装器
|
||||
|
||||
> **范围说明**:本 Skill 仅关注 PineSound 内部 Skills(`PineSoundSkills` 组织下的仓库)。当用户查询或管理技能时,**只返回 PineSound 内部 Skills**,不涉及其他来源的技能。如果 Gitea 不可达,fallback 也只列出本地已安装的 PineSound 内部技能。
|
||||
|
||||
---
|
||||
|
||||
## 零、路径定义
|
||||
|
||||
> **⚠️ 需要请求 escalate 权限**:本 Skill 需要在 `~/.codex/skills/PineSoundSkills/` 下创建目录和 `.env` 令牌文件,以及通过 `curl` 访问内网 Gitea 服务。该路径不在沙箱默认 `writable_roots` 中,内网地址不再默认网络允许列表。**所有涉及 `mkdir`、`printf`(写入 .env)、`curl`(请求 Gitea API)的命令,都必须通过 `sandbox_permissions: "require_escalated"` 请求 escalate 权限后执行。**
|
||||
|
||||
整个 skill 统一使用以下路径:
|
||||
|
||||
| 含义 | Unix | Windows |
|
||||
@@ -23,13 +27,17 @@ allowed-tools: Read, Grep, Edit, Write, Bash
|
||||
|
||||
### 1.1 获取令牌(首次/令牌无效时)
|
||||
|
||||
**流程**: agent 通过 `AskUserQuestion` 工具向用户索要令牌 → 用户输入 → 写入 `.env` → 验证。
|
||||
**流程**: agent 向用户索要令牌 → 用户输入 → 先 escalate → 写入 `.env` → 验证。
|
||||
|
||||
> **需 escalate**: 以下命令涉及文件系统写入。
|
||||
|
||||
```bash
|
||||
# 1. 检查 .env 是否存在且非空
|
||||
# 1. 确保目录存在,再检查 .env 中是否包含 GITEA_TOKEN
|
||||
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
|
||||
mkdir -p "$(dirname "$TOKEN_FILE")"
|
||||
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
# KEY=VALUE 格式:检查是否存在且包含 GITEA_TOKEN 键
|
||||
if [ ! -f "$TOKEN_FILE" ] || ! grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
|
||||
echo "TOKEN_MISSING"
|
||||
fi
|
||||
|
||||
@@ -39,18 +47,18 @@ fi
|
||||
索要到令牌后,执行存储:
|
||||
|
||||
```bash
|
||||
# 2. 创建目录并写入令牌
|
||||
# 2. 创建目录并写入令牌(KEY=VALUE 格式)
|
||||
mkdir -p ~/.codex/skills/PineSoundSkills
|
||||
|
||||
# 用 printf 写入,避免 echo 对特殊字符的转义问题
|
||||
printf '%s' "$GITEA_TOKEN" > ~/.codex/skills/PineSoundSkills/.env
|
||||
# 用 KEY=VALUE 格式写入,避免 echo 对特殊字符的转义问题
|
||||
printf "GITEA_TOKEN=%s\n" "$GITEA_TOKEN" > ~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 3. Unix: 设置权限仅 owner 可读写
|
||||
chmod 600 ~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 4. 验证写入成功
|
||||
if [ -s ~/.codex/skills/PineSoundSkills/.env ]; then
|
||||
echo "令牌已持久化存储"
|
||||
if grep -q "^GITEA_TOKEN=" ~/.codex/skills/PineSoundSkills/.env 2>/dev/null; then
|
||||
echo "令牌已持久化存储(KEY=VALUE 格式)"
|
||||
else
|
||||
echo "错误: 令牌写入失败"
|
||||
exit 1
|
||||
@@ -58,12 +66,12 @@ fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows 版
|
||||
# Windows 版(KEY=VALUE 格式)
|
||||
New-Item -ItemType Directory -Force "$env:USERPROFILE\.codex\skills\PineSoundSkills"
|
||||
$env:GITEA_TOKEN | Out-File -FilePath "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -NoNewline -Encoding ASCII
|
||||
"GITEA_TOKEN=$env:GITEA_TOKEN" | Out-File -FilePath "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Encoding ASCII
|
||||
# 验证
|
||||
if ((Get-Item "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env").Length -gt 0) {
|
||||
Write-Host "令牌已持久化存储"
|
||||
if (Select-String -Path "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Pattern "^GITEA_TOKEN=" -Quiet) {
|
||||
Write-Host "令牌已持久化存储(KEY=VALUE 格式)"
|
||||
} else {
|
||||
Write-Host "错误: 令牌写入失败"
|
||||
exit 1
|
||||
@@ -73,20 +81,24 @@ if ((Get-Item "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env").Length -gt
|
||||
### 1.2 读取令牌(后续使用)
|
||||
|
||||
```bash
|
||||
# Unix — 去除首尾空白,避免换行符污染 API 请求
|
||||
GITEA_TOKEN=$(cat ~/.codex/skills/PineSoundSkills/.env 2>/dev/null | tr -d '\n\r' | xargs)
|
||||
# Unix — 确保目录存在后再读取令牌(按 KEY 提取)
|
||||
mkdir -p ~/.codex/skills/PineSoundSkills
|
||||
|
||||
# 从 .env 中按 KEY 提取 GITEA_TOKEN(KEY=VALUE 格式)
|
||||
GITEA_TOKEN=$(grep "^GITEA_TOKEN=" ~/.codex/skills/PineSoundSkills/.env 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\n\r' | xargs)
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "错误: 令牌文件缺失或为空,请先设置令牌"
|
||||
echo "错误: .env 中缺少 GITEA_TOKEN,请先设置令牌"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
$GITEA_TOKEN = (Get-Content "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Raw).Trim()
|
||||
# Windows — 确保目录存在后再读取令牌(按 KEY 提取)
|
||||
$null = New-Item -ItemType Directory -Force "$env:USERPROFILE\.codex\skills\PineSoundSkills" 2>$null
|
||||
$GITEA_TOKEN = (Select-String -Path "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Pattern "^GITEA_TOKEN=" | ForEach-Object { $_.Line.Split("=", 2)[1].Trim() })
|
||||
if ([string]::IsNullOrEmpty($GITEA_TOKEN)) {
|
||||
Write-Host "错误: 令牌文件缺失或为空,请先设置令牌"
|
||||
Write-Host "错误: .env 中缺少 GITEA_TOKEN,请先设置令牌"
|
||||
exit 1
|
||||
}
|
||||
```
|
||||
@@ -95,7 +107,7 @@ if ([string]::IsNullOrEmpty($GITEA_TOKEN)) {
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 首次运行 | agent 用 `AskUserQuestion` 索要 → 写入 `.env` |
|
||||
| 首次运行 | agent 索要 → escalate(fs 写入)→ 写入 `.env` |
|
||||
| 再次运行 | 直接从 `.env` 读取,跳过询问 |
|
||||
| 令牌失效(API 返回 401) | 提示用户令牌可能已过期,重新执行 1.1 流程 |
|
||||
| 用户明确要求重新设置 | 覆盖 `.env`,执行 1.1 流程 |
|
||||
@@ -121,22 +133,27 @@ echo "$PLATFORM"
|
||||
|
||||
## 三、查看 Gitea 仓库中的 Skills 列表
|
||||
|
||||
> **需 escalate**: 以下步骤需要同时 escalate 文件系统写入(mkdir/读取 .env)和网络访问(curl 请求内网 Gitea)。
|
||||
|
||||
```bash
|
||||
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 读取并清理令牌
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "错误: 令牌文件缺失或为空。请先执行令牌获取流程(见第一章)"
|
||||
# 确保目录存在
|
||||
mkdir -p "$(dirname "$TOKEN_FILE")"
|
||||
|
||||
# 按 KEY 读取 GITEA_TOKEN
|
||||
if [ ! -f "$TOKEN_FILE" ] || ! grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
|
||||
echo "错误: .env 中缺少 GITEA_TOKEN。请先执行令牌获取流程(见第一章)"
|
||||
exit 1
|
||||
fi
|
||||
GITEA_TOKEN=$(cat "$TOKEN_FILE" 2>/dev/null | tr -d '\n\r' | xargs)
|
||||
GITEA_TOKEN=$(grep "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\n\r' | xargs)
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "错误: 令牌为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 请求 API
|
||||
# 请求 API(内网 Gitea,需 escalate 网络权限)
|
||||
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||
"http://192.168.1.3:10082/api/v1/orgs/PineSoundSkills/repos" \
|
||||
| python3 -c "
|
||||
@@ -153,10 +170,62 @@ for r in repos:
|
||||
|
||||
> 注意:`cat` 读取的令牌可能包含尾部换行符,通过 `tr -d '\n\r'` 和 `xargs` 做双重清理。如果 API 返回 401,说明令牌无效或已过期。
|
||||
|
||||
### 3.1 网络不可用时的 fallback(仅列出 PineSound 内部技能)
|
||||
|
||||
如果 `curl` 返回 `exit code 7`(连接失败)、`exit code 6`(DNS 解析失败)或 HTTP 状态码非 200,按以下方式处理:
|
||||
|
||||
1. 先检查 `~/.codex/skills/PineSoundSkills/` 下是否有已安装的 PineSound 技能
|
||||
2. 向用户报告 Gitea 连不通,仅列出本地已安装的 PineSound 内部技能
|
||||
3. 询问用户是否要重试连接或手动操作
|
||||
|
||||
**注意**:不要在输出中混入其他来源的技能(如系统 skills、社区 skills 等),只显示 PineSound 内部的。
|
||||
|
||||
```bash
|
||||
# 当 Gitea API 不可达时的 fallback:列出本地已安装的 PineSound 内部技能
|
||||
echo "Gitea 服务 (192.168.1.3:10082) 当前不可达。"
|
||||
echo ""
|
||||
echo "=== 已安装的 PineSound 内部 Skills ==="
|
||||
SKILL_DIR=~/.codex/skills/PineSoundSkills
|
||||
if [ -d "$SKILL_DIR" ]; then
|
||||
for d in "$SKILL_DIR"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
name=$(basename "$d")
|
||||
[ -f "${d}SKILL.md" ] && desc=$(grep '^description:' "${d}SKILL.md" | head -1 | sed 's/^description:\s*//i' | sed 's/"//g') || desc=""
|
||||
echo " $name${desc:+ — $desc}"
|
||||
done
|
||||
else
|
||||
echo "(暂未安装任何 PineSound 内部技能)"
|
||||
fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows fallback — 仅列出 PineSound 内部技能
|
||||
Write-Host "Gitea 服务 (192.168.1.3:10082) 当前不可达。"
|
||||
Write-Host ""
|
||||
Write-Host "=== 已安装的 PineSound 内部 Skills ==="
|
||||
$skillDir = "$env:USERPROFILE\.codex\skills\PineSoundSkills"
|
||||
if (Test-Path $skillDir) {
|
||||
foreach ($d in Get-ChildItem -Directory $skillDir) {
|
||||
$name = $d.Name
|
||||
$sf = "$($d.FullName)\SKILL.md"
|
||||
$desc = ""
|
||||
if (Test-Path $sf) {
|
||||
$match = Select-String -Path $sf -Pattern '(?im)^description:\s*(.+)'
|
||||
if ($match) { $desc = $match.Matches.Value -replace '(?im)^description:\s*', '' }
|
||||
}
|
||||
Write-Host " $name$(if ($desc) { " -- $desc" })"
|
||||
}
|
||||
} else {
|
||||
Write-Host "(暂未安装任何 PineSound 内部技能)"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、安装 Skills
|
||||
|
||||
> **需 escalate**: 以下涉及文件系统写入和 git 克隆(网络访问)。
|
||||
|
||||
### 4.1 确保目录存在
|
||||
|
||||
```bash
|
||||
@@ -527,28 +596,32 @@ GITEA_API="http://192.168.1.3:10082/api/v1"
|
||||
|
||||
# ========== 令牌管理 ==========
|
||||
|
||||
# 检查令牌文件是否存在且非空
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "令牌文件不存在或为空。"
|
||||
# 确保目录存在
|
||||
mkdir -p "$(dirname "$TOKEN_FILE")"
|
||||
|
||||
# 检查 .env 是否存在且包含 GITEA_TOKEN(KEY=VALUE 格式)
|
||||
if [ ! -f "$TOKEN_FILE" ] || ! grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
|
||||
echo "令牌文件不存在或未设置 GITEA_TOKEN。"
|
||||
echo "请通过 AskUserQuestion 向用户索要 Gitea 令牌。"
|
||||
|
||||
# [agent 操作] 用 AskUserQuestion 获取用户输入的令牌
|
||||
# 假设用户提供了令牌,存入变量 USER_TOKEN
|
||||
|
||||
mkdir -p "$SKILL_DIR"
|
||||
printf '%s' "$USER_TOKEN" > "$TOKEN_FILE"
|
||||
printf "GITEA_TOKEN=%s\n" "$USER_TOKEN" > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
|
||||
# 写入后验证
|
||||
if [ ! -s "$TOKEN_FILE" ]; then
|
||||
if grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
|
||||
echo "令牌已持久化存储到 $TOKEN_FILE(KEY=VALUE 格式)"
|
||||
else
|
||||
echo "错误: 令牌写入失败!"
|
||||
exit 1
|
||||
fi
|
||||
echo "令牌已持久化存储到 $TOKEN_FILE"
|
||||
fi
|
||||
|
||||
# 读取令牌(清理空白字符)
|
||||
GITEA_TOKEN=$(cat "$TOKEN_FILE" 2>/dev/null | tr -d '\n\r' | xargs)
|
||||
# 按 KEY 读取令牌(清理空白字符)
|
||||
GITEA_TOKEN=$(grep "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\n\r' | xargs)
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "错误: 令牌为空"
|
||||
exit 1
|
||||
@@ -563,6 +636,21 @@ HTTP_CODE=$(curl -s -o /tmp/pinesound_repos.json -w "%{http_code}" \
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "API 请求失败 (HTTP $HTTP_CODE),令牌可能无效或已过期"
|
||||
echo "请重新执行令牌获取流程"
|
||||
|
||||
# fallback:列出本地已安装的 PineSound 内部技能
|
||||
echo ""
|
||||
echo "=== 已安装的 PineSound 内部 Skills ==="
|
||||
SKILL_DIR=~/.codex/skills/PineSoundSkills
|
||||
if [ -d "$SKILL_DIR" ]; then
|
||||
for d in "$SKILL_DIR"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
name=$(basename "$d")
|
||||
[ -f "${d}SKILL.md" ] && desc=$(grep '^description:' "${d}SKILL.md" | head -1 | sed 's/^description:\s*//i' | sed 's/"//g') || desc=""
|
||||
echo " $name${desc:+ — $desc}"
|
||||
done
|
||||
else
|
||||
echo "(暂未安装任何 PineSound 内部技能)"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user