MCPcopy Create free account
hub / github.com/github/gh-aw / addSkillFileWithTracking

Function addSkillFileWithTracking

pkg/cli/add_command.go:713–765  ·  view source on GitHub ↗

addSkillFileWithTracking installs a single skill file from a package to the agentic engine skill directory. The file's path relative to the skill directory is preserved so that nested files (e.g. scripts/ subdirectories) are written with their full structure intact.

(resolved *ResolvedWorkflow, tracker *FileTracker, opts AddOptions, gitRoot string)

Source from the content-addressed store, hash-verified

711// skill directory. The file's path relative to the skill directory is preserved so that
712// nested files (e.g. scripts/ subdirectories) are written with their full structure intact.
713func addSkillFileWithTracking(resolved *ResolvedWorkflow, tracker *FileTracker, opts AddOptions, gitRoot string) error {
714 engineSkillDir := workflow.GetEngineSkillDir(opts.EngineOverride)
715 skillDir := filepath.Join(gitRoot, engineSkillDir, resolved.SkillName)
716 relPath, err := resolveSkillRelativePath(resolved)
717 if err != nil {
718 return err
719 }
720
721 destFile := filepath.Join(skillDir, relPath)
722 relToSkillDir, err := filepath.Rel(skillDir, destFile)
723 if err != nil {
724 return fmt.Errorf("failed to validate destination path %q for skill %q: %w", destFile, resolved.SkillName, err)
725 }
726 if relToSkillDir == ".." || strings.HasPrefix(relToSkillDir, ".."+string(os.PathSeparator)) {
727 return fmt.Errorf("skill file path %q escapes destination skill directory %q", relPath, skillDir)
728 }
729
730 // Ensure the destination directory exists (handles nested subdirectories).
731 if err := os.MkdirAll(filepath.Dir(destFile), constants.DirPermPublic); err != nil {
732 return fmt.Errorf("failed to create skill directory %s: %w", filepath.Dir(destFile), err)
733 }
734
735 addLog.Printf("Adding skill file: dest=%s, skill=%s, content_size=%d bytes", destFile, resolved.SkillName, len(resolved.Content))
736 if opts.Verbose {
737 fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Adding skill file to %s: %s", engineSkillDir+"/"+resolved.SkillName, relPath)))
738 }
739
740 fileExists := false
741 if fileutil.FileExists(destFile) {
742 fileExists = true
743 if !opts.Force {
744 if opts.Verbose {
745 fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Skill file '%s' already exists. Skipping.", destFile)))
746 }
747 return nil
748 }
749 fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Overwriting existing skill file: "+destFile))
750 }
751 if tracker != nil {
752 if fileExists {
753 tracker.TrackModified(destFile)
754 } else {
755 tracker.TrackCreated(destFile)
756 }
757 }
758 if err := os.WriteFile(destFile, resolved.Content, constants.FilePermPublic); err != nil {
759 return fmt.Errorf("failed to write skill file '%s': %w", destFile, err)
760 }
761 if !opts.Quiet {
762 fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Added skill file: %s/%s/%s", engineSkillDir, resolved.SkillName, relPath)))
763 }
764 return nil
765}
766
767func resolveSkillRelativePath(resolved *ResolvedWorkflow) (string, error) {
768 // Determine the relative path under the skill directory so nested files preserve

Calls 10

GetEngineSkillDirFunction · 0.92
FormatInfoMessageFunction · 0.92
FileExistsFunction · 0.92
FormatWarningMessageFunction · 0.92
FormatSuccessMessageFunction · 0.92
resolveSkillRelativePathFunction · 0.85
TrackModifiedMethod · 0.80
TrackCreatedMethod · 0.65
ErrorfMethod · 0.45
PrintfMethod · 0.45