blob: 21034eecf36f98d053bba396d2d2e8f7a5dfc528 [file] [log] [blame]
Josh Bleecher Snyder31785ae2025-05-06 01:50:58 +00001package claudetool
2
3import (
4 "context"
5 _ "embed"
6 "encoding/json"
7 "fmt"
8 "log/slog"
9 "strings"
10 "text/template"
11
12 "sketch.dev/llm"
13 "sketch.dev/llm/conversation"
14)
15
16// KnowledgeBase provides on-demand specialized knowledge to the agent.
17var KnowledgeBase = &llm.Tool{
18 Name: kbName,
19 Description: kbDescription,
20 InputSchema: llm.MustSchema(kbInputSchema),
21 Run: kbRun,
22}
23
24// TODO: BYO knowledge bases? could do that for strings.Lines, for example.
25// TODO: support Q&A mode instead of reading full text in?
26
27const (
28 kbName = "knowledge_base"
29 kbDescription = `Retrieve specialized information that you need but don't have in your context.
30
31When to use this tool:
32
33For the "sketch" topic:
34- The user is asking how to USE Sketch itself (not asking Sketch to perform a task)
35- The user has questions about Sketch functionality, setup, or capabilities
36- The user needs help with Sketch-specific concepts like running commands, secrets management, git integration
37- The query is about "How do I do X in Sketch?" or "Is it possible to Y in Sketch?" or just "Help"
38- The user is confused about how a Sketch feature works or how to access it
39
40For the "strings_lines" topic:
41- Any mentions of strings.Lines in the code, by the codereview, or by the user
42- When implementing code that iterates over lines in a Go string
43
44Available topics:
45- sketch: documentation on Sketch usage
46- strings_lines: details about the Go strings.Lines API
47`
48
49 kbInputSchema = `
50{
51 "type": "object",
52 "required": ["topic"],
53 "properties": {
54 "topic": {
55 "type": "string",
56 "description": "Topic to retrieve information about",
57 "enum": ["sketch", "strings_lines"]
58 }
59 }
60}
61`
62)
63
64type kbInput struct {
65 Topic string `json:"topic"`
66}
67
68//go:embed kb/sketch.txt
69var sketchContent string
70
71//go:embed kb/strings_lines.txt
72var stringsLinesContent string
73
74var sketchTemplate = template.Must(template.New("sketch").Parse(sketchContent))
75
76func kbRun(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
77 var input kbInput
78 if err := json.Unmarshal(m, &input); err != nil {
79 return nil, err
80 }
81
82 // Sanitize topic name (simple lowercase conversion for now)
83 topic := strings.ToLower(strings.TrimSpace(input.Topic))
84 slog.InfoContext(ctx, "knowledge base request", "topic", topic)
85
86 // Process content based on topic
87 switch input.Topic {
88 case "sketch":
89 info := conversation.ToolCallInfoFromContext(ctx)
90 sessionID, _ := info.Convo.ExtraData["session_id"].(string)
91 branch, _ := info.Convo.ExtraData["branch"].(string)
92 dot := struct {
93 SessionID string
94 Branch string
95 }{
96 SessionID: sessionID,
97 Branch: branch,
98 }
99 buf := new(strings.Builder)
100 if err := sketchTemplate.Execute(buf, dot); err != nil {
101 return nil, fmt.Errorf("template execution error: %w", err)
102 }
103 return llm.TextContent(buf.String()), nil
104 case "strings_lines":
105 // No special processing for other topics
106 return llm.TextContent(stringsLinesContent), nil
107 default:
108 return nil, fmt.Errorf("unknown topic: %s", input.Topic)
109 }
110}