Metadata-Version: 2.4
Name: qkernel
Version: 0.1.1
Summary: CLI tool for executing Quarto cells through a Jupyter kernel
Requires-Python: >=3.10
Requires-Dist: click>=8.0.0
Requires-Dist: ipykernel>=7.1.0
Requires-Dist: jupyter-client>=8.0.0
Requires-Dist: matplotlib>=3.10.8
Requires-Dist: pyyaml>=6.0.0
Provides-Extra: dev
Requires-Dist: pytest-timeout>=2.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# qkernel

A CLI tool for executing Quarto (`.qmd`) code cells through a Jupyter kernel.

## Features

- **Execute Quarto cells** from the command line with live output
- **Persistent kernel sessions** - start a kernel once, run cells across multiple invocations
- **Automatic virtual environment detection** - uses `.venv` in your project automatically
- **Cell selection** - run specific cells by index or label
- **Image saving** - automatically saves generated images to cache directory
- **Animated progress** - shows live spinner and timing for each cell

## Installation

With uv (recommended)
```bash
uv tool install qkernel
```

With pip
```bash
pip install qkernel
```

### Requirements

- Python 3.10+
- `ipykernel` must be installed in the target Python environment

## Usage

### Running Cells

```bash
# Run all cells in a file
qkernel run notebook.qmd

# Run specific cells by index (0-based)
qkernel run notebook.qmd --cells 0,2,5

# Run specific cells by label
qkernel run notebook.qmd --cells setup,analysis,plot

# Mix indices and labels
qkernel run notebook.qmd --cells 0,setup,2

# Set execution timeout (in seconds)
qkernel run notebook.qmd --timeout 600
```

### Persistent Kernel Sessions

For faster iteration, start a kernel that persists across runs:

```bash
# Start a background kernel
qkernel start

# Run cells (uses the persistent kernel)
qkernel run notebook.qmd --cells setup
qkernel run notebook.qmd --cells analysis  # Variables from setup are available

# Check kernel status
qkernel status

# Restart the kernel (clears state)
qkernel restart

# Stop the kernel when done
qkernel stop
```

### Verbose Mode

Enable debug logging with `-v`:

```bash
qkernel -v run notebook.qmd
```

## Python Environment Resolution

qkernel automatically detects which Python to use for the kernel, following this order:

1. **`QUARTO_PYTHON`** - Environment variable (path to Python executable or venv directory)
2. **`VIRTUAL_ENV`** - Activated virtual environment
3. **`.venv`** - Virtual environment in current directory or any parent directory
4. **System Python** - Fallback

This means if you have a `.venv` in your project root, qkernel will use it automatically even when running from a subdirectory.

```bash
# Explicit Python
QUARTO_PYTHON=/path/to/python qkernel run notebook.qmd

# Uses activated venv
source .venv/bin/activate
qkernel run notebook.qmd

# Auto-detects .venv in project
cd /project/src/deep/folder
qkernel run ../../../notebook.qmd  # Uses /project/.venv
```

## Output Format

qkernel shows clean, informative output with timing:

```
Kernel started (/path/to/python)
● ━━ [0] setup ━━━━━━━━━━━━━━━━━━━━━━━━━━ ✓ 23ms
    Loading data...
    Done!
● ━━ [1] analysis ━━━━━━━━━━━━━━━━━━━━━━━ ✓ 1205ms
    Processing 1000 rows
    Results: [1, 2, 3, 4, 5]
● ━━ [2] plot ━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✓ 89ms
    [Image: ~/.cache/qkernel/notebook/plot/output.png]
Kernel stopped

══════════════════════════════════════════════════
Executed 3 cell(s) • 1 image(s) saved
  → /Users/you/.cache/qkernel/notebook/plot/output.png
```

In interactive terminals, a spinner animates while cells are running.

## Image Caching

Images generated by cells are saved to `~/.cache/qkernel/<filename>/<cell_label>/`:

```
~/.cache/qkernel/
└── notebook/
    ├── plot/
    │   └── output.png
    └── visualization/
        ├── output_0.png
        └── output_1.png
```

The cache is cleared before each run to ensure fresh output.

## Cell Labels

qkernel recognizes cell labels from Quarto's YAML-style comments:

````markdown
```{python}
#| label: setup
import pandas as pd
data = pd.read_csv("data.csv")
```

```{python}
#| label: analysis
results = data.groupby("category").sum()
print(results)
```
````

Use these labels to run specific cells: `qkernel run file.qmd --cells setup,analysis`

## Development

```bash
# Install with dev dependencies
uv pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run with timeout
pytest tests/ -v --timeout=60
```

## License

MIT
