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)
-	}
-}