BREAKING CHANGE: None (backward compatible - headless mode remains default) Adds 4 execution modes for LOCAL enhancement to support different use cases: from foreground execution to fully detached daemon processes. New Features: ------------ - **4 Execution Modes**: - Headless (default): Runs in foreground, waits for completion - Background (--background): Runs in background thread, returns immediately - Daemon (--daemon): Fully detached process with nohup, survives parent exit - Terminal (--interactive-enhancement): Opens new terminal window (existing) - **Force Mode (--force/-f)**: Skip all confirmations for automation - "Dangerously skip mode" requested by user - Perfect for CI/CD pipelines and unattended execution - Works with all modes: headless, background, daemon - **Status Monitoring**: - New `enhance-status` command for background/daemon processes - Real-time watch mode (--watch) - JSON output for scripting (--json) - Status file: .enhancement_status.json (status, progress, PID, errors) - **Daemon Features**: - Fully detached process using nohup - Survives parent process exit, logout, SSH disconnection - Logging to .enhancement_daemon.log - PID tracking in status file Implementation Details: ----------------------- - Status file format: JSON with status, message, progress (0.0-1.0), timestamp, PID, errors - Background mode: Python threading with daemon threads - Daemon mode: subprocess.Popen with nohup and start_new_session=True - Exit codes: 0 = success, 1 = failed, 2 = no status found CLI Integration: ---------------- - skill-seekers enhance output/react/ (headless - default) - skill-seekers enhance output/react/ --background (background thread) - skill-seekers enhance output/react/ --daemon (detached process) - skill-seekers enhance output/react/ --force (skip confirmations) - skill-seekers enhance-status output/react/ (check status) - skill-seekers enhance-status output/react/ --watch (real-time) Files Changed: -------------- - src/skill_seekers/cli/enhance_skill_local.py (+500 lines) - Added background mode with threading - Added daemon mode with nohup - Added force mode support - Added status file management (write_status, read_status) - src/skill_seekers/cli/enhance_status.py (NEW, 200 lines) - Status checking command - Watch mode with real-time updates - JSON output for scripting - Exit codes based on status - src/skill_seekers/cli/main.py - Added enhance-status subcommand - Added --background, --daemon, --force flags to enhance command - Added argument forwarding - pyproject.toml - Added enhance-status entry point - docs/ENHANCEMENT_MODES.md (NEW, 600 lines) - Complete guide to all 4 modes - Usage examples for each mode - Status file format documentation - Advanced workflows (batch processing, CI/CD) - Comparison table - Troubleshooting guide - CHANGELOG.md - Documented all new features under [Unreleased] Use Cases: ---------- 1. CI/CD Pipelines: --force for unattended execution 2. Long-running tasks: --daemon for tasks that survive logout 3. Parallel processing: --background for batch enhancement 4. Debugging: --interactive-enhancement to watch Claude Code work Testing Recommendations: ------------------------ - Test headless mode (default behavior, should be unchanged) - Test background mode (returns immediately, check status file) - Test daemon mode (survives parent exit, check logs) - Test force mode (no confirmations) - Test enhance-status command (check, watch, json modes) - Test timeout handling in all modes Addresses User Request: ----------------------- User asked for "dangeressly skipp mode that didint ask anything" and "headless instance maybe background task" alternatives. This delivers: - Force mode (--force): No confirmations - Background mode: Returns immediately, runs in background - Daemon mode: Fully detached, survives logout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
210 lines
4.9 KiB
Python
210 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Check Enhancement Status
|
|
|
|
Monitor the status of background/daemon enhancement processes.
|
|
|
|
Usage:
|
|
skill-seekers enhance-status output/react/
|
|
skill-seekers enhance-status output/react/ --watch
|
|
skill-seekers enhance-status output/react/ --json
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
|
|
|
|
def read_status(skill_dir):
|
|
"""Read enhancement status from file.
|
|
|
|
Args:
|
|
skill_dir: Path to skill directory
|
|
|
|
Returns:
|
|
dict: Status data or None if not found
|
|
"""
|
|
status_file = Path(skill_dir) / ".enhancement_status.json"
|
|
|
|
if not status_file.exists():
|
|
return None
|
|
|
|
try:
|
|
return json.loads(status_file.read_text(encoding='utf-8'))
|
|
except Exception as e:
|
|
return {"error": f"Failed to read status: {e}"}
|
|
|
|
|
|
def format_status(status):
|
|
"""Format status for display.
|
|
|
|
Args:
|
|
status: Status dict
|
|
|
|
Returns:
|
|
str: Formatted status string
|
|
"""
|
|
if not status:
|
|
return "❌ No enhancement in progress (no status file found)"
|
|
|
|
if "error" in status:
|
|
return f"❌ {status['error']}"
|
|
|
|
# Status emoji mapping
|
|
status_emojis = {
|
|
"pending": "⏳",
|
|
"running": "🔄",
|
|
"completed": "✅",
|
|
"failed": "❌"
|
|
}
|
|
|
|
emoji = status_emojis.get(status.get('status', ''), '❓')
|
|
status_text = status.get('status', 'unknown').upper()
|
|
message = status.get('message', '')
|
|
progress = status.get('progress', 0.0)
|
|
timestamp = status.get('timestamp', 'unknown')
|
|
error = status.get('error')
|
|
pid = status.get('pid')
|
|
|
|
# Build output
|
|
lines = []
|
|
lines.append(f"\n{'='*60}")
|
|
lines.append(f"ENHANCEMENT STATUS: {status_text}")
|
|
lines.append(f"{'='*60}\n")
|
|
|
|
lines.append(f"{emoji} Status: {status_text}")
|
|
|
|
if message:
|
|
lines.append(f" Message: {message}")
|
|
|
|
if progress > 0:
|
|
progress_pct = int(progress * 100)
|
|
progress_bar = '█' * (progress_pct // 5) + '░' * (20 - progress_pct // 5)
|
|
lines.append(f" Progress: [{progress_bar}] {progress_pct}%")
|
|
|
|
if pid:
|
|
lines.append(f" PID: {pid}")
|
|
|
|
lines.append(f" Timestamp: {timestamp}")
|
|
|
|
if error:
|
|
lines.append(f"\n❌ Error: {error}")
|
|
|
|
lines.append("")
|
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
def watch_status(skill_dir, interval=2):
|
|
"""Watch status in real-time.
|
|
|
|
Args:
|
|
skill_dir: Path to skill directory
|
|
interval: Update interval in seconds
|
|
"""
|
|
print(f"👀 Watching enhancement status for: {skill_dir}")
|
|
print(f" Update interval: {interval} seconds")
|
|
print(f" Press Ctrl+C to stop\n")
|
|
|
|
try:
|
|
last_status = None
|
|
|
|
while True:
|
|
status = read_status(skill_dir)
|
|
|
|
# Only print if status changed
|
|
if status != last_status:
|
|
# Clear screen (optional, comment out if you don't want this)
|
|
# os.system('clear' if os.name != 'nt' else 'cls')
|
|
|
|
print(format_status(status))
|
|
last_status = status
|
|
|
|
# Exit if completed or failed
|
|
if status and status.get('status') in ['completed', 'failed']:
|
|
break
|
|
|
|
time.sleep(interval)
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n\n👋 Stopped watching")
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Check enhancement status",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Check status once
|
|
skill-seekers enhance-status output/react/
|
|
|
|
# Watch status in real-time
|
|
skill-seekers enhance-status output/react/ --watch
|
|
|
|
# Get JSON output (for scripts)
|
|
skill-seekers enhance-status output/react/ --json
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
'skill_directory',
|
|
help='Path to skill directory (e.g., output/react/)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--watch', '-w',
|
|
action='store_true',
|
|
help='Watch status in real-time (updates every 2 seconds)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--json',
|
|
action='store_true',
|
|
help='Output raw JSON (for scripting)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--interval',
|
|
type=int,
|
|
default=2,
|
|
help='Watch update interval in seconds (default: 2)'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Watch mode
|
|
if args.watch:
|
|
watch_status(args.skill_directory, args.interval)
|
|
return
|
|
|
|
# Read status
|
|
status = read_status(args.skill_directory)
|
|
|
|
# JSON output
|
|
if args.json:
|
|
print(json.dumps(status, indent=2))
|
|
return
|
|
|
|
# Human-readable output
|
|
print(format_status(status))
|
|
|
|
# Exit code based on status
|
|
if not status:
|
|
sys.exit(2) # No status found
|
|
elif status.get('status') == 'completed':
|
|
sys.exit(0) # Success
|
|
elif status.get('status') == 'failed':
|
|
sys.exit(1) # Failed
|
|
else:
|
|
sys.exit(0) # In progress
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|