lightcone.cli.commands¶
The Click surface. Defined in src/lightcone/cli/commands.py. Six
public commands: init, run, status, verify, build, export
(plus eval when the optional eval extra is installed).
The user-facing reference is in CLI Overview. This page is a tour of the module internals.
Entry point¶
@click.group()
@click.version_option(package_name="lightcone-cli")
@click.pass_context
def main(ctx: click.Context) -> None:
ctx.ensure_object(dict)
if ctx.invoked_subcommand in ("init", "eval"):
return
if not _config_path().exists():
# print friendly error, sys.exit(1)
main is exposed as lightcone.cli.main (re-exported from
lightcone.cli.__init__) and is the entry point declared in
pyproject.toml::project.scripts:
PERMISSION_TIERS¶
PERMISSION_TIERS: dict[str, dict[str, list[str]]] = { "yolo": {...}, "recommended": {...}, "minimal": {...} }
Used by lc init --permissions to populate .claude/settings.json.
The constant lives at module top so tests and external tools can read
it directly. To add a new tier, edit this dict and update the
click.Choice on lc init.
Helpers¶
_config_path() → Path¶
Returns ~/.lightcone/config.yaml. Used by the main group's
auto-init check and by setup.
_project_root(start: Path | None = None) → Path¶
Walks up from start (or cwd) looking for astra.yaml. Raises
click.ClickException if none found. Used by run, status, verify,
build.
_target_for(project: Path, output_id: str, universe: str) → str¶
Translate an output_id (or qualified <analysis_id>.<output_id>) into
the Snakemake target path that materializes it — specifically the
manifest file results/<universe>/<output_id>/.lightcone-manifest.json.
Raises click.ClickException if the id is unknown or ambiguous.
_run_filtered(cmd, *, env)¶
Spawn snakemake, line-filter its stdout/stderr to suppress executor
banner chatter, and return the exit code. The recipe's own output
streams through untouched, as do unfamiliar diagnostic lines.
_status_label(s: str) → str¶
Map a status literal to the Rich-formatted display label:
| Status | Display |
|---|---|
ok |
[green]✓ ok[/green] |
stale |
[yellow]✸ stale[/yellow] |
missing |
[red]✗ miss[/red] |
alias |
[dim]→ alias[/dim] |
Boilerplate text¶
_BOILERPLATE_ASTRA, _GITIGNORE, and _PROJECT_CLAUDE_MD are
multi-line strings written at lc init time. Edit them to change what
new projects look like.
Plugin install¶
_install_claude_plugin(project_dir, permissions) wires up the Claude
Code plugin for a freshly scaffolded project. Two steps:
- Project-scoped permissions. Writes
project_dir/.claude/settings.jsonfrom the chosen permission tier (PERMISSION_TIERS[permissions]). This is the only thinglc initputs inside the project's.claude/. - User-scoped plugin install. Resolves the marketplace root via
lightcone.cli.plugin.get_marketplace_root()and shells out, in order, to: Both Claude CLI calls are idempotent — a secondlc init(in any project) is a no-op for the plugin. The plugin's skills, agents, and hooks land user-scoped under~/.claude/plugins/, not per-project.
When the claude CLI isn't on PATH (Codex users, …) the install step
soft-fails with a printed pointer to the manual commands; lc init
itself still succeeds.
lightcone.cli.plugin¶
Marketplace discovery — defined in src/lightcone/cli/plugin.py.
from lightcone.cli.plugin import (
MARKETPLACE_NAME, # "lightcone-cli"
PLUGIN_NAME, # "lightcone"
get_marketplace_root,
)
get_marketplace_root() returns the directory containing
.claude-plugin/marketplace.json, looking in two places in order:
- Bundled (installed wheel): the
lightcone/cli/package directory itself — populated by theforce-includerules inpyproject.toml::tool.hatch.build.targets.wheel.force-includeso the marketplace ships with the wheel. - Development (running from a checkout): the repo root, three
levels above the
lightcone/cli/package in the src layout.
Returns None if neither location has a marketplace.json (caller
prints a warning and skips the plugin install).