Source code for neurocore.cli.skill_cmd
"""neurocore skill — list and inspect discovered skills.
Usage:
neurocore skill list
neurocore skill info echo
neurocore skill info neuroweave --project-root /path/to/project
"""
from __future__ import annotations
from pathlib import Path
from typing import Optional
import typer
from rich.console import Console
from rich.table import Table
from neurocore.errors import NeuroCoreError
console = Console()
skill_app = typer.Typer(
name="skill",
help="Discover and inspect skills.",
no_args_is_help=True,
)
def _discover(project_root: Path | None) -> tuple:
"""Discover skills and return (registry, config).
Lazy import to keep CLI startup fast.
"""
from neurocore.config.loader import load_config
from neurocore.skills.loader import discover_skills
config = load_config(project_root=project_root)
registry = discover_skills(config)
return registry, config
[docs]
@skill_app.command("list")
def skill_list(
project_root: Optional[Path] = typer.Option(
None,
"--project-root",
"-p",
help="Project root directory (auto-detected if not provided).",
),
) -> None:
"""List all discovered skills."""
try:
registry, config = _discover(project_root)
except NeuroCoreError as e:
console.print(f"[red]Error:[/red] {e}")
raise typer.Exit(code=1) from None
metas = registry.list_skill_metas()
if not metas:
console.print("[yellow]No skills discovered.[/yellow]")
console.print(
"[dim]Add skills to the skills/ directory or install "
"via pip with entry points.[/dim]"
)
return
table = Table(title="Discovered Skills")
table.add_column("Name", style="cyan", no_wrap=True)
table.add_column("Version", style="green")
table.add_column("Description")
table.add_column("Tags", style="dim")
for meta in metas:
tags = ", ".join(meta.tags) if meta.tags else ""
table.add_row(meta.name, meta.version, meta.description, tags)
console.print(table)
console.print(f"\n[dim]{len(metas)} skill(s) found[/dim]")
[docs]
@skill_app.command("info")
def skill_info(
name: str = typer.Argument(help="Skill name to inspect."),
project_root: Optional[Path] = typer.Option(
None,
"--project-root",
"-p",
help="Project root directory (auto-detected if not provided).",
),
) -> None:
"""Show detailed information about a skill."""
try:
registry, config = _discover(project_root)
except NeuroCoreError as e:
console.print(f"[red]Error:[/red] {e}")
raise typer.Exit(code=1) from None
skill_cls = registry.get(name)
if skill_cls is None:
available = ", ".join(registry.list_skills()) or "(none)"
console.print(
f"[red]Error:[/red] Skill '{name}' not found. "
f"Available: {available}"
)
raise typer.Exit(code=1)
meta = skill_cls.skill_meta
console.print()
console.print(f"[bold cyan]{meta.name}[/bold cyan] v{meta.version}")
if meta.description:
console.print(f" {meta.description}")
if meta.author:
console.print(f" [dim]Author:[/dim] {meta.author}")
console.print()
if meta.tags:
console.print(f" [dim]Tags:[/dim] {', '.join(meta.tags)}")
if meta.provides:
console.print(f" [dim]Provides:[/dim] {', '.join(meta.provides)}")
if meta.consumes:
console.print(f" [dim]Consumes:[/dim] {', '.join(meta.consumes)}")
if meta.requires:
console.print(f" [dim]Requires:[/dim] {', '.join(meta.requires)}")
if meta.config_schema:
console.print()
console.print(" [dim]Config schema:[/dim]")
import json
from rich.syntax import Syntax
schema_json = json.dumps(meta.config_schema, indent=2)
syntax = Syntax(schema_json, "json", theme="monokai", padding=1)
console.print(syntax)
# Health check
console.print()
try:
instance = skill_cls()
instance.init(config.get_skill_config(name))
healthy = instance.health_check()
status = "[green]healthy[/green]" if healthy else "[yellow]unhealthy[/yellow]"
except Exception:
status = "[red]error[/red]"
console.print(f" [dim]Health:[/dim] {status}")
console.print()