init
This commit is contained in:
@@ -0,0 +1,629 @@
|
||||
---
|
||||
name: pinesound-skills-installer
|
||||
description: PineSound 内部 Skills 安装器。通过 Gitea API 获取技能列表、安装/卸载/查询已安装的内部技能。当用户需要管理 PineSound 内部技能时使用。
|
||||
allowed-tools: Read, Grep, Edit, Write, Bash
|
||||
---
|
||||
|
||||
# PineSound 内部 Skills 安装器
|
||||
|
||||
---
|
||||
|
||||
## 零、路径定义
|
||||
|
||||
整个 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 通过 `AskUserQuestion` 工具向用户索要令牌 → 用户输入 → 写入 `.env` → 验证。
|
||||
|
||||
```bash
|
||||
# 1. 检查 .env 是否存在且非空
|
||||
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "TOKEN_MISSING"
|
||||
fi
|
||||
|
||||
# 如果输出 TOKEN_MISSING,则需要向用户索要令牌
|
||||
```
|
||||
|
||||
索要到令牌后,执行存储:
|
||||
|
||||
```bash
|
||||
# 2. 创建目录并写入令牌
|
||||
mkdir -p ~/.codex/skills/PineSoundSkills
|
||||
|
||||
# 用 printf 写入,避免 echo 对特殊字符的转义问题
|
||||
printf '%s' "$GITEA_TOKEN" > ~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 3. Unix: 设置权限仅 owner 可读写
|
||||
chmod 600 ~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 4. 验证写入成功
|
||||
if [ -s ~/.codex/skills/PineSoundSkills/.env ]; then
|
||||
echo "令牌已持久化存储"
|
||||
else
|
||||
echo "错误: 令牌写入失败"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows 版
|
||||
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
|
||||
# 验证
|
||||
if ((Get-Item "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env").Length -gt 0) {
|
||||
Write-Host "令牌已持久化存储"
|
||||
} else {
|
||||
Write-Host "错误: 令牌写入失败"
|
||||
exit 1
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 读取令牌(后续使用)
|
||||
|
||||
```bash
|
||||
# Unix — 去除首尾空白,避免换行符污染 API 请求
|
||||
GITEA_TOKEN=$(cat ~/.codex/skills/PineSoundSkills/.env 2>/dev/null | tr -d '\n\r' | xargs)
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "错误: 令牌文件缺失或为空,请先设置令牌"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
$GITEA_TOKEN = (Get-Content "$env:USERPROFILE\.codex\skills\PineSoundSkills\.env" -Raw).Trim()
|
||||
if ([string]::IsNullOrEmpty($GITEA_TOKEN)) {
|
||||
Write-Host "错误: 令牌文件缺失或为空,请先设置令牌"
|
||||
exit 1
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 令牌生命周期
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 首次运行 | agent 用 `AskUserQuestion` 索要 → 写入 `.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 列表
|
||||
|
||||
```bash
|
||||
TOKEN_FILE=~/.codex/skills/PineSoundSkills/.env
|
||||
|
||||
# 读取并清理令牌
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "错误: 令牌文件缺失或为空。请先执行令牌获取流程(见第一章)"
|
||||
exit 1
|
||||
fi
|
||||
GITEA_TOKEN=$(cat "$TOKEN_FILE" 2>/dev/null | tr -d '\n\r' | xargs)
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "错误: 令牌为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 请求 API
|
||||
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,说明令牌无效或已过期。
|
||||
|
||||
---
|
||||
|
||||
## 四、安装 Skills
|
||||
|
||||
### 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"
|
||||
|
||||
# ========== 令牌管理 ==========
|
||||
|
||||
# 检查令牌文件是否存在且非空
|
||||
if [ ! -f "$TOKEN_FILE" ] || [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "令牌文件不存在或为空。"
|
||||
echo "请通过 AskUserQuestion 向用户索要 Gitea 令牌。"
|
||||
|
||||
# [agent 操作] 用 AskUserQuestion 获取用户输入的令牌
|
||||
# 假设用户提供了令牌,存入变量 USER_TOKEN
|
||||
|
||||
mkdir -p "$SKILL_DIR"
|
||||
printf '%s' "$USER_TOKEN" > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
|
||||
# 写入后验证
|
||||
if [ ! -s "$TOKEN_FILE" ]; then
|
||||
echo "错误: 令牌写入失败!"
|
||||
exit 1
|
||||
fi
|
||||
echo "令牌已持久化存储到 $TOKEN_FILE"
|
||||
fi
|
||||
|
||||
# 读取令牌(清理空白字符)
|
||||
GITEA_TOKEN=$(cat "$TOKEN_FILE" 2>/dev/null | 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 "请重新执行令牌获取流程"
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user