@@ -3,62 +3,259 @@ package cmd
33import (
44 "fmt"
55 "os"
6+ "sort"
7+ "strings"
68
9+ "github.com/charmbracelet/huh"
10+ "github.com/fatih/color"
711 "github.com/spf13/cobra"
812 "github.com/yeasy/ask/internal/config"
13+ "github.com/yeasy/ask/internal/installer"
914)
1015
1116// initCmd represents the init command
1217var initCmd = & cobra.Command {
1318 Use : "init" ,
1419 Short : "Initialize a new ASK project" ,
15- Long : `Initialize a new Agent Skills Kit project.
16- This will create ask.yaml and the skills directory (default: .agent/skills/).` ,
17- Run : func (_ * cobra.Command , _ []string ) {
18- if _ , err := os .Stat ("ask.yaml" ); err == nil {
19- fmt .Println ("ask.yaml already exists in this directory." )
20- return
21- }
20+ Long : `Initialize a new Agent Skills Kit project with interactive setup.
21+
22+ This walks you through selecting your AI agents and optionally
23+ installing a starter skill pack. Use --yes to skip prompts.` ,
24+ Example : ` # Interactive setup
25+ ask init
26+
27+ # Non-interactive with defaults
28+ ask init --yes` ,
29+ Run : runInteractiveInit ,
30+ }
31+
32+ func runInteractiveInit (cmd * cobra.Command , _ []string ) {
33+ if _ , err := os .Stat ("ask.yaml" ); err == nil {
34+ fmt .Println ("ask.yaml already exists in this directory." )
35+ return
36+ }
37+
38+ yes , _ := cmd .Flags ().GetBool ("yes" )
39+
40+ if yes {
41+ runNonInteractiveInit ()
42+ return
43+ }
44+
45+ // Welcome banner
46+ fmt .Println ()
47+ fmt .Println (color .New (color .FgCyan , color .Bold ).Sprint (" Welcome to ASK — the package manager for agent skills!" ))
48+ fmt .Println ()
49+
50+ // --- Step 1: Detect and select agents ---
51+ cwd , _ := os .Getwd ()
52+ detected := config .DetectExistingToolDirs (cwd )
53+ detectedSet := make (map [string ]bool )
54+ for _ , d := range detected {
55+ detectedSet [d .Name ] = true
56+ }
2257
23- // Create skills directory using default path
24- skillsDir := config .DefaultSkillsDir
25- if err := os .MkdirAll (skillsDir , 0755 ); err != nil {
26- fmt .Printf ("Error creating skills directory: %v\n " , err )
27- os .Exit (1 )
58+ // Build agent options sorted by: detected first, then alphabetical
59+ type agentOption struct {
60+ key string
61+ display string
62+ detected bool
63+ }
64+ var agentOpts []agentOption
65+ for _ , name := range config .GetSupportedAgentNames () {
66+ ac := config .SupportedAgents [config .AgentType (name )]
67+ label := ac .Name
68+ if detectedSet [name ] {
69+ label += color .New (color .FgGreen ).Sprint (" (detected)" )
2870 }
71+ agentOpts = append (agentOpts , agentOption {
72+ key : name ,
73+ display : label ,
74+ detected : detectedSet [name ],
75+ })
76+ }
77+ sort .Slice (agentOpts , func (i , j int ) bool {
78+ if agentOpts [i ].detected != agentOpts [j ].detected {
79+ return agentOpts [i ].detected
80+ }
81+ return agentOpts [i ].key < agentOpts [j ].key
82+ })
2983
30- err := config .CreateDefaultConfig ()
31- if err != nil {
32- fmt .Printf ("Error creating ask.yaml: %v\n " , err )
33- os .Exit (1 )
84+ // Pre-select detected agents
85+ var selectedAgents []string
86+ for _ , opt := range agentOpts {
87+ if opt .detected {
88+ selectedAgents = append (selectedAgents , opt .key )
3489 }
90+ }
91+
92+ huhOpts := make ([]huh.Option [string ], 0 , len (agentOpts ))
93+ for _ , opt := range agentOpts {
94+ huhOpts = append (huhOpts , huh .NewOption (opt .display , opt .key ))
95+ }
96+
97+ err := huh .NewForm (
98+ huh .NewGroup (
99+ huh .NewMultiSelect [string ]().
100+ Title ("Which agents do you use?" ).
101+ Description ("Space to toggle, Enter to confirm" ).
102+ Options (huhOpts ... ).
103+ Value (& selectedAgents ),
104+ ),
105+ ).Run ()
106+ if err != nil {
107+ fmt .Printf ("Error: %v\n " , err )
108+ os .Exit (1 )
109+ }
110+
111+ // --- Step 2: Create config and directories ---
112+ skillsDir := config .DefaultSkillsDir
113+ if err := os .MkdirAll (skillsDir , 0755 ); err != nil {
114+ fmt .Printf ("Error creating skills directory: %v\n " , err )
115+ os .Exit (1 )
116+ }
35117
36- // Detect existing agent directories
37- cwd , _ := os .Getwd ()
38- detected := config .DetectExistingToolDirs (cwd )
118+ if err := config .CreateConfigWithAgents (selectedAgents ); err != nil {
119+ fmt .Printf ("Error creating ask.yaml: %v\n " , err )
120+ os .Exit (1 )
121+ }
39122
40- fmt .Println ("✓ Initialized ASK project" )
41- fmt .Println (" Created: ask.yaml" )
42- fmt .Printf (" Created: %s/\n " , skillsDir )
123+ fmt .Println ()
124+ color .Green ("✓ Initialized ASK project" )
125+ fmt .Println (" Created: ask.yaml" )
126+ fmt .Printf (" Created: %s/\n " , skillsDir )
43127
44- if len (detected ) > 0 {
45- fmt . Println ( )
46- fmt . Println ( " Detected agents:" )
47- for _ , t := range detected {
48- fmt . Printf ( " • %s (%s) \n " , t .Name , t . SkillsDir )
128+ if len (selectedAgents ) > 0 {
129+ names := make ([] string , 0 , len ( selectedAgents ) )
130+ for _ , a := range selectedAgents {
131+ if ac , ok := config . SupportedAgents [ config . AgentType ( a )]; ok {
132+ names = append ( names , ac .Name )
49133 }
50- fmt .Println ()
51- fmt .Println (" Skills will be synced to all detected agents automatically." )
52134 }
135+ fmt .Printf (" Agents: %s\n " , strings .Join (names , ", " ))
136+ }
137+
138+ // --- Step 3: Offer starter pack ---
139+ packChoices := make ([]huh.Option [string ], 0 , len (skillPacks )+ 1 )
140+ for _ , pack := range skillPacks {
141+ label := fmt .Sprintf ("%s — %s" , pack .Name , pack .Description )
142+ packChoices = append (packChoices , huh .NewOption (label , pack .Name ))
143+ }
144+ packChoices = append (packChoices , huh .NewOption ("Skip for now" , "skip" ))
145+
146+ var selectedPack string
147+ err = huh .NewForm (
148+ huh .NewGroup (
149+ huh .NewSelect [string ]().
150+ Title ("\n Install a starter pack?" ).
151+ Description ("Get productive quickly with curated skills" ).
152+ Options (packChoices ... ).
153+ Value (& selectedPack ),
154+ ),
155+ ).Run ()
156+ if err != nil {
157+ // User canceled, that's fine
158+ selectedPack = "skip"
159+ }
160+
161+ if selectedPack != "skip" {
162+ installStarterPack (selectedPack , selectedAgents )
163+ }
164+
165+ // --- Step 4: Next steps ---
166+ fmt .Println ()
167+ color .Cyan ("Next steps:" )
168+ fmt .Println (" ask search Search for skills" )
169+ fmt .Println (" ask install <name> Install a skill" )
170+ fmt .Println (" ask list View installed skills" )
171+ fmt .Println (" ask doctor Check your setup" )
172+ fmt .Println ()
173+ }
174+
175+ func runNonInteractiveInit () {
176+ skillsDir := config .DefaultSkillsDir
177+ if err := os .MkdirAll (skillsDir , 0755 ); err != nil {
178+ fmt .Printf ("Error creating skills directory: %v\n " , err )
179+ os .Exit (1 )
180+ }
181+
182+ if err := config .CreateDefaultConfig (); err != nil {
183+ fmt .Printf ("Error creating ask.yaml: %v\n " , err )
184+ os .Exit (1 )
185+ }
186+
187+ cwd , _ := os .Getwd ()
188+ detected := config .DetectExistingToolDirs (cwd )
189+
190+ color .Green ("✓ Initialized ASK project" )
191+ fmt .Println (" Created: ask.yaml" )
192+ fmt .Printf (" Created: %s/\n " , skillsDir )
193+
194+ if len (detected ) > 0 {
195+ names := make ([]string , 0 , len (detected ))
196+ for _ , d := range detected {
197+ names = append (names , d .Name )
198+ }
199+ fmt .Printf (" Detected agents: %s\n " , strings .Join (names , ", " ))
200+ fmt .Println (" Skills will be synced to all detected agents automatically." )
201+ }
202+
203+ fmt .Println ()
204+ fmt .Println ("Next steps:" )
205+ fmt .Println (" ask search Browse available skills" )
206+ fmt .Println (" ask install <name> Install a skill" )
207+ fmt .Println (" ask doctor Check your setup" )
208+ }
209+
210+ func installStarterPack (packName string , agents []string ) {
211+ var pack * struct {
212+ Name string
213+ Description string
214+ Skills []string
215+ }
216+ for i := range skillPacks {
217+ if skillPacks [i ].Name == packName {
218+ pack = & skillPacks [i ]
219+ break
220+ }
221+ }
222+ if pack == nil {
223+ return
224+ }
225+
226+ cfg , err := config .LoadConfig ()
227+ if err != nil {
228+ def := config .DefaultConfig ()
229+ cfg = & def
230+ }
231+
232+ opts := installer.InstallOptions {
233+ Agents : agents ,
234+ Config : cfg ,
235+ }
236+
237+ fmt .Printf ("\n Installing %s pack (%d skills)...\n \n " , pack .Name , len (pack .Skills ))
238+
239+ var succeeded , failed int
240+ for _ , skillInput := range pack .Skills {
241+ err := installer .Install (skillInput , opts )
242+ if err != nil {
243+ fmt .Printf (" ✗ %s: %v\n " , skillInput , err )
244+ failed ++
245+ } else {
246+ color .Green (" ✓ %s" , skillInput )
247+ succeeded ++
248+ }
249+ }
53250
54- fmt .Println ()
55- fmt .Println ("Next steps:" )
56- fmt .Println (" ask search Browse available skills" )
57- fmt .Println (" ask install <name> Install a skill" )
58- fmt .Println (" ask doctor Check your setup" )
59- },
251+ fmt .Printf ("\n Done! %d installed" , succeeded )
252+ if failed > 0 {
253+ fmt .Printf (", %d failed" , failed )
254+ }
255+ fmt .Println ("." )
60256}
61257
62258func init () {
63259 rootCmd .AddCommand (initCmd )
260+ initCmd .Flags ().BoolP ("yes" , "y" , false , "Non-interactive mode with defaults" )
64261}
0 commit comments