mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-04-22 02:36:40 +02:00
166 lines
3.6 KiB
Go
166 lines
3.6 KiB
Go
package skillpackage
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var reH2 = regexp.MustCompile(`(?m)^##\s+(.+)$`)
|
|
|
|
const summaryContentRunes = 6000
|
|
|
|
type markdownSection struct {
|
|
Heading string
|
|
Title string
|
|
Content string
|
|
}
|
|
|
|
func splitMarkdownSections(body string) []markdownSection {
|
|
body = strings.TrimSpace(body)
|
|
if body == "" {
|
|
return nil
|
|
}
|
|
idxs := reH2.FindAllStringIndex(body, -1)
|
|
titles := reH2.FindAllStringSubmatch(body, -1)
|
|
if len(idxs) == 0 {
|
|
return []markdownSection{{
|
|
Heading: "",
|
|
Title: "_body",
|
|
Content: body,
|
|
}}
|
|
}
|
|
var out []markdownSection
|
|
for i := range idxs {
|
|
title := strings.TrimSpace(titles[i][1])
|
|
start := idxs[i][0]
|
|
end := len(body)
|
|
if i+1 < len(idxs) {
|
|
end = idxs[i+1][0]
|
|
}
|
|
chunk := strings.TrimSpace(body[start:end])
|
|
out = append(out, markdownSection{
|
|
Heading: "## " + title,
|
|
Title: title,
|
|
Content: chunk,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func deriveSections(body string) []SkillSection {
|
|
md := splitMarkdownSections(body)
|
|
out := make([]SkillSection, 0, len(md))
|
|
for _, ms := range md {
|
|
if ms.Title == "_body" {
|
|
continue
|
|
}
|
|
out = append(out, SkillSection{
|
|
ID: slugifySectionID(ms.Title),
|
|
Title: ms.Title,
|
|
Heading: ms.Heading,
|
|
Level: 2,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func slugifySectionID(title string) string {
|
|
title = strings.TrimSpace(strings.ToLower(title))
|
|
if title == "" {
|
|
return "section"
|
|
}
|
|
var b strings.Builder
|
|
for _, r := range title {
|
|
switch {
|
|
case r >= 'a' && r <= 'z', r >= '0' && r <= '9':
|
|
b.WriteRune(r)
|
|
case r == ' ', r == '-', r == '_':
|
|
b.WriteRune('-')
|
|
}
|
|
}
|
|
s := strings.Trim(b.String(), "-")
|
|
if s == "" {
|
|
return "section"
|
|
}
|
|
return s
|
|
}
|
|
|
|
func findSectionContent(sections []markdownSection, sec string) string {
|
|
sec = strings.TrimSpace(sec)
|
|
if sec == "" {
|
|
return ""
|
|
}
|
|
want := strings.ToLower(sec)
|
|
for _, s := range sections {
|
|
if strings.EqualFold(slugifySectionID(s.Title), want) || strings.EqualFold(s.Title, sec) {
|
|
return s.Content
|
|
}
|
|
if strings.EqualFold(strings.ReplaceAll(s.Title, " ", "-"), want) {
|
|
return s.Content
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func buildSummaryMarkdown(name, description string, tags []string, scripts []SkillScriptInfo, sections []SkillSection, body string) string {
|
|
var b strings.Builder
|
|
if description != "" {
|
|
b.WriteString(description)
|
|
b.WriteString("\n\n")
|
|
}
|
|
if len(tags) > 0 {
|
|
b.WriteString("**Tags**: ")
|
|
b.WriteString(strings.Join(tags, ", "))
|
|
b.WriteString("\n\n")
|
|
}
|
|
if len(scripts) > 0 {
|
|
b.WriteString("### Bundled scripts\n\n")
|
|
for _, sc := range scripts {
|
|
line := "- `" + sc.RelPath + "`"
|
|
if sc.Description != "" {
|
|
line += " — " + sc.Description
|
|
}
|
|
b.WriteString(line)
|
|
b.WriteString("\n")
|
|
}
|
|
b.WriteString("\n")
|
|
}
|
|
if len(sections) > 0 {
|
|
b.WriteString("### Sections\n\n")
|
|
for _, sec := range sections {
|
|
line := "- **" + sec.ID + "**"
|
|
if sec.Title != "" && sec.Title != sec.ID {
|
|
line += ": " + sec.Title
|
|
}
|
|
b.WriteString(line)
|
|
b.WriteString("\n")
|
|
}
|
|
b.WriteString("\n")
|
|
}
|
|
mdSecs := splitMarkdownSections(body)
|
|
preview := body
|
|
if len(mdSecs) > 0 && mdSecs[0].Title != "_body" {
|
|
preview = mdSecs[0].Content
|
|
}
|
|
b.WriteString("### Preview (SKILL.md)\n\n")
|
|
b.WriteString(truncateRunes(strings.TrimSpace(preview), summaryContentRunes))
|
|
b.WriteString("\n\n---\n\n_(Summary for admin UI. Agents use Eino `skill` tool for full SKILL.md progressive loading.)_")
|
|
if name != "" {
|
|
b.WriteString(fmt.Sprintf("\n\n_Skill name: %s_", name))
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func truncateRunes(s string, max int) string {
|
|
if max <= 0 || s == "" {
|
|
return s
|
|
}
|
|
r := []rune(s)
|
|
if len(r) <= max {
|
|
return s
|
|
}
|
|
return string(r[:max]) + "…"
|
|
}
|
|
|