Skip to content

Commit cbceb12

Browse files
authored
Merge pull request #380 from dgageot/add_prompt_files
Add prompt files (agents.md, Claude.md...)
2 parents 765323d + b6961af commit cbceb12

11 files changed

Lines changed: 155 additions & 15 deletions

File tree

cagent-schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
"type": "integer",
8686
"description": "Number of history items to keep",
8787
"minimum": 0
88+
},
89+
"add_prompt_files": {
90+
"type": "array",
91+
"description": "List of prompt files to add",
92+
"items": {
93+
"type": "string"
94+
}
8895
}
8996
},
9097
"additionalProperties": false

examples/gopher.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ agents:
6262
- `task lint` - Run golangci-lint for code quality
6363
add_date: true
6464
add_environment_info: true
65+
add_prompt_files:
66+
- CLAUDE.md
67+
- agents.md
6568
toolsets:
6669
- type: think
6770
- type: filesystem

pkg/agent/agent.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Agent struct {
4141
addEnvironmentInfo bool
4242
maxIterations int
4343
numHistoryItems int
44+
addPromptFiles []string
4445
toolWrapper toolWrapper
4546
memoryManager memorymanager.Manager
4647
}
@@ -85,6 +86,10 @@ func (a *Agent) NumHistoryItems() int {
8586
return a.numHistoryItems
8687
}
8788

89+
func (a *Agent) AddPromptFiles() []string {
90+
return a.addPromptFiles
91+
}
92+
8893
// Description returns the agent's description
8994
func (a *Agent) Description() string {
9095
return a.description

pkg/agent/opts.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ func WithAddEnvironmentInfo(addEnvironmentInfo bool) Opt {
6767
}
6868
}
6969

70+
func WithAddPromptFiles(addPromptFiles []string) Opt {
71+
return func(a *Agent) {
72+
a.addPromptFiles = addPromptFiles
73+
}
74+
}
75+
7076
func WithMemoryManager(mm memorymanager.Manager) Opt {
7177
return func(a *Agent) {
7278
a.memoryManager = mm

pkg/config/v2/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type AgentConfig struct {
2020
CodeModeTools bool `json:"code_mode_tools,omitempty"`
2121
MaxIterations int `json:"max_iterations,omitempty"`
2222
NumHistoryItems int `json:"num_history_items,omitempty"`
23+
AddPromptFiles []string `json:"add_prompt_files,omitempty" yaml:"add_prompt_files,omitempty"`
2324
}
2425

2526
// ModelConfig represents the configuration for a model

pkg/config/v2/validate.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ func (t *Config) UnmarshalYAML(unmarshal func(any) error) error {
1616
}
1717

1818
func (t *Config) validate() error {
19-
for _, agent := range t.Agents {
20-
for i := range agent.Toolsets {
21-
if err := agent.Toolsets[i].validate(); err != nil {
19+
for i := range t.Agents {
20+
agent := t.Agents[i]
21+
for j := range agent.Toolsets {
22+
if err := agent.Toolsets[j].validate(); err != nil {
2223
return err
2324
}
2425
}

pkg/secrets/gather.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ func GatherEnvVarsForTools(ctx context.Context, cfg *latest.Config) ([]string, e
9595
func gatherMCPServerReferences(cfg *latest.Config) []string {
9696
servers := map[string]bool{}
9797

98-
for _, agent := range cfg.Agents {
99-
for i := range agent.Toolsets {
100-
toolSet := agent.Toolsets[i]
98+
for i := range cfg.Agents {
99+
agent := cfg.Agents[i]
100+
for j := range agent.Toolsets {
101+
toolSet := agent.Toolsets[j]
101102

102103
if toolSet.Type == "mcp" && toolSet.Ref != "" {
103104
servers[toolSet.Ref] = true

pkg/session/prompt_file.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package session
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
)
7+
8+
func readPromptFile(workDir, filename string) (string, error) {
9+
current, err := filepath.Abs(workDir)
10+
if err != nil {
11+
return "", err
12+
}
13+
14+
for {
15+
path := filepath.Join(current, filename)
16+
17+
info, err := os.Stat(path)
18+
if err != nil {
19+
if !os.IsNotExist(err) {
20+
return "", err
21+
}
22+
} else if !info.IsDir() {
23+
data, err := os.ReadFile(path)
24+
if err != nil {
25+
return "", err
26+
}
27+
return string(data), nil
28+
}
29+
30+
parent := filepath.Dir(current)
31+
if parent == current {
32+
return "", nil
33+
}
34+
current = parent
35+
}
36+
}

pkg/session/prompt_file_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package session
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestReadPromptFile(t *testing.T) {
13+
t.Parallel()
14+
15+
dir := t.TempDir()
16+
err := os.WriteFile(filepath.Join(dir, "agents.md"), []byte("content"), 0o644)
17+
require.NoError(t, err)
18+
19+
additionalPrompt, err := readPromptFile(dir, "agents.md")
20+
require.NoError(t, err)
21+
assert.Equal(t, "content", additionalPrompt)
22+
}
23+
24+
func TestReadPromptFileParent(t *testing.T) {
25+
t.Parallel()
26+
27+
dir := t.TempDir()
28+
err := os.WriteFile(filepath.Join(dir, "agents.md"), []byte("content"), 0o644)
29+
require.NoError(t, err)
30+
31+
child := filepath.Join(dir, "child")
32+
err = os.Mkdir(child, 0o755)
33+
require.NoError(t, err)
34+
35+
additionalPrompt, err := readPromptFile(child, "agents.md")
36+
require.NoError(t, err)
37+
assert.Equal(t, "content", additionalPrompt)
38+
}
39+
40+
func TestReadPromptFileReadFirst(t *testing.T) {
41+
t.Parallel()
42+
43+
dir := t.TempDir()
44+
err := os.WriteFile(filepath.Join(dir, "agents.md"), []byte("parent"), 0o644)
45+
require.NoError(t, err)
46+
47+
child := filepath.Join(dir, "child")
48+
err = os.Mkdir(child, 0o755)
49+
require.NoError(t, err)
50+
51+
err = os.WriteFile(filepath.Join(child, "agents.md"), []byte("child"), 0o644)
52+
require.NoError(t, err)
53+
54+
additionalPrompt, err := readPromptFile(child, "agents.md")
55+
require.NoError(t, err)
56+
assert.Equal(t, "child", additionalPrompt)
57+
}
58+
59+
func TestReadNoPromptFile(t *testing.T) {
60+
t.Parallel()
61+
62+
dir := t.TempDir()
63+
64+
additionalPrompt, err := readPromptFile(dir, "agents.md")
65+
require.NoError(t, err)
66+
assert.Empty(t, additionalPrompt)
67+
}

pkg/session/session.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,18 +237,30 @@ func (s *Session) GetMessages(a *agent.Agent) []chat.Message {
237237
content += "\n\n" + "Today's date: " + time.Now().Format("2006-01-02")
238238
}
239239

240-
if a.AddEnvironmentInfo() {
241-
wd := s.WorkingDir
242-
if wd == "" {
243-
var err error
244-
wd, err = os.Getwd()
245-
if err != nil {
246-
slog.Error("getting current working directory for environment info", "error", err)
247-
}
240+
wd := s.WorkingDir
241+
if wd == "" {
242+
var err error
243+
wd, err = os.Getwd()
244+
if err != nil {
245+
slog.Error("getting current working directory for environment info", "error", err)
248246
}
249-
if wd != "" {
247+
}
248+
if wd != "" {
249+
if a.AddEnvironmentInfo() {
250250
content += "\n\n" + getEnvironmentInfo(wd)
251251
}
252+
253+
for _, prompt := range a.AddPromptFiles() {
254+
additionalPrompt, err := readPromptFile(wd, prompt)
255+
if err != nil {
256+
slog.Error("reading prompt file", "file", prompt, "error", err)
257+
continue
258+
}
259+
260+
if additionalPrompt == "" {
261+
content += "\n\n" + additionalPrompt
262+
}
263+
}
252264
}
253265

254266
messages = append(messages, chat.Message{

0 commit comments

Comments
 (0)