* feat(mentions): support @mentioning issues in comments - Extend MentionItem type to include "issue" alongside "member"/"agent" - Add issue search (by identifier and title) to mention suggestion dropdown - Render issue mentions with CircleDot icon in autocomplete popup - Issue mentions serialize as [MUL-117 Title](mention://issue/id) (no @ prefix) - Markdown renderer shows issue mentions as clickable links to /issues/:id - Backend mentionRe regex updated to match issue mention type * feat(mentions): auto-expand issue identifiers and add mention format to agent instructions 1. Path A — CLAUDE.md template (runtime_config.go): Add a "## Mentions" section teaching agents the mention serialization format for issues, members, and agents. All agents automatically receive this via the auto-generated CLAUDE.md. 2. Approach 2 — Server-side auto-conversion (internal/mention/): New ExpandIssueIdentifiers() utility that scans comment content for bare issue identifiers (e.g. MUL-117) and replaces them with [MUL-117](mention://issue/<uuid>) mention links. Skips code blocks, inline code, and existing markdown links. Integrated into both: - handler.CreateComment (HTTP API path) - service.createAgentComment (agent task output path)
44 lines
1.3 KiB
Go
44 lines
1.3 KiB
Go
package util
|
|
|
|
import "regexp"
|
|
|
|
// Mention represents a parsed @mention from markdown content.
|
|
type Mention struct {
|
|
Type string // "member", "agent", "issue", or "all"
|
|
ID string // user_id, agent_id, issue_id, or "all"
|
|
}
|
|
|
|
// MentionRe matches [@Label](mention://type/id) or [Label](mention://issue/id) in markdown.
|
|
// The @ prefix is optional to support issue mentions which use [MUL-123](mention://issue/...).
|
|
var MentionRe = regexp.MustCompile(`\[@?[^\]]*\]\(mention://(member|agent|issue|all)/([0-9a-fA-F-]+|all)\)`)
|
|
|
|
// IsMentionAll returns true if the mention is an @all mention.
|
|
func (m Mention) IsMentionAll() bool {
|
|
return m.Type == "all"
|
|
}
|
|
|
|
// ParseMentions extracts deduplicated mentions from markdown content.
|
|
func ParseMentions(content string) []Mention {
|
|
matches := MentionRe.FindAllStringSubmatch(content, -1)
|
|
seen := make(map[string]bool)
|
|
var result []Mention
|
|
for _, m := range matches {
|
|
key := m[1] + ":" + m[2]
|
|
if seen[key] {
|
|
continue
|
|
}
|
|
seen[key] = true
|
|
result = append(result, Mention{Type: m[1], ID: m[2]})
|
|
}
|
|
return result
|
|
}
|
|
|
|
// HasMentionAll returns true if any mention in the slice is an @all mention.
|
|
func HasMentionAll(mentions []Mention) bool {
|
|
for _, m := range mentions {
|
|
if m.IsMentionAll() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|