claude-evolve 1.6.21 → 1.6.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lib/config.py +32 -19
  2. package/lib/config.sh +124 -158
  3. package/package.json +1 -1
package/lib/config.py CHANGED
@@ -44,31 +44,44 @@ class Config:
44
44
 
45
45
  def load(self, config_path=None, working_dir=None):
46
46
  """Load configuration from YAML file."""
47
- # Determine config file path
47
+ # Determine local config file path
48
+ local_config_path = None
48
49
  if config_path:
49
- # Explicit config path provided
50
- self.config_path = Path(config_path)
50
+ # Explicit config path provided, treat as local
51
+ local_config_path = Path(config_path)
51
52
  elif working_dir:
52
53
  # Look for config.yaml in working directory
53
54
  self.working_dir = Path(working_dir)
54
- self.config_path = self.working_dir / 'config.yaml'
55
+ local_config_path = self.working_dir / 'config.yaml'
55
56
  else:
56
57
  # Default to evolution/config.yaml
57
- self.config_path = Path('evolution/config.yaml')
58
-
59
- # Load config if it exists
60
- if self.config_path.exists():
61
- with open(self.config_path, 'r') as f:
62
- yaml_data = yaml.safe_load(f) or {}
63
-
64
- # Merge with defaults
65
- self.data.update(yaml_data)
66
-
67
- # Handle nested structures
68
- if 'ideation' in yaml_data:
69
- self.data['ideation'] = {**self.DEFAULTS['ideation'], **yaml_data['ideation']}
70
- if 'parallel' in yaml_data:
71
- self.data['parallel'] = {**self.DEFAULTS['parallel'], **yaml_data['parallel']}
58
+ local_config_path = Path('evolution/config.yaml')
59
+
60
+ # Store the resolved config_path for path resolution later
61
+ self.config_path = local_config_path
62
+
63
+ # Load local config if it exists
64
+ if local_config_path.exists():
65
+ with open(local_config_path, 'r') as f:
66
+ local_yaml_data = yaml.safe_load(f) or {}
67
+ self._merge_config_data(local_yaml_data)
68
+
69
+ # Load global config from ~/.config/claude-evolve/config.yaml
70
+ global_config_path = Path.home() / '.config' / 'claude-evolve' / 'config.yaml'
71
+ if global_config_path.exists():
72
+ with open(global_config_path, 'r') as f:
73
+ global_yaml_data = yaml.safe_load(f) or {}
74
+ self._merge_config_data(global_yaml_data)
75
+
76
+ def _merge_config_data(self, new_data):
77
+ """Helper to merge new data into self.data, handling nested structures."""
78
+ for key, value in new_data.items():
79
+ if key in self.data and isinstance(self.data[key], dict) and isinstance(value, dict):
80
+ # Recursively merge nested dictionaries
81
+ self.data[key].update(value)
82
+ else:
83
+ # Overwrite or add other types of values
84
+ self.data[key] = value
72
85
 
73
86
  def resolve_path(self, relative_path):
74
87
  """Resolve a path relative to the config directory."""
package/lib/config.sh CHANGED
@@ -59,23 +59,119 @@ DEFAULT_LLM_RUN="codex-qwen3 codex-oss gemini-flash"
59
59
  # Ideate: Commercial models for idea generation + local fallback
60
60
  DEFAULT_LLM_IDEATE="gemini sonnet-think gpt5high glm grok-4 codex-qwen3 codex-oss"
61
61
 
62
- # Load configuration from config file
63
- load_config() {
64
- # Accept config file path as parameter. If not supplied, look for a user override in
65
- # $HOME/.config/claude-evolve/config.yaml first, then fall back to the repo default.
66
- local user_config="$1"
62
+ # Load configuration from a YAML file and update variables
63
+ _load_yaml_config() {
64
+ local config_file="$1"
65
+ if [[ ! -f "$config_file" ]]; then
66
+ return 0 # File does not exist, nothing to load
67
+ fi
67
68
 
68
- if [[ -n "$user_config" ]]; then
69
- config_file="$user_config"
70
- else
71
- local home_config="$HOME/.config/claude-evolve/config.yaml"
72
- if [[ -f "$home_config" ]]; then
73
- config_file="$home_config"
69
+ echo "[DEBUG] Loading configuration from: $config_file" >&2
70
+
71
+ local in_ideation_section=false
72
+ local in_parallel_section=false
73
+ local in_llm_cli_section=false
74
+ local llm_cli_subsection=""
75
+
76
+ while IFS='' read -r line; do
77
+ [[ $line =~ ^[[:space:]]*# ]] || [[ -z $line ]] && continue
78
+
79
+ if [[ ! $line =~ ^([^:]+):(.*)$ ]]; then
80
+ continue
81
+ fi
82
+ local key="${BASH_REMATCH[1]}"
83
+ local value="${BASH_REMATCH[2]}"
84
+
85
+ local is_indented=false
86
+ [[ $key =~ ^[[:space:]]+ ]] && is_indented=true
87
+
88
+ key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
89
+ value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
90
+
91
+ if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
92
+ echo "[CONFIG DEBUG] Before comment removal: key='$key' value='$value'" >&2
93
+ fi
94
+
95
+ value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
96
+ value=$(echo "$value" | sed 's/^"//;s/"$//')
97
+
98
+ if [[ $key == "ideation_strategies" ]]; then
99
+ in_ideation_section=true
100
+ in_parallel_section=false
101
+ in_llm_cli_section=false
102
+ continue
103
+ elif [[ $key == "parallel" ]]; then
104
+ in_parallel_section=true
105
+ in_ideation_section=false
106
+ in_llm_cli_section=false
107
+ continue
108
+ elif [[ $key == "llm_cli" ]]; then
109
+ in_llm_cli_section=true
110
+ in_ideation_section=false
111
+ in_parallel_section=false
112
+ llm_cli_subsection=""
113
+ continue
114
+ elif [[ $is_indented == false ]] && [[ $in_ideation_section == true || $in_parallel_section == true || $in_llm_cli_section == true ]]; then
115
+ in_ideation_section=false
116
+ in_parallel_section=false
117
+ in_llm_cli_section=false
118
+ llm_cli_subsection=""
119
+ fi
120
+
121
+ if [[ $in_ideation_section == true ]]; then
122
+ case $key in
123
+ total_ideas) TOTAL_IDEAS="$value" ;;
124
+ novel_exploration) NOVEL_EXPLORATION="$value" ;;
125
+ hill_climbing) HILL_CLIMBING="$value" ;;
126
+ structural_mutation) STRUCTURAL_MUTATION="$value" ;;
127
+ crossover_hybrid) CROSSOVER_HYBRID="$value" ;;
128
+ num_elites) NUM_ELITES="$value" ;;
129
+ num_revolution) NUM_REVOLUTION="$value" ;;
130
+ esac
131
+ elif [[ $in_parallel_section == true ]]; then
132
+ case $key in
133
+ enabled) PARALLEL_ENABLED="$value" ;;
134
+ max_workers) MAX_WORKERS="$value" ;;
135
+ lock_timeout) LOCK_TIMEOUT="$value" ;;
136
+ esac
137
+ elif [[ $in_llm_cli_section == true ]]; then
138
+ if [[ $key == "run" || $key == "ideate" ]]; then
139
+ case $key in
140
+ run) LLM_RUN="$value" ;;
141
+ ideate) LLM_IDEATE="$value" ;;
142
+ esac
143
+ else
144
+ value=$(echo "$value" | sed "s/^'//;s/'$//")
145
+ local var_key=$(echo "$key" | sed 's/-/_/g')
146
+ if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
147
+ echo "[CONFIG DEBUG] Setting LLM_CLI_${var_key} = '$value'" >&2
148
+ fi
149
+ eval "LLM_CLI_${var_key}=\"$value\""
150
+ fi
74
151
  else
75
- config_file="evolution/config.yaml"
152
+ case $key in
153
+ algorithm_file) ALGORITHM_FILE="$value" ;;
154
+ evaluator_file) EVALUATOR_FILE="$value" ;;
155
+ brief_file) BRIEF_FILE="$value" ;;
156
+ evolution_csv) EVOLUTION_CSV="$value" ;;
157
+ output_dir) OUTPUT_DIR="$value" ;;
158
+ parent_selection) PARENT_SELECTION="$value" ;;
159
+ python_cmd) PYTHON_CMD="$value" ;;
160
+ auto_ideate) AUTO_IDEATE="$value" ;;
161
+ max_retries) MAX_RETRIES="$value" ;;
162
+ memory_limit_mb) MEMORY_LIMIT_MB="$value" ;;
163
+ evolution_dir):
164
+ echo "[WARN] evolution_dir in config is ignored - automatically inferred from config file location" >&2
165
+ ;;
166
+ esac
76
167
  fi
77
- fi
78
-
168
+ done < "$config_file"
169
+ # Keep track of the last config file loaded to infer evolution_dir
170
+ LAST_CONFIG_FILE_LOADED="$config_file"
171
+ }
172
+
173
+ # Load configuration from config file
174
+ load_config() {
79
175
  # Set defaults first
80
176
  EVOLUTION_DIR="$DEFAULT_EVOLUTION_DIR"
81
177
  ALGORITHM_FILE="$DEFAULT_ALGORITHM_FILE"
@@ -86,7 +182,6 @@ load_config() {
86
182
  PARENT_SELECTION="$DEFAULT_PARENT_SELECTION"
87
183
  PYTHON_CMD="$DEFAULT_PYTHON_CMD"
88
184
 
89
- # Set ideation strategy defaults
90
185
  TOTAL_IDEAS="$DEFAULT_TOTAL_IDEAS"
91
186
  NOVEL_EXPLORATION="$DEFAULT_NOVEL_EXPLORATION"
92
187
  HILL_CLIMBING="$DEFAULT_HILL_CLIMBING"
@@ -95,23 +190,14 @@ load_config() {
95
190
  NUM_ELITES="$DEFAULT_NUM_ELITES"
96
191
  NUM_REVOLUTION="$DEFAULT_NUM_REVOLUTION"
97
192
 
98
- # Set parallel execution defaults
99
193
  PARALLEL_ENABLED="$DEFAULT_PARALLEL_ENABLED"
100
194
  MAX_WORKERS="$DEFAULT_MAX_WORKERS"
101
195
  LOCK_TIMEOUT="$DEFAULT_LOCK_TIMEOUT"
102
196
 
103
- # Set auto ideation default
104
197
  AUTO_IDEATE="$DEFAULT_AUTO_IDEATE"
105
-
106
- # Set retry default
107
198
  MAX_RETRIES="$DEFAULT_MAX_RETRIES"
108
-
109
- # Set memory limit default
110
199
  MEMORY_LIMIT_MB="$DEFAULT_MEMORY_LIMIT_MB"
111
200
 
112
- # Set LLM CLI defaults (compatibility for older bash)
113
- # Initialize associative array for LLM commands
114
- # Use simpler approach for compatibility
115
201
  LLM_CLI_gpt5high='codex exec --profile gpt5high --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
116
202
  LLM_CLI_o3high='codex exec --profile o3high --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
117
203
  LLM_CLI_codex='codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
@@ -127,142 +213,22 @@ load_config() {
127
213
  LLM_CLI_deepseek='opencode -m openrouter/deepseek/deepseek-v3.1-terminus run "{{PROMPT}}"'
128
214
  LLM_RUN="$DEFAULT_LLM_RUN"
129
215
  LLM_IDEATE="$DEFAULT_LLM_IDEATE"
130
-
131
- # Load config if found
132
- if [[ -f "$config_file" ]]; then
133
- echo "[DEBUG] Loading configuration from: $config_file" >&2
134
- # Simple YAML parsing for key: value pairs and nested structures
135
- local in_ideation_section=false
136
- local in_parallel_section=false
137
- local in_llm_cli_section=false
138
- local llm_cli_subsection=""
139
- while IFS='' read -r line; do
140
- # Skip comments and empty lines
141
- [[ $line =~ ^[[:space:]]*# ]] || [[ -z $line ]] && continue
142
-
143
- # Parse key:value from line
144
- if [[ ! $line =~ ^([^:]+):(.*)$ ]]; then
145
- continue
146
- fi
147
- key="${BASH_REMATCH[1]}"
148
- value="${BASH_REMATCH[2]}"
149
-
150
- # Check if key is indented (for nested sections)
151
- local is_indented=false
152
- [[ $key =~ ^[[:space:]]+ ]] && is_indented=true
153
-
154
-
155
- # Remove leading/trailing whitespace
156
- key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
157
- value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
158
-
159
- # Debug before comment removal
160
- if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
161
- echo "[CONFIG DEBUG] Before comment removal: key='$key' value='$value'" >&2
162
- fi
163
-
164
- # Remove inline comments from value
165
- value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
166
-
167
- # Remove quotes from value
168
- value=$(echo "$value" | sed 's/^"//;s/"$//')
169
-
170
- # Handle nested sections
171
- if [[ $key == "ideation_strategies" ]]; then
172
- in_ideation_section=true
173
- in_parallel_section=false
174
- in_llm_cli_section=false
175
- continue
176
- elif [[ $key == "parallel" ]]; then
177
- in_parallel_section=true
178
- in_ideation_section=false
179
- in_llm_cli_section=false
180
- continue
181
- elif [[ $key == "llm_cli" ]]; then
182
- in_llm_cli_section=true
183
- in_ideation_section=false
184
- in_parallel_section=false
185
- llm_cli_subsection=""
186
- continue
187
- elif [[ $is_indented == false ]] && [[ $in_ideation_section == true || $in_parallel_section == true || $in_llm_cli_section == true ]]; then
188
- # Non-indented key found while in a section, exit nested sections
189
- in_ideation_section=false
190
- in_parallel_section=false
191
- in_llm_cli_section=false
192
- llm_cli_subsection=""
193
- fi
194
-
195
- if [[ $in_ideation_section == true ]]; then
196
- # Handle indented keys in ideation_strategies
197
- case $key in
198
- total_ideas) TOTAL_IDEAS="$value" ;;
199
- novel_exploration) NOVEL_EXPLORATION="$value" ;;
200
- hill_climbing) HILL_CLIMBING="$value" ;;
201
- structural_mutation) STRUCTURAL_MUTATION="$value" ;;
202
- crossover_hybrid) CROSSOVER_HYBRID="$value" ;;
203
- num_elites) NUM_ELITES="$value" ;;
204
- num_revolution) NUM_REVOLUTION="$value" ;;
205
- esac
206
- elif [[ $in_parallel_section == true ]]; then
207
- # Handle indented keys in parallel section
208
- case $key in
209
- enabled) PARALLEL_ENABLED="$value" ;;
210
- max_workers) MAX_WORKERS="$value" ;;
211
- lock_timeout) LOCK_TIMEOUT="$value" ;;
212
- esac
213
- elif [[ $in_llm_cli_section == true ]]; then
214
- # Handle indented keys in llm_cli section
215
- # Check if this is a model definition (o3, codex, gemini, etc.) or a command list (run, ideate)
216
- if [[ $key == "run" || $key == "ideate" ]]; then
217
- # Command list - value is a space-separated list of models
218
- case $key in
219
- run) LLM_RUN="$value" ;;
220
- ideate) LLM_IDEATE="$value" ;;
221
- esac
222
- else
223
- # Model definition - key is model name, value is command template
224
- # Remove single quotes from value if present
225
- value=$(echo "$value" | sed "s/^'//;s/'$//")
226
- # Convert dashes to underscores for bash variable names
227
- var_key=$(echo "$key" | sed 's/-/_/g')
228
- # Debug config loading
229
- if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
230
- echo "[CONFIG DEBUG] Setting LLM_CLI_${var_key} = '$value'" >&2
231
- fi
232
- # Use dynamic variable name for compatibility
233
- eval "LLM_CLI_${var_key}=\"$value\""
234
- fi
235
- else
236
- # Handle top-level keys
237
- case $key in
238
- algorithm_file) ALGORITHM_FILE="$value" ;;
239
- evaluator_file) EVALUATOR_FILE="$value" ;;
240
- brief_file) BRIEF_FILE="$value" ;;
241
- evolution_csv) EVOLUTION_CSV="$value" ;;
242
- output_dir) OUTPUT_DIR="$value" ;;
243
- parent_selection) PARENT_SELECTION="$value" ;;
244
- python_cmd) PYTHON_CMD="$value" ;;
245
- auto_ideate) AUTO_IDEATE="$value" ;;
246
- max_retries) MAX_RETRIES="$value" ;;
247
- memory_limit_mb) MEMORY_LIMIT_MB="$value" ;;
248
- evolution_dir)
249
- echo "[WARN] evolution_dir in config is ignored - automatically inferred from config file location" >&2
250
- ;;
251
- esac
252
- fi
253
- done < "$config_file"
254
- fi
255
216
 
256
- # If config file is in a different directory, use that as the evolution dir
257
- if [[ "$config_file" != "evolution/config.yaml" ]]; then
258
- # Extract directory from config file path
259
- local config_dir=$(dirname "$config_file")
260
- # Use the config directory as evolution directory (including "." for current dir)
261
- if [[ "$config_dir" != "" ]]; then
262
- EVOLUTION_DIR="$config_dir"
263
- echo "[DEBUG] Using evolution directory from config path: $EVOLUTION_DIR" >&2
264
- fi
217
+ # If a config file is explicitly provided, use it as the local config
218
+ local local_config_file="evolution/config.yaml"
219
+ if [[ -n "$1" ]]; then
220
+ local_config_file="$1"
265
221
  fi
222
+
223
+ # Load local config
224
+ _load_yaml_config "$local_config_file"
225
+
226
+ # Load global config (overrides local config)
227
+ local global_config_file="$HOME/.config/claude-evolve/config.yaml"
228
+ _load_yaml_config "$global_config_file"
229
+
230
+ # EVOLUTION_DIR should always be the project's evolution directory, not derived from config file location
231
+ # It is initialized to DEFAULT_EVOLUTION_DIR at the start of load_config and should not be changed here.
266
232
 
267
233
  # Create full paths - ALL paths are relative to evolution_dir
268
234
  # Make evolution_dir absolute if it's relative
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.6.21",
3
+ "version": "1.6.23",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",