claude-evolve 1.7.6 → 1.7.7

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.
@@ -421,7 +421,7 @@ validate_and_apply_csv_modification_old() {
421
421
 
422
422
  # Check if the response looks like an error message (but not if it's just CSV data containing these words)
423
423
  if echo "$modified_csv" | head -1 | grep -q "id,basedOnId,description,performance,status"; then
424
- # This looks like a CSV file, not an error message
424
+ : # This looks like a CSV file, not an error message - continue
425
425
  elif echo "$modified_csv" | grep -qi "error\|failed\|limit\|exceeded\|sorry\|cannot\|unable"; then
426
426
  echo "[ERROR] AI failed to modify CSV and returned an error message:" >&2
427
427
  echo "$modified_csv" | head -200 >&2
@@ -409,8 +409,12 @@ try:
409
409
  from lib.evolution_csv import EvolutionCSV
410
410
 
411
411
  with EvolutionCSV(csv_file) as csv_ops:
412
+ # Auto-fix any corrupted status fields before counting
413
+ fixed = csv_ops.cleanup_corrupted_status_fields()
414
+ if fixed > 0:
415
+ print(f'[INFO] Auto-fixed {fixed} corrupted status field(s)', file=sys.stderr)
412
416
  pending = csv_ops.count_pending_candidates()
413
-
417
+
414
418
  print(f'[INFO] CSV loaded: {len(rows)-1} total candidates, {pending} pending')
415
419
 
416
420
  except csv.Error as e:
@@ -119,17 +119,26 @@ class EvolutionCSV:
119
119
  return True # Incomplete row is pending
120
120
 
121
121
  # Check status field (5th column, index 4)
122
- status = row[4].strip().lower() if row[4] else ''
123
-
122
+ # Clean status: remove newlines and control characters, then normalize
123
+ status = row[4].strip() if row[4] else ''
124
+ # Remove any embedded newlines or control characters (CSV corruption)
125
+ status = status.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')
126
+ status = ' '.join(status.split()).lower() # Normalize whitespace and lowercase
127
+
124
128
  # Only blank, missing, or "pending" mean pending
125
129
  # "running" should NOT be considered pending to avoid duplicate processing
126
130
  if not status or status == 'pending':
127
131
  return True
128
-
132
+
133
+ # Handle corrupted status fields that start with "pending"
134
+ # e.g., "pending gen08-013" from CSV corruption
135
+ if status.startswith('pending '):
136
+ return True
137
+
129
138
  # Check for retry statuses
130
139
  if status.startswith('failed-retry'):
131
140
  return True
132
-
141
+
133
142
  return False
134
143
 
135
144
  def get_pending_candidates(self) -> List[Tuple[str, str]]:
@@ -367,7 +376,44 @@ class EvolutionCSV:
367
376
  self._write_csv(new_rows)
368
377
 
369
378
  return deleted
370
-
379
+
380
+ def cleanup_corrupted_status_fields(self) -> int:
381
+ """
382
+ Detect and fix corrupted status fields (e.g., with embedded newlines).
383
+ Returns the number of corrupted fields fixed.
384
+ """
385
+ rows = self._read_csv()
386
+ if not rows:
387
+ return 0
388
+
389
+ fixed_count = 0
390
+ has_header = rows and rows[0] and rows[0][0].lower() == 'id'
391
+ start_idx = 1 if has_header else 0
392
+
393
+ for i in range(start_idx, len(rows)):
394
+ row = rows[i]
395
+ if len(row) >= 5 and row[4]:
396
+ original_status = row[4]
397
+ # Apply the same cleaning logic as is_pending_candidate
398
+ cleaned_status = original_status.strip()
399
+ cleaned_status = cleaned_status.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')
400
+ cleaned_status = ' '.join(cleaned_status.split())
401
+
402
+ # If status starts with a known status but has garbage after it, fix it
403
+ for valid_status in ['pending', 'running', 'complete', 'failed', 'skipped']:
404
+ if cleaned_status.lower().startswith(valid_status + ' '):
405
+ # Corrupted status found - extract just the valid part
406
+ row[4] = valid_status
407
+ fixed_count += 1
408
+ print(f"[WARN] Fixed corrupted status in row {i}: '{original_status}' -> '{valid_status}'", file=sys.stderr)
409
+ break
410
+
411
+ if fixed_count > 0:
412
+ self._write_csv(rows)
413
+ print(f"[INFO] Fixed {fixed_count} corrupted status field(s)", file=sys.stderr)
414
+
415
+ return fixed_count
416
+
371
417
  def has_pending_work(self) -> bool:
372
418
  """Check if there are any pending candidates. Used by dispatcher."""
373
419
  return self.count_pending_candidates() > 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",