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',
|
parser.add_argument('--enhance', action='store_true',
|
||||||
help='Enhance SKILL.md using Claude API after building (requires API key)')
|
help='Enhance SKILL.md using Claude API after building (requires API key)')
|
||||||
parser.add_argument('--enhance-local', action='store_true',
|
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,
|
parser.add_argument('--api-key', type=str,
|
||||||
help='Anthropic API key for --enhance (or set ANTHROPIC_API_KEY)')
|
help='Anthropic API key for --enhance (or set ANTHROPIC_API_KEY)')
|
||||||
parser.add_argument('--resume', action='store_true',
|
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)
|
# Optional enhancement with Claude Code (local, no API key)
|
||||||
if args.enhance_local:
|
if args.enhance_local:
|
||||||
logger.info("\n" + "=" * 60)
|
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")
|
logger.info("=" * 60 + "\n")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
enhance_cmd = ['python3', 'cli/enhance_skill_local.py', f'output/{config["name"]}/']
|
enhance_cmd = ['skill-seekers-enhance', f'output/{config["name"]}/']
|
||||||
subprocess.run(enhance_cmd, check=True)
|
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:
|
except subprocess.CalledProcessError:
|
||||||
logger.warning("\n⚠ Enhancement failed, but skill was still built")
|
logger.warning("\n⚠ Enhancement failed, but skill was still built")
|
||||||
except FileNotFoundError:
|
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'])
|
logger.info(" skill-seekers-enhance output/%s/", config['name'])
|
||||||
|
|
||||||
# Print packaging instructions
|
# Print packaging instructions
|
||||||
@@ -1759,10 +1770,11 @@ def execute_enhancement(config: Dict[str, Any], args: argparse.Namespace) -> Non
|
|||||||
# Suggest enhancement if not done
|
# Suggest enhancement if not done
|
||||||
if not args.enhance and not args.enhance_local:
|
if not args.enhance and not args.enhance_local:
|
||||||
logger.info("\n💡 Optional: Enhance SKILL.md with Claude:")
|
logger.info("\n💡 Optional: Enhance SKILL.md with Claude:")
|
||||||
logger.info(" API-based: skill-seekers-enhance output/%s/", config['name'])
|
logger.info(" Local (recommended): skill-seekers-enhance output/%s/", config['name'])
|
||||||
logger.info(" or re-run with: --enhance")
|
logger.info(" or re-run with: --enhance-local")
|
||||||
logger.info(" Local (no API key): skill-seekers-enhance output/%s/", config['name'])
|
logger.info(" API-based: skill-seekers-enhance-api output/%s/", config['name'])
|
||||||
logger.info(" or re-run with: --enhance-local")
|
logger.info(" or re-run with: --enhance")
|
||||||
|
logger.info("\n💡 Tip: Use --interactive-enhancement with --enhance-local to open terminal window")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|||||||
@@ -166,8 +166,13 @@ First, backup the original to: {self.skill_md_path.with_suffix('.md.backup').abs
|
|||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def run(self):
|
def run(self, headless=True, timeout=600):
|
||||||
"""Main enhancement workflow"""
|
"""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"\n{'='*60}")
|
||||||
print(f"LOCAL ENHANCEMENT: {self.skill_dir.name}")
|
print(f"LOCAL ENHANCEMENT: {self.skill_dir.name}")
|
||||||
print(f"{'='*60}\n")
|
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")
|
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("🚀 Launching Claude Code in new terminal...")
|
||||||
print(" This will:")
|
print(" This will:")
|
||||||
print(" 1. Open a new terminal window")
|
print(" 1. Open a new terminal window")
|
||||||
@@ -281,20 +290,159 @@ rm {prompt_file}
|
|||||||
|
|
||||||
return True
|
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():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
import argparse
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
# Interactive mode (opens terminal window)
|
||||||
success = enhancer.run()
|
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)
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user