Files
Pine a2b5a7c1f5 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,不混入其他来源
2026-06-08 13:56:06 +08:00

718 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: pinesound-skills-installer
description: PineSound 内部 Skills 安装器。通过 Gitea API 获取技能列表、安装/卸载/查询已安装的内部技能。当用户需要管理 PineSound 内部技能时使用。
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 |
|------|------|---------|
| Skills 根目录 | `~/.codex/skills/PineSoundSkills` | `$env:USERPROFILE\.codex\skills\PineSoundSkills` |
| 令牌文件 | `~/.codex/skills/PineSoundSkills/.env` | `$env:USERPROFILE\.codex\skills\PineSoundSkills\.env` |
---
## 一、令牌管理
### 1.1 获取令牌(首次/令牌无效时)
**流程**: agent 向用户索要令牌 → 用户输入 → 先 escalate → 写入 `.env` → 验证。
> **需 escalate**: 以下命令涉及文件系统写入。
```bash
# 1. 确保目录存在,再检查 .env 中是否包含 GITEA_TOKEN
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
mkdir -p "$(dirname "$TOKEN_FILE")"
# KEY=VALUE 格式:检查是否存在且包含 GITEA_TOKEN 键
if [ ! -f "$TOKEN_FILE" ] || ! grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
echo "TOKEN_MISSING"
fi
# 如果输出 TOKEN_MISSING,则需要向用户索要令牌
```
索要到令牌后,执行存储:
```bash
# 2. 创建目录并写入令牌(KEY=VALUE 格式)
mkdir -p ~/.codex/skills/PineSoundSkills
# 用 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 grep -q "^GITEA_TOKEN=" ~/.codex/skills/PineSoundSkills/.env 2>/dev/null; then
echo "令牌已持久化存储(KEY=VALUE 格式)"
else
echo "错误: 令牌写入失败"
exit 1
fi
```
```powershell
# Windows 版(KEY=VALUE 格式)
New-Item -ItemType Directory -Force "$env:USERPROFILE\.codex\skills\PineSoundSkills"
"GITEA_TOKEN=$env:GITEA_TOKEN" | Out-File -FilePath "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Encoding ASCII
# 验证
if (Select-String -Path "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Pattern "^GITEA_TOKEN=" -Quiet) {
Write-Host "令牌已持久化存储(KEY=VALUE 格式)"
} else {
Write-Host "错误: 令牌写入失败"
exit 1
}
```
### 1.2 读取令牌(后续使用)
```bash
# Unix — 确保目录存在后再读取令牌(按 KEY 提取)
mkdir -p ~/.codex/skills/PineSoundSkills
# 从 .env 中按 KEY 提取 GITEA_TOKENKEY=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 "错误: .env 中缺少 GITEA_TOKEN,请先设置令牌"
exit 1
fi
```
```powershell
# 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 "错误: .env 中缺少 GITEA_TOKEN,请先设置令牌"
exit 1
}
```
### 1.3 令牌生命周期
| 场景 | 行为 |
|------|------|
| 首次运行 | agent 索要 → escalatefs 写入)→ 写入 `.env` |
| 再次运行 | 直接从 `.env` 读取,跳过询问 |
| 令牌失效(API 返回 401) | 提示用户令牌可能已过期,重新执行 1.1 流程 |
| 用户明确要求重新设置 | 覆盖 `.env`,执行 1.1 流程 |
---
## 二、平台检测
```bash
OS=$(uname -s)
case "$OS" in
Darwin) PLATFORM="macOS" ;;
Linux) PLATFORM="Linux" ;;
MINGW*|MSYS*|CYGWIN*) PLATFORM="Windows" ;;
*) PLATFORM="Unknown" ;;
esac
echo "$PLATFORM"
```
根据 `PLATFORM` 选择对应平台的命令执行。
---
## 三、查看 Gitea 仓库中的 Skills 列表
> **需 escalate**: 以下步骤需要同时 escalate 文件系统写入(mkdir/读取 .env)和网络访问(curl 请求内网 Gitea)。
```bash
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
# 确保目录存在
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=$(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(内网 Gitea,需 escalate 网络权限)
curl -s -H "Authorization: token $GITEA_TOKEN" \
"http://192.168.1.3:10082/api/v1/orgs/PineSoundSkills/repos" \
| python3 -c "
import json, sys
repos = json.load(sys.stdin)
if not repos:
print('(空列表 — 检查令牌或组织名称是否正确)')
for r in repos:
print(f\" {r['name']:30s} {r.get('description', '')}\")
" 2>/dev/null \
|| curl -s -H "Authorization: token $GITEA_TOKEN" \
"http://192.168.1.3:10082/api/v1/orgs/PineSoundSkills/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
mkdir -p ~/.codex/skills/PineSoundSkills
```
```powershell
New-Item -ItemType Directory -Force "$env:USERPROFILE\.codex\skills\PineSoundSkills"
```
### 4.2 克隆仓库
```bash
cd ~/.codex/skills/PineSoundSkills && git clone <仓库地址>
```
```powershell
cd "$env:USERPROFILE\.codex\skills\PineSoundSkills"; git clone <仓库地址>
```
`<仓库地址>` 格式示例: `http://192.168.1.3:10082/PineSoundSkills/<技能仓库名称>.git`
### 4.3 完成后提示用户
> 技能已安装。请执行刷新技能列表操作以使新技能生效(如 `/reload` 或重新加载会话)。
---
## 五、查看已安装的 Skills
**核心要求**: 只读取每个 SKILL.md 的 frontmatter`---``---` 之间的元数据),不加载整个文件内容。
### Unix (macOS / Linux)
```bash
SKILL_DIR=~/.codex/skills/PineSoundSkills
if [ ! -d "$SKILL_DIR" ]; then
echo "技能目录不存在: $SKILL_DIR"
echo "请先安装技能(见第四章)"
exit 0
fi
echo "=== 已安装的 PineSound Skills ==="
for dir in "$SKILL_DIR"/*/; do
[ -d "$dir" ] || continue
name=$(basename "$dir")
skill_file=""
[ -f "${dir}SKILL.md" ] && skill_file="${dir}SKILL.md"
[ -z "$skill_file" ] && [ -f "${dir}skill.md" ] && skill_file="${dir}skill.md"
if [ -n "$skill_file" ]; then
# 仅提取 frontmatter(第一个 --- 到第二个 --- 之间的内容)
meta=$(awk 'BEGIN{c=0} /^---/{c++; next} c==1{print} c==2{exit}' "$skill_file")
skill_name=$(echo "$meta" | grep -i '^name:' | head -1 | sed 's/^name:\s*//i' | sed 's/"//g')
skill_desc=$(echo "$meta" | grep -i '^description:' | head -1 | sed 's/^description:\s*//i' | sed 's/"//g')
echo " 📦 $name"
echo " 名称: ${skill_name:-未设置}"
echo " 说明: ${skill_desc:-无描述}"
else
echo " 📁 $name (无 SKILL.md)"
fi
echo ""
done
```
### Windows (PowerShell)
```powershell
$skillDir = "$env:USERPROFILE\.codex\skills\PineSoundSkills"
if (-not (Test-Path $skillDir)) {
Write-Host "技能目录不存在: $skillDir" -ForegroundColor Yellow
Write-Host "请先安装技能(见第四章)"
exit 0
}
Write-Host "=== 已安装的 PineSound Skills ===" -ForegroundColor Cyan
Get-ChildItem -Directory $skillDir | ForEach-Object {
$folder = $_.FullName
$name = $_.Name
$skillFile = $null
if (Test-Path "$folder\SKILL.md") { $skillFile = "$folder\SKILL.md" }
elseif (Test-Path "$folder\skill.md") { $skillFile = "$folder\skill.md" }
if ($skillFile) {
# 只读取 frontmatter(第一个 --- 到第二个 --- 之间)
$content = Get-Content $skillFile -Raw
if ($content -match '(?s)^---\s*\n(.*?)\n---') {
$frontmatter = $matches[1]
$skillName = ($frontmatter | Select-String '(?im)^name:\s*(.+)').Matches.Value -replace '(?im)^name:\s*', ''
$skillDesc = ($frontmatter | Select-String '(?im)^description:\s*(.+)').Matches.Value -replace '(?im)^description:\s*', ''
}
Write-Host " 📦 $name"
Write-Host " 名称: $(if ($skillName) { $skillName.Trim('" ') } else { '未设置' })"
Write-Host " 说明: $(if ($skillDesc) { $skillDesc.Trim('" ') } else { '无描述' })"
} else {
Write-Host " 📁 $name (无 SKILL.md)" -ForegroundColor DarkYellow
}
Write-Host ""
}
```
---
## 六、卸载 Skills
```bash
# Unix
SKILL_DIR=~/.codex/skills/PineSoundSkills
if [ ! -d "$SKILL_DIR/<文件夹名称>" ]; then
echo "错误: 技能目录不存在: $SKILL_DIR/<文件夹名称>"
exit 1
fi
rm -rf "$SKILL_DIR/<文件夹名称>"
echo "已卸载: <文件夹名称>"
```
```powershell
# Windows
$path = "$env:USERPROFILE\.codex\skills\PineSoundSkills\<文件夹名称>"
if (-not (Test-Path $path)) {
Write-Host "错误: 技能目录不存在: $path"
exit 1
}
Remove-Item -Recurse -Force $path
Write-Host "已卸载: <文件夹名称>"
```
卸载后提示用户刷新技能列表。
---
## 七、技能更新
检查本地已安装的技能仓库与远程是否一致,如有更新则拉取最新代码。
### 7.1 更新指定技能
```bash
# Unix
SKILL_DIR=~/.codex/skills/PineSoundSkills
SKILL_NAME="$1" # 要更新的技能文件夹名称
if [ ! -d "$SKILL_DIR/$SKILL_NAME" ]; then
echo "错误: 技能 '$SKILL_NAME' 未安装"
exit 1
fi
cd "$SKILL_DIR/$SKILL_NAME"
if [ ! -d ".git" ]; then
echo "跳过: '$SKILL_NAME' 不是 git 仓库,无法更新"
exit 0
fi
# 获取远程最新信息
git fetch origin
# 检查本地与远程的差异
BEHIND=$(git rev-list --count HEAD..origin/$(git rev-parse --abbrev-ref HEAD) 2>/dev/null)
if [ "$BEHIND" -gt 0 ]; then
echo "检测到 '$SKILL_NAME' 落后远程 $BEHIND 个提交,正在更新..."
git pull
if [ $? -eq 0 ]; then
echo "✅ '$SKILL_NAME' 已更新到最新版本"
echo "提示: 可能需要刷新技能列表使更新生效(如 /reload)"
else
echo "❌ '$SKILL_NAME' 更新失败,请检查网络或手动处理"
exit 1
fi
else
echo "'$SKILL_NAME' 已是最新"
fi
```
```powershell
# Windows
$skillDir = "$env:USERPROFILE\.codex\skills\PineSoundSkills"
$skillName = $args[0]
if (-not (Test-Path "$skillDir\$skillName")) {
Write-Host "错误: 技能 '$skillName' 未安装"
exit 1
}
Push-Location "$skillDir\$skillName"
if (-not (Test-Path ".git")) {
Write-Host "跳过: '$skillName' 不是 git 仓库,无法更新"
Pop-Location
exit 0
}
git fetch origin
$branch = git rev-parse --abbrev-ref HEAD
$behind = git rev-list --count HEAD..origin/$branch
if ([int]$behind -gt 0) {
Write-Host "检测到 '$skillName' 落后远程 $behind 个提交,正在更新..."
git pull
if ($LASTEXITCODE -eq 0) {
Write-Host "✅ '$skillName' 已更新到最新版本"
Write-Host "提示: 可能需要刷新技能列表使更新生效(如 /reload)"
} else {
Write-Host "❌ '$skillName' 更新失败,请检查网络或手动处理"
exit 1
}
} else {
Write-Host "'$skillName' 已是最新"
}
Pop-Location
```
### 7.2 更新所有已安装的技能
```bash
# Unix — 遍历每个技能目录执行更新
SKILL_DIR=~/.codex/skills/PineSoundSkills
if [ ! -d "$SKILL_DIR" ]; then
echo "没有已安装的技能"
exit 0
fi
echo "=== 检查所有技能更新 ==="
echo ""
UPDATED=0
CURRENT=0
FAILED=0
for dir in "$SKILL_DIR"/*/; do
[ -d "$dir" ] || continue
name=$(basename "$dir")
if [ ! -d "${dir}.git" ]; then
echo " ⏭️ $name (非 git 仓库,跳过)"
continue
fi
cd "$dir"
# 保存当前分支名
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -z "$BRANCH" ]; then
echo " ⏭️ $name (无法获取分支信息,跳过)"
continue
fi
# 获取远程信息
git fetch origin 2>/dev/null
BEHIND=$(git rev-list --count HEAD..origin/$BRANCH 2>/dev/null)
if [ "$BEHIND" -gt 0 ]; then
echo " 🔄 $name (落后 $BEHIND 提交,正在更新...)"
if git pull 2>/dev/null; then
echo " ✅ 更新完成"
UPDATED=$((UPDATED + 1))
else
echo " ❌ 更新失败"
FAILED=$((FAILED + 1))
fi
else
echo "$name (已是最新)"
CURRENT=$((CURRENT + 1))
fi
done
echo ""
echo "=== 更新总结: $CURRENT 已最新, $UPDATED 已更新, $FAILED 失败 ==="
if [ "$UPDATED" -gt 0 ]; then
echo "提示: 如有技能已更新,建议刷新技能列表(如 /reload)"
fi
```
```powershell
# Windows — 遍历每个技能目录执行更新
$skillDir = "$env:USERPROFILE\.codex\skills\PineSoundSkills"
if (-not (Test-Path $skillDir)) {
Write-Host "没有已安装的技能"
exit 0
}
Write-Host "=== 检查所有技能更新 ==="
Write-Host ""
$updated = 0
$current = 0
$failed = 0
Get-ChildItem -Directory $skillDir | ForEach-Object {
$dir = $_.FullName
$name = $_.Name
if (-not (Test-Path "$dir\.git")) {
Write-Host " ⏭️ $name (非 git 仓库,跳过)"
return
}
Push-Location $dir
$branch = git rev-parse --abbrev-ref HEAD
if ([string]::IsNullOrEmpty($branch)) {
Write-Host " ⏭️ $name (无法获取分支信息,跳过)"
Pop-Location
return
}
git fetch origin 2>$null
$behind = git rev-list --count HEAD..origin/$branch
if ([int]$behind -gt 0) {
Write-Host " 🔄 $name (落后 $behind 提交,正在更新...)"
git pull 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host " ✅ 更新完成"
$updated++
} else {
Write-Host " ❌ 更新失败"
$failed++
}
} else {
Write-Host "$name (已是最新)"
$current++
}
Pop-Location
}
Write-Host ""
Write-Host "=== 更新总结: $current 已最新, $updated 已更新, $failed 失败 ==="
if ($updated -gt 0) {
Write-Host "提示: 如有技能已更新,建议刷新技能列表(如 /reload)"
}
```
### 7.3 自动跳过非 git 目录
更新时自动检测每个技能目录是否为 git 仓库(检查 `.git` 目录是否存在):
- **是 git 仓库** → 执行 `git fetch` + `git rev-list --count` 检查差异 → 按需 `git pull`
- **不是 git 仓库** → 跳过并提示"非 git 仓库"
- **git 命令失败** → 跳过并提示错误,不中断其余技能更新
> **注意**: 如果本地有未提交的修改,`git pull` 可能失败或产生合并冲突。如有冲突,agent 应手动解决或提示用户处理。
---
## 八、完整工作流示例
以下端到端脚本(Unix)涵盖令牌获取 → 验证 → 列出 → 安装 → 更新 → 查看全过程:
```bash
#!/bin/bash
SKILL_DIR=~/.codex/skills/PineSoundSkills
TOKEN_FILE="$SKILL_DIR/.env"
GITEA_API="http://192.168.1.3:10082/api/v1"
# ========== 令牌管理 ==========
# 确保目录存在
mkdir -p "$(dirname "$TOKEN_FILE")"
# 检查 .env 是否存在且包含 GITEA_TOKENKEY=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 "GITEA_TOKEN=%s\n" "$USER_TOKEN" > "$TOKEN_FILE"
chmod 600 "$TOKEN_FILE"
# 写入后验证
if grep -q "^GITEA_TOKEN=" "$TOKEN_FILE" 2>/dev/null; then
echo "令牌已持久化存储到 $TOKEN_FILEKEY=VALUE 格式)"
else
echo "错误: 令牌写入失败!"
exit 1
fi
fi
# 按 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
fi
# ========== 列出可安装 Skills ==========
echo ""
echo "=== 可安装的 Skills ==="
HTTP_CODE=$(curl -s -o /tmp/pinesound_repos.json -w "%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" "$GITEA_API/orgs/PineSoundSkills/repos")
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
python3 -c "
import json
with open('/tmp/pinesound_repos.json') as f:
repos = json.load(f)
if not repos:
print('(空列表)')
for r in repos:
print(f\" {r['name']:30s} {r.get('description', '')}\")
"
rm -f /tmp/pinesound_repos.json
# ========== 已安装 Skills 处理(更新 + 查看)==========
echo ""
if [ ! -d "$SKILL_DIR" ]; then
echo "(暂无已安装的技能)"
exit 0
fi
# 更新所有已安装的技能
echo "=== 更新已安装的 Skills ==="
UPDATED=0
for dir in "$SKILL_DIR"/*/; do
[ -d "$dir" ] || continue
name=$(basename "$dir")
if [ ! -d "${dir}.git" ]; then
echo " ⏭️ $name (非 git)" && continue
fi
cd "$dir"
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
[ -z "$BRANCH" ] && continue
git fetch origin 2>/dev/null
BEHIND=$(git rev-list --count HEAD..origin/$BRANCH 2>/dev/null)
if [ "$BEHIND" -gt 0 ]; then
echo " 🔄 $name (落后 $BEHIND 提交,更新中...)"
git pull 2>/dev/null && echo " ✅ 完成" && UPDATED=$((UPDATED + 1)) || echo " ❌ 失败"
else
echo "$name (已最新)"
fi
done
[ "$UPDATED" -gt 0 ] && echo "提示: 建议刷新技能列表(如 /reload" || true
# 查看已安装的技能
echo ""
echo "=== 已安装的 Skills ==="
for dir in "$SKILL_DIR"/*/; do
[ -d "$dir" ] || continue
name=$(basename "$dir")
sf="${dir}SKILL.md"; [ -f "$sf" ] || sf="${dir}skill.md"; [ -f "$sf" ] || sf=""
if [ -n "$sf" ]; then
meta=$(awk 'BEGIN{c=0} /^---/{c++; next} c==1{print} c==2{exit}' "$sf")
sname=$(echo "$meta" | grep -i '^name:' | head -1 | sed 's/^name:\s*//i' | sed 's/"//g')
sdesc=$(echo "$meta" | grep -i '^description:' | head -1 | sed 's/^description:\s*//i' | sed 's/"//g')
echo " $name: ${sname:-$name}${sdesc:-无描述}"
else
echo " $name (无 SKILL.md)"
fi
done
```