claudetool: remove knowledge base, focus on about sketch
We should have a more general kb.
Meanwhile, this is important and standalone.
Make it all clearer and sharper.
diff --git a/claudetool/about_sketch.go b/claudetool/about_sketch.go
new file mode 100644
index 0000000..15a4059
--- /dev/null
+++ b/claudetool/about_sketch.go
@@ -0,0 +1,64 @@
+package claudetool
+
+import (
+ "context"
+ _ "embed"
+ "encoding/json"
+ "fmt"
+ "log/slog"
+ "strings"
+ "text/template"
+
+ "sketch.dev/llm"
+ "sketch.dev/llm/conversation"
+)
+
+// AboutSketch provides information about how to use Sketch.
+var AboutSketch = &llm.Tool{
+ Name: "about_sketch",
+ Description: aboutSketchDescription,
+ InputSchema: llm.EmptySchema(),
+ Run: aboutSketchRun,
+}
+
+// TODO: BYO knowledge bases? could do that for strings.Lines, for example.
+// TODO: support Q&A mode instead of reading full text in?
+
+const (
+ aboutSketchDescription = `Provides information about Sketch.
+
+When to use this tool:
+
+- The user is asking how to USE Sketch itself (not asking Sketch to perform a task)
+- The user has questions about Sketch functionality, setup, or capabilities
+- The user needs help with Sketch-specific concepts like running commands, secrets management, git integration
+- The query is about "How do I do X in Sketch?" or "Is it possible to Y in Sketch?" or just "Help"
+- The user is confused about how a Sketch feature works or how to access it
+- You need to know how to interact with the host environment, e.g. port forwarding or pulling changes the user has made outside of Sketch
+`
+)
+
+//go:embed about_sketch.txt
+var aboutSketch string
+
+var aboutSketchTemplate = template.Must(template.New("sketch").Parse(aboutSketch))
+
+func aboutSketchRun(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
+ slog.InfoContext(ctx, "about_sketch called")
+
+ info := conversation.ToolCallInfoFromContext(ctx)
+ sessionID, _ := info.Convo.ExtraData["session_id"].(string)
+ branch, _ := info.Convo.ExtraData["branch"].(string)
+ dot := struct {
+ SessionID string
+ Branch string
+ }{
+ SessionID: sessionID,
+ Branch: branch,
+ }
+ buf := new(strings.Builder)
+ if err := aboutSketchTemplate.Execute(buf, dot); err != nil {
+ return nil, fmt.Errorf("template execution error: %w", err)
+ }
+ return llm.TextContent(buf.String()), nil
+}
diff --git a/claudetool/kb/sketch.txt b/claudetool/about_sketch.txt
similarity index 100%
rename from claudetool/kb/sketch.txt
rename to claudetool/about_sketch.txt
diff --git a/claudetool/browse/browse.go b/claudetool/browse/browse.go
index 40974e3..fdc83c6 100644
--- a/claudetool/browse/browse.go
+++ b/claudetool/browse/browse.go
@@ -531,7 +531,7 @@
"properties": {
"selector": {
"type": "string",
- "description": "CSS selector for the element to screenshot (optional)"
+ "description": "CSS selector for the element to screenshot (optional)"
},
"format": {
"type": "string",
@@ -961,11 +961,8 @@
return &llm.Tool{
Name: "browser_clear_console_logs",
Description: "Clear all captured browser console logs",
- InputSchema: json.RawMessage(`{
- "type": "object",
- "properties": {}
- }`),
- Run: b.clearConsoleLogsRun,
+ InputSchema: llm.EmptySchema(),
+ Run: b.clearConsoleLogsRun,
}
}
diff --git a/claudetool/codereview/differential.go b/claudetool/codereview/differential.go
index f7ec670..e3a819a 100644
--- a/claudetool/codereview/differential.go
+++ b/claudetool/codereview/differential.go
@@ -31,7 +31,7 @@
Name: "codereview",
Description: `Run an automated code review.`,
// If you modify this, update the termui template for prettier rendering.
- InputSchema: llm.MustSchema(`{"type": "object", "properties": {}}`),
+ InputSchema: llm.EmptySchema(),
Run: r.Run,
}
return spec
diff --git a/claudetool/kb/strings_lines.txt b/claudetool/kb/strings_lines.txt
deleted file mode 100644
index 4d237ae..0000000
--- a/claudetool/kb/strings_lines.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-`strings.Lines` — added in Go 1.24
-
-- `func Lines(s string) iter.Seq[string]`
-- Lazily yields successive newline-terminated substrings of `s`. *Includes* the exact trailing `\n`/`\r\n`; if the input ends without a newline the final element is unterminated.
-- Zero copying: each value is just a slice header into `s`, so iteration is O(1) memory.
-
-Idiomatic loop:
-
-```go
-for line := range strings.Lines(buf) {
- handle(line)
-}
-```
-
-### How it differs from common alternatives
-
-- strings.Split – eager `[]string` allocation; caller chooses the separator (newline usually `"\n"`); separator *removed*, so you lose information about line endings and trailing newline presence.
-- strings.SplitSeq – same lazy `iter.Seq[string]` style as `Lines`, but again the caller supplies the separator and it is dropped; use this for arbitrary delimiters.
-- bufio.Scanner – token-oriented reader for any `io.Reader`. Default split function treats `\n` as the delimiter and strips it, so newline bytes are not preserved. Each token is copied into new memory, and there is a 64 KiB default token-size cap (adjustable). Scanner is the choice when the data is coming from a stream rather than an in-memory string.
-
-Use `strings.Lines` by default when the data is already in a string. (bytes.Lines provides the []byte equivalent.)
-
-Fallback to `Split` if you need a slice of lines in memory, to `SplitSeq` for lazy iteration over other separators, and to `bufio.Scanner` for streaming input where newline bytes are irrelevant.
diff --git a/claudetool/knowledge_base.go b/claudetool/knowledge_base.go
deleted file mode 100644
index f5bf0e7..0000000
--- a/claudetool/knowledge_base.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package claudetool
-
-import (
- "context"
- _ "embed"
- "encoding/json"
- "fmt"
- "log/slog"
- "strings"
- "text/template"
-
- "sketch.dev/llm"
- "sketch.dev/llm/conversation"
-)
-
-// KnowledgeBase provides on-demand specialized knowledge to the agent.
-var KnowledgeBase = &llm.Tool{
- Name: kbName,
- Description: kbDescription,
- InputSchema: llm.MustSchema(kbInputSchema),
- Run: kbRun,
-}
-
-// TODO: BYO knowledge bases? could do that for strings.Lines, for example.
-// TODO: support Q&A mode instead of reading full text in?
-
-const (
- kbName = "knowledge_base"
- kbDescription = `Retrieve specialized information that you need but don't have in your context.
-
-When to use this tool:
-
-For the "sketch" topic:
-- The user is asking how to USE Sketch itself (not asking Sketch to perform a task)
-- The user has questions about Sketch functionality, setup, or capabilities
-- The user needs help with Sketch-specific concepts like running commands, secrets management, git integration
-- The query is about "How do I do X in Sketch?" or "Is it possible to Y in Sketch?" or just "Help"
-- The user is confused about how a Sketch feature works or how to access it
-- You need to know how to interact with the host machine, ed forwarding a port or pulling changes that the user has made outside of Sketch
-
-For the "strings_lines" topic:
-- Any mentions of strings.Lines in the code, by the codereview, or by the user
-- When implementing code that iterates over lines in a Go string
-
-Available topics:
-- sketch: documentation on Sketch usage
-- strings_lines: details about the Go strings.Lines API
-`
-
- kbInputSchema = `
-{
- "type": "object",
- "required": ["topic"],
- "properties": {
- "topic": {
- "type": "string",
- "description": "Topic to retrieve information about",
- "enum": ["sketch", "strings_lines"]
- }
- }
-}
-`
-)
-
-type kbInput struct {
- Topic string `json:"topic"`
-}
-
-//go:embed kb/sketch.txt
-var sketchContent string
-
-//go:embed kb/strings_lines.txt
-var stringsLinesContent string
-
-var sketchTemplate = template.Must(template.New("sketch").Parse(sketchContent))
-
-func kbRun(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
- var input kbInput
- if err := json.Unmarshal(m, &input); err != nil {
- return nil, err
- }
-
- // Sanitize topic name (simple lowercase conversion for now)
- topic := strings.ToLower(strings.TrimSpace(input.Topic))
- slog.InfoContext(ctx, "knowledge base request", "topic", topic)
-
- // Process content based on topic
- switch input.Topic {
- case "sketch":
- info := conversation.ToolCallInfoFromContext(ctx)
- sessionID, _ := info.Convo.ExtraData["session_id"].(string)
- branch, _ := info.Convo.ExtraData["branch"].(string)
- dot := struct {
- SessionID string
- Branch string
- }{
- SessionID: sessionID,
- Branch: branch,
- }
- buf := new(strings.Builder)
- if err := sketchTemplate.Execute(buf, dot); err != nil {
- return nil, fmt.Errorf("template execution error: %w", err)
- }
- return llm.TextContent(buf.String()), nil
- case "strings_lines":
- // No special processing for other topics
- return llm.TextContent(stringsLinesContent), nil
- default:
- return nil, fmt.Errorf("unknown topic: %s", input.Topic)
- }
-}
diff --git a/experiment/experiment.go b/experiment/experiment.go
index a038f60..4c6e530 100644
--- a/experiment/experiment.go
+++ b/experiment/experiment.go
@@ -36,7 +36,7 @@
{
Name: "kb",
- Description: "Enable knowledge_base tool",
+ Description: "Enable about_sketch tool",
},
}
byName = map[string]*Experiment{}
diff --git a/llm/llm.go b/llm/llm.go
index 9331961..3de6b7f 100644
--- a/llm/llm.go
+++ b/llm/llm.go
@@ -27,6 +27,10 @@
return json.RawMessage(bytes)
}
+func EmptySchema() json.RawMessage {
+ return MustSchema(`{"type": "object", "properties": {}}`)
+}
+
type Request struct {
Messages []Message
ToolChoice *ToolChoice
diff --git a/loop/agent.go b/loop/agent.go
index 8992bb3..c83dad1 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -956,7 +956,7 @@
}
if experiment.Enabled("kb") {
- convo.Tools = append(convo.Tools, claudetool.KnowledgeBase)
+ convo.Tools = append(convo.Tools, claudetool.AboutSketch)
}
// One-shot mode is non-interactive, multiple choice requires human response
diff --git a/loop/testdata/agent_loop.httprr b/loop/testdata/agent_loop.httprr
index a961a6a..66e823a 100644
--- a/loop/testdata/agent_loop.httprr
+++ b/loop/testdata/agent_loop.httprr
@@ -1,5 +1,5 @@
httprr trace v1
-17065 1994
+17065 2169
POST https://api.anthropic.com/v1/messages HTTP/1.1
Host: api.anthropic.com
User-Agent: Go-http-client/1.1
@@ -546,24 +546,24 @@
Anthropic-Organization-Id: 3c473a21-7208-450a-a9f8-80aebda45c1b
Anthropic-Ratelimit-Input-Tokens-Limit: 200000
Anthropic-Ratelimit-Input-Tokens-Remaining: 200000
-Anthropic-Ratelimit-Input-Tokens-Reset: 2025-05-15T19:39:53Z
+Anthropic-Ratelimit-Input-Tokens-Reset: 2025-05-15T19:40:18Z
Anthropic-Ratelimit-Output-Tokens-Limit: 80000
Anthropic-Ratelimit-Output-Tokens-Remaining: 80000
-Anthropic-Ratelimit-Output-Tokens-Reset: 2025-05-15T19:39:57Z
+Anthropic-Ratelimit-Output-Tokens-Reset: 2025-05-15T19:40:22Z
Anthropic-Ratelimit-Requests-Limit: 4000
Anthropic-Ratelimit-Requests-Remaining: 3999
-Anthropic-Ratelimit-Requests-Reset: 2025-05-15T19:39:51Z
+Anthropic-Ratelimit-Requests-Reset: 2025-05-15T19:40:16Z
Anthropic-Ratelimit-Tokens-Limit: 280000
Anthropic-Ratelimit-Tokens-Remaining: 280000
-Anthropic-Ratelimit-Tokens-Reset: 2025-05-15T19:39:53Z
+Anthropic-Ratelimit-Tokens-Reset: 2025-05-15T19:40:18Z
Cf-Cache-Status: DYNAMIC
-Cf-Ray: 94051a8be9baf997-SJC
+Cf-Ray: 94051b2c59caf99f-SJC
Content-Type: application/json
-Date: Thu, 15 May 2025 19:39:57 GMT
-Request-Id: req_011CP9tAwcwybG36MzDW9PQ3
+Date: Thu, 15 May 2025 19:40:22 GMT
+Request-Id: req_011CP9tCqLUse3jVkYatN3Pe
Server: cloudflare
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Via: 1.1 google
X-Robots-Tag: none
-{"id":"msg_01VWuYGvzq7i6mXQcwpJfmV1","type":"message","role":"assistant","model":"claude-3-7-sonnet-20250219","content":[{"type":"text","text":"Here are the tools available to me:\n\n1. bash - Executes shell commands\n2. keyword_search - Searches for files based on keywords\n3. think - For thinking out loud and planning\n4. title - Sets conversation title\n5. precommit - Creates git branch and provides commit guidance\n6. done - Marks task completion with checklist\n7. codereview - Runs automated code review\n8. multiplechoice - Presents multiple choice options to user\n9. Browser tools (navigate, click, type, wait_for, get_text, eval, etc.)\n10. patch - For precise text edits in files\n\nThese tools allow me to navigate code, execute commands, make code modifications, interact with browsers, and complete various coding tasks."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":3909,"cache_read_input_tokens":0,"output_tokens":169}}
\ No newline at end of file
+{"id":"msg_01C1MJnT26WAW5fnwyrziYLG","type":"message","role":"assistant","model":"claude-3-7-sonnet-20250219","content":[{"type":"text","text":"Here's a brief list of the tools available to me:\n\n1. bash - Execute shell commands\n2. keyword_search - Search for files based on keywords\n3. think - Record thoughts, notes, or plans\n4. title - Set conversation title\n5. precommit - Create a git branch for tracking work\n6. done - Indicate task completion with a checklist\n7. codereview - Run an automated code review\n8. multiplechoice - Present multiple choice questions to the user\n9. Browser tools:\n - browser_navigate, browser_click, browser_type, browser_wait_for, \n - browser_get_text, browser_eval, browser_scroll_into_view,\n - browser_resize, browser_recent_console_logs, browser_clear_console_logs,\n - browser_take_screenshot, browser_read_image\n10. patch - Tool for precise text edits\n\nThese tools help me assist with coding tasks, research, browser automation, and file modifications."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":0,"cache_read_input_tokens":3909,"output_tokens":226}}
\ No newline at end of file
diff --git a/termui/termui.go b/termui/termui.go
index 6dc5f87..6293824 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -43,8 +43,8 @@
🏷️ {{.input.title}}
{{else if eq .msg.ToolName "precommit" -}}
🌱 git branch: sketch/{{.input.branch_name}}
-{{else if eq .msg.ToolName "knowledge_base" -}}
-📚 Knowledge: {{.input.topic}}
+{{else if eq .msg.ToolName "about_sketch" -}}
+📚 About Sketch
{{else if eq .msg.ToolName "str_replace_editor" -}}
✏️ {{.input.file_path -}}
{{else if eq .msg.ToolName "codereview" -}}
diff --git a/webui/src/web-components/sketch-tool-calls.ts b/webui/src/web-components/sketch-tool-calls.ts
index 99b0f21..a5afa50 100644
--- a/webui/src/web-components/sketch-tool-calls.ts
+++ b/webui/src/web-components/sketch-tool-calls.ts
@@ -4,7 +4,7 @@
import { ToolCall } from "../types";
import "./sketch-tool-card";
import "./sketch-tool-card-take-screenshot";
-import "./sketch-tool-card-knowledge-base";
+import "./sketch-tool-card-about-sketch";
@customElement("sketch-tool-calls")
export class SketchToolCalls extends LitElement {
@@ -128,11 +128,11 @@
.open=${open}
.toolCall=${toolCall}
></sketch-tool-card-take-screenshot>`;
- case "knowledge_base":
- return html`<sketch-tool-card-knowledge-base
+ case "about_sketch":
+ return html`<sketch-tool-card-about-sketch
.open=${open}
.toolCall=${toolCall}
- ></sketch-tool-card-knowledge-base>`;
+ ></sketch-tool-card-about-sketch>`;
}
return html`<sketch-tool-card-generic
.open=${open}
diff --git a/webui/src/web-components/sketch-tool-card-knowledge-base.ts b/webui/src/web-components/sketch-tool-card-about-sketch.ts
similarity index 75%
rename from webui/src/web-components/sketch-tool-card-knowledge-base.ts
rename to webui/src/web-components/sketch-tool-card-about-sketch.ts
index ab0e723..b480568 100644
--- a/webui/src/web-components/sketch-tool-card-knowledge-base.ts
+++ b/webui/src/web-components/sketch-tool-card-about-sketch.ts
@@ -18,8 +18,8 @@
}
}
-@customElement("sketch-tool-card-knowledge-base")
-export class SketchToolCardKnowledgeBase extends LitElement {
+@customElement("sketch-tool-card-about-sketch")
+export class SketchToolCardAboutSketch extends LitElement {
@property() toolCall: ToolCall;
@property() open: boolean;
@@ -27,7 +27,7 @@
.summary-text {
font-style: italic;
}
- .knowledge-content {
+ .about-sketch-content {
background: rgb(246, 248, 250);
border-radius: 6px;
padding: 12px;
@@ -36,7 +36,7 @@
overflow-y: auto;
border: 1px solid #e1e4e8;
}
- .topic-label {
+ .sketch-label {
font-weight: bold;
color: #24292e;
}
@@ -46,21 +46,19 @@
`;
render() {
- const inputData = JSON.parse(this.toolCall?.input || "{}");
- const topic = inputData.topic || "unknown";
const resultText = this.toolCall?.result_message?.tool_result || "";
return html`
<sketch-tool-card .open=${this.open} .toolCall=${this.toolCall}>
<span slot="summary" class="summary-text">
- <span class="icon">📚</span> Knowledge: ${topic}
+ <span class="icon">📚</span> About Sketch
</span>
<div slot="input">
- <div><span class="topic-label">Topic:</span> ${topic}</div>
+ <div><span class="sketch-label"></span></div>
</div>
${this.toolCall?.result_message?.tool_result
? html`<div slot="result">
- <div class="knowledge-content">
+ <div class="about-sketch-content">
${unsafeHTML(renderMarkdown(resultText))}
</div>
</div>`
@@ -72,6 +70,6 @@
declare global {
interface HTMLElementTagNameMap {
- "sketch-tool-card-knowledge-base": SketchToolCardKnowledgeBase;
+ "sketch-tool-card-about-sketch": SketchToolCardAboutSketch;
}
}