Skip to content

Commit beceaef

Browse files
arsenyinfoclaude
andauthored
Replace GitHub API with raw URLs in skills installer (#4509)
## Summary - Skills repo is now public — replace GitHub Contents API with `raw.githubusercontent.com` URLs - Remove `getGitHubToken()`, `addGitHubAuth()`, `fetchSkillFileList()`, `fetchSubdirFiles()` - Get file lists from manifest instead of API directory listing - Net -142 lines ## Dependencies - Requires databricks/databricks-agent-skills PR to add `files` to manifest first ## Test plan - [x] `go build ./experimental/...` - [x] Manual: `go run . experimental aitools skills list` - [x] Manual: `go run . experimental aitools skills install` --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 963e80a commit beceaef

File tree

1 file changed

+10
-152
lines changed

1 file changed

+10
-152
lines changed

experimental/aitools/cmd/skills.go

Lines changed: 10 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import (
77
"io"
88
"net/http"
99
"os"
10-
"os/exec"
1110
"path/filepath"
12-
"strings"
1311
"time"
1412

1513
"github.com/databricks/cli/experimental/aitools/lib/agents"
@@ -33,41 +31,16 @@ func getSkillsBranch() string {
3331
return defaultSkillsRepoBranch
3432
}
3533

36-
// getGitHubToken returns GitHub token from environment or gh CLI.
37-
// TODO: once databricks-agent-skills repo is public, replace GitHub API calls
38-
// with raw.githubusercontent.com URLs and remove authentication logic.
39-
func getGitHubToken() string {
40-
// check environment variables first
41-
if token := os.Getenv("GITHUB_TOKEN"); token != "" {
42-
return token
43-
}
44-
if token := os.Getenv("GH_TOKEN"); token != "" {
45-
return token
46-
}
47-
// try gh CLI
48-
out, err := exec.Command("gh", "auth", "token").Output()
49-
if err == nil {
50-
return strings.TrimSpace(string(out))
51-
}
52-
return ""
53-
}
54-
55-
// addGitHubAuth adds authentication header if token is available.
56-
func addGitHubAuth(req *http.Request) {
57-
if token := getGitHubToken(); token != "" {
58-
req.Header.Set("Authorization", "Bearer "+token)
59-
}
60-
}
61-
6234
type Manifest struct {
6335
Version string `json:"version"`
6436
UpdatedAt string `json:"updated_at"`
6537
Skills map[string]SkillMeta `json:"skills"`
6638
}
6739

6840
type SkillMeta struct {
69-
Version string `json:"version"`
70-
UpdatedAt string `json:"updated_at"`
41+
Version string `json:"version"`
42+
UpdatedAt string `json:"updated_at"`
43+
Files []string `json:"files"`
7144
}
7245

7346
func newSkillsCmd() *cobra.Command {
@@ -114,16 +87,12 @@ Supported agents: Claude Code, Cursor, Codex CLI, OpenCode, GitHub Copilot, Anti
11487
}
11588

11689
func fetchManifest(ctx context.Context) (*Manifest, error) {
117-
// use GitHub API for private repo support
118-
// manifest.json is at repo root, skills are in skillsRepoPath subdirectory
119-
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/contents/manifest.json?ref=%s",
90+
url := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/manifest.json",
12091
skillsRepoOwner, skillsRepoName, getSkillsBranch())
12192
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
12293
if err != nil {
12394
return nil, fmt.Errorf("failed to create request: %w", err)
12495
}
125-
req.Header.Set("Accept", "application/vnd.github.raw+json")
126-
addGitHubAuth(req)
12796

12897
client := &http.Client{Timeout: 30 * time.Second}
12998
resp, err := client.Do(req)
@@ -145,16 +114,13 @@ func fetchManifest(ctx context.Context) (*Manifest, error) {
145114
}
146115

147116
func fetchSkillFile(ctx context.Context, skillName, filePath string) ([]byte, error) {
148-
// use GitHub API for private repo support
149-
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/contents/%s/%s/%s?ref=%s",
150-
skillsRepoOwner, skillsRepoName, skillsRepoPath, skillName, filePath, getSkillsBranch())
117+
url := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s/%s/%s",
118+
skillsRepoOwner, skillsRepoName, getSkillsBranch(), skillsRepoPath, skillName, filePath)
151119

152120
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
153121
if err != nil {
154122
return nil, fmt.Errorf("failed to create request: %w", err)
155123
}
156-
req.Header.Set("Accept", "application/vnd.github.raw+json")
157-
addGitHubAuth(req)
158124

159125
client := &http.Client{Timeout: 30 * time.Second}
160126
resp, err := client.Do(req)
@@ -170,108 +136,6 @@ func fetchSkillFile(ctx context.Context, skillName, filePath string) ([]byte, er
170136
return io.ReadAll(resp.Body)
171137
}
172138

173-
func fetchSkillFileList(ctx context.Context, skillName string) ([]string, error) {
174-
// use GitHub API to list files in skill directory
175-
skillPath := skillsRepoPath + "/" + skillName
176-
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/contents/%s?ref=%s",
177-
skillsRepoOwner, skillsRepoName, skillPath, getSkillsBranch())
178-
179-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
180-
if err != nil {
181-
return nil, err
182-
}
183-
req.Header.Set("Accept", "application/vnd.github.v3+json")
184-
addGitHubAuth(req)
185-
186-
client := &http.Client{Timeout: 30 * time.Second}
187-
resp, err := client.Do(req)
188-
if err != nil {
189-
return nil, err
190-
}
191-
defer resp.Body.Close()
192-
193-
if resp.StatusCode != http.StatusOK {
194-
return nil, fmt.Errorf("failed to list skill files: HTTP %d", resp.StatusCode)
195-
}
196-
197-
var items []struct {
198-
Path string `json:"path"`
199-
Type string `json:"type"`
200-
}
201-
if err := json.NewDecoder(resp.Body).Decode(&items); err != nil {
202-
return nil, err
203-
}
204-
205-
var files []string
206-
for _, item := range items {
207-
switch item.Type {
208-
case "file":
209-
// strip skills/skill-name prefix from path
210-
relPath := strings.TrimPrefix(item.Path, skillPath+"/")
211-
files = append(files, relPath)
212-
case "dir":
213-
// recursively list subdirectory
214-
subFiles, err := fetchSubdirFiles(ctx, item.Path)
215-
if err != nil {
216-
return nil, err
217-
}
218-
for _, sf := range subFiles {
219-
relPath := strings.TrimPrefix(sf, skillPath+"/")
220-
files = append(files, relPath)
221-
}
222-
}
223-
}
224-
225-
return files, nil
226-
}
227-
228-
func fetchSubdirFiles(ctx context.Context, dirPath string) ([]string, error) {
229-
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/contents/%s?ref=%s",
230-
skillsRepoOwner, skillsRepoName, dirPath, getSkillsBranch())
231-
232-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
233-
if err != nil {
234-
return nil, err
235-
}
236-
req.Header.Set("Accept", "application/vnd.github.v3+json")
237-
addGitHubAuth(req)
238-
239-
client := &http.Client{Timeout: 30 * time.Second}
240-
resp, err := client.Do(req)
241-
if err != nil {
242-
return nil, err
243-
}
244-
defer resp.Body.Close()
245-
246-
if resp.StatusCode != http.StatusOK {
247-
return nil, fmt.Errorf("failed to list directory %s: HTTP %d", dirPath, resp.StatusCode)
248-
}
249-
250-
var items []struct {
251-
Path string `json:"path"`
252-
Type string `json:"type"`
253-
}
254-
if err := json.NewDecoder(resp.Body).Decode(&items); err != nil {
255-
return nil, err
256-
}
257-
258-
var files []string
259-
for _, item := range items {
260-
switch item.Type {
261-
case "file":
262-
files = append(files, item.Path)
263-
case "dir":
264-
subFiles, err := fetchSubdirFiles(ctx, item.Path)
265-
if err != nil {
266-
return nil, err
267-
}
268-
files = append(files, subFiles...)
269-
}
270-
}
271-
272-
return files, nil
273-
}
274-
275139
func listSkills(ctx context.Context) error {
276140
manifest, err := fetchManifest(ctx)
277141
if err != nil {
@@ -306,8 +170,8 @@ func installAllSkills(ctx context.Context) error {
306170

307171
printDetectedAgents(ctx, detectedAgents)
308172

309-
for name := range manifest.Skills {
310-
if err := installSkillForAgents(ctx, name, detectedAgents); err != nil {
173+
for name, meta := range manifest.Skills {
174+
if err := installSkillForAgents(ctx, name, meta.Files, detectedAgents); err != nil {
311175
return err
312176
}
313177
}
@@ -347,16 +211,10 @@ func installSkill(ctx context.Context, skillName string) error {
347211

348212
printDetectedAgents(ctx, detectedAgents)
349213

350-
return installSkillForAgents(ctx, skillName, detectedAgents)
214+
return installSkillForAgents(ctx, skillName, manifest.Skills[skillName].Files, detectedAgents)
351215
}
352216

353-
func installSkillForAgents(ctx context.Context, skillName string, detectedAgents []*agents.Agent) error {
354-
// get list of files in skill
355-
files, err := fetchSkillFileList(ctx, skillName)
356-
if err != nil {
357-
return fmt.Errorf("failed to list skill files: %w", err)
358-
}
359-
217+
func installSkillForAgents(ctx context.Context, skillName string, files []string, detectedAgents []*agents.Agent) error {
360218
homeDir, err := os.UserHomeDir()
361219
if err != nil {
362220
return fmt.Errorf("failed to get home directory: %w", err)

0 commit comments

Comments
 (0)