Skip to content

Commit 9354f36

Browse files
committed
feat: Add ask update command and version pinning
- Add cmd/update.go: Update one or all installed skills - Add version pinning: ask install skill@v1.0.0 - Update SPEC.md with completed features - Update task.md with progress
1 parent 33ee4f2 commit 9354f36

File tree

3 files changed

+116
-5
lines changed

3 files changed

+116
-5
lines changed

SPEC.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ ASK 是一个用于管理 AI Agent 技能的命令行工具,类似于 Homebrew
2020
| `ask init` | 初始化项目,创建 `ask.yaml` ||
2121
| `ask search <keyword>` | 搜索技能(并行多源) ||
2222
| `ask install <skill>` | 安装技能 ||
23+
| `ask install skill@v1.0` | 版本锁定安装 ||
2324
| `ask uninstall <skill>` | 卸载技能 ||
2425
| `ask list` | 列出已安装技能 ||
2526
| `ask info <skill>` | 显示技能详情 ||
27+
| `ask update [skill]` | 更新技能到最新版本 ||
2628

2729
### 待实现功能 ⏳
2830

2931
| 命令 | 功能 | 优先级 |
3032
|------|------|--------|
31-
| `ask update [skill]` | 更新技能到最新版本 | P1 |
32-
| `ask install skill@v1.0` | 版本锁定安装 | P1 |
3333
| `ask create <name>` | 创建技能模板 | P2 |
3434

3535
---
@@ -58,6 +58,14 @@ sources:
5858
url: modelcontextprotocol/servers/src
5959
```
6060
61+
### 可添加的来源(待验证)
62+
63+
| 来源 | URL | 说明 |
64+
|------|-----|------|
65+
| OpenAI Skills | `openai/skills/skills` | OpenAI Codex 官方技能 |
66+
| GitHub Copilot | `github/awesome-copilot/skills` | GitHub Copilot 技能 |
67+
| SkillsMP | skillsmp.com | 技能市场(需API) |
68+
6169
---
6270

6371
## SKILL.md 规范
@@ -184,8 +192,8 @@ ask/
184192
## 待办事项
185193
186194
### 高优先级 (P1)
187-
- [ ] `ask update` 命令
188-
- [ ] 版本锁定 (`skill@v1.0`)
195+
- [x] `ask update` 命令
196+
- [x] 版本锁定 (`skill@v1.0`)
189197
- [ ] Homebrew tap 仓库创建
190198
191199
### 中优先级 (P2)

cmd/install.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,19 @@ var installCmd = &cobra.Command{
1717
Use: "install [url]",
1818
Short: "Install a skill from a git repository",
1919
Long: `Download and install a skill into the ./skills directory.
20-
You can provide a full git URL or a GitHub shorthand (owner/repo).`,
20+
You can provide a full git URL or a GitHub shorthand (owner/repo).
21+
You can also specify a version: owner/repo@v1.0.0`,
2122
Args: cobra.ExactArgs(1),
2223
Run: func(cmd *cobra.Command, args []string) {
2324
input := args[0]
2425

26+
// Parse version if specified (skill@version)
27+
var version string
28+
if idx := strings.LastIndex(input, "@"); idx != -1 && !strings.HasPrefix(input, "git@") {
29+
version = input[idx+1:]
30+
input = input[:idx]
31+
}
32+
2533
// Check if it's a direct URL or shorthand
2634
isURL := strings.HasPrefix(input, "http") || strings.HasPrefix(input, "git@")
2735

@@ -70,6 +78,14 @@ You can provide a full git URL or a GitHub shorthand (owner/repo).`,
7078
os.Exit(1)
7179
}
7280

81+
// Checkout specific version if specified
82+
if version != "" && subDir == "" {
83+
fmt.Printf("Checking out version %s...\n", version)
84+
if err := git.Checkout(destPath, version); err != nil {
85+
fmt.Printf("Warning: Failed to checkout version %s: %v\n", version, err)
86+
}
87+
}
88+
7389
// Update config
7490
cfg, err := config.LoadConfig()
7591
if err == nil {

cmd/update.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
9+
"github.com/spf13/cobra"
10+
"github.com/yeasy/ask/internal/config"
11+
)
12+
13+
// updateCmd represents the update command
14+
var updateCmd = &cobra.Command{
15+
Use: "update [skill-name]",
16+
Short: "Update installed skills to latest version",
17+
Long: `Update one or all installed skills to their latest versions.
18+
If no skill name is provided, updates all installed skills.`,
19+
Run: func(cmd *cobra.Command, args []string) {
20+
cfg, err := config.LoadConfig()
21+
if err != nil {
22+
if os.IsNotExist(err) {
23+
fmt.Println("No ask.yaml found. Run 'ask init' first.")
24+
return
25+
}
26+
fmt.Printf("Error loading config: %v\n", err)
27+
os.Exit(1)
28+
}
29+
30+
if len(cfg.Skills) == 0 {
31+
fmt.Println("No skills installed.")
32+
return
33+
}
34+
35+
// Determine which skills to update
36+
var skillsToUpdate []string
37+
if len(args) > 0 {
38+
// Update specific skill
39+
skillName := args[0]
40+
found := false
41+
for _, s := range cfg.Skills {
42+
if s == skillName {
43+
found = true
44+
break
45+
}
46+
}
47+
if !found {
48+
fmt.Printf("Skill '%s' is not installed.\n", skillName)
49+
os.Exit(1)
50+
}
51+
skillsToUpdate = []string{skillName}
52+
} else {
53+
// Update all skills
54+
skillsToUpdate = cfg.Skills
55+
}
56+
57+
for _, skillName := range skillsToUpdate {
58+
skillPath := filepath.Join("skills", skillName)
59+
60+
// Check if it's a git repository
61+
gitDir := filepath.Join(skillPath, ".git")
62+
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
63+
fmt.Printf("Skipping %s (not a git repository)\n", skillName)
64+
continue
65+
}
66+
67+
fmt.Printf("Updating %s...\n", skillName)
68+
69+
// Run git pull
70+
gitCmd := exec.Command("git", "pull", "--rebase")
71+
gitCmd.Dir = skillPath
72+
gitCmd.Stdout = os.Stdout
73+
gitCmd.Stderr = os.Stderr
74+
75+
if err := gitCmd.Run(); err != nil {
76+
fmt.Printf(" Failed to update %s: %v\n", skillName, err)
77+
continue
78+
}
79+
80+
fmt.Printf(" Updated %s successfully!\n", skillName)
81+
}
82+
},
83+
}
84+
85+
func init() {
86+
rootCmd.AddCommand(updateCmd)
87+
}

0 commit comments

Comments
 (0)