on main help sign in
feat(config): default to AGENTS.md w/ new setting (#1403)
4e40b47Amolith
internal/agent/prompts.go+10−4
@@ -1,9 +1,11 @@
1package agent
1package agent
2
2
3import (
3import (
4+ "context"
4 _ "embed"
5 _ "embed"
5
6
6 "github.com/charmbracelet/crush/internal/agent/prompt"
7 "github.com/charmbracelet/crush/internal/agent/prompt"
8+ "github.com/charmbracelet/crush/internal/config"
7)
9)
8
10
9//go:embed templates/coder.md.tpl
11//go:embed templates/coder.md.tpl
@@ -12,8 +14,8 @@ var coderPromptTmpl []byte
12//go:embed templates/task.md.tpl
14//go:embed templates/task.md.tpl
13var taskPromptTmpl []byte
15var taskPromptTmpl []byte
14
16
15−//go:embed templates/initialize.md
17+//go:embed templates/initialize.md.tpl
16−var initializePrompt []byte
18+var initializePromptTmpl []byte
17
19
18func coderPrompt(opts ...prompt.Option) (*prompt.Prompt, error) {
20func coderPrompt(opts ...prompt.Option) (*prompt.Prompt, error) {
19 systemPrompt, err := prompt.NewPrompt("coder", string(coderPromptTmpl), opts...)
21 systemPrompt, err := prompt.NewPrompt("coder", string(coderPromptTmpl), opts...)
@@ -31,6 +33,10 @@ func taskPrompt(opts ...prompt.Option) (*prompt.Prompt, error) {
31 return systemPrompt, nil
33 return systemPrompt, nil
32}
34}
33
35
34−func InitializePrompt() string {
36+func InitializePrompt(cfg config.Config) (string, error) {
35− return string(initializePrompt)
37+ systemPrompt, err := prompt.NewPrompt("initialize", string(initializePromptTmpl))
38+ if err != nil {
39+ return "", err
40+ }
41+ return systemPrompt.Build(context.Background(), "", "", cfg)
36}
42}
internal/agent/templates/initialize.md → internal/agent/templates/initialize.md.tplrenamed+3−3
@@ -1,6 +1,6 @@
1−Analyze this codebase and create/update **CRUSH.md** to help future agents work effectively in this repository.
1+Analyze this codebase and create/update **{{.Config.Options.InitializeAs}}** to help future agents work effectively in this repository.
2
2
3−**First**: Check if directory is empty or contains only config files. If so, stop and say "Directory appears empty or only contains config. Add source code first, then run this command to generate CRUSH.md."
3+**First**: Check if directory is empty or contains only config files. If so, stop and say "Directory appears empty or only contains config. Add source code first, then run this command to generate {{.Config.Options.InitializeAs}}."
4
4
5**Goal**: Document what an agent needs to know to work in this codebase - commands, patterns, conventions, gotchas.
5**Goal**: Document what an agent needs to know to work in this codebase - commands, patterns, conventions, gotchas.
6
6
@@ -11,7 +11,7 @@ Analyze this codebase and create/update **CRUSH.md** to help future agents work
113. Identify project type from config files and directory structure
113. Identify project type from config files and directory structure
124. Find build/test/lint commands from config files, scripts, Makefiles, or CI configs
124. Find build/test/lint commands from config files, scripts, Makefiles, or CI configs
135. Read representative source files to understand code patterns
135. Read representative source files to understand code patterns
14−6. If CRUSH.md exists, read and improve it
14+6. If {{.Config.Options.InitializeAs}} exists, read and improve it
15
15
16**Content to include**:
16**Content to include**:
17
17
internal/config/config.go+2
@@ -21,6 +21,7 @@ import (
21const (
21const (
22 appName = "crush"
22 appName = "crush"
23 defaultDataDirectory = ".crush"
23 defaultDataDirectory = ".crush"
24+ defaultInitializeAs = "AGENTS.md"
24)
25)
25
26
26var defaultContextPaths = []string{
27var defaultContextPaths = []string{
@@ -201,6 +202,7 @@ type Options struct {
201 DisableProviderAutoUpdate bool `json:"disable_provider_auto_update,omitempty" jsonschema:"description=Disable providers auto-update,default=false"`
202 DisableProviderAutoUpdate bool `json:"disable_provider_auto_update,omitempty" jsonschema:"description=Disable providers auto-update,default=false"`
202 Attribution *Attribution `json:"attribution,omitempty" jsonschema:"description=Attribution settings for generated content"`
203 Attribution *Attribution `json:"attribution,omitempty" jsonschema:"description=Attribution settings for generated content"`
203 DisableMetrics bool `json:"disable_metrics,omitempty" jsonschema:"description=Disable sending metrics,default=false"`
204 DisableMetrics bool `json:"disable_metrics,omitempty" jsonschema:"description=Disable sending metrics,default=false"`
205+ InitializeAs string `json:"initialize_as,omitempty" jsonschema:"description=Name of the context file to create/update during project initialization,default=AGENTS.md,example=AGENTS.md,example=CRUSH.md,example=CLAUDE.md,example=docs/LLMs.md"`
204}
206}
205
207
206type MCPs map[string]MCPConfig
208type MCPs map[string]MCPConfig
internal/config/load.go+3
@@ -364,6 +364,9 @@ func (c *Config) setDefaults(workingDir, dataDir string) {
364 c.Options.Attribution.TrailerStyle = TrailerStyleCoAuthoredBy
364 c.Options.Attribution.TrailerStyle = TrailerStyleCoAuthoredBy
365 }
365 }
366 }
366 }
367+ if c.Options.InitializeAs == "" {
368+ c.Options.InitializeAs = defaultInitializeAs
369+ }
367}
370}
368
371
369// applyLSPDefaults applies default values from powernap to LSP configurations
372// applyLSPDefaults applies default values from powernap to LSP configurations
internal/config/load_test.go+1
@@ -50,6 +50,7 @@ func TestConfig_setDefaults(t *testing.T) {
50 require.NotNil(t, cfg.LSP)
50 require.NotNil(t, cfg.LSP)
51 require.NotNil(t, cfg.MCP)
51 require.NotNil(t, cfg.MCP)
52 require.Equal(t, filepath.Join("/tmp", ".crush"), cfg.Options.DataDirectory)
52 require.Equal(t, filepath.Join("/tmp", ".crush"), cfg.Options.DataDirectory)
53+ require.Equal(t, "AGENTS.md", cfg.Options.InitializeAs)
53 for _, path := range defaultContextPaths {
54 for _, path := range defaultContextPaths {
54 require.Contains(t, cfg.Options.ContextPaths, path)
55 require.Contains(t, cfg.Options.ContextPaths, path)
55 }
56 }
internal/tui/components/chat/splash/splash.go+7−2
@@ -331,10 +331,14 @@ func (s *splashCmp) initializeProject() tea.Cmd {
331
331
332 cmds = append(cmds, util.CmdHandler(OnboardingCompleteMsg{}))
332 cmds = append(cmds, util.CmdHandler(OnboardingCompleteMsg{}))
333 if !s.selectedNo {
333 if !s.selectedNo {
334+ initPrompt, err := agent.InitializePrompt(*config.Get())
335+ if err != nil {
336+ return util.ReportError(err)
337+ }
334 cmds = append(cmds,
338 cmds = append(cmds,
335 util.CmdHandler(chat.SessionClearedMsg{}),
339 util.CmdHandler(chat.SessionClearedMsg{}),
336 util.CmdHandler(chat.SendMsg{
340 util.CmdHandler(chat.SendMsg{
337− Text: agent.InitializePrompt(),
341+ Text: initPrompt,
338 }),
342 }),
339 )
343 )
340 }
344 }
@@ -458,6 +462,7 @@ func (s *splashCmp) View() string {
458 bodyStyle := t.S().Base.Foreground(t.FgMuted)
462 bodyStyle := t.S().Base.Foreground(t.FgMuted)
459 shortcutStyle := t.S().Base.Foreground(t.Success)
463 shortcutStyle := t.S().Base.Foreground(t.Success)
460
464
465+ initFile := config.Get().Options.InitializeAs
461 initText := lipgloss.JoinVertical(
466 initText := lipgloss.JoinVertical(
462 lipgloss.Left,
467 lipgloss.Left,
463 titleStyle.Render("Would you like to initialize this project?"),
468 titleStyle.Render("Would you like to initialize this project?"),
@@ -465,7 +470,7 @@ func (s *splashCmp) View() string {
465 pathStyle.Render(s.cwd()),
470 pathStyle.Render(s.cwd()),
466 "",
471 "",
467 bodyStyle.Render("When I initialize your codebase I examine the project and put the"),
472 bodyStyle.Render("When I initialize your codebase I examine the project and put the"),
468− bodyStyle.Render("result into a CRUSH.md file which serves as general context."),
473+ bodyStyle.Render(fmt.Sprintf("result into an %s file which serves as general context.", initFile)),
469 "",
474 "",
470 bodyStyle.Render("You can also initialize anytime via ")+shortcutStyle.Render("ctrl+p")+bodyStyle.Render("."),
475 bodyStyle.Render("You can also initialize anytime via ")+shortcutStyle.Render("ctrl+p")+bodyStyle.Render("."),
471 "",
476 "",
internal/tui/components/dialogs/commands/commands.go+7−2
@@ -1,6 +1,7 @@
1package commands
1package commands
2
2
3import (
3import (
4+ "fmt"
4 "os"
5 "os"
5 "slices"
6 "slices"
6 "strings"
7 "strings"
@@ -454,10 +455,14 @@ func (c *commandDialogCmp) defaultCommands() []Command {
454 {
455 {
455 ID: "init",
456 ID: "init",
456 Title: "Initialize Project",
457 Title: "Initialize Project",
457− Description: "Create/Update the CRUSH.md memory file",
458+ Description: fmt.Sprintf("Create/Update the %s memory file", config.Get().Options.InitializeAs),
458 Handler: func(cmd Command) tea.Cmd {
459 Handler: func(cmd Command) tea.Cmd {
460+ initPrompt, err := agent.InitializePrompt(*config.Get())
461+ if err != nil {
462+ return util.ReportError(err)
463+ }
459 return util.CmdHandler(chat.SendMsg{
464 return util.CmdHandler(chat.SendMsg{
460− Text: agent.InitializePrompt(),
465+ Text: initPrompt,
461 })
466 })
462 },
467 },
463 },
468 },
internal/tui/page/chat/chat.go+1−1
@@ -144,7 +144,7 @@ func (p *chatPage) Init() tea.Cmd {
144 p.isOnboarding = true
144 p.isOnboarding = true
145 p.splashFullScreen = true
145 p.splashFullScreen = true
146 } else if b, _ := config.ProjectNeedsInitialization(); b {
146 } else if b, _ := config.ProjectNeedsInitialization(); b {
147− // Project needs CRUSH.md initialization
147+ // Project needs context initialization
148 p.splash.SetProjectInit(true)
148 p.splash.SetProjectInit(true)
149 p.isProjectInit = true
149 p.isProjectInit = true
150 p.splashFullScreen = true
150 p.splashFullScreen = true