feat: RAG, memory, plugins, lessons, file editor, split panes, Markdown rendering, PWA + UI overhaul
All checks were successful
Stable Release / stable (push) Successful in 1m34s

Major additions:
- RAG pipeline (indexing, chunking, search) with sidebar upload button
- Memory system with CRUD API
- Plugins and lessons modules
- MCP discovery and MCP server
- Advanced skills (auto-create, conditional, improver)
- Agent browser/image support, delegate, sessions
- File editor with CodeMirror in split panes
- Markdown rendering via react-markdown + KaTeX + highlight.js
- Raw markdown toggle
- PWA manifest + service worker
- Extension UI redesign with new design tokens and studio-style chat
- Pipeline API for chat streaming
- Mobile responsive layout

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-27 21:01:08 +02:00
parent 62c20eb174
commit 4523bbd42c
50 changed files with 11144 additions and 469 deletions

View File

@@ -0,0 +1,177 @@
package skills
import (
"testing"
"time"
)
func TestCheckActivationNoConditions(t *testing.T) {
skill := &Skill{
Name: "test-skill",
Description: "A test skill",
}
result := CheckActivation(skill, []string{"terminal"})
if !result.Active {
t.Error("expected skill with no conditions to be active")
}
}
func TestCheckActivationRequiresTools(t *testing.T) {
skill := &Skill{
Name: "docker-setup",
RequiresTools: []string{"terminal", "docker"},
}
result := CheckActivation(skill, []string{"terminal", "docker"})
if !result.Active {
t.Error("expected skill to be active when all required tools present")
}
result = CheckActivation(skill, []string{"terminal"})
if result.Active {
t.Error("expected skill to be inactive when required tool missing")
}
}
func TestCheckActivationFallbackForTools(t *testing.T) {
skill := &Skill{
Name: "basic-review",
FallbackForTools: []string{"crush_run", "claude_run"},
}
result := CheckActivation(skill, []string{"terminal"})
if !result.Active {
t.Error("expected fallback skill to activate when primary tools absent")
}
result = CheckActivation(skill, []string{"crush_run", "claude_run"})
if result.Active {
t.Error("expected fallback skill to stay inactive when primary tools present")
}
}
func TestFilterActiveSkills(t *testing.T) {
skills := []Skill{
{Name: "basic", Description: "basic"},
{Name: "needs-docker", RequiresTools: []string{"docker"}},
{Name: "fallback-review", FallbackForTools: []string{"crush_run"}},
}
active := FilterActiveSkills(skills, []string{"terminal"})
if len(active) != 2 {
t.Errorf("expected 2 active skills, got %d", len(active))
}
}
func TestGroupByReadiness(t *testing.T) {
skills := []Skill{
{Name: "basic", Description: "basic"},
{Name: "needs-docker", RequiresTools: []string{"docker"}},
}
available, needsSetup, unsupported := GroupByReadiness(skills, []string{})
if len(available) != 1 {
t.Errorf("expected 1 available, got %d", len(available))
}
if len(unsupported) != 1 {
t.Errorf("expected 1 unsupported, got %d", len(unsupported))
}
_ = needsSetup
}
func TestAnalyzeConversation(t *testing.T) {
snippets := []ConversationSnippet{
{Role: "assistant", Content: "go test ./... -race", Timestamp: time.Now()},
{Role: "assistant", Content: "go test ./... -race -cover", Timestamp: time.Now()},
{Role: "assistant", Content: "go test ./internal/... -v", Timestamp: time.Now()},
}
proposals := AnalyzeConversation(snippets)
if len(proposals) == 0 {
t.Error("expected at least one proposal from recurring patterns")
}
for _, p := range proposals {
if p.Confidence <= 0 {
t.Error("expected positive confidence")
}
if p.CreatedFrom != "conversation" {
t.Errorf("expected created_from=conversation, got %s", p.CreatedFrom)
}
}
}
func TestCategorize(t *testing.T) {
tests := []struct {
pattern string
want string
}{
{"go test", "testing"},
{"docker build", "devops"},
{"git commit", "workflow"},
{"npm test", "testing"},
{"make", "build"},
{"unknown", "general"},
}
for _, tt := range tests {
got := categorize(tt.pattern)
if got != tt.want {
t.Errorf("categorize(%q) = %q, want %q", tt.pattern, got, tt.want)
}
}
}
func TestImproverAnalyze(t *testing.T) {
improver, err := NewSkillImprover()
if err != nil {
t.Fatalf("new improver: %v", err)
}
skill := &Skill{
Name: "test-skill",
Description: "A test skill",
Content: "# Test\n\nSome basic content without structure.",
}
suggestions, err := improver.Analyze(skill, "")
if err != nil {
t.Fatalf("analyze: %v", err)
}
if len(suggestions) == 0 {
t.Error("expected improvement suggestions for minimal skill")
}
}
func TestImproverAnalyzeComplete(t *testing.T) {
improver, _ := NewSkillImprover()
skill := &Skill{
Name: "complete-skill",
Description: "A well-structured skill",
Content: `# Complete Skill
## Steps
1. Do step one
2. Do step two
## Error Handling
- Handle error A
- Handle error B
## When to use
Use this skill when doing X.
`,
Tags: []string{"testing", "go"},
}
suggestions, _ := improver.Analyze(skill, "testing go code")
if len(suggestions) > 2 {
t.Errorf("expected few suggestions for complete skill, got %d", len(suggestions))
}
}