/ internal / skills / provenance.go
provenance.go
 1  package skills
 2  
 3  import (
 4  	"encoding/json"
 5  	"os"
 6  	"path/filepath"
 7  )
 8  
 9  const (
10  	InstallSourceMarketplace = "marketplace"
11  	InstallSourceLocal       = "local"
12  	InstallSourceBundled     = "bundled"
13  
14  	marketplaceProvenanceFile = ".marketplace.json"
15  )
16  
17  type marketplaceProvenance struct {
18  	MarketplaceSlug string `json:"marketplace_slug"`
19  }
20  
21  func installProvenanceForSkill(source, skillDir string) (string, string) {
22  	switch source {
23  	case SourceBundled:
24  		return InstallSourceBundled, ""
25  	case SourceGlobal:
26  		if slug, ok := readMarketplaceProvenance(skillDir); ok {
27  			return InstallSourceMarketplace, slug
28  		}
29  		return InstallSourceLocal, ""
30  	default:
31  		return "", ""
32  	}
33  }
34  
35  func readMarketplaceProvenance(skillDir string) (string, bool) {
36  	data, err := os.ReadFile(filepath.Join(skillDir, marketplaceProvenanceFile))
37  	if err != nil {
38  		return "", false
39  	}
40  
41  	var provenance marketplaceProvenance
42  	if err := json.Unmarshal(data, &provenance); err != nil {
43  		return "", false
44  	}
45  	if err := ValidateSkillName(provenance.MarketplaceSlug); err != nil {
46  		return "", false
47  	}
48  	if provenance.MarketplaceSlug != filepath.Base(skillDir) {
49  		return "", false
50  	}
51  	return provenance.MarketplaceSlug, true
52  }
53  
54  func writeMarketplaceProvenance(skillDir, slug string) error {
55  	data, err := json.Marshal(marketplaceProvenance{MarketplaceSlug: slug})
56  	if err != nil {
57  		return err
58  	}
59  	return atomicWrite(filepath.Join(skillDir, marketplaceProvenanceFile), data)
60  }
61  
62  func clearMarketplaceProvenance(skillDir string) error {
63  	err := os.Remove(filepath.Join(skillDir, marketplaceProvenanceFile))
64  	if err == nil || os.IsNotExist(err) {
65  		return nil
66  	}
67  	return err
68  }