Skip to content

Commit 8498bdf

Browse files
committed
feat(skills): add --allow-hidden-dirs flag to preview command
Add support for the --allow-hidden-dirs flag in `gh skill preview`, matching the existing pattern in `gh skill install`. This allows users to preview skills located in hidden directories (e.g. .claude/skills/, .agents/skills/). Changes: - Add AllowHiddenDirs field to PreviewOptions - Register --allow-hidden-dirs flag on the preview command - Switch from DiscoverSkills to DiscoverSkillsWithOptions to get all skills including hidden-dir ones - Add filterHiddenDirSkills to exclude hidden-dir skills by default, showing a hint when they are found but excluded - Print a warning when --allow-hidden-dirs is used and hidden skills are present - Return an error when only hidden-dir skills exist without the flag Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3ad2958 commit 8498bdf

4 files changed

Lines changed: 329 additions & 27 deletions

File tree

internal/skills/discovery/discovery.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,26 @@ func HasHiddenDirSkills(skills []Skill) bool {
101101
return false
102102
}
103103

104+
// HiddenDirFilterResult holds the outcome of partitioning skills into standard
105+
// and hidden-dir buckets.
106+
type HiddenDirFilterResult struct {
107+
Standard []Skill
108+
HiddenCount int
109+
}
110+
111+
// PartitionHiddenDirSkills splits skills into standard and hidden-dir groups.
112+
func PartitionHiddenDirSkills(skills []Skill) HiddenDirFilterResult {
113+
var r HiddenDirFilterResult
114+
for _, s := range skills {
115+
if s.IsHiddenDirConvention() {
116+
r.HiddenCount++
117+
} else {
118+
r.Standard = append(r.Standard, s)
119+
}
120+
}
121+
return r
122+
}
123+
104124
// ResolvedRef contains the resolved git reference and its SHA.
105125
type ResolvedRef struct {
106126
Ref string // fully qualified ref (refs/heads/*, refs/tags/*) or commit SHA

pkg/cmd/skills/install/install.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,10 +1180,9 @@ func kiroResourcePath(installDir, gitRoot string) string {
11801180
return filepath.ToSlash(installDir)
11811181
}
11821182

1183-
// filterHiddenDirSkills separates hidden-dir skills from the full list and
1184-
// applies the --allow-hidden-dirs flag logic. When the flag is set, all skills
1185-
// are returned and a warning is printed. When the flag is not set, hidden-dir
1186-
// skills are excluded and an error is returned if no standard skills remain.
1183+
// filterHiddenDirSkills applies the --allow-hidden-dirs flag logic. When the
1184+
// flag is set, all skills are returned with a warning. Otherwise, hidden-dir
1185+
// skills are excluded with an error if no standard skills remain.
11871186
func filterHiddenDirSkills(opts *InstallOptions, allSkills []discovery.Skill) ([]discovery.Skill, error) {
11881187
cs := opts.IO.ColorScheme()
11891188

@@ -1198,25 +1197,16 @@ func filterHiddenDirSkills(opts *InstallOptions, allSkills []discovery.Skill) ([
11981197
return allSkills, nil
11991198
}
12001199

1201-
var standard []discovery.Skill
1202-
var hiddenCount int
1203-
for _, s := range allSkills {
1204-
if s.IsHiddenDirConvention() {
1205-
hiddenCount++
1206-
} else {
1207-
standard = append(standard, s)
1208-
}
1209-
}
1210-
1211-
if len(standard) == 0 && hiddenCount > 0 {
1200+
r := discovery.PartitionHiddenDirSkills(allSkills)
1201+
if len(r.Standard) == 0 && r.HiddenCount > 0 {
12121202
return nil, fmt.Errorf(
12131203
"no standard skills found, but %d skill(s) exist in hidden directories\n"+
12141204
" Use --allow-hidden-dirs to include them",
1215-
hiddenCount,
1205+
r.HiddenCount,
12161206
)
12171207
}
12181208

1219-
return standard, nil
1209+
return r.Standard, nil
12201210
}
12211211

12221212
// checkUpstreamProvenance fetches the skill's SKILL.md via the contents API

pkg/cmd/skills/preview/preview.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ type PreviewOptions struct {
3232
ExecutablePath string
3333
RenderFile func(string, string) string
3434

35-
RepoArg string
36-
SkillName string
37-
Version string // resolved from @suffix on SkillName
35+
RepoArg string
36+
SkillName string
37+
Version string // resolved from @suffix on SkillName
38+
AllowHiddenDirs bool // include skills in dot-prefixed directories
3839

3940
repo ghrepo.Interface
4041
}
@@ -110,6 +111,8 @@ func NewCmdPreview(f *cmdutil.Factory, telemetry ghtelemetry.CommandRecorder, ru
110111
},
111112
}
112113

114+
cmd.Flags().BoolVar(&opts.AllowHiddenDirs, "allow-hidden-dirs", false, "Include skills in hidden directories (e.g. .claude/skills/, .agents/skills/)")
115+
113116
return cmd
114117
}
115118

@@ -151,12 +154,17 @@ func previewRun(opts *PreviewOptions) error {
151154
}
152155

153156
opts.IO.StartProgressIndicatorWithLabel("Discovering skills")
154-
skills, err := discovery.DiscoverSkills(apiClient, hostname, owner, repoName, resolved.SHA)
157+
allSkills, err := discovery.DiscoverSkillsWithOptions(apiClient, hostname, owner, repoName, resolved.SHA, discovery.DiscoverOptions{})
155158
opts.IO.StopProgressIndicator()
156159
if err != nil {
157160
return err
158161
}
159162

163+
skills, err := filterHiddenDirSkills(opts, allSkills)
164+
if err != nil {
165+
return err
166+
}
167+
160168
sort.Slice(skills, func(i, j int) bool {
161169
return skills[i].DisplayName() < skills[j].DisplayName()
162170
})
@@ -388,6 +396,39 @@ func isMarkdownFile(filePath string) bool {
388396
}
389397
}
390398

399+
// filterHiddenDirSkills applies the --allow-hidden-dirs flag logic. When the
400+
// flag is set, all skills are returned with a warning. Otherwise, hidden-dir
401+
// skills are excluded with a hint or error.
402+
func filterHiddenDirSkills(opts *PreviewOptions, allSkills []discovery.Skill) ([]discovery.Skill, error) {
403+
cs := opts.IO.ColorScheme()
404+
405+
if opts.AllowHiddenDirs {
406+
if discovery.HasHiddenDirSkills(allSkills) {
407+
fmt.Fprint(opts.IO.ErrOut, heredoc.Docf(`
408+
%[1]s Skills in hidden directories (e.g. .claude/, .agents/) may be installed
409+
copies from another publisher. Verify the skill's origin and check for a
410+
canonical source.
411+
`, cs.WarningIcon()))
412+
}
413+
return allSkills, nil
414+
}
415+
416+
r := discovery.PartitionHiddenDirSkills(allSkills)
417+
if r.HiddenCount > 0 {
418+
if len(r.Standard) == 0 {
419+
return nil, fmt.Errorf(
420+
"no standard skills found, but %d skill(s) exist in hidden directories\n"+
421+
" Use --allow-hidden-dirs to include them",
422+
r.HiddenCount,
423+
)
424+
}
425+
fmt.Fprintf(opts.IO.ErrOut, "%s %d skill(s) in hidden directories were excluded, use --%s to include them\n",
426+
cs.Yellow("!"), r.HiddenCount, "allow-hidden-dirs")
427+
}
428+
429+
return r.Standard, nil
430+
}
431+
391432
func selectSkill(opts *PreviewOptions, skills []discovery.Skill) (discovery.Skill, error) {
392433
if opts.SkillName != "" {
393434
for _, s := range skills {

0 commit comments

Comments
 (0)