Files
skill-seekers-reference/src/skill_seekers/cli/enhance_status.py
yusyus 909fde6d27 feat: Enhanced LOCAL enhancement modes with background/daemon/force options
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>
2026-01-03 23:15:51 +03:00

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()