136 lines
3.2 KiB
Go
136 lines
3.2 KiB
Go
package ingest
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var _ Ingester = (*SkillsIngester)(nil)
|
|
|
|
type SkillsIngester struct {
|
|
Version string
|
|
}
|
|
|
|
func NewSkillsIngester() *SkillsIngester {
|
|
return &SkillsIngester{Version: "skills-index/v1"}
|
|
}
|
|
|
|
func (s *SkillsIngester) SourceID() string { return "skills-catalog" }
|
|
|
|
func (s *SkillsIngester) Ingest(path string) (*IngestResult, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read skill: %w", err)
|
|
}
|
|
if len(data) < 20 {
|
|
return nil, nil
|
|
}
|
|
|
|
content := string(data)
|
|
title := extractSkillName(content, path)
|
|
summary := extractSkillSummary(content)
|
|
tags := extractSkillTags(content)
|
|
projects := extractProjects(tags)
|
|
createdAt := extractCreatedAt(content)
|
|
|
|
fp := FingerprintWithMeta(data, map[string]string{
|
|
"source": "skills-catalog",
|
|
"title": title,
|
|
})
|
|
|
|
return &IngestResult{
|
|
Fingerprint: fp,
|
|
SourceID: s.SourceID(),
|
|
SourcePath: path,
|
|
Project: projects,
|
|
ContentType: ".md",
|
|
Title: title,
|
|
Summary: summary,
|
|
CreatedAt: createdAt,
|
|
LineStart: 0,
|
|
LineEnd: len(strings.Split(content, "\n")),
|
|
Confidence: 0.9,
|
|
IngesterVersion: s.Version,
|
|
Provenance: map[string]any{
|
|
"skill_name": title,
|
|
"tags": tags,
|
|
"line_count": len(strings.Split(content, "\n")),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func extractSkillName(content, path string) string {
|
|
base := filepath.Base(filepath.Dir(path))
|
|
if base != "" && base != "." && base != "/" {
|
|
return base
|
|
}
|
|
return strings.TrimSuffix(filepath.Base(path), ".md")
|
|
}
|
|
|
|
func extractSkillSummary(content string) string {
|
|
for _, line := range strings.Split(content, "\n") {
|
|
trimmed := strings.TrimSpace(line)
|
|
if strings.HasPrefix(trimmed, "description:") {
|
|
desc := strings.TrimSpace(trimmed[len("description:"):])
|
|
if len(desc) > 200 {
|
|
desc = desc[:200] + "..."
|
|
}
|
|
return desc
|
|
}
|
|
}
|
|
for _, line := range strings.Split(content, "\n") {
|
|
trimmed := strings.TrimSpace(line)
|
|
if strings.HasPrefix(trimmed, "# ") {
|
|
continue
|
|
}
|
|
if trimmed != "" && !strings.HasPrefix(trimmed, "---") {
|
|
return truncate(trimmed, 200)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func extractSkillTags(content string) []string {
|
|
for _, line := range strings.Split(content, "\n") {
|
|
trimmed := strings.TrimSpace(line)
|
|
if strings.HasPrefix(trimmed, "tags:") {
|
|
raw := strings.TrimSpace(trimmed[len("tags:"):])
|
|
raw = strings.Trim(raw, "\"[]")
|
|
var tags []string
|
|
for _, t := range strings.Split(raw, ",") {
|
|
t = strings.TrimSpace(t)
|
|
t = strings.Trim(t, "\" ")
|
|
if t != "" {
|
|
tags = append(tags, t)
|
|
}
|
|
}
|
|
return tags
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func extractProjects(tags []string) string {
|
|
for _, t := range tags {
|
|
if strings.HasPrefix(t, "scope:project:") {
|
|
return strings.TrimPrefix(t, "scope:project:")
|
|
}
|
|
if strings.HasPrefix(t, "scope:machine:") {
|
|
return "[machine]"
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func extractCreatedAt(content string) string {
|
|
for _, line := range strings.Split(content, "\n") {
|
|
trimmed := strings.TrimSpace(line)
|
|
if strings.HasPrefix(trimmed, "created_at:") || strings.HasPrefix(trimmed, "updated_at:") {
|
|
return strings.TrimSpace(trimmed[strings.Index(trimmed, ":")+1:])
|
|
}
|
|
}
|
|
return ""
|
|
}
|