Detailed changes
@@ -259,13 +259,17 @@ func (Attribution) JSONSchemaExtend(schema *jsonschema.Schema) {
}
type Options struct {
- ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
- SkillsPaths []string `json:"skills_paths,omitempty" jsonschema:"description=Paths to directories containing Agent Skills (folders with SKILL.md files),example=~/.config/crush/skills,example=./skills"`
- TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
- Debug bool `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
- DebugLSP bool `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
- DisableAutoSummarize bool `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
- DataDirectory string `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data (relative to working directory),default=.crush,example=.crush"` // Relative to the cwd
+ ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
+ SkillsPaths []string `json:"skills_paths,omitempty" jsonschema:"description=Paths to directories containing Agent Skills (folders with SKILL.md files),example=~/.config/crush/skills,example=./skills"`
+ TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
+ Debug bool `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
+ DebugLSP bool `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
+ DisableAutoSummarize bool `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
+ // DataDirectory is where Crush keeps per-project state such as
+ // the SQLite database and workspace overrides. Relative paths are
+ // resolved against the working directory; absolute paths are used
+ // verbatim. After defaulting the stored value is always absolute.
+ DataDirectory string `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data. Relative paths are resolved against the working directory; absolute paths are used as-is.,default=.crush,example=.crush"`
DisabledTools []string `json:"disabled_tools,omitempty" jsonschema:"description=List of built-in tools to disable and hide from the agent,example=bash,example=sourcegraph"`
DisableProviderAutoUpdate bool `json:"disable_provider_auto_update,omitempty" jsonschema:"description=Disable providers auto-update,default=false"`
DisableDefaultProviders bool `json:"disable_default_providers,omitempty" jsonschema:"description=Ignore all default/embedded providers. When enabled\\, providers must be fully specified in the config file with base_url\\, models\\, and api_key - no merging with defaults occurs,default=false"`
@@ -205,6 +205,36 @@ func TestConfig_setDefaults(t *testing.T) {
require.Equal(t, filepath.Join(workingDir, "state"), cfg.Options.DataDirectory)
})
+ t.Run("preserves absolute configured data directory", func(t *testing.T) {
+ // Use a platform-appropriate absolute path so the test runs
+ // the same way on POSIX and Windows.
+ absDir := filepath.Join(t.TempDir(), "data")
+ cfg := &Config{Options: &Options{DataDirectory: absDir}}
+
+ cfg.setDefaults(filepath.Join(t.TempDir(), "worktree"), "")
+
+ require.Equal(t, absDir, cfg.Options.DataDirectory)
+ })
+
+ t.Run("workspace merge re-entry keeps an absolute data directory", func(t *testing.T) {
+ // Simulate the load and reload paths: defaults are applied
+ // twice with the data directory potentially carried through
+ // from an earlier merge as a relative string.
+ workingDir := filepath.Join(t.TempDir(), "worktree")
+ cfg := &Config{}
+ cfg.setDefaults(workingDir, "")
+
+ // Workspace JSON sets data_directory to a relative value; the
+ // merge replaces the struct, then setDefaults runs again.
+ cfg.Options.DataDirectory = "./state"
+ cfg.setDefaults(workingDir, "")
+
+ require.True(t, filepath.IsAbs(cfg.Options.DataDirectory),
+ "data directory must remain absolute after re-merge, got %q",
+ cfg.Options.DataDirectory)
+ require.Equal(t, filepath.Join(workingDir, "state"), cfg.Options.DataDirectory)
+ })
+
t.Run("does not adopt .crush from a parent project", func(t *testing.T) {
parent := t.TempDir()
@@ -2939,7 +2939,7 @@ const docTemplate = `{
}
},
"data_directory": {
- "description": "Relative to the cwd",
+ "description": "DataDirectory is where Crush keeps per-project state such as the SQLite database and workspace overrides. Relative paths are resolved against the working directory; absolute paths are used verbatim. After defaulting the stored value is always absolute.",
"type": "string"
},
"debug": {
@@ -2932,7 +2932,7 @@
}
},
"data_directory": {
- "description": "Relative to the cwd",
+ "description": "DataDirectory is where Crush keeps per-project state such as the SQLite database and workspace overrides. Relative paths are resolved against the working directory; absolute paths are used verbatim. After defaulting the stored value is always absolute.",
"type": "string"
},
"debug": {
@@ -287,7 +287,11 @@ definitions:
type: string
type: array
data_directory:
- description: Relative to the cwd
+ description: |-
+ DataDirectory is where Crush keeps per-project state such as the SQLite
+ database and workspace overrides. Relative paths are resolved against
+ the working directory; absolute paths are used verbatim. After
+ defaulting the stored value is always absolute.
type: string
debug:
type: boolean
@@ -423,7 +423,7 @@
},
"data_directory": {
"type": "string",
- "description": "Directory for storing application data (relative to working directory)",
+ "description": "Directory for storing application data. Relative paths are resolved against the working directory; absolute paths are used as-is.",
"default": ".crush",
"examples": [
".crush"