fix: Enhancement race condition - add headless mode that waits
Phase 1: Fix race condition where main console exits before enhancement completes Changes to enhance_skill_local.py: - Add headless mode (default) using subprocess.run() which WAITS for completion - Add timeout protection (default 10 minutes, configurable) - Verify SKILL.md was actually updated (check mtime and size) - Add --interactive-enhancement flag to use old terminal mode - Detailed progress messages and error handling - Clean up temp files after completion Changes to doc_scraper.py: - Use skill-seekers-enhance entry point instead of direct python path - Pass --interactive-enhancement flag through if requested - Update help text to reflect new headless default behavior - Show proper status messages (HEADLESS vs INTERACTIVE) Benefits: - Main console now waits for enhancement to complete - No more "Package your skill" message while enhancement is running - Timeout prevents infinite hangs - Terminal mode still available for users who want it - Better error messages and progress tracking Fixes user request: "make sure 1. console wait for it to finish" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1506,7 +1506,9 @@ def setup_argument_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument('--enhance', action='store_true',
|
||||
help='Enhance SKILL.md using Claude API after building (requires API key)')
|
||||
parser.add_argument('--enhance-local', action='store_true',
|
||||
help='Enhance SKILL.md using Claude Code in new terminal (no API key needed)')
|
||||
help='Enhance SKILL.md using Claude Code (no API key needed, runs in background)')
|
||||
parser.add_argument('--interactive-enhancement', action='store_true',
|
||||
help='Open terminal window for enhancement (use with --enhance-local)')
|
||||
parser.add_argument('--api-key', type=str,
|
||||
help='Anthropic API key for --enhance (or set ANTHROPIC_API_KEY)')
|
||||
parser.add_argument('--resume', action='store_true',
|
||||
@@ -1740,16 +1742,25 @@ def execute_enhancement(config: Dict[str, Any], args: argparse.Namespace) -> Non
|
||||
# Optional enhancement with Claude Code (local, no API key)
|
||||
if args.enhance_local:
|
||||
logger.info("\n" + "=" * 60)
|
||||
logger.info("ENHANCING SKILL.MD WITH CLAUDE CODE (LOCAL)")
|
||||
if args.interactive_enhancement:
|
||||
logger.info("ENHANCING SKILL.MD WITH CLAUDE CODE (INTERACTIVE)")
|
||||
else:
|
||||
logger.info("ENHANCING SKILL.MD WITH CLAUDE CODE (HEADLESS)")
|
||||
logger.info("=" * 60 + "\n")
|
||||
|
||||
try:
|
||||
enhance_cmd = ['python3', 'cli/enhance_skill_local.py', f'output/{config["name"]}/']
|
||||
subprocess.run(enhance_cmd, check=True)
|
||||
enhance_cmd = ['skill-seekers-enhance', f'output/{config["name"]}/']
|
||||
if args.interactive_enhancement:
|
||||
enhance_cmd.append('--interactive-enhancement')
|
||||
|
||||
result = subprocess.run(enhance_cmd, check=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info("\n✅ Enhancement complete!")
|
||||
except subprocess.CalledProcessError:
|
||||
logger.warning("\n⚠ Enhancement failed, but skill was still built")
|
||||
except FileNotFoundError:
|
||||
logger.warning("\n⚠ enhance_skill_local.py not found. Run manually:")
|
||||
logger.warning("\n⚠ skill-seekers-enhance command not found. Run manually:")
|
||||
logger.info(" skill-seekers-enhance output/%s/", config['name'])
|
||||
|
||||
# Print packaging instructions
|
||||
@@ -1759,10 +1770,11 @@ def execute_enhancement(config: Dict[str, Any], args: argparse.Namespace) -> Non
|
||||
# Suggest enhancement if not done
|
||||
if not args.enhance and not args.enhance_local:
|
||||
logger.info("\n💡 Optional: Enhance SKILL.md with Claude:")
|
||||
logger.info(" API-based: skill-seekers-enhance output/%s/", config['name'])
|
||||
logger.info(" or re-run with: --enhance")
|
||||
logger.info(" Local (no API key): skill-seekers-enhance output/%s/", config['name'])
|
||||
logger.info(" or re-run with: --enhance-local")
|
||||
logger.info(" Local (recommended): skill-seekers-enhance output/%s/", config['name'])
|
||||
logger.info(" or re-run with: --enhance-local")
|
||||
logger.info(" API-based: skill-seekers-enhance-api output/%s/", config['name'])
|
||||
logger.info(" or re-run with: --enhance")
|
||||
logger.info("\n💡 Tip: Use --interactive-enhancement with --enhance-local to open terminal window")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
@@ -166,8 +166,13 @@ First, backup the original to: {self.skill_md_path.with_suffix('.md.backup').abs
|
||||
|
||||
return prompt
|
||||
|
||||
def run(self):
|
||||
"""Main enhancement workflow"""
|
||||
def run(self, headless=True, timeout=600):
|
||||
"""Main enhancement workflow
|
||||
|
||||
Args:
|
||||
headless: If True, run claude directly without opening terminal (default: True)
|
||||
timeout: Maximum time to wait for enhancement in seconds (default: 600 = 10 minutes)
|
||||
"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"LOCAL ENHANCEMENT: {self.skill_dir.name}")
|
||||
print(f"{'='*60}\n")
|
||||
@@ -207,7 +212,11 @@ First, backup the original to: {self.skill_md_path.with_suffix('.md.backup').abs
|
||||
|
||||
print(f" ✓ Prompt saved ({len(prompt):,} characters)\n")
|
||||
|
||||
# Launch Claude Code in new terminal
|
||||
# Headless mode: Run claude directly without opening terminal
|
||||
if headless:
|
||||
return self._run_headless(prompt_file, timeout)
|
||||
|
||||
# Terminal mode: Launch Claude Code in new terminal
|
||||
print("🚀 Launching Claude Code in new terminal...")
|
||||
print(" This will:")
|
||||
print(" 1. Open a new terminal window")
|
||||
@@ -281,20 +290,159 @@ rm {prompt_file}
|
||||
|
||||
return True
|
||||
|
||||
def _run_headless(self, prompt_file, timeout):
|
||||
"""Run Claude enhancement in headless mode (no terminal window)
|
||||
|
||||
Args:
|
||||
prompt_file: Path to prompt file
|
||||
timeout: Maximum seconds to wait
|
||||
|
||||
Returns:
|
||||
bool: True if enhancement succeeded
|
||||
"""
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
print("✨ Running Claude Code enhancement (headless mode)...")
|
||||
print(f" Timeout: {timeout} seconds ({timeout//60} minutes)")
|
||||
print()
|
||||
|
||||
# Record initial state
|
||||
initial_mtime = self.skill_md_path.stat().st_mtime if self.skill_md_path.exists() else 0
|
||||
initial_size = self.skill_md_path.stat().st_size if self.skill_md_path.exists() else 0
|
||||
|
||||
# Start timer
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# Run claude command directly (this WAITS for completion)
|
||||
print(" Running: claude {prompt_file}")
|
||||
print(" ⏳ Please wait...")
|
||||
print()
|
||||
|
||||
result = subprocess.run(
|
||||
['claude', prompt_file],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
# Check if successful
|
||||
if result.returncode == 0:
|
||||
# Verify SKILL.md was actually updated
|
||||
if self.skill_md_path.exists():
|
||||
new_mtime = self.skill_md_path.stat().st_mtime
|
||||
new_size = self.skill_md_path.stat().st_size
|
||||
|
||||
if new_mtime > initial_mtime and new_size > initial_size:
|
||||
print(f"✅ Enhancement complete! ({elapsed:.1f} seconds)")
|
||||
print(f" SKILL.md updated: {new_size:,} bytes")
|
||||
print()
|
||||
|
||||
# Clean up prompt file
|
||||
try:
|
||||
os.unlink(prompt_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ Claude finished but SKILL.md was not updated")
|
||||
print(f" This might indicate an error during enhancement")
|
||||
print()
|
||||
return False
|
||||
else:
|
||||
print(f"❌ SKILL.md not found after enhancement")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ Claude Code returned error (exit code: {result.returncode})")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr[:200]}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
elapsed = time.time() - start_time
|
||||
print(f"\n⚠️ Enhancement timed out after {elapsed:.0f} seconds")
|
||||
print(f" Timeout limit: {timeout} seconds")
|
||||
print()
|
||||
print(" Possible reasons:")
|
||||
print(" - Skill is very large (many references)")
|
||||
print(" - Claude is taking longer than usual")
|
||||
print(" - Network issues")
|
||||
print()
|
||||
print(" Try:")
|
||||
print(" 1. Use terminal mode: --interactive-enhancement")
|
||||
print(" 2. Reduce reference content")
|
||||
print(" 3. Try again later")
|
||||
|
||||
# Clean up
|
||||
try:
|
||||
os.unlink(prompt_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ 'claude' command not found")
|
||||
print()
|
||||
print(" Make sure Claude Code CLI is installed:")
|
||||
print(" See: https://docs.claude.com/claude-code")
|
||||
print()
|
||||
print(" Try terminal mode instead: --interactive-enhancement")
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unexpected error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: skill-seekers enhance <skill_directory>")
|
||||
print()
|
||||
print("Examples:")
|
||||
print(" skill-seekers enhance output/steam-inventory/")
|
||||
print(" skill-seekers enhance output/react/")
|
||||
sys.exit(1)
|
||||
import argparse
|
||||
|
||||
skill_dir = sys.argv[1]
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Enhance a skill with Claude Code (local)",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Headless mode (default - runs in background)
|
||||
skill-seekers enhance output/react/
|
||||
|
||||
enhancer = LocalSkillEnhancer(skill_dir)
|
||||
success = enhancer.run()
|
||||
# Interactive mode (opens terminal window)
|
||||
skill-seekers enhance output/react/ --interactive-enhancement
|
||||
|
||||
# Custom timeout
|
||||
skill-seekers enhance output/react/ --timeout 1200
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'skill_directory',
|
||||
help='Path to skill directory (e.g., output/react/)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--interactive-enhancement',
|
||||
action='store_true',
|
||||
help='Open terminal window for enhancement (default: headless mode)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--timeout',
|
||||
type=int,
|
||||
default=600,
|
||||
help='Timeout in seconds for headless mode (default: 600 = 10 minutes)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Run enhancement
|
||||
enhancer = LocalSkillEnhancer(args.skill_directory)
|
||||
headless = not args.interactive_enhancement # Invert: default is headless
|
||||
success = enhancer.run(headless=headless, timeout=args.timeout)
|
||||
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user