claude-evolve 1.4.8 → 1.4.10

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,318 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Auto-updating status display for claude-evolve that fits to terminal size.
4
+ Updates in real-time without flicker using ANSI escape sequences.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import time
10
+ import termios
11
+ import tty
12
+ import select
13
+ import signal
14
+ import argparse
15
+ import subprocess
16
+ from datetime import datetime
17
+
18
+ # Add parent directory to path for imports
19
+ script_dir = os.path.dirname(os.path.abspath(__file__))
20
+ sys.path.insert(0, os.path.join(script_dir, '..'))
21
+
22
+ from lib.config import Config
23
+ from lib.evolution_csv import EvolutionCSV
24
+
25
+
26
+ class TerminalDisplay:
27
+ """Handles terminal display with ANSI escape sequences for flicker-free updates."""
28
+
29
+ def __init__(self):
30
+ self.rows, self.cols = self.get_terminal_size()
31
+ signal.signal(signal.SIGWINCH, self.handle_resize)
32
+
33
+ def get_terminal_size(self):
34
+ """Get current terminal size."""
35
+ try:
36
+ rows, cols = os.popen('stty size', 'r').read().split()
37
+ return int(rows), int(cols)
38
+ except:
39
+ return 24, 80 # Default fallback
40
+
41
+ def handle_resize(self, signum, frame):
42
+ """Handle terminal resize signal."""
43
+ self.rows, self.cols = self.get_terminal_size()
44
+
45
+ def clear_screen(self):
46
+ """Clear the entire screen."""
47
+ print('\033[2J\033[H', end='')
48
+
49
+ def move_cursor(self, row, col):
50
+ """Move cursor to specific position."""
51
+ print(f'\033[{row};{col}H', end='')
52
+
53
+ def clear_line(self):
54
+ """Clear current line."""
55
+ print('\033[2K', end='')
56
+
57
+ def hide_cursor(self):
58
+ """Hide the cursor."""
59
+ print('\033[?25l', end='')
60
+
61
+ def show_cursor(self):
62
+ """Show the cursor."""
63
+ print('\033[?25h', end='')
64
+
65
+ def reset(self):
66
+ """Reset terminal to normal state."""
67
+ self.show_cursor()
68
+ print('\033[0m', end='') # Reset colors
69
+
70
+
71
+ class AutoStatus:
72
+ """Auto-updating status display."""
73
+
74
+ def __init__(self, working_dir=None):
75
+ self.config = Config()
76
+
77
+ # Load config using same mechanism as other commands
78
+ # First check CLAUDE_EVOLVE_CONFIG env var
79
+ config_env = os.environ.get('CLAUDE_EVOLVE_CONFIG')
80
+ if config_env:
81
+ self.config.load(config_env)
82
+ else:
83
+ # Load from working directory or current directory
84
+ self.config.load(working_dir=working_dir)
85
+
86
+ self.display = TerminalDisplay()
87
+ self.running = True
88
+
89
+ def get_status_data(self):
90
+ """Get current status data from CSV."""
91
+ csv_path = self.config.resolve_path(self.config.data['csv_file'])
92
+
93
+ with EvolutionCSV(csv_path) as csv:
94
+ df = csv.df
95
+
96
+ # Count by status
97
+ status_counts = {
98
+ 'pending': len(df[df['status'] == 'pending']),
99
+ 'running': len(df[df['status'] == 'running']),
100
+ 'complete': len(df[df['status'] == 'complete']),
101
+ 'failed': len(df[df['status'] == 'failed']),
102
+ 'total': len(df)
103
+ }
104
+
105
+ # Get performance stats for completed
106
+ completed_df = df[df['status'] == 'complete']
107
+ if not completed_df.empty and 'performance' in completed_df.columns:
108
+ perf_values = completed_df['performance'].dropna()
109
+ if not perf_values.empty:
110
+ perf_stats = {
111
+ 'min': perf_values.min(),
112
+ 'max': perf_values.max(),
113
+ 'mean': perf_values.mean(),
114
+ 'count': len(perf_values)
115
+ }
116
+ else:
117
+ perf_stats = None
118
+ else:
119
+ perf_stats = None
120
+
121
+ # Get recent candidates (last N that fit on screen)
122
+ max_candidates = max(1, self.display.rows - 15) # Reserve space for header/stats
123
+ recent = df.tail(max_candidates)
124
+
125
+ return {
126
+ 'counts': status_counts,
127
+ 'performance': perf_stats,
128
+ 'recent': recent,
129
+ 'csv_path': csv_path
130
+ }
131
+
132
+ def format_duration(self, seconds):
133
+ """Format duration in human-readable form."""
134
+ if seconds < 60:
135
+ return f"{seconds}s"
136
+ elif seconds < 3600:
137
+ return f"{seconds//60}m {seconds%60}s"
138
+ else:
139
+ hours = seconds // 3600
140
+ mins = (seconds % 3600) // 60
141
+ return f"{hours}h {mins}m"
142
+
143
+ def render(self):
144
+ """Render the current status to the terminal."""
145
+ try:
146
+ data = self.get_status_data()
147
+ except Exception as e:
148
+ self.display.clear_screen()
149
+ self.display.move_cursor(1, 1)
150
+ print(f"Error reading status: {e}")
151
+ return
152
+
153
+ # Clear screen and start rendering
154
+ self.display.clear_screen()
155
+ row = 1
156
+
157
+ # Header
158
+ self.display.move_cursor(row, 1)
159
+ header = "Claude Evolution Auto-Status"
160
+ print(f"\033[1;36m{header.center(self.display.cols)}\033[0m")
161
+ row += 1
162
+
163
+ # Timestamp
164
+ self.display.move_cursor(row, 1)
165
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
166
+ print(f"Updated: {timestamp} | Press 'q' to quit")
167
+ row += 2
168
+
169
+ # File info
170
+ self.display.move_cursor(row, 1)
171
+ print(f"CSV: {data['csv_path']}")
172
+ row += 2
173
+
174
+ # Status summary
175
+ self.display.move_cursor(row, 1)
176
+ print("\033[1mStatus Summary:\033[0m")
177
+ row += 1
178
+
179
+ counts = data['counts']
180
+ status_line = (f" Total: {counts['total']} | "
181
+ f"\033[33mPending: {counts['pending']}\033[0m | "
182
+ f"\033[36mRunning: {counts['running']}\033[0m | "
183
+ f"\033[32mComplete: {counts['complete']}\033[0m | "
184
+ f"\033[31mFailed: {counts['failed']}\033[0m")
185
+
186
+ self.display.move_cursor(row, 1)
187
+ print(status_line)
188
+ row += 2
189
+
190
+ # Performance stats
191
+ if data['performance']:
192
+ self.display.move_cursor(row, 1)
193
+ print("\033[1mPerformance Stats:\033[0m")
194
+ row += 1
195
+
196
+ perf = data['performance']
197
+ self.display.move_cursor(row, 1)
198
+ print(f" Min: {perf['min']:.4f} | Max: {perf['max']:.4f} | "
199
+ f"Mean: {perf['mean']:.4f} | Count: {perf['count']}")
200
+ row += 2
201
+
202
+ # Recent candidates
203
+ if not data['recent'].empty:
204
+ self.display.move_cursor(row, 1)
205
+ print("\033[1mRecent Candidates:\033[0m")
206
+ row += 1
207
+
208
+ # Table header
209
+ self.display.move_cursor(row, 1)
210
+ header_fmt = f"{'ID':>8} | {'Status':^10} | {'Performance':>11} | {'Description'}"
211
+ print(header_fmt[:self.display.cols])
212
+ row += 1
213
+
214
+ self.display.move_cursor(row, 1)
215
+ print("-" * min(self.display.cols, len(header_fmt)))
216
+ row += 1
217
+
218
+ # Table rows
219
+ for _, candidate in data['recent'].iterrows():
220
+ if row >= self.display.rows - 1: # Leave room for bottom
221
+ break
222
+
223
+ self.display.move_cursor(row, 1)
224
+
225
+ # Color based on status
226
+ status = candidate.get('status', 'unknown')
227
+ if status == 'complete':
228
+ color = '\033[32m' # Green
229
+ elif status == 'running':
230
+ color = '\033[36m' # Cyan
231
+ elif status == 'failed':
232
+ color = '\033[31m' # Red
233
+ elif status == 'pending':
234
+ color = '\033[33m' # Yellow
235
+ else:
236
+ color = '\033[0m' # Default
237
+
238
+ # Format performance
239
+ if status == 'complete' and 'performance' in candidate:
240
+ perf = f"{candidate['performance']:.4f}"
241
+ else:
242
+ perf = "-"
243
+
244
+ # Truncate description to fit
245
+ desc = candidate.get('description', '')
246
+ max_desc_len = self.display.cols - 35 # Account for other columns
247
+ if len(desc) > max_desc_len:
248
+ desc = desc[:max_desc_len-3] + "..."
249
+
250
+ line = f"{candidate['id']:>8} | {color}{status:^10}\033[0m | {perf:>11} | {desc}"
251
+ print(line[:self.display.cols])
252
+ row += 1
253
+
254
+ # Ensure cursor is at bottom
255
+ self.display.move_cursor(self.display.rows, 1)
256
+ sys.stdout.flush()
257
+
258
+ def check_input(self):
259
+ """Check for keyboard input without blocking."""
260
+ if select.select([sys.stdin], [], [], 0)[0]:
261
+ char = sys.stdin.read(1)
262
+ if char.lower() == 'q':
263
+ self.running = False
264
+ return True
265
+ return False
266
+
267
+ def run(self):
268
+ """Main loop for auto-updating display."""
269
+ # Save terminal settings
270
+ old_settings = termios.tcgetattr(sys.stdin)
271
+
272
+ try:
273
+ # Set terminal to raw mode for immediate input
274
+ tty.setraw(sys.stdin.fileno())
275
+
276
+ self.display.hide_cursor()
277
+
278
+ while self.running:
279
+ self.render()
280
+
281
+ # Check for input and wait
282
+ for _ in range(10): # Check 10 times per second
283
+ if self.check_input():
284
+ break
285
+ time.sleep(0.1)
286
+
287
+ except KeyboardInterrupt:
288
+ pass
289
+
290
+ finally:
291
+ # Restore terminal settings
292
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
293
+ self.display.reset()
294
+ self.display.clear_screen()
295
+ self.display.move_cursor(1, 1)
296
+ print("Exiting auto-status...")
297
+
298
+
299
+ def main():
300
+ """Main entry point."""
301
+ parser = argparse.ArgumentParser(
302
+ description="Auto-updating status display for claude-evolve that fits to terminal size.",
303
+ epilog="Press 'q' to quit while running."
304
+ )
305
+ parser.add_argument(
306
+ '--working-dir',
307
+ help='Working directory containing claude-evolve.yaml config file'
308
+ )
309
+
310
+ args = parser.parse_args()
311
+
312
+ # Run auto-status
313
+ auto_status = AutoStatus(working_dir=args.working_dir)
314
+ auto_status.run()
315
+
316
+
317
+ if __name__ == '__main__':
318
+ main()
@@ -64,6 +64,7 @@ COMMANDS:
64
64
  analyze Analyze evolution results
65
65
  edit Manage candidate statuses by generation
66
66
  status Show evolution progress and current leader
67
+ autostatus Auto-updating status display (real-time)
67
68
  cleanup Clean up unchanged algorithms and descendants
68
69
  cleanup-duplicates Alias for cleanup (deprecated)
69
70
  help Show this help message
@@ -91,15 +92,16 @@ show_menu() {
91
92
  echo
92
93
  echo "What would you like to do?"
93
94
  echo
94
- echo " 1) setup - Initialize evolution workspace"
95
- echo " 2) ideate - Generate new algorithm ideas"
96
- echo " 3) run - Execute evolution candidates"
97
- echo " 4) analyze - Analyze evolution results"
98
- echo " 5) edit - Manage candidate statuses by generation"
99
- echo " 6) status - Show evolution progress and current leader"
100
- echo " 7) config - Manage configuration settings"
101
- echo " 8) help - Show help message"
102
- echo " 9) exit - Exit"
95
+ echo " 1) setup - Initialize evolution workspace"
96
+ echo " 2) ideate - Generate new algorithm ideas"
97
+ echo " 3) run - Execute evolution candidates"
98
+ echo " 4) analyze - Analyze evolution results"
99
+ echo " 5) edit - Manage candidate statuses by generation"
100
+ echo " 6) status - Show evolution progress and current leader"
101
+ echo " 7) autostatus - Auto-updating status display (real-time)"
102
+ echo " 8) config - Manage configuration settings"
103
+ echo " 9) help - Show help message"
104
+ echo " 0) exit - Exit"
103
105
  echo
104
106
 
105
107
  # Show workspace status
@@ -145,7 +147,7 @@ check_for_updates
145
147
  # Main logic
146
148
  if [[ $# -eq 0 ]]; then
147
149
  show_menu
148
- read -r -p "Enter your choice (1-9): " choice
150
+ read -r -p "Enter your choice (1-9, 0): " choice
149
151
 
150
152
  case $choice in
151
153
  1) exec "$SCRIPT_DIR/claude-evolve-setup" ;;
@@ -154,14 +156,15 @@ if [[ $# -eq 0 ]]; then
154
156
  4) exec "$SCRIPT_DIR/claude-evolve-analyze" ;;
155
157
  5) exec "$SCRIPT_DIR/claude-evolve-edit" ;;
156
158
  6) exec "$SCRIPT_DIR/claude-evolve-status" ;;
157
- 7) exec "$SCRIPT_DIR/claude-evolve-config" ;;
158
- 8) show_help ;;
159
- 9)
159
+ 7) exec "$SCRIPT_DIR/claude-evolve-autostatus" ;;
160
+ 8) exec "$SCRIPT_DIR/claude-evolve-config" ;;
161
+ 9) show_help ;;
162
+ 0)
160
163
  echo "Goodbye!"
161
164
  exit 0
162
165
  ;;
163
166
  *)
164
- echo -e "${RED}Invalid choice. Please select 1-9.${NC}"
167
+ echo -e "${RED}Invalid choice. Please select 1-9 or 0.${NC}"
165
168
  exit 1
166
169
  ;;
167
170
  esac
@@ -198,6 +201,10 @@ status)
198
201
  shift
199
202
  exec "$SCRIPT_DIR/claude-evolve-status" "$@"
200
203
  ;;
204
+ autostatus)
205
+ shift
206
+ exec "$SCRIPT_DIR/claude-evolve-autostatus" "$@"
207
+ ;;
201
208
  cleanup-duplicates|cleanup)
202
209
  shift
203
210
  exec "$SCRIPT_DIR/claude-evolve-cleanup" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",