Add multi-agent local enhancement support
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
SKILL.md Enhancement Script (Local - Using Claude Code)
|
SKILL.md Enhancement Script (Local - Using CLI Coding Agents)
|
||||||
Opens a new terminal with Claude Code to enhance SKILL.md, then reports back.
|
Uses a local coding agent CLI (Claude Code, Codex CLI, Copilot CLI, OpenCode CLI)
|
||||||
No API key needed - uses your existing Claude Code Max plan!
|
to enhance SKILL.md, then reports back. No API key needed.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
# Headless mode (default - runs in foreground, waits for completion)
|
# Headless mode (default - runs in foreground, waits for completion)
|
||||||
@@ -11,8 +11,8 @@ Usage:
|
|||||||
# Background mode (runs in background, returns immediately)
|
# Background mode (runs in background, returns immediately)
|
||||||
skill-seekers enhance output/react/ --background
|
skill-seekers enhance output/react/ --background
|
||||||
|
|
||||||
# Force mode (no confirmations, auto-yes to everything)
|
# Disable force mode (enable confirmations)
|
||||||
skill-seekers enhance output/react/ --force
|
skill-seekers enhance output/react/ --no-force
|
||||||
|
|
||||||
# Daemon mode (persistent background process)
|
# Daemon mode (persistent background process)
|
||||||
skill-seekers enhance output/react/ --daemon
|
skill-seekers enhance output/react/ --daemon
|
||||||
@@ -20,9 +20,17 @@ Usage:
|
|||||||
# Interactive terminal mode
|
# Interactive terminal mode
|
||||||
skill-seekers enhance output/react/ --interactive-enhancement
|
skill-seekers enhance output/react/ --interactive-enhancement
|
||||||
|
|
||||||
|
# Use a different local coding agent
|
||||||
|
skill-seekers enhance output/react/ --agent codex
|
||||||
|
skill-seekers enhance output/react/ --agent copilot
|
||||||
|
skill-seekers enhance output/react/ --agent opencode
|
||||||
|
|
||||||
|
# Custom agent command (advanced)
|
||||||
|
skill-seekers enhance output/react/ --agent custom --agent-cmd "my-agent --prompt {prompt_file}"
|
||||||
|
|
||||||
Modes:
|
Modes:
|
||||||
- headless: Runs claude CLI directly, BLOCKS until done (default)
|
- headless: Runs local CLI directly, BLOCKS until done (default)
|
||||||
- background: Runs claude CLI in background, returns immediately
|
- background: Runs local CLI in background, returns immediately
|
||||||
- daemon: Runs as persistent background process with monitoring
|
- daemon: Runs as persistent background process with monitoring
|
||||||
- terminal: Opens new terminal window (interactive)
|
- terminal: Opens new terminal window (interactive)
|
||||||
|
|
||||||
@@ -38,6 +46,7 @@ Terminal Selection:
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -104,19 +113,157 @@ def detect_terminal_app():
|
|||||||
return "Terminal", "default"
|
return "Terminal", "default"
|
||||||
|
|
||||||
|
|
||||||
|
AGENT_PRESETS = {
|
||||||
|
"claude": {
|
||||||
|
"display_name": "Claude Code",
|
||||||
|
"command": ["claude", "{prompt_file}"],
|
||||||
|
"supports_skip_permissions": True,
|
||||||
|
},
|
||||||
|
"codex": {
|
||||||
|
"display_name": "OpenAI Codex CLI",
|
||||||
|
"command": ["codex", "exec", "--full-auto", "--skip-git-repo-check", "-"],
|
||||||
|
"supports_skip_permissions": False,
|
||||||
|
},
|
||||||
|
"copilot": {
|
||||||
|
"display_name": "GitHub Copilot CLI",
|
||||||
|
"command": ["gh", "copilot", "chat"],
|
||||||
|
"supports_skip_permissions": False,
|
||||||
|
},
|
||||||
|
"opencode": {
|
||||||
|
"display_name": "OpenCode CLI",
|
||||||
|
"command": ["opencode"],
|
||||||
|
"supports_skip_permissions": False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_agent_name(agent_name: str) -> str:
|
||||||
|
if not agent_name:
|
||||||
|
return "claude"
|
||||||
|
normalized = agent_name.strip().lower()
|
||||||
|
aliases = {
|
||||||
|
"claude-code": "claude",
|
||||||
|
"claude_code": "claude",
|
||||||
|
"codex-cli": "codex",
|
||||||
|
"copilot-cli": "copilot",
|
||||||
|
"open-code": "opencode",
|
||||||
|
"open_code": "opencode",
|
||||||
|
}
|
||||||
|
return aliases.get(normalized, normalized)
|
||||||
|
|
||||||
|
|
||||||
class LocalSkillEnhancer:
|
class LocalSkillEnhancer:
|
||||||
def __init__(self, skill_dir, force=True):
|
def __init__(self, skill_dir, force=True, agent=None, agent_cmd=None):
|
||||||
"""Initialize enhancer.
|
"""Initialize enhancer.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
skill_dir: Path to skill directory
|
skill_dir: Path to skill directory
|
||||||
force: If True, skip all confirmations (default: True, use --no-force to disable)
|
force: If True, skip all confirmations (default: True, use --no-force to disable)
|
||||||
|
agent: Local coding agent identifier (claude, codex, copilot, opencode, custom)
|
||||||
|
agent_cmd: Override command template (use {prompt_file} placeholder or stdin)
|
||||||
"""
|
"""
|
||||||
self.skill_dir = Path(skill_dir)
|
self.skill_dir = Path(skill_dir)
|
||||||
self.references_dir = self.skill_dir / "references"
|
self.references_dir = self.skill_dir / "references"
|
||||||
self.skill_md_path = self.skill_dir / "SKILL.md"
|
self.skill_md_path = self.skill_dir / "SKILL.md"
|
||||||
self.force = force
|
self.force = force
|
||||||
self.status_file = self.skill_dir / ".enhancement_status.json"
|
self.status_file = self.skill_dir / ".enhancement_status.json"
|
||||||
|
self.agent, self.agent_cmd, self.agent_display = self._resolve_agent(agent, agent_cmd)
|
||||||
|
|
||||||
|
def _resolve_agent(self, agent, agent_cmd):
|
||||||
|
env_agent = os.environ.get("SKILL_SEEKER_AGENT", "").strip()
|
||||||
|
env_cmd = os.environ.get("SKILL_SEEKER_AGENT_CMD", "").strip()
|
||||||
|
|
||||||
|
agent_name = _normalize_agent_name(agent or env_agent or "claude")
|
||||||
|
cmd_override = agent_cmd or env_cmd or None
|
||||||
|
|
||||||
|
if agent_name == "custom":
|
||||||
|
if not cmd_override:
|
||||||
|
raise ValueError(
|
||||||
|
"Custom agent requires --agent-cmd or SKILL_SEEKER_AGENT_CMD to be set."
|
||||||
|
)
|
||||||
|
display_name = "Custom CLI Agent"
|
||||||
|
return agent_name, cmd_override, display_name
|
||||||
|
|
||||||
|
if agent_name not in AGENT_PRESETS:
|
||||||
|
available = ", ".join(sorted(AGENT_PRESETS.keys()))
|
||||||
|
raise ValueError(
|
||||||
|
f"Unknown agent '{agent_name}'. Choose one of: {available} or use --agent custom."
|
||||||
|
)
|
||||||
|
|
||||||
|
display_name = AGENT_PRESETS[agent_name]["display_name"]
|
||||||
|
return agent_name, cmd_override, display_name
|
||||||
|
|
||||||
|
def _build_agent_command(self, prompt_file, include_permissions_flag):
|
||||||
|
if self.agent_cmd:
|
||||||
|
cmd_parts = shlex.split(self.agent_cmd)
|
||||||
|
supports_skip_permissions = False
|
||||||
|
else:
|
||||||
|
preset = AGENT_PRESETS[self.agent]
|
||||||
|
cmd_parts = list(preset["command"])
|
||||||
|
supports_skip_permissions = preset.get("supports_skip_permissions", False)
|
||||||
|
|
||||||
|
if (
|
||||||
|
include_permissions_flag
|
||||||
|
and supports_skip_permissions
|
||||||
|
and "--dangerously-skip-permissions" not in cmd_parts
|
||||||
|
):
|
||||||
|
cmd_parts.insert(1, "--dangerously-skip-permissions")
|
||||||
|
|
||||||
|
uses_prompt_file = False
|
||||||
|
for idx, arg in enumerate(cmd_parts):
|
||||||
|
if "{prompt_file}" in arg:
|
||||||
|
cmd_parts[idx] = arg.replace("{prompt_file}", prompt_file)
|
||||||
|
uses_prompt_file = True
|
||||||
|
|
||||||
|
return cmd_parts, uses_prompt_file
|
||||||
|
|
||||||
|
def _format_agent_command(self, prompt_file, include_permissions_flag):
|
||||||
|
cmd_parts, uses_prompt_file = self._build_agent_command(
|
||||||
|
prompt_file, include_permissions_flag
|
||||||
|
)
|
||||||
|
cmd_str = shlex.join(cmd_parts)
|
||||||
|
if uses_prompt_file:
|
||||||
|
return cmd_str
|
||||||
|
return f"cat {shlex.quote(prompt_file)} | {cmd_str}"
|
||||||
|
|
||||||
|
def _run_agent_command(self, prompt_file, timeout, include_permissions_flag, quiet=False):
|
||||||
|
cmd_parts, uses_prompt_file = self._build_agent_command(
|
||||||
|
prompt_file, include_permissions_flag
|
||||||
|
)
|
||||||
|
|
||||||
|
if not quiet:
|
||||||
|
cmd_display = self._format_agent_command(prompt_file, include_permissions_flag)
|
||||||
|
print(f" Command: {cmd_display}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if uses_prompt_file:
|
||||||
|
return (
|
||||||
|
subprocess.run(
|
||||||
|
cmd_parts,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout,
|
||||||
|
cwd=str(self.skill_dir),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
prompt_text = Path(prompt_file).read_text(encoding="utf-8")
|
||||||
|
return (
|
||||||
|
subprocess.run(
|
||||||
|
cmd_parts,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout,
|
||||||
|
cwd=str(self.skill_dir),
|
||||||
|
input=prompt_text,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None, f"Command not found: {cmd_parts[0]}"
|
||||||
|
except Exception as e:
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
def summarize_reference(self, content: str, target_ratio: float = 0.3) -> str:
|
def summarize_reference(self, content: str, target_ratio: float = 0.3) -> str:
|
||||||
"""Intelligently summarize reference content to reduce size.
|
"""Intelligently summarize reference content to reduce size.
|
||||||
@@ -190,7 +337,7 @@ class LocalSkillEnhancer:
|
|||||||
return "\n".join(result)
|
return "\n".join(result)
|
||||||
|
|
||||||
def create_enhancement_prompt(self, use_summarization=False, summarization_ratio=0.3):
|
def create_enhancement_prompt(self, use_summarization=False, summarization_ratio=0.3):
|
||||||
"""Create the prompt file for Claude Code
|
"""Create the prompt file for a local coding agent
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
use_summarization: If True, apply smart summarization to reduce size
|
use_summarization: If True, apply smart summarization to reduce size
|
||||||
@@ -415,7 +562,7 @@ CRITICAL INSTRUCTIONS:
|
|||||||
2. Then, write the enhanced content to: SKILL.md
|
2. Then, write the enhanced content to: SKILL.md
|
||||||
|
|
||||||
This is NOT a read-only task - you have permission to modify SKILL.md.
|
This is NOT a read-only task - you have permission to modify SKILL.md.
|
||||||
Even if running from within another Claude Code session, this modification is ALLOWED and EXPECTED.
|
Even if running from within another coding agent session, this modification is ALLOWED and EXPECTED.
|
||||||
|
|
||||||
VERIFICATION:
|
VERIFICATION:
|
||||||
After writing, the file SKILL.md should:
|
After writing, the file SKILL.md should:
|
||||||
@@ -464,7 +611,7 @@ After writing, the file SKILL.md should:
|
|||||||
"""Main enhancement workflow with automatic smart summarization for large skills.
|
"""Main enhancement workflow with automatic smart summarization for large skills.
|
||||||
|
|
||||||
Automatically detects large skills (>30K chars) and applies smart summarization
|
Automatically detects large skills (>30K chars) and applies smart summarization
|
||||||
to ensure compatibility with Claude CLI's ~30-40K character limit.
|
to reduce input size for local coding agent CLIs.
|
||||||
|
|
||||||
Smart summarization strategy:
|
Smart summarization strategy:
|
||||||
- Keeps first 20% (introduction/overview)
|
- Keeps first 20% (introduction/overview)
|
||||||
@@ -473,7 +620,7 @@ After writing, the file SKILL.md should:
|
|||||||
- Reduces to ~30% of original size
|
- Reduces to ~30% of original size
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
headless: If True, run claude directly without opening terminal (default: True)
|
headless: If True, run local agent directly without opening terminal (default: True)
|
||||||
timeout: Maximum time to wait for enhancement in seconds (default: 600 = 10 minutes)
|
timeout: Maximum time to wait for enhancement in seconds (default: 600 = 10 minutes)
|
||||||
background: If True, run in background and return immediately (default: False)
|
background: If True, run in background and return immediately (default: False)
|
||||||
daemon: If True, run as persistent daemon with monitoring (default: False)
|
daemon: If True, run as persistent daemon with monitoring (default: False)
|
||||||
@@ -490,6 +637,7 @@ After writing, the file SKILL.md should:
|
|||||||
return self._run_daemon(timeout)
|
return self._run_daemon(timeout)
|
||||||
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"Agent: {self.agent_display}")
|
||||||
print(f"{'=' * 60}\n")
|
print(f"{'=' * 60}\n")
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
@@ -517,7 +665,10 @@ After writing, the file SKILL.md should:
|
|||||||
if use_summarization:
|
if use_summarization:
|
||||||
print("⚠️ LARGE SKILL DETECTED")
|
print("⚠️ LARGE SKILL DETECTED")
|
||||||
print(f" 📊 Reference content: {total_size:,} characters")
|
print(f" 📊 Reference content: {total_size:,} characters")
|
||||||
print(" 💡 Claude CLI limit: ~30,000-40,000 characters")
|
if self.agent == "claude":
|
||||||
|
print(" 💡 Claude CLI limit: ~30,000-40,000 characters")
|
||||||
|
else:
|
||||||
|
print(" 💡 Local CLI agents often have input limits; summarizing to be safe")
|
||||||
print()
|
print()
|
||||||
print(" 🔧 Applying smart summarization to ensure success...")
|
print(" 🔧 Applying smart summarization to ensure success...")
|
||||||
print(" • Keeping introductions and overviews")
|
print(" • Keeping introductions and overviews")
|
||||||
@@ -542,27 +693,31 @@ After writing, the file SKILL.md should:
|
|||||||
|
|
||||||
if use_summarization:
|
if use_summarization:
|
||||||
print(f" ✓ Prompt created and optimized ({len(prompt):,} characters)")
|
print(f" ✓ Prompt created and optimized ({len(prompt):,} characters)")
|
||||||
print(" ✓ Ready for Claude CLI (within safe limits)")
|
if self.agent == "claude":
|
||||||
|
print(" ✓ Ready for Claude CLI (within safe limits)")
|
||||||
|
else:
|
||||||
|
print(" ✓ Ready for local CLI (within safe limits)")
|
||||||
print()
|
print()
|
||||||
else:
|
else:
|
||||||
print(f" ✓ Prompt saved ({len(prompt):,} characters)\n")
|
print(f" ✓ Prompt saved ({len(prompt):,} characters)\n")
|
||||||
|
|
||||||
# Headless mode: Run claude directly without opening terminal
|
# Headless mode: Run local agent directly without opening terminal
|
||||||
if headless:
|
if headless:
|
||||||
return self._run_headless(prompt_file, timeout)
|
return self._run_headless(prompt_file, timeout)
|
||||||
|
|
||||||
# Terminal mode: Launch Claude Code in new terminal
|
# Terminal mode: Launch local agent in new terminal
|
||||||
print("🚀 Launching Claude Code in new terminal...")
|
print(f"🚀 Launching {self.agent_display} in new terminal...")
|
||||||
print(" This will:")
|
print(" This will:")
|
||||||
print(" 1. Open a new terminal window")
|
print(" 1. Open a new terminal window")
|
||||||
print(" 2. Run Claude Code with the enhancement task")
|
print(" 2. Run the local coding agent with the enhancement task")
|
||||||
print(" 3. Claude will read the docs and enhance SKILL.md")
|
print(" 3. The agent will read the docs and enhance SKILL.md")
|
||||||
print(" 4. Terminal will auto-close when done")
|
print(" 4. Terminal will auto-close when done")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Create a shell script to run in the terminal
|
# Create a shell script to run in the terminal
|
||||||
|
command_line = self._format_agent_command(prompt_file, include_permissions_flag=False)
|
||||||
shell_script = f"""#!/bin/bash
|
shell_script = f"""#!/bin/bash
|
||||||
claude {prompt_file}
|
{command_line}
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Enhancement complete!"
|
echo "✅ Enhancement complete!"
|
||||||
echo "Press any key to close..."
|
echo "Press any key to close..."
|
||||||
@@ -602,12 +757,12 @@ rm {prompt_file}
|
|||||||
else:
|
else:
|
||||||
print("⚠️ Auto-launch only works on macOS")
|
print("⚠️ Auto-launch only works on macOS")
|
||||||
print("\nManually run this command in a new terminal:")
|
print("\nManually run this command in a new terminal:")
|
||||||
print(f" claude '{prompt_file}'")
|
print(f" {self._format_agent_command(prompt_file, include_permissions_flag=False)}")
|
||||||
print("\nThen delete the prompt file:")
|
print("\nThen delete the prompt file:")
|
||||||
print(f" rm '{prompt_file}'")
|
print(f" rm '{prompt_file}'")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("✅ New terminal launched with Claude Code!")
|
print(f"✅ New terminal launched with {self.agent_display}!")
|
||||||
print()
|
print()
|
||||||
print("📊 Status:")
|
print("📊 Status:")
|
||||||
print(f" - Prompt file: {prompt_file}")
|
print(f" - Prompt file: {prompt_file}")
|
||||||
@@ -617,7 +772,7 @@ rm {prompt_file}
|
|||||||
f" - Original backed up to: {self.skill_md_path.with_suffix('.md.backup').absolute()}"
|
f" - Original backed up to: {self.skill_md_path.with_suffix('.md.backup').absolute()}"
|
||||||
)
|
)
|
||||||
print()
|
print()
|
||||||
print("⏳ Wait for Claude Code to finish in the other terminal...")
|
print("⏳ Wait for the local agent to finish in the other terminal...")
|
||||||
print(" (Usually takes 30-60 seconds)")
|
print(" (Usually takes 30-60 seconds)")
|
||||||
print()
|
print()
|
||||||
print("💡 When done:")
|
print("💡 When done:")
|
||||||
@@ -630,7 +785,7 @@ rm {prompt_file}
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _run_headless(self, prompt_file, timeout):
|
def _run_headless(self, prompt_file, timeout):
|
||||||
"""Run Claude enhancement in headless mode (no terminal window)
|
"""Run local agent enhancement in headless mode (no terminal window)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prompt_file: Path to prompt file
|
prompt_file: Path to prompt file
|
||||||
@@ -641,7 +796,7 @@ rm {prompt_file}
|
|||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
|
||||||
print("✨ Running Claude Code enhancement (headless mode)...")
|
print(f"✨ Running {self.agent_display} enhancement (headless mode)...")
|
||||||
print(f" Timeout: {timeout} seconds ({timeout // 60} minutes)")
|
print(f" Timeout: {timeout} seconds ({timeout // 60} minutes)")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -653,21 +808,21 @@ rm {prompt_file}
|
|||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Run claude command directly (this WAITS for completion)
|
# Run local agent command directly (this WAITS for completion)
|
||||||
# Use --dangerously-skip-permissions to bypass ALL permission checks
|
|
||||||
print(f" Running: claude --dangerously-skip-permissions {prompt_file}")
|
|
||||||
print(" ⏳ Please wait...")
|
print(" ⏳ Please wait...")
|
||||||
print(f" Working directory: {self.skill_dir}")
|
print(f" Working directory: {self.skill_dir}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
result = subprocess.run(
|
result, error = self._run_agent_command(
|
||||||
["claude", "--dangerously-skip-permissions", prompt_file],
|
prompt_file, timeout, include_permissions_flag=True
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=timeout,
|
|
||||||
cwd=str(self.skill_dir), # Run from skill directory
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
print(f"❌ {error}")
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
os.unlink(prompt_file)
|
||||||
|
return False
|
||||||
|
|
||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
|
|
||||||
# Check if successful
|
# Check if successful
|
||||||
@@ -688,14 +843,14 @@ rm {prompt_file}
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print("⚠️ Claude finished but SKILL.md was not updated")
|
print("⚠️ Agent finished but SKILL.md was not updated")
|
||||||
print(f" Initial: mtime={initial_mtime}, size={initial_size}")
|
print(f" Initial: mtime={initial_mtime}, size={initial_size}")
|
||||||
print(f" Final: mtime={new_mtime}, size={new_size}")
|
print(f" Final: mtime={new_mtime}, size={new_size}")
|
||||||
print(" This might indicate an error during enhancement")
|
print(" This might indicate an error during enhancement")
|
||||||
print()
|
print()
|
||||||
# Show last 20 lines of stdout for debugging
|
# Show last 20 lines of stdout for debugging
|
||||||
if result.stdout:
|
if result.stdout:
|
||||||
print(" Last output from Claude:")
|
print(" Last output from agent:")
|
||||||
lines = result.stdout.strip().split("\n")[-20:]
|
lines = result.stdout.strip().split("\n")[-20:]
|
||||||
for line in lines:
|
for line in lines:
|
||||||
print(f" | {line}")
|
print(f" | {line}")
|
||||||
@@ -705,7 +860,9 @@ rm {prompt_file}
|
|||||||
print("❌ SKILL.md not found after enhancement")
|
print("❌ SKILL.md not found after enhancement")
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
print(f"❌ Claude Code returned error (exit code: {result.returncode})")
|
print(
|
||||||
|
f"❌ {self.agent_display} returned error (exit code: {result.returncode})"
|
||||||
|
)
|
||||||
if result.stderr:
|
if result.stderr:
|
||||||
print(f" Error: {result.stderr[:200]}")
|
print(f" Error: {result.stderr[:200]}")
|
||||||
return False
|
return False
|
||||||
@@ -717,7 +874,7 @@ rm {prompt_file}
|
|||||||
print()
|
print()
|
||||||
print(" Possible reasons:")
|
print(" Possible reasons:")
|
||||||
print(" - Skill is very large (many references)")
|
print(" - Skill is very large (many references)")
|
||||||
print(" - Claude is taking longer than usual")
|
print(" - Agent is taking longer than usual")
|
||||||
print(" - Network issues")
|
print(" - Network issues")
|
||||||
print()
|
print()
|
||||||
print(" Try:")
|
print(" Try:")
|
||||||
@@ -732,10 +889,9 @@ rm {prompt_file}
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print("❌ 'claude' command not found")
|
print(f"❌ '{self._build_agent_command(prompt_file, True)[0][0]}' command not found")
|
||||||
print()
|
print()
|
||||||
print(" Make sure Claude Code CLI is installed:")
|
print(" Make sure your local coding agent CLI is installed and on PATH.")
|
||||||
print(" See: https://docs.claude.com/claude-code")
|
|
||||||
print()
|
print()
|
||||||
print(" Try terminal mode instead: --interactive-enhancement")
|
print(" Try terminal mode instead: --interactive-enhancement")
|
||||||
|
|
||||||
@@ -776,7 +932,7 @@ rm {prompt_file}
|
|||||||
self.write_status("failed", error="No reference files found")
|
self.write_status("failed", error="No reference files found")
|
||||||
return
|
return
|
||||||
|
|
||||||
total_size = sum(len(c) for c in references.values())
|
total_size = sum(meta["size"] for meta in references.values())
|
||||||
use_summarization = total_size > 30000
|
use_summarization = total_size > 30000
|
||||||
|
|
||||||
self.write_status("running", "Creating enhancement prompt...", progress=0.3)
|
self.write_status("running", "Creating enhancement prompt...", progress=0.3)
|
||||||
@@ -794,26 +950,30 @@ rm {prompt_file}
|
|||||||
prompt_file = f.name
|
prompt_file = f.name
|
||||||
f.write(prompt)
|
f.write(prompt)
|
||||||
|
|
||||||
self.write_status("running", "Running Claude Code enhancement...", progress=0.5)
|
self.write_status(
|
||||||
|
"running", f"Running {self.agent_display} enhancement...", progress=0.5
|
||||||
|
)
|
||||||
|
|
||||||
# Run enhancement
|
# Run enhancement
|
||||||
if headless:
|
if headless:
|
||||||
# Run headless (subprocess.run - blocking in thread)
|
# Run headless (subprocess.run - blocking in thread)
|
||||||
result = subprocess.run(
|
result, error = self._run_agent_command(
|
||||||
["claude", prompt_file], capture_output=True, text=True, timeout=timeout
|
prompt_file, timeout, include_permissions_flag=True, quiet=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
with contextlib.suppress(Exception):
|
with contextlib.suppress(Exception):
|
||||||
os.unlink(prompt_file)
|
os.unlink(prompt_file)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if error:
|
||||||
|
self.write_status("failed", error=error)
|
||||||
|
elif result.returncode == 0:
|
||||||
self.write_status(
|
self.write_status(
|
||||||
"completed", "Enhancement completed successfully!", progress=1.0
|
"completed", "Enhancement completed successfully!", progress=1.0
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.write_status(
|
self.write_status(
|
||||||
"failed", error=f"Claude returned error: {result.returncode}"
|
"failed", error=f"Agent returned error: {result.returncode}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Terminal mode in background doesn't make sense
|
# Terminal mode in background doesn't make sense
|
||||||
@@ -895,7 +1055,11 @@ try:
|
|||||||
sys.path.insert(0, "{os.path.dirname(os.path.dirname(os.path.abspath(__file__)))}")
|
sys.path.insert(0, "{os.path.dirname(os.path.dirname(os.path.abspath(__file__)))}")
|
||||||
from skill_seekers.cli.enhance_skill_local import LocalSkillEnhancer
|
from skill_seekers.cli.enhance_skill_local import LocalSkillEnhancer
|
||||||
|
|
||||||
enhancer = LocalSkillEnhancer("{self.skill_dir}")
|
enhancer = LocalSkillEnhancer(
|
||||||
|
"{self.skill_dir}",
|
||||||
|
agent="{self.agent}",
|
||||||
|
agent_cmd={repr(self.agent_cmd)}
|
||||||
|
)
|
||||||
|
|
||||||
# Create prompt
|
# Create prompt
|
||||||
write_status("running", "Creating enhancement prompt...", progress=0.3)
|
write_status("running", "Creating enhancement prompt...", progress=0.3)
|
||||||
@@ -910,14 +1074,14 @@ try:
|
|||||||
prompt_file = f.name
|
prompt_file = f.name
|
||||||
f.write(prompt)
|
f.write(prompt)
|
||||||
|
|
||||||
write_status("running", "Running Claude Code...", progress=0.5)
|
write_status("running", "Running local agent...", progress=0.5)
|
||||||
|
|
||||||
# Run Claude
|
# Run local agent
|
||||||
result = subprocess.run(
|
result, error = enhancer._run_agent_command(
|
||||||
['claude', prompt_file],
|
prompt_file,
|
||||||
capture_output=True,
|
timeout={timeout},
|
||||||
text=True,
|
include_permissions_flag=True,
|
||||||
timeout={timeout}
|
quiet=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
@@ -926,11 +1090,14 @@ try:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if error:
|
||||||
|
write_status("failed", error=error)
|
||||||
|
sys.exit(1)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
write_status("completed", "Enhancement completed successfully!", progress=1.0)
|
write_status("completed", "Enhancement completed successfully!", progress=1.0)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
write_status("failed", error=f"Claude returned error: {{result.returncode}}")
|
write_status("failed", error=f"Agent returned error: {{result.returncode}}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
@@ -1005,7 +1172,7 @@ def main():
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Enhance a skill with Claude Code (local)",
|
description="Enhance a skill with a local coding agent (no API key)",
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog="""
|
epilog="""
|
||||||
Examples:
|
Examples:
|
||||||
@@ -1028,7 +1195,7 @@ Examples:
|
|||||||
skill-seekers enhance output/react/ --timeout 1200
|
skill-seekers enhance output/react/ --timeout 1200
|
||||||
|
|
||||||
Mode Comparison:
|
Mode Comparison:
|
||||||
- headless: Runs claude CLI directly, BLOCKS until done (default)
|
- headless: Runs local agent CLI directly, BLOCKS until done (default)
|
||||||
- background: Runs in background thread, returns immediately
|
- background: Runs in background thread, returns immediately
|
||||||
- daemon: Fully detached process, continues after parent exits
|
- daemon: Fully detached process, continues after parent exits
|
||||||
- terminal: Opens new terminal window (interactive)
|
- terminal: Opens new terminal window (interactive)
|
||||||
@@ -1041,6 +1208,20 @@ Force Mode (Default ON):
|
|||||||
|
|
||||||
parser.add_argument("skill_directory", help="Path to skill directory (e.g., output/react/)")
|
parser.add_argument("skill_directory", help="Path to skill directory (e.g., output/react/)")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--agent",
|
||||||
|
choices=sorted(list(AGENT_PRESETS.keys()) + ["custom"]),
|
||||||
|
help="Local coding agent to use (default: claude or SKILL_SEEKER_AGENT)",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--agent-cmd",
|
||||||
|
help=(
|
||||||
|
"Override agent command template. Use {prompt_file} placeholder or omit to use stdin. "
|
||||||
|
"Can also be set via SKILL_SEEKER_AGENT_CMD."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--interactive-enhancement",
|
"--interactive-enhancement",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -1083,7 +1264,16 @@ Force Mode (Default ON):
|
|||||||
|
|
||||||
# Run enhancement
|
# Run enhancement
|
||||||
# Force mode is ON by default, use --no-force to disable
|
# Force mode is ON by default, use --no-force to disable
|
||||||
enhancer = LocalSkillEnhancer(args.skill_directory, force=not args.no_force)
|
try:
|
||||||
|
enhancer = LocalSkillEnhancer(
|
||||||
|
args.skill_directory,
|
||||||
|
force=not args.no_force,
|
||||||
|
agent=args.agent,
|
||||||
|
agent_cmd=args.agent_cmd,
|
||||||
|
)
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
headless = not args.interactive_enhancement # Invert: default is headless
|
headless = not args.interactive_enhancement # Invert: default is headless
|
||||||
success = enhancer.run(
|
success = enhancer.run(
|
||||||
headless=headless, timeout=args.timeout, background=args.background, daemon=args.daemon
|
headless=headless, timeout=args.timeout, background=args.background, daemon=args.daemon
|
||||||
|
|||||||
@@ -170,9 +170,18 @@ For more information: https://github.com/yusufkaraaslan/Skill_Seekers
|
|||||||
enhance_parser = subparsers.add_parser(
|
enhance_parser = subparsers.add_parser(
|
||||||
"enhance",
|
"enhance",
|
||||||
help="AI-powered enhancement (local, no API key)",
|
help="AI-powered enhancement (local, no API key)",
|
||||||
description="Enhance SKILL.md using Claude Code (local)",
|
description="Enhance SKILL.md using a local coding agent",
|
||||||
)
|
)
|
||||||
enhance_parser.add_argument("skill_directory", help="Skill directory path")
|
enhance_parser.add_argument("skill_directory", help="Skill directory path")
|
||||||
|
enhance_parser.add_argument(
|
||||||
|
"--agent",
|
||||||
|
choices=["claude", "codex", "copilot", "opencode", "custom"],
|
||||||
|
help="Local coding agent to use (default: claude or SKILL_SEEKER_AGENT)",
|
||||||
|
)
|
||||||
|
enhance_parser.add_argument(
|
||||||
|
"--agent-cmd",
|
||||||
|
help="Override agent command template (use {prompt_file} or stdin).",
|
||||||
|
)
|
||||||
enhance_parser.add_argument("--background", action="store_true", help="Run in background")
|
enhance_parser.add_argument("--background", action="store_true", help="Run in background")
|
||||||
enhance_parser.add_argument("--daemon", action="store_true", help="Run as daemon")
|
enhance_parser.add_argument("--daemon", action="store_true", help="Run as daemon")
|
||||||
enhance_parser.add_argument(
|
enhance_parser.add_argument(
|
||||||
@@ -486,6 +495,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
from skill_seekers.cli.enhance_skill_local import main as enhance_main
|
from skill_seekers.cli.enhance_skill_local import main as enhance_main
|
||||||
|
|
||||||
sys.argv = ["enhance_skill_local.py", args.skill_directory]
|
sys.argv = ["enhance_skill_local.py", args.skill_directory]
|
||||||
|
if args.agent:
|
||||||
|
sys.argv.extend(["--agent", args.agent])
|
||||||
|
if args.agent_cmd:
|
||||||
|
sys.argv.extend(["--agent-cmd", args.agent_cmd])
|
||||||
if args.background:
|
if args.background:
|
||||||
sys.argv.append("--background")
|
sys.argv.append("--background")
|
||||||
if args.daemon:
|
if args.daemon:
|
||||||
|
|||||||
Reference in New Issue
Block a user