on main help sign in
feat(prompts): template prompts and add github and ripgrep info

Co-authored-by: Tai Groot <tai@taigrr.com>

commit5ad8beee37a55c4d3b9e0137c3ed9960aad42063 authorKieran Klukas <kieran@dunkirk.sh> dateTue 12 May 2026, 11:33 (5 months ago) parent609cc10
12 files changed+66−14
internal/agent/tools/bash.go+3−1 internal/agent/tools/bash.tpl → internal/agent/tools/bash.md.tpl+4−1 internal/agent/tools/fetch.go+9−3 internal/agent/tools/fetch.md−1 internal/agent/tools/fetch.md.tpl+2 internal/agent/tools/tools.go+26 internal/agent/tools/web_fetch.go+9−3 internal/agent/tools/web_fetch.md−1 internal/agent/tools/web_fetch.md.tpl+2 internal/agent/tools/web_search.go+9−3 internal/agent/tools/web_search.md−1 internal/agent/tools/web_search.md.tpl+2
internal/agent/tools/bash.go+3−1
@@ -53,7 +53,7 @@ const (
5353 BashNoOutput = "no output"
5454)
5555
56−//go:embed bash.tpl
56+//go:embed bash.md.tpl
5757var bashDescriptionTmpl []byte
5858
5959var bashDescriptionTpl = template.Must(
@@ -66,6 +66,7 @@ type bashDescriptionData struct {
6666 MaxOutputLength int
6767 Attribution config.Attribution
6868 ModelID string
69+ RgAvailable bool
6970}
7071
7172var bannedCommands = []string{
@@ -149,6 +150,7 @@ func bashDescription(attribution *config.Attribution, modelID string) string {
149150 MaxOutputLength: MaxOutputLength,
150151 Attribution: *attribution,
151152 ModelID: modelID,
153+ RgAvailable: getRg() != "",
152154 }); err != nil {
153155 // this should never happen.
154156 panic("failed to execute bash description template: " + err.Error())
internal/agent/tools/bash.tpl → internal/agent/tools/bash.md.tplrenamed+4−1
@@ -21,6 +21,9 @@ Common shell builtins and core utils available on Windows.
2121- Chain with ';' or '&&', avoid newlines except in quoted strings
2222- Each command runs in independent shell (no state persistence between calls)
2323- Prefer absolute paths over 'cd' (use 'cd' only if user explicitly requests)
24+{{- if .RgAvailable }}
25+- Ripgrep (`rg`) is available; prefer it over `grep` for faster, more intuitive searching
26+{{- end }}
2427</usage_notes>
2528
2629<background_execution>
@@ -82,7 +85,7 @@ When user asks to create git commit:
8285
83866. Run git status to verify.
8487
85−Notes: Use "git commit -am" when possible, don't stage unrelated files, NEVER update config, don't push, no -i flags, no empty commits, return empty response.
88+Notes: Use "git commit -am" when possible, don't stage unrelated files, NEVER update config, don't push, no -i flags, no empty commits, return empty response, when rebasing always use -m or GIT_EDITOR=true.
8689</git_commits>
8790
8891<pull_requests>
internal/agent/tools/fetch.go+9−3
@@ -4,6 +4,7 @@ import (
44 "context"
55 _ "embed"
66 "fmt"
7+ "html/template"
78 "io"
89 "net/http"
910 "strings"
@@ -21,8 +22,13 @@ const (
2122 MaxFetchSize = 100 * 1024 // 100KB
2223)
2324
24−//go:embed fetch.md
25−var fetchDescription string
25+//go:embed fetch.md.tpl
26+var fetchDescriptionTmpl []byte
27+
28+var fetchDescriptionTpl = template.Must(
29+ template.New("fetchDescription").
30+ Parse(string(fetchDescriptionTmpl)),
31+)
2632
2733func NewFetchTool(permissions permission.Service, workingDir string, client *http.Client) fantasy.AgentTool {
2834 if client == nil {
@@ -39,7 +45,7 @@ func NewFetchTool(permissions permission.Service, workingDir string, client *htt
3945
4046 return fantasy.NewParallelAgentTool(
4147 FetchToolName,
42− fetchDescription,
48+ renderToolDescription(fetchDescriptionTpl),
4349 func(ctx context.Context, params FetchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
4450 if params.URL == "" {
4551 return fantasy.NewTextErrorResponse("URL parameter is required"), nil
internal/agent/tools/fetch.mddeleted−1
@@ -1 +0,0 @@
1−Fetch raw content from a URL as text, markdown, or html (max 100KB); no AI processing. For analysis or extraction use agentic_fetch.
internal/agent/tools/fetch.md.tplnew file+2
@@ -0,0 +1,2 @@
1+Fetch raw content from a URL as text, markdown, or html (max 100KB); no AI processing. For analysis or extraction use agentic_fetch.
2+{{- if .GhAvailable }} For GitHub content when an exact repo, issue, or PR link is provided, use `gh` CLI in bash instead.{{- end }}
internal/agent/tools/tools.go+26
@@ -1,7 +1,10 @@
11package tools
22
33import (
4+ "bytes"
45 "context"
6+ "html/template"
7+ "os/exec"
58
69 "charm.land/fantasy"
710)
@@ -64,3 +67,26 @@ func NewPermissionDeniedResponse() fantasy.ToolResponse {
6467 resp.StopTurn = true
6568 return resp
6669}
70+
71+// ghAvailable indicates whether the `gh` CLI is available on PATH.
72+var ghAvailable = func() bool {
73+ _, err := exec.LookPath("gh")
74+ return err == nil
75+}()
76+
77+// toolDescriptionData is the common data structure for tool description templates.
78+type toolDescriptionData struct {
79+ GhAvailable bool
80+}
81+
82+// renderToolDescription renders a tool description template with the given data.
83+func renderToolDescription(tmpl *template.Template) string {
84+ data := toolDescriptionData{
85+ GhAvailable: ghAvailable,
86+ }
87+ var out bytes.Buffer
88+ if err := tmpl.Execute(&out, data); err != nil {
89+ panic("failed to execute tool description template: " + err.Error())
90+ }
91+ return out.String()
92+}
internal/agent/tools/web_fetch.go+9−3
@@ -4,6 +4,7 @@ import (
44 "context"
55 _ "embed"
66 "fmt"
7+ "html/template"
78 "net/http"
89 "os"
910 "strings"
@@ -12,8 +13,13 @@ import (
1213 "charm.land/fantasy"
1314)
1415
15−//go:embed web_fetch.md
16−var webFetchToolDescription string
16+//go:embed web_fetch.md.tpl
17+var webFetchDescriptionTmpl []byte
18+
19+var webFetchDescriptionTpl = template.Must(
20+ template.New("webFetchDescription").
21+ Parse(string(webFetchDescriptionTmpl)),
22+)
1723
1824// NewWebFetchTool creates a simple web fetch tool for sub-agents (no permissions needed).
1925func NewWebFetchTool(workingDir string, client *http.Client) fantasy.AgentTool {
@@ -31,7 +37,7 @@ func NewWebFetchTool(workingDir string, client *http.Client) fantasy.AgentTool {
3137
3238 return fantasy.NewParallelAgentTool(
3339 WebFetchToolName,
34− webFetchToolDescription,
40+ renderToolDescription(webFetchDescriptionTpl),
3541 func(ctx context.Context, params WebFetchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
3642 if params.URL == "" {
3743 return fantasy.NewTextErrorResponse("url is required"), nil
internal/agent/tools/web_fetch.mddeleted−1
@@ -1 +0,0 @@
1−Fetch a web URL and return content as markdown; for use inside sub-agents. Large pages (>50KB) are saved to a temp file for grep/view.
internal/agent/tools/web_fetch.md.tplnew file+2
@@ -0,0 +1,2 @@
1+Fetch a web URL and return content as markdown; for use inside sub-agents. Large pages (>50KB) are saved to a temp file for grep/view.
2+{{- if .GhAvailable }} For GitHub content when an exact repo, issue, or PR link is provided, use `gh` CLI in bash instead.{{- end }}
internal/agent/tools/web_search.go+9−3
@@ -3,6 +3,7 @@ package tools
33import (
44 "context"
55 _ "embed"
6+ "html/template"
67 "log/slog"
78 "net/http"
89 "time"
@@ -10,8 +11,13 @@ import (
1011 "charm.land/fantasy"
1112)
1213
13−//go:embed web_search.md
14−var webSearchToolDescription string
14+//go:embed web_search.md.tpl
15+var webSearchDescriptionTmpl []byte
16+
17+var webSearchDescriptionTpl = template.Must(
18+ template.New("webSearchDescription").
19+ Parse(string(webSearchDescriptionTmpl)),
20+)
1521
1622// NewWebSearchTool creates a web search tool for sub-agents (no permissions needed).
1723func NewWebSearchTool(client *http.Client) fantasy.AgentTool {
@@ -29,7 +35,7 @@ func NewWebSearchTool(client *http.Client) fantasy.AgentTool {
2935
3036 return fantasy.NewParallelAgentTool(
3137 WebSearchToolName,
32− webSearchToolDescription,
38+ renderToolDescription(webSearchDescriptionTpl),
3339 func(ctx context.Context, params WebSearchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
3440 if params.Query == "" {
3541 return fantasy.NewTextErrorResponse("query is required"), nil
internal/agent/tools/web_search.mddeleted−1
@@ -1 +0,0 @@
1−Search the web via DuckDuckGo; returns titles, URLs, and snippets. Follow up with web_fetch to get full page content.
internal/agent/tools/web_search.md.tplnew file+2
@@ -0,0 +1,2 @@
1+Search the web via DuckDuckGo; returns titles, URLs, and snippets. Follow up with web_fetch to get full page content.
2+{{- if .GhAvailable }} For GitHub searches when an exact repo name, issue, or link is provided, use `gh search` in bash instead.{{- end }}