First version. I can init
This commit is contained in:
+130
@@ -0,0 +1,130 @@
|
||||
"""Command-line interface for myclaude."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
|
||||
from myclaude import config as cfg
|
||||
from myclaude import fs_ops, git_ops
|
||||
|
||||
app = typer.Typer(help="Manage Claude skills across projects.")
|
||||
|
||||
|
||||
@app.command()
|
||||
def init(
|
||||
project_dir: Path = typer.Argument(..., help="Target project directory"),
|
||||
repo: Optional[str] = typer.Option(None, "--repo", help="Git repo SSH URL"),
|
||||
skill: Optional[list[str]] = typer.Option(None, "--skill", help="Skills to install"),
|
||||
force: bool = typer.Option(False, "--force", help="Overwrite existing skills"),
|
||||
keep_tmp: bool = typer.Option(False, "--keep-tmp", help="Keep temporary clone directory for inspection"),
|
||||
) -> None:
|
||||
"""Initialize a project with Claude skills from the central repo."""
|
||||
if repo:
|
||||
cfg.save_config(repo)
|
||||
typer.echo(f"Repository URL saved: {repo}")
|
||||
|
||||
try:
|
||||
repo_url = cfg.get_repo_url()
|
||||
except (FileNotFoundError, KeyError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
with git_ops.temp_clone(repo_url, keep=keep_tmp) as (repo_path, _):
|
||||
try:
|
||||
fs_ops.copy_claude_md_to_project(repo_path, project_dir)
|
||||
fs_ops.copy_skills_to_project(repo_path, project_dir, skills=skill, force=force)
|
||||
except (FileExistsError, ValueError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
skill_label = ", ".join(skill) if skill else "all"
|
||||
typer.echo(f"Initialized '{project_dir}' with skills: {skill_label}")
|
||||
|
||||
|
||||
@app.command()
|
||||
def push(
|
||||
skill: Optional[list[str]] = typer.Option(None, "--skill", help="Skills to push"),
|
||||
claude_md: bool = typer.Option(False, "--claude-md", help="Also push CLAUDE.md"),
|
||||
) -> None:
|
||||
"""Push local skills to the central repo."""
|
||||
project_dir = Path.cwd()
|
||||
|
||||
try:
|
||||
repo_url = cfg.get_repo_url()
|
||||
except (FileNotFoundError, KeyError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
with git_ops.temp_clone(repo_url) as (repo_path, repo):
|
||||
try:
|
||||
fs_ops.copy_skills_to_repo(project_dir, repo_path, skills=skill)
|
||||
if claude_md:
|
||||
fs_ops.copy_claude_md_to_repo(project_dir, repo_path)
|
||||
|
||||
pushed_skills = skill if skill else fs_ops.collect_local_skills(project_dir)
|
||||
commit_message = f"chore: update skills {', '.join(pushed_skills)}"
|
||||
if claude_md:
|
||||
commit_message += " + CLAUDE.md"
|
||||
|
||||
git_ops.commit_and_push(repo, commit_message)
|
||||
except (ValueError, RuntimeError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
typer.echo("Skills pushed successfully.")
|
||||
|
||||
|
||||
@app.command()
|
||||
def status() -> None:
|
||||
"""Compare local skills with the central repo."""
|
||||
project_dir = Path.cwd()
|
||||
|
||||
try:
|
||||
repo_url = cfg.get_repo_url()
|
||||
except (FileNotFoundError, KeyError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
with git_ops.temp_clone(repo_url) as (repo_path, _):
|
||||
result = fs_ops.get_skills_status(project_dir, repo_path)
|
||||
|
||||
if not any([result.in_both, result.local_only, result.remote_only]):
|
||||
typer.echo("No skills found locally or remotely.")
|
||||
return
|
||||
|
||||
if result.in_both:
|
||||
typer.echo("Installed:")
|
||||
for s in result.in_both:
|
||||
typer.echo(f" [ok] {s}")
|
||||
|
||||
if result.local_only:
|
||||
typer.echo("Local only (not pushed):")
|
||||
for s in result.local_only:
|
||||
typer.echo(f" [+] {s}")
|
||||
|
||||
if result.remote_only:
|
||||
typer.echo("Remote only (not installed):")
|
||||
for s in result.remote_only:
|
||||
typer.echo(f" [-] {s}")
|
||||
|
||||
|
||||
@app.command(name="list")
|
||||
def list_skills() -> None:
|
||||
"""List all skills available in the central repo."""
|
||||
try:
|
||||
repo_url = cfg.get_repo_url()
|
||||
except (FileNotFoundError, KeyError) as e:
|
||||
typer.echo(f"Error: {e}", err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
with git_ops.temp_clone(repo_url) as (repo_path, _):
|
||||
skills = fs_ops.list_skills_in_repo(repo_path)
|
||||
|
||||
if not skills:
|
||||
typer.echo("No skills found in the central repo.")
|
||||
return
|
||||
|
||||
typer.echo("Available skills:")
|
||||
for s in skills:
|
||||
typer.echo(f" - {s}")
|
||||
Reference in New Issue
Block a user