Files
multica/server/internal/daemon/slash_skill.go
Qi Yijiazhen 91c1e51411 feat(editor): add / slash-command palette for invoking agent skills (#3159)
* feat(editor): add / slash-command palette for invoking agent skills

Adds a `/` trigger in the chat box that opens a popover listing the active
agent's skills. Selecting an item inserts a `[/label](slash://skill/<id>)`
token; the daemon extracts those IDs in `buildChatPrompt` and emits an
"Explicitly selected skills:" block using the canonical names from the
agent's skill registry — labels are display-only and never trusted.

Built on Tiptap's `Mention` extension so the suggestion lifecycle,
keyboard routing, and IME handling mirror the existing `@` mention UX.
Item list is sourced from the React Query workspace cache (no per-keystroke
fetch). Gated behind a new `enableSlashCommands` prop so only `chat-input`
opts in; other `ContentEditor` consumers (issue editor, comments) are
unaffected. Read-only markdown surfaces render the token as a `.slash-command`
pill via a custom link renderer + sanitize-schema/url-transform allowlists.

Closes #3108

* fix(i18n): add slash_command editor copy for ko/ja

The PR added slash_command popover empty-state keys to en + zh-Hans only;
locales/parity.test.ts requires every locale to cover every EN key, so ko
and ja failed CI. Add the two keys (no_skills_configured, no_results)
matching existing skill terminology (스킬 / スキル).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:57:42 +08:00

36 lines
693 B
Go

package daemon
import (
"regexp"
"strings"
)
var slashSkillRe = regexp.MustCompile(
`\[/((?:[^\]\\]|\\.)+)\]\(slash://skill/([^)]+)\)`,
)
type SlashSkillRef struct {
Label string
ID string
}
func ExtractSlashSkills(md string) []SlashSkillRef {
matches := slashSkillRe.FindAllStringSubmatch(md, -1)
seen := make(map[string]struct{}, len(matches))
refs := make([]SlashSkillRef, 0, len(matches))
for _, m := range matches {
id := m[2]
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
label := strings.ReplaceAll(m[1], `\[`, "[")
label = strings.ReplaceAll(label, `\]`, "]")
refs = append(refs, SlashSkillRef{Label: label, ID: id})
}
return refs
}