Refactor Goal Name Extraction and Add Markdown Formatting Stripping
- Enhanced the `extractGoalName` function to skip empty lines, markdown headers, and horizontal rules while extracting the first meaningful line as the goal name. - Implemented a new `stripMarkdownFormatting` function to remove various markdown formatting elements, including bold, italic, inline code, and link syntax. - Added comprehensive unit tests for both `extractGoalName` and `stripMarkdownFormatting` to ensure correct functionality across various markdown scenarios.
This commit is contained in:
parent
0e72aa9d8f
commit
fe67deb3e2
2 changed files with 209 additions and 18 deletions
|
|
@ -2,6 +2,7 @@ package standard
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -523,29 +524,88 @@ func extractGoalName(goals *robottypes.Goals) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Extract first line or first sentence as the goal name
|
||||
// Extract first non-empty, non-markdown-header line as the goal name
|
||||
content := goals.Content
|
||||
// Find first newline
|
||||
if idx := indexAny(content, "\n\r"); idx > 0 {
|
||||
content = content[:idx]
|
||||
}
|
||||
// Limit length
|
||||
if len(content) > 150 {
|
||||
content = content[:150] + "..."
|
||||
}
|
||||
return content
|
||||
}
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
// indexAny returns the index of the first occurrence of any char in chars
|
||||
func indexAny(s string, chars string) int {
|
||||
for i, c := range s {
|
||||
for _, ch := range chars {
|
||||
if c == ch {
|
||||
return i
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// Skip markdown headers (# ## ### etc.)
|
||||
if strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
// Skip markdown horizontal rules (--- or ***)
|
||||
if strings.HasPrefix(line, "---") || strings.HasPrefix(line, "***") {
|
||||
continue
|
||||
}
|
||||
// Found a content line - strip markdown formatting
|
||||
line = stripMarkdownFormatting(line)
|
||||
// Limit length
|
||||
if len(line) > 150 {
|
||||
line = line[:150] + "..."
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// Fallback: if all lines are headers, use first header without # prefix
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// Strip leading # symbols
|
||||
line = strings.TrimLeft(line, "#")
|
||||
line = strings.TrimSpace(line)
|
||||
line = stripMarkdownFormatting(line)
|
||||
if line != "" {
|
||||
if len(line) > 150 {
|
||||
line = line[:150] + "..."
|
||||
}
|
||||
return line
|
||||
}
|
||||
}
|
||||
return -1
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// stripMarkdownFormatting removes common markdown formatting from text
|
||||
func stripMarkdownFormatting(s string) string {
|
||||
// Remove bold/italic markers
|
||||
s = strings.ReplaceAll(s, "**", "")
|
||||
s = strings.ReplaceAll(s, "__", "")
|
||||
s = strings.ReplaceAll(s, "*", "")
|
||||
s = strings.ReplaceAll(s, "_", "")
|
||||
// Remove inline code
|
||||
s = strings.ReplaceAll(s, "`", "")
|
||||
// Remove link syntax [text](url) -> text
|
||||
// Simple approach: just remove brackets and parentheses content
|
||||
for {
|
||||
start := strings.Index(s, "[")
|
||||
if start == -1 {
|
||||
break
|
||||
}
|
||||
end := strings.Index(s[start:], "]")
|
||||
if end == -1 {
|
||||
break
|
||||
}
|
||||
linkEnd := start + end
|
||||
// Check if followed by (url)
|
||||
if linkEnd+1 < len(s) && s[linkEnd+1] == '(' {
|
||||
parenEnd := strings.Index(s[linkEnd+1:], ")")
|
||||
if parenEnd != -1 {
|
||||
// Extract just the link text
|
||||
linkText := s[start+1 : linkEnd]
|
||||
s = s[:start] + linkText + s[linkEnd+1+parenEnd+1:]
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Just remove brackets
|
||||
s = s[:start] + s[start+1:linkEnd] + s[linkEnd+1:]
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
// Verify Executor implements types.Executor
|
||||
|
|
|
|||
|
|
@ -307,6 +307,137 @@ func TestExtractGoalName(t *testing.T) {
|
|||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "First goal", name)
|
||||
})
|
||||
|
||||
t.Run("skips_markdown_h1_header", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "# Goals\nSystem optimization and monitoring",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "System optimization and monitoring", name)
|
||||
})
|
||||
|
||||
t.Run("skips_markdown_h2_header", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "## Goals\n\nPerform system maintenance tasks",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Perform system maintenance tasks", name)
|
||||
})
|
||||
|
||||
t.Run("skips_multiple_markdown_headers", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "## Goals\n### 1. [High] First Goal\nActual description here",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Actual description here", name)
|
||||
})
|
||||
|
||||
t.Run("strips_bold_formatting", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "**Important** task to complete",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Important task to complete", name)
|
||||
})
|
||||
|
||||
t.Run("strips_italic_formatting", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "*Urgent* system update needed",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Urgent system update needed", name)
|
||||
})
|
||||
|
||||
t.Run("strips_inline_code", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "Run `npm install` command",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Run npm install command", name)
|
||||
})
|
||||
|
||||
t.Run("skips_empty_lines", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "\n\n\nFirst real content\nSecond line",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "First real content", name)
|
||||
})
|
||||
|
||||
t.Run("fallback_to_header_content_if_only_headers", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "## Goals\n### Tasks",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Goals", name)
|
||||
})
|
||||
|
||||
t.Run("skips_horizontal_rules", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "---\nActual content here",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Actual content here", name)
|
||||
})
|
||||
|
||||
t.Run("handles_complex_markdown_content", func(t *testing.T) {
|
||||
goals := &robottypes.Goals{
|
||||
Content: "## Goals\n\n### 1. [High] System Maintenance\n**Description**: Perform system optimization based on diagnostic results\n**Reason**: Time-sensitive maintenance",
|
||||
}
|
||||
|
||||
name := extractGoalName(goals)
|
||||
assert.Equal(t, "Description: Perform system optimization based on diagnostic results", name)
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// stripMarkdownFormatting Tests
|
||||
// ============================================================================
|
||||
|
||||
func TestStripMarkdownFormatting(t *testing.T) {
|
||||
t.Run("strips_bold", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("**bold text**")
|
||||
assert.Equal(t, "bold text", result)
|
||||
})
|
||||
|
||||
t.Run("strips_italic_asterisk", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("*italic text*")
|
||||
assert.Equal(t, "italic text", result)
|
||||
})
|
||||
|
||||
t.Run("strips_italic_underscore", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("_italic text_")
|
||||
assert.Equal(t, "italic text", result)
|
||||
})
|
||||
|
||||
t.Run("strips_inline_code", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("`code`")
|
||||
assert.Equal(t, "code", result)
|
||||
})
|
||||
|
||||
t.Run("strips_link_syntax", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("[link text](https://example.com)")
|
||||
assert.Equal(t, "link text", result)
|
||||
})
|
||||
|
||||
t.Run("preserves_plain_text", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("plain text without formatting")
|
||||
assert.Equal(t, "plain text without formatting", result)
|
||||
})
|
||||
|
||||
t.Run("handles_mixed_formatting", func(t *testing.T) {
|
||||
result := stripMarkdownFormatting("**bold** and *italic* and `code`")
|
||||
assert.Equal(t, "bold and italic and code", result)
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue