claude-evolve 1.4.12 → 1.5.0

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.
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate parent IDs in AI-generated ideas for claude-evolve ideation.
4
+ """
5
+
6
+ import csv
7
+ import json
8
+ import sys
9
+ import re
10
+ from typing import List, Set, Dict, Tuple, Optional
11
+
12
+
13
+ def get_valid_parent_ids(csv_path: str) -> Set[str]:
14
+ """Extract all valid candidate IDs from the CSV that can be used as parents."""
15
+ valid_ids = set()
16
+ valid_ids.add("") # Empty string is valid for novel ideas
17
+ valid_ids.add("000") # Special ID for baseline algorithm
18
+ valid_ids.add("0") # Alternative baseline ID
19
+ valid_ids.add("gen00-000") # Another baseline format
20
+
21
+ try:
22
+ with open(csv_path, 'r') as f:
23
+ reader = csv.reader(f)
24
+ next(reader, None) # Skip header
25
+ for row in reader:
26
+ if row and len(row) > 0:
27
+ candidate_id = row[0].strip()
28
+ if candidate_id:
29
+ valid_ids.add(candidate_id)
30
+ except Exception as e:
31
+ print(f"[ERROR] Failed to read CSV: {e}", file=sys.stderr)
32
+
33
+ return valid_ids
34
+
35
+
36
+ def validate_and_fix_parent_id(parent_id: str, valid_ids: Set[str], idea_type: str,
37
+ top_performers: Optional[List[Tuple[str, str, float]]] = None) -> str:
38
+ """
39
+ Validate a parent ID and fix it if invalid.
40
+
41
+ Args:
42
+ parent_id: The parent ID to validate
43
+ valid_ids: Set of valid parent IDs
44
+ idea_type: Type of idea (novel, hill-climbing, structural, crossover)
45
+ top_performers: List of (id, description, score) tuples for non-novel ideas
46
+
47
+ Returns:
48
+ A valid parent ID (may be fixed)
49
+ """
50
+ # Novel ideas should have empty parent
51
+ if idea_type == "novel":
52
+ return ""
53
+
54
+ # Check if parent ID is valid
55
+ if parent_id in valid_ids:
56
+ return parent_id
57
+
58
+ # For non-novel ideas, we need a valid parent
59
+ if top_performers and len(top_performers) > 0:
60
+ # Return the first top performer's ID
61
+ return top_performers[0][0]
62
+
63
+ # If no top performers, return empty (will be caught as error later)
64
+ return ""
65
+
66
+
67
+ def parse_ai_line(line: str, idea_type: str) -> Tuple[str, str]:
68
+ """
69
+ Parse a line from AI output to extract parent ID and description.
70
+
71
+ Returns:
72
+ Tuple of (parent_id, description)
73
+ """
74
+ line = line.strip()
75
+ parent_id = ""
76
+ description = line
77
+
78
+ if idea_type != "novel":
79
+ # Look for "From X:" pattern
80
+ match = re.match(r'^From\s+([^:]+):\s*(.+)$', line, re.IGNORECASE)
81
+ if match:
82
+ parent_id = match.group(1).strip()
83
+ description = match.group(2).strip()
84
+
85
+ return parent_id, description
86
+
87
+
88
+ def validate_ai_output(ai_output: str, count: int, idea_type: str, csv_path: str,
89
+ top_performers_str: str = "") -> List[Dict[str, str]]:
90
+ """
91
+ Validate AI output and return validated ideas.
92
+
93
+ Args:
94
+ ai_output: Raw AI output
95
+ count: Expected number of ideas
96
+ idea_type: Type of idea (novel, hill-climbing, structural, crossover)
97
+ csv_path: Path to CSV file
98
+ top_performers_str: String containing top performers (format: "id,description,score\n...")
99
+
100
+ Returns:
101
+ List of validated ideas with 'parent_id' and 'description' keys
102
+ """
103
+ # Get valid parent IDs
104
+ valid_ids = get_valid_parent_ids(csv_path)
105
+
106
+ # Parse top performers
107
+ top_performers = []
108
+ if top_performers_str:
109
+ for line in top_performers_str.strip().split('\n'):
110
+ if line:
111
+ parts = line.split(',', 2)
112
+ if len(parts) >= 3:
113
+ try:
114
+ top_performers.append((parts[0], parts[1], float(parts[2])))
115
+ except ValueError:
116
+ pass
117
+
118
+ # Process AI output
119
+ validated_ideas = []
120
+ lines = ai_output.strip().split('\n')
121
+
122
+ print(f"[DEBUG] Processing {len(lines)} lines from AI output for {idea_type} ideas", file=sys.stderr)
123
+
124
+ for line in lines:
125
+ # Skip empty lines and metadata
126
+ if not line or line.strip() == '' or line.startswith('#') or line.startswith('[') or line.startswith('=='):
127
+ continue
128
+
129
+ # Skip debug/info messages from AI tools
130
+ if line.strip().startswith('[INFO]') or line.strip().startswith('[WARN]') or line.strip().startswith('[ERROR]') or line.strip().startswith('[DEBUG]'):
131
+ continue
132
+
133
+ # Clean the line
134
+ line = line.strip()
135
+ line = re.sub(r'^[0-9]+\.?\s*', '', line) # Remove numbering
136
+ line = re.sub(r'^-\s*', '', line) # Remove bullet points
137
+
138
+ # Parse parent ID and description
139
+ parent_id, description = parse_ai_line(line, idea_type)
140
+
141
+ # Validate parent ID
142
+ if parent_id and parent_id not in valid_ids:
143
+ print(f"[WARN] Invalid parent ID '{parent_id}' for {idea_type} idea - fixing...", file=sys.stderr)
144
+ print(f"[INFO] Valid parent IDs are: {', '.join(sorted([id for id in valid_ids if id]))[:200]}...", file=sys.stderr)
145
+ parent_id = validate_and_fix_parent_id(parent_id, valid_ids, idea_type, top_performers)
146
+ print(f"[INFO] Fixed parent ID to: '{parent_id}'", file=sys.stderr)
147
+
148
+ # For non-novel ideas, ensure we have a parent
149
+ if idea_type != "novel" and not parent_id:
150
+ if top_performers:
151
+ parent_id = top_performers[0][0]
152
+ print(f"[INFO] Assigned parent ID '{parent_id}' to idea without parent", file=sys.stderr)
153
+ else:
154
+ print(f"[ERROR] Non-novel idea without parent and no top performers available", file=sys.stderr)
155
+ continue
156
+
157
+ # Skip if description is too short or contains shell artifacts
158
+ if len(description) < 20:
159
+ continue
160
+
161
+ if any(word in description for word in ['EOF', '/dev/null', '<<<', '>>>', '#!/bin/bash']):
162
+ print(f"[WARN] Skipping description with shell artifacts: {description[:50]}...", file=sys.stderr)
163
+ continue
164
+
165
+ validated_ideas.append({
166
+ 'parent_id': parent_id,
167
+ 'description': description
168
+ })
169
+
170
+ if len(validated_ideas) >= count:
171
+ break
172
+
173
+ return validated_ideas
174
+
175
+
176
+ def main():
177
+ """Main entry point for validation script."""
178
+ if len(sys.argv) < 5:
179
+ print("Usage: validate_parent_ids.py <ai_output_file> <count> <idea_type> <csv_path> [top_performers_file]", file=sys.stderr)
180
+ sys.exit(1)
181
+
182
+ ai_output_file = sys.argv[1]
183
+ count = int(sys.argv[2])
184
+ idea_type = sys.argv[3]
185
+ csv_path = sys.argv[4]
186
+ top_performers_file = sys.argv[5] if len(sys.argv) > 5 else None
187
+
188
+ try:
189
+ # Read AI output
190
+ with open(ai_output_file, 'r') as f:
191
+ ai_output = f.read()
192
+ except Exception as e:
193
+ print(f"[ERROR] Failed to read AI output file {ai_output_file}: {e}", file=sys.stderr)
194
+ sys.exit(1)
195
+
196
+ # Read top performers if provided
197
+ top_performers_str = ""
198
+ if top_performers_file and top_performers_file != "none":
199
+ try:
200
+ with open(top_performers_file, 'r') as f:
201
+ top_performers_str = f.read()
202
+ except Exception as e:
203
+ print(f"[WARN] Failed to read top performers file {top_performers_file}: {e}", file=sys.stderr)
204
+
205
+ # Check if AI output is empty or looks like an error
206
+ if not ai_output.strip():
207
+ print(f"[ERROR] AI output is empty", file=sys.stderr)
208
+ sys.exit(1)
209
+
210
+ if len(ai_output) < 50:
211
+ print(f"[WARN] AI output is suspiciously short: {ai_output}", file=sys.stderr)
212
+
213
+ # Validate
214
+ validated_ideas = validate_ai_output(ai_output, count, idea_type, csv_path, top_performers_str)
215
+
216
+ # Output validated ideas as JSON
217
+ print(json.dumps(validated_ideas))
218
+
219
+ # Return error ONLY if no valid ideas at all
220
+ if len(validated_ideas) == 0:
221
+ print(f"[ERROR] No valid ideas found in AI output. First 500 chars:", file=sys.stderr)
222
+ print(ai_output[:500], file=sys.stderr)
223
+ sys.exit(1)
224
+ elif len(validated_ideas) < count:
225
+ print(f"[WARN] Only validated {len(validated_ideas)} out of {count} requested {idea_type} ideas", file=sys.stderr)
226
+ print(f"[INFO] AI appears to have generated fewer ideas than requested.", file=sys.stderr)
227
+ print(f"[INFO] Proceeding with {len(validated_ideas)} valid ideas.", file=sys.stderr)
228
+ # Don't exit with error - we have some valid ideas!
229
+
230
+
231
+ if __name__ == "__main__":
232
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.4.12",
3
+ "version": "1.5.0",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",