@@ -362,7 +362,7 @@ func checkCodexMCP() DoctorCheck {
362362// 1. Checks if opencode.json exists at project root
363363// 2. Validates JSON structure and taskwing-mcp entry
364364// 3. Verifies command is JSON array and type is "local"
365- // 4. Validates .opencode/skills/*/SKILL .md files
365+ // 4. Validates .opencode/commands/* .md files
366366func checkOpenCodeMCP (cwd string ) []DoctorCheck {
367367 checks := []DoctorCheck {}
368368
@@ -468,108 +468,91 @@ func checkOpenCodeMCP(cwd string) []DoctorCheck {
468468 Message : fmt .Sprintf ("%s registered in opencode.json" , serverName ),
469469 })
470470
471- // Check 7: Validate skills (optional - warn if issues)
472- skillsChecks := checkOpenCodeSkills (cwd )
473- checks = append (checks , skillsChecks ... )
471+ // Check 7: Validate commands (optional - warn if issues)
472+ commandsChecks := checkOpenCodeCommands (cwd )
473+ checks = append (checks , commandsChecks ... )
474474
475475 return checks
476476}
477477
478- // checkOpenCodeSkills validates .opencode/skills/*/SKILL.md files
479- func checkOpenCodeSkills (cwd string ) []DoctorCheck {
478+ // checkOpenCodeCommands validates .opencode/commands/*.md files
479+ // OpenCode commands use flat structure: .opencode/commands/<name>.md with description frontmatter
480+ // See: https://opencode.ai/docs/commands/
481+ func checkOpenCodeCommands (cwd string ) []DoctorCheck {
480482 checks := []DoctorCheck {}
481483
482- skillsDir := filepath .Join (cwd , ".opencode" , "skills " )
483- if _ , err := os .Stat (skillsDir ); os .IsNotExist (err ) {
484- // No skills directory - not an error, skills are optional
484+ commandsDir := filepath .Join (cwd , ".opencode" , "commands " )
485+ if _ , err := os .Stat (commandsDir ); os .IsNotExist (err ) {
486+ // No commands directory - not an error, commands are optional
485487 return checks
486488 }
487489
488- // Find all SKILL .md files
489- pattern := filepath .Join (skillsDir , "*" , "SKILL .md" )
490+ // Find all .md files in commands directory (flat structure)
491+ pattern := filepath .Join (commandsDir , "*.md" )
490492 matches , err := filepath .Glob (pattern )
491493 if err != nil || len (matches ) == 0 {
492- // No skills found - not an error
494+ // No commands found - not an error
493495 return checks
494496 }
495497
496- validSkills := 0
497- invalidSkills := []string {}
498+ validCommands := 0
499+ invalidCommands := []string {}
498500
499- for _ , skillPath := range matches {
500- // Get the skill directory name
501- skillDirName := filepath .Base (filepath .Dir (skillPath ))
501+ for _ , cmdPath := range matches {
502+ // Skip marker file
503+ if filepath .Base (cmdPath ) == ".taskwing-managed" {
504+ continue
505+ }
506+
507+ // Get the command name from filename (without .md extension)
508+ cmdName := strings .TrimSuffix (filepath .Base (cmdPath ), ".md" )
502509
503- // Read and validate SKILL.md
504- content , err := os .ReadFile (skillPath )
510+ // Read and validate command file
511+ content , err := os .ReadFile (cmdPath )
505512 if err != nil {
506- invalidSkills = append (invalidSkills , skillDirName + ": unreadable" )
513+ invalidCommands = append (invalidCommands , cmdName + ": unreadable" )
507514 continue
508515 }
509516
510517 // Check for frontmatter markers
511518 contentStr := string (content )
512519 if ! strings .HasPrefix (contentStr , "---" ) {
513- invalidSkills = append (invalidSkills , skillDirName + ": missing YAML frontmatter" )
520+ invalidCommands = append (invalidCommands , cmdName + ": missing YAML frontmatter" )
514521 continue
515522 }
516523
517524 // Extract frontmatter
518525 parts := strings .SplitN (contentStr , "---" , 3 )
519526 if len (parts ) < 3 {
520- invalidSkills = append (invalidSkills , skillDirName + ": incomplete frontmatter" )
527+ invalidCommands = append (invalidCommands , cmdName + ": incomplete frontmatter" )
521528 continue
522529 }
523530
524531 frontmatter := parts [1 ]
525532
526- // Check for required fields (simple validation - name and description)
527- hasName := strings .Contains (frontmatter , "name:" )
533+ // Check for required field (OpenCode only requires description)
528534 hasDescription := strings .Contains (frontmatter , "description:" )
529535
530- if ! hasName || ! hasDescription {
531- missing := []string {}
532- if ! hasName {
533- missing = append (missing , "name" )
534- }
535- if ! hasDescription {
536- missing = append (missing , "description" )
537- }
538- invalidSkills = append (invalidSkills , skillDirName + ": missing " + strings .Join (missing , ", " ))
536+ if ! hasDescription {
537+ invalidCommands = append (invalidCommands , cmdName + ": missing description" )
539538 continue
540539 }
541540
542- // Extract name from frontmatter and verify it matches directory
543- // Simple extraction - look for "name: value" pattern
544- for _ , line := range strings .Split (frontmatter , "\n " ) {
545- line = strings .TrimSpace (line )
546- if strings .HasPrefix (line , "name:" ) {
547- nameValue := strings .TrimSpace (strings .TrimPrefix (line , "name:" ))
548- // Remove quotes if present
549- nameValue = strings .Trim (nameValue , "\" '" )
550- if nameValue != skillDirName {
551- invalidSkills = append (invalidSkills , fmt .Sprintf ("%s: name mismatch (name: %q != dir: %q)" , skillDirName , nameValue , skillDirName ))
552- continue
553- }
554- break
555- }
556- }
557-
558- validSkills ++
541+ validCommands ++
559542 }
560543
561- if len (invalidSkills ) > 0 {
544+ if len (invalidCommands ) > 0 {
562545 checks = append (checks , DoctorCheck {
563- Name : "Skills (OpenCode)" ,
546+ Name : "Commands (OpenCode)" ,
564547 Status : "warn" ,
565- Message : fmt .Sprintf ("%d valid, %d invalid skills " , validSkills , len (invalidSkills )),
566- Hint : "Invalid: " + strings .Join (invalidSkills , "; " ) + ". For development, use taskwing-local-dev-mcp" ,
548+ Message : fmt .Sprintf ("%d valid, %d invalid commands " , validCommands , len (invalidCommands )),
549+ Hint : "Invalid: " + strings .Join (invalidCommands , "; " ) + ". For development, use taskwing-local-dev-mcp" ,
567550 })
568- } else if validSkills > 0 {
551+ } else if validCommands > 0 {
569552 checks = append (checks , DoctorCheck {
570- Name : "Skills (OpenCode)" ,
553+ Name : "Commands (OpenCode)" ,
571554 Status : "ok" ,
572- Message : fmt .Sprintf ("%d skills configured" , validSkills ),
555+ Message : fmt .Sprintf ("%d commands configured" , validCommands ),
573556 })
574557 }
575558
0 commit comments