Skip to content

Commit 7ec2ad1

Browse files
salman1993lukealvoeiro
authored andcommitted
feat: make goosehints jinja templated (#43)
1 parent bd4c18c commit 7ec2ad1

File tree

5 files changed

+86
-20
lines changed

5 files changed

+86
-20
lines changed

README.md

+32-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ goose
99
</p>
1010

1111
<p align="center">
12-
<a href="#usage">Usage</a> •
12+
<a href="#usage">Usage</a> •
1313
<a href="#configuration">Configuration</a> •
1414
<a href="#tips">Tips</a> •
1515
<a href="#faq">FAQ</a> •
@@ -25,7 +25,7 @@ To solve problems, `goose` breaks down instructions into sequences of tasks and
2525

2626

2727
## Usage
28-
### Installation
28+
### Installation
2929

3030
To install `goose`, we recommend `pipx`
3131

@@ -36,7 +36,7 @@ brew install pipx
3636
pipx ensurepath
3737
```
3838

39-
Then you can install `goose` with
39+
Then you can install `goose` with
4040

4141
```sh
4242
pipx install goose-ai
@@ -45,7 +45,7 @@ pipx install goose-ai
4545
There is an early version of a VS Code extension with goose support you can try here: https://github.com/square/goose-vscode - more to come soon.
4646

4747
### LLM provider access setup
48-
`goose` works on top of LLMs (you need to bring your own LLM). By default, `goose` uses `openai` as LLM provider. You need to set OPENAI_API_KEY as an environment variable if you would like to use `openai`.
48+
`goose` works on top of LLMs (you need to bring your own LLM). By default, `goose` uses `openai` as LLM provider. You need to set OPENAI_API_KEY as an environment variable if you would like to use `openai`.
4949
```sh
5050
export OPENAI_API_KEY=your_open_api_key
5151
```
@@ -80,10 +80,10 @@ goose session resume
8080

8181
## Configuration
8282

83-
`goose` can detect what LLM and toolkits it can work with from the configuration file `~/.config/goose/profiles.yaml` automatically.
83+
`goose` can detect what LLM and toolkits it can work with from the configuration file `~/.config/goose/profiles.yaml` automatically.
8484

8585
### Configuration options
86-
Example:
86+
Example:
8787

8888
```yaml
8989
default:
@@ -124,12 +124,30 @@ Rules designed to control or manage the output of the model. Moderators that cur
124124

125125
#### toolkits
126126

127-
`goose` can be extended with toolkits, and out of the box there are some available:
127+
`goose` can be extended with toolkits, and out of the box there are some available:
128128

129+
* `developer`: for general-purpose development capabilities, including plan management, shell execution, and file operations, with default shell strategies like using ripgrep.
129130
* `screen`: for letting goose take a look at your screen to help debug or work on designs (gives goose eyes)
130131
* `github`: for awareness and suggestions on how to use github
131132
* `repo_context`: for summarizing and understanding a repository you are working in.
132133

134+
#### Configuring goose per repo
135+
136+
If you are using the `developer` toolkit, `goose` adds the content from `.goosehints`
137+
file in working directory to the system prompt of the `developer` toolkit. The hints
138+
file is meant to provide additional context about your project. The context can be
139+
user-specific or at the project level in which case, you
140+
can commit it to git. `.goosehints` file is Jinja templated so you could have something
141+
like this:
142+
```
143+
Here is an overview of how to contribute:
144+
{% include 'CONTRIBUTING.md' %}
145+
146+
The following justfile shows our common commands:
147+
```just
148+
{% include 'justfile' %}
149+
```
150+
133151
### Examples
134152
#### provider as `anthropic`
135153

@@ -158,33 +176,33 @@ Here are some collected tips we have for working efficiently with `goose`
158176

159177
- **`goose` can and will edit files**. Use a git strategy to avoid losing anything - such as staging your
160178
personal edits and leaving `goose` edits unstaged until reviewed. Or consider using individual commits which can be reverted.
161-
- **`goose` can and will run commands**. You can ask it to check with you first if you are concerned. It will check commands for safety as well.
179+
- **`goose` can and will run commands**. You can ask it to check with you first if you are concerned. It will check commands for safety as well.
162180
- You can interrupt `goose` with `CTRL+C` to correct it or give it more info.
163181
- `goose` works best when solving concrete problems - experiment with how far you need to break that problem
164-
down to get `goose` to solve it. Be specific! E.g. it will likely fail to `"create a banking app"`,
182+
down to get `goose` to solve it. Be specific! E.g. it will likely fail to `"create a banking app"`,
165183
but probably does a good job if prompted with `"create a Fastapi app with an endpoint for deposit and withdrawal
166184
and with account balances stored in mysql keyed by id"`
167185
- If `goose` doesn't have enough context to start with, it might go down the wrong direction. Tell it
168186
to read files that you are referring to or search for objects in code. Even better, ask it to summarize
169187
them for you, which will help it set up its own next steps.
170188
- Refer to any objects in files with something that is easy to search for, such as `"the MyExample class"
171189
- `goose` *loves* to know how to run tests to get a feedback loop going, just like you do. If you tell it how you test things locally and quickly, it can make use of that when working on your project
172-
- You can use `goose` for tasks that would require scripting at times, even looking at your screen and correcting designs/helping you fix bugs, try asking it to help you in a way you would ask a person.
190+
- You can use `goose` for tasks that would require scripting at times, even looking at your screen and correcting designs/helping you fix bugs, try asking it to help you in a way you would ask a person.
173191
- `goose` will make mistakes, and go in the wrong direction from times, feel free to correct it, or start again.
174192
- You can tell `goose` to run things for you continuously (and it will iterate, try, retry) but you can also tell it to check with you before doing things (and then later on tell it to go off on its own and do its best to solve).
175193
- `goose` can run anywhere, doesn't have to be in a repo, just ask it!
176194

177195

178196
### Examples
179197

180-
Here are some examples that have been used:
198+
Here are some examples that have been used:
181199

182200
```
183201
G❯ Looking at the in progress changes in this repo, help me finish off the feature. CONTRIBUTING.md shows how to run the tests.
184202
```
185203

186204
```
187-
G❯ In this golang project, I want you to add open telemetry to help me get started with it. Look in the moneymovements module, run the `just test` command to check things work.
205+
G❯ In this golang project, I want you to add open telemetry to help me get started with it. Look in the moneymovements module, run the `just test` command to check things work.
188206
```
189207
190208
```
@@ -196,7 +214,7 @@ G❯ This is a fresh checkout of a golang project. I do not have my golang envir
196214
```
197215
198216
```
199-
G❯ In this repo, I want you to look at how to add a new provider for azure.
217+
G❯ In this repo, I want you to look at how to add a new provider for azure.
200218
Some hints are in this github issue: https://github.com/square/exchange/issues
201219
/4 (you can use gh cli to access it).
202220
```
@@ -213,7 +231,7 @@ G❯ I want you to help me increase the test coverage in src/java... use mvn tes
213231
214232
## Open Source
215233
216-
Yes, `goose` is open source and always will be. `goose` is released under the ASL2.0 license meaning you can use it however you like.
234+
Yes, `goose` is open source and always will be. `goose` is released under the ASL2.0 license meaning you can use it however you like.
217235
See LICENSE.md for more details.
218236
219237
To run `goose` from source, please see `CONTRIBUTING.md` for instructions on how to set up your environment and you can then run `uv run `goose` session start`.

justfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ integration *FLAGS:
1010
uv run pytest tests -m integration {{FLAGS}}
1111

1212
format:
13-
ruff check . --fix
14-
ruff format .
13+
uvx ruff check . --fix
14+
uvx ruff format .
1515

1616
coverage *FLAGS:
1717
uv run coverage run -m pytest tests -m "not integration" {{FLAGS}}

src/goose/toolkit/developer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from rich.text import Text
1313

1414
from goose.toolkit.base import Toolkit, tool
15-
from goose.toolkit.utils import get_language
15+
from goose.toolkit.utils import get_language, render_template
1616

1717

1818
def keep_unsafe_command_prompt(command: str) -> PromptType:
@@ -37,7 +37,7 @@ def system(self) -> str:
3737
hints_path = Path(".goosehints")
3838
system_prompt = Message.load("prompts/developer.jinja").text
3939
if hints_path.is_file():
40-
goosehints = hints_path.read_text()
40+
goosehints = render_template(hints_path)
4141
system_prompt = f"{system_prompt}\n\nHints:\n{goosehints}"
4242
return system_prompt
4343

src/goose/toolkit/utils.py

+23
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from pathlib import Path
2+
from typing import Optional
23

34
from pygments.lexers import get_lexer_for_filename
45
from pygments.util import ClassNotFound
56

7+
from jinja2 import Environment, FileSystemLoader
8+
69

710
def get_language(filename: Path) -> str:
811
"""
@@ -19,3 +22,23 @@ def get_language(filename: Path) -> str:
1922
return lexer.name
2023
except ClassNotFound:
2124
return ""
25+
26+
27+
def render_template(template_path: Path, context: Optional[dict] = None) -> str:
28+
"""
29+
Renders a Jinja2 template given a Pathlib path, with no context needed.
30+
31+
:param template_path: Path to the Jinja2 template file.
32+
:param context: Optional dictionary containing the context for rendering the template.
33+
:return: Rendered template as a string.
34+
"""
35+
# Ensure the path is absolute and exists
36+
if not template_path.is_absolute():
37+
template_path = template_path.resolve()
38+
39+
if not template_path.exists():
40+
raise FileNotFoundError(f"Template file {template_path} does not exist.")
41+
42+
env = Environment(loader=FileSystemLoader(template_path.parent))
43+
template = env.get_template(template_path.name)
44+
return template.render(context or {})

tests/toolkit/test_developer.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
from pathlib import Path
2-
3-
42
from tempfile import TemporaryDirectory
53
from unittest.mock import MagicMock, Mock
64

75
import pytest
86
from goose.toolkit.base import Requirements
97
from goose.toolkit.developer import Developer
8+
from contextlib import contextmanager
9+
import os
10+
11+
12+
@contextmanager
13+
def change_dir(new_dir):
14+
"""Context manager to temporarily change the current working directory."""
15+
original_dir = os.getcwd()
16+
os.chdir(new_dir)
17+
try:
18+
yield
19+
finally:
20+
os.chdir(original_dir)
1021

1122

1223
@pytest.fixture
@@ -30,6 +41,20 @@ def developer_toolkit():
3041
return toolkit
3142

3243

44+
def test_system_prompt_with_goosehints(temp_dir, developer_toolkit):
45+
readme_file = temp_dir / "README.md"
46+
readme_file.write_text("This is from the README.md file.")
47+
48+
hints_file = temp_dir / ".goosehints"
49+
jinja_template_content = "Hints:\n\n{% include 'README.md' %}\nEnd."
50+
hints_file.write_text(jinja_template_content)
51+
52+
with change_dir(temp_dir):
53+
system_prompt = developer_toolkit.system()
54+
expected_end = "Hints:\n\nThis is from the README.md file.\nEnd."
55+
assert system_prompt.endswith(expected_end)
56+
57+
3358
def test_update_plan(developer_toolkit):
3459
tasks = [
3560
{"description": "Task 1", "status": "planned"},

0 commit comments

Comments
 (0)