Skip to content

Commit b260d90

Browse files
jayhackcodegen-bot
and
codegen-bot
authored
feat(cg-10482): codegen notebook (#78)
- Install `codegen` globally via `pipx` - `codegen notebook` will run init + spin up a notebook with a `Codebase` - removes auth from `codegen init` - temporarily removes docs fetching as part of init --------- Co-authored-by: codegen-bot <[email protected]>
1 parent e9f8401 commit b260d90

File tree

11 files changed

+359
-65
lines changed

11 files changed

+359
-65
lines changed

docs/cli/about.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Codegen CLI"
33
sidebarTitle: "Overview"
4-
icon: "terminal"
4+
icon: "square-info"
55
iconType: "solid"
66
---
77

docs/cli/notebook.mdx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: "notebook"
3+
sidebarTitle: "notebook"
4+
description: "Open a Jupyter notebook with the current codebase loaded"
5+
icon: "book"
6+
iconType: "solid"
7+
---
8+
9+
The `notebook` command launches a Jupyter Lab instance with a pre-configured notebook for exploring your codebase.
10+
11+
## Usage
12+
13+
```bash
14+
codegen notebook [--background]
15+
```
16+
17+
### Options
18+
19+
- `--background`: Run Jupyter Lab in the background (default: false)
20+
21+
By default, Jupyter Lab runs in the foreground, making it easy to stop with Ctrl+C. Use `--background` if you want to run it in the background.
22+
23+
## What it Does
24+
25+
This will:
26+
27+
1. Create a Python virtual environment in `.codegen/.venv` if it doesn't exist
28+
2. Install required packages (codegen and jupyterlab)
29+
3. Create a starter notebook in `.codegen/jupyter/tmp.ipynb`
30+
4. Launch Jupyter Lab with the notebook open
31+
32+
The notebook comes pre-configured with:
33+
34+
```python
35+
from codegen import Codebase
36+
37+
# Initialize codebase
38+
codebase = Codebase('../../')
39+
```
40+
41+
<Note>
42+
The notebook is created in `.codegen/jupyter/`, so the relative path `../../`
43+
points to your repository root.
44+
</Note>
45+
46+
## Directory Structure
47+
48+
After running `codegen notebook`, your repository will have this structure:
49+
50+
```
51+
.codegen/
52+
├── .venv/ # Virtual environment for this project
53+
├── jupyter/ # Jupyter notebooks
54+
│ └── tmp.ipynb # Pre-configured notebook
55+
└── config.toml # Project configuration
56+
```
57+
58+
## Examples
59+
60+
```bash
61+
# Run Jupyter in the foreground (default)
62+
codegen notebook
63+
64+
# Run Jupyter in the background
65+
codegen notebook --background
66+
67+
# Initialize a new repository and launch notebook
68+
codegen init && codegen notebook
69+
```
70+
71+
<Tip>
72+
The notebook command will automatically initialize codegen if needed, so you
73+
can run it directly in a new repository.
74+
</Tip>

docs/introduction/getting-started.mdx

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,55 @@ icon: "bolt"
55
iconType: "solid"
66
---
77

8-
Follow our step-by-step tutorial to get up and running with Codegen in a jupyter notebook.
8+
Follow our step-by-step tutorial to get up and running with Codegen.
99

1010
## Installation
1111

12-
We recommend using [`uv`](https://github.com/astral-sh/uv) with Python 3.13 for the best experience.
12+
We recommend using [`pipx`](https://pypa.github.io/pipx/) to install Codegen globally. `pipx` is a tool to help you install and run Python applications in isolated environments. This isolation ensures that Codegen's dependencies won't conflict with your other Python projects.
1313

14-
First, install `uv` if you haven't already:
14+
First, install `pipx` if you haven't already:
1515

1616
```bash
17-
brew install uv
17+
brew install pipx
18+
pipx ensurepath # Ensure pipx binaries are on your PATH
1819
```
1920

20-
`cd` into the directory you want to parse:
21+
Then install Codegen globally:
2122

2223
```bash
23-
cd path/to/git/repository
24+
pipx install codegen
2425
```
2526

26-
Create and activate a Python 3.13 virtual environment:
27+
<Note>
28+
This makes the `codegen` command available globally in your terminal, while
29+
keeping its dependencies isolated.
30+
</Note>
2731

28-
```bash
29-
uv venv --python 3.13.0 && source .venv/bin/activate
30-
```
32+
## Quick Start with Jupyter
3133

32-
Install [Codegen from PyPI](https://pypi.org/project/codegen/) using `uv`:
34+
The fastest way to explore a codebase is using Jupyter. Codegen provides a built-in command to set this up:
3335

3436
```bash
35-
uv pip install codegen
36-
```
37+
# Navigate to your repository
38+
cd path/to/git/repository
3739

38-
Install and run Jupyter (optional):
40+
# Initialize codegen and launch Jupyter
41+
codegen init
42+
codegen notebook
43+
```
3944

40-
<Tip>Jupyter notebooks are great for exploration and debugging.</Tip>
45+
This will:
4146

42-
```bash
43-
uv pip install jupyterlab && jupyter lab
44-
```
47+
1. Create a `.codegen/` directory with:
48+
- `.venv/` - A dedicated virtual environment for this project
49+
- `jupyter/` - Jupyter notebooks for exploring your code
50+
- `config.toml` - Project configuration
51+
2. Launch Jupyter Lab with a pre-configured notebook
4552

46-
<Info>
47-
You can also use the CLI if you prefer to work in a terminal. [Learn more
48-
here](/cli/about).
49-
</Info>
53+
<Tip>
54+
The notebook comes pre-configured to load your codebase, so you can start
55+
exploring right away!
56+
</Tip>
5057

5158
## Initializing a Codebase
5259

docs/mint.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,14 @@
114114
},
115115
{
116116
"group": "CLI",
117-
"pages": ["cli/about", "cli/init", "cli/create", "cli/run", "cli/expert"]
117+
"pages": [
118+
"cli/about",
119+
"cli/init",
120+
"cli/notebook",
121+
"cli/create",
122+
"cli/run",
123+
"cli/expert"
124+
]
118125
},
119126
{
120127
"group": "Changelog",

src/codegen/cli/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from codegen.cli.commands.list.main import list_command
99
from codegen.cli.commands.login.main import login_command
1010
from codegen.cli.commands.logout.main import logout_command
11+
from codegen.cli.commands.notebook.main import notebook_command
1112
from codegen.cli.commands.profile.main import profile_command
1213
from codegen.cli.commands.run.main import run_command
1314
from codegen.cli.commands.run_on_pr.main import run_on_pr_command
@@ -35,6 +36,7 @@ def main():
3536
main.add_command(deploy_command)
3637
main.add_command(style_debug_command)
3738
main.add_command(run_on_pr_command)
39+
main.add_command(notebook_command)
3840

3941

4042
if __name__ == "__main__":

src/codegen/cli/commands/init/main.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import subprocess
22
import sys
3+
from pathlib import Path
34

45
import rich
56
import rich_click as click
7+
import toml
68

7-
from codegen.cli.auth.decorators import requires_auth
9+
from codegen.cli.auth.constants import CODEGEN_DIR
810
from codegen.cli.auth.session import CodegenSession
911
from codegen.cli.commands.init.render import get_success_message
1012
from codegen.cli.git.url import get_git_organization_and_repo
@@ -15,8 +17,8 @@
1517
@click.command(name="init")
1618
@click.option("--repo-name", type=str, help="The name of the repository")
1719
@click.option("--organization-name", type=str, help="The name of the organization")
18-
@requires_auth
19-
def init_command(session: CodegenSession, repo_name: str | None = None, organization_name: str | None = None):
20+
@click.option("--fetch-docs", is_flag=True, help="Fetch docs and examples (requires auth)")
21+
def init_command(repo_name: str | None = None, organization_name: str | None = None, fetch_docs: bool = False):
2022
"""Initialize or update the Codegen folder."""
2123
# Print a message if not in a git repo
2224
try:
@@ -30,27 +32,36 @@ def init_command(session: CodegenSession, repo_name: str | None = None, organiza
3032
rich.print(format_command("codegen init"))
3133
sys.exit(1)
3234

33-
codegen_dir = session.codegen_dir
35+
# Only create session if we need to fetch docs
36+
session = CodegenSession() if fetch_docs else None
37+
codegen_dir = Path.cwd() / CODEGEN_DIR if not session else session.codegen_dir
3438
is_update = codegen_dir.exists()
3539

36-
if organization_name is not None:
37-
session.config.organization_name = organization_name
38-
if repo_name is not None:
39-
session.config.repo_name = repo_name
40-
if not session.config.organization_name or not session.config.repo_name:
41-
cwd_org, cwd_repo = get_git_organization_and_repo(session.git_repo)
42-
session.config.organization_name = session.config.organization_name or cwd_org
43-
session.config.repo_name = session.config.repo_name or cwd_repo
44-
session.write_config()
40+
# Only update config if we have a session
41+
if session:
42+
if organization_name is not None:
43+
session.config.organization_name = organization_name
44+
if repo_name is not None:
45+
session.config.repo_name = repo_name
46+
if not session.config.organization_name or not session.config.repo_name:
47+
cwd_org, cwd_repo = get_git_organization_and_repo(session.git_repo)
48+
session.config.organization_name = session.config.organization_name or cwd_org
49+
session.config.repo_name = session.config.repo_name or cwd_repo
50+
session.write_config()
4551

4652
action = "Updating" if is_update else "Initializing"
4753
rich.print("") # Add a newline before the spinner
48-
codegen_dir, docs_dir, examples_dir = initialize_codegen(action=action)
54+
codegen_dir, docs_dir, examples_dir = initialize_codegen(action, session=session, fetch_docs=fetch_docs)
4955

5056
# Print success message
5157
rich.print(f"✅ {action} complete")
52-
rich.print(f" [dim]Organization:[/dim] {session.config.organization_name}")
53-
rich.print(f" [dim]Repository:[/dim] {session.config.repo_name}")
58+
59+
# Show repo info from config.toml
60+
config_path = codegen_dir / "config.toml"
61+
if config_path.exists():
62+
config = toml.load(config_path)
63+
rich.print(f" [dim]Organization:[/dim] {config.get('organization_name', 'unknown')}")
64+
rich.print(f" [dim]Repository:[/dim] {config.get('repo_name', 'unknown')}")
5465
rich.print("")
5566
rich.print(get_success_message(codegen_dir, docs_dir, examples_dir))
5667

src/codegen/cli/commands/init/render.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
def get_success_message(codegen_dir: Path, docs_dir: Path, examples_dir: Path) -> str:
55
"""Get the success message to display after initialization."""
66
return """📁 Folders Created:
7-
[dim] Location:[/dim] .codegen-sh
8-
[dim] Docs:[/dim] .codegen-sh/docs
9-
[dim] Examples:[/dim] .codegen-sh/examples"""
7+
[dim] Location:[/dim] .codegen
8+
[dim] Docs:[/dim] .codegen/docs
9+
[dim] Examples:[/dim] .codegen/examples"""
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import os
2+
import subprocess
3+
from pathlib import Path
4+
5+
import rich_click as click
6+
7+
from codegen.cli.auth.constants import CODEGEN_DIR
8+
from codegen.cli.auth.decorators import requires_auth
9+
from codegen.cli.auth.session import CodegenSession
10+
from codegen.cli.rich.spinners import create_spinner
11+
from codegen.cli.workspace.decorators import requires_init
12+
from codegen.cli.workspace.venv_manager import VenvManager
13+
14+
15+
def create_jupyter_dir() -> Path:
16+
"""Create and return the jupyter directory."""
17+
jupyter_dir = Path.cwd() / CODEGEN_DIR / "jupyter"
18+
jupyter_dir.mkdir(parents=True, exist_ok=True)
19+
return jupyter_dir
20+
21+
22+
def create_notebook(jupyter_dir: Path) -> Path:
23+
"""Create a new Jupyter notebook if it doesn't exist."""
24+
notebook_path = jupyter_dir / "tmp.ipynb"
25+
if not notebook_path.exists():
26+
notebook_content = {
27+
"cells": [
28+
{
29+
"cell_type": "code",
30+
"execution_count": None,
31+
"metadata": {},
32+
"outputs": [],
33+
"source": ["from codegen import Codebase\n", "\n", "# Initialize codebase\n", "codebase = Codebase('../../')\n"],
34+
}
35+
],
36+
"metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}},
37+
"nbformat": 4,
38+
"nbformat_minor": 4,
39+
}
40+
import json
41+
42+
notebook_path.write_text(json.dumps(notebook_content, indent=2))
43+
return notebook_path
44+
45+
46+
@click.command(name="notebook")
47+
@click.option("--background", is_flag=True, help="Run Jupyter Lab in the background")
48+
@requires_auth
49+
@requires_init
50+
def notebook_command(session: CodegenSession, background: bool = False):
51+
"""Open a Jupyter notebook with the current codebase loaded."""
52+
with create_spinner("Setting up Jupyter environment...") as status:
53+
venv = VenvManager()
54+
55+
if not venv.is_initialized():
56+
status.update("Creating virtual environment...")
57+
venv.create_venv()
58+
59+
status.update("Installing required packages...")
60+
venv.install_packages("codegen", "jupyterlab")
61+
62+
jupyter_dir = create_jupyter_dir()
63+
notebook_path = create_notebook(jupyter_dir)
64+
65+
status.update("Starting Jupyter Lab...")
66+
67+
# Prepare the environment with the virtual environment activated
68+
env = {**os.environ, "VIRTUAL_ENV": str(venv.venv_dir), "PATH": f"{venv.venv_dir}/bin:{os.environ['PATH']}"}
69+
70+
# Start Jupyter Lab
71+
if background:
72+
subprocess.Popen(["jupyter", "lab", str(notebook_path)], env=env, start_new_session=True)
73+
else:
74+
# Run in foreground
75+
subprocess.run(["jupyter", "lab", str(notebook_path)], env=env, check=True)

src/codegen/cli/utils/codemod_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def get_decorated(cls, start_path: Path | None = None) -> builtins.list[Decorate
100100
".ruff_cache",
101101
".coverage",
102102
"htmlcov",
103-
".codegen-sh",
103+
".codegen",
104104
}
105105

106106
all_functions = []

0 commit comments

Comments
 (0)