██████╗ ██████╗████████╗ ██████╗ ██╔═══██╗██╔════╝╚══██╔══╝██╔═══██╗ ██║ ██║██║ ██║ ██║ ██║ ██║ ██║██║ ██║ ██║ ██║ ╚██████╔╝╚██████╗ ██║ ╚██████╔╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝
Eight arms. One terminal. All of GitHub.
Octo is a fully interactive, terminal-native GitHub companion. Browse repositories, preview files with syntax highlighting, clone specific subdirectories, commit and push changes, search code, read issues and pull requests, and inspect contributor stats — all without leaving your shell.
It talks directly to the GitHub REST API with no dependencies beyond three pure-Python packages. No Electron, no browser tab, no copy-pasting URLs.
✦ Features
git sparse-checkout. Fast on large monorepos.⬡ Installation
Requirements: Python 3.10 or later · git on your $PATH
Step 1 — Get the project
# Clone from GitHub
git clone https://github.com/threed2y/Octo-Git.git
cd Octo-Git
Step 2 — Create a virtual environment
python -m venv .venv
Activate it:
| Platform | Command |
|---|---|
| macOS / Linux | source .venv/bin/activate |
| Windows (CMD) | .venv\Scripts\activate.bat |
| Windows (PowerShell) | .venv\Scripts\Activate.ps1 |
Your prompt will show (.venv) when active.
Step 3 — Install dependencies
pip install -r requirements.txt
Step 4 — Run
python -m octo
pip install -e . once to register octo as a command in your virtual environment. After that, just type octo from any directory while the venv is active.
◈ Authentication
Octo works without a token but GitHub limits unauthenticated requests to 60 per hour. A Personal Access Token raises this to 5,000 per hour and unlocks private repositories.
Generate a token
octo-cli so you can identify it later.Add the token to Octo
Main menu → Setup Token
Enter a profile name (e.g. default) and paste your token. Octo validates it against the GitHub API immediately — the token is only saved if validation succeeds.
~/.octo_profiles.json with 0600 permissions on Unix. The token never appears in process arguments or shell history. See the Security section for full details.
◈ Usage overview
Launch Octo with:
python -m octo
# or, if installed via pip install -e .
octo
Keyboard controls
| Key | Action |
|---|---|
↑ / ↓ | Move through options |
Enter | Select / confirm |
Space | Toggle checkbox (file staging) |
Ctrl-C | Cancel / exit at any point |
📂 Browse repositories
django, microsoft, torvalds. Recent usernames are offered as a quick-pick list — no retyping. Fetches all repos with full pagination — no 100-repo cap.📄 File browser & preview
Navigate the directory tree with arrow keys. Folders are prefixed with ▸. Selecting a file opens an inline syntax-highlighted preview (up to 50 KB, with line numbers).
Supported preview types
.py .js .ts .tsx .jsx
.go .rs .java .c .cpp
.rb .php .html .css .scss
.json .yaml .toml .md .sh
.xml .svg .ini .conf and more.
Binary files and unsupported extensions display the file's GitHub URL instead of attempting a preview.
After any preview, the action menu offers Open in Browser, Copy URL, and Download file — saving the raw file directly to disk at a path you choose.
🔽 Sparse checkout
Navigate to any folder in the file browser and select Clone this path. Octo uses git sparse-checkout to download only that subdirectory — not the entire repository. This is significantly faster for large monorepos.
Browse Files → navigate into any folder → Clone this path
The clone is saved as ./repo_folder_clone/ in your current working directory. Octo asks if you want to open it in $EDITOR immediately, then offers to start a commit & push session.
~/.octo_clones.json. Access them from Clone History in the main menu to commit, push, or reopen without navigating the file browser again.
How sparse checkout works
git clone --no-checkout --depth 1 --branch BRANCH REPO_URL TARGET
git sparse-checkout init --cone
git sparse-checkout set PATH
git checkout BRANCH
⬆️ Commit & push
Available from the main menu, or automatically offered after a sparse checkout.
git status and displays all modified, added, and deleted files.git add -A) or pick files individually from a checkbox list.🔍 Code search
Browse → select repo → Search Code
Uses the GitHub Search API scoped to the selected repository. Results list matching file paths. Select any file to load its full content with syntax highlighting.
Query tips
| Query | Finds |
|---|---|
def authenticate | Any file containing that exact phrase |
TODO | All TODO comments across the repo |
import requests | Files that import the requests library |
password | Any mention of "password" — useful for auditing |
🐛 Issues & Pull Requests
Browse → select repo → Issues & Pull Requests
Choose between Issues and Pull Requests. Filter by state (open / closed / all) and optionally by label. Select any item to read its full body and optionally load up to 10 comment threads inline.
Each detail view shows: number, title, state, author, creation date, last updated, labels, assignees, and a direct URL. Pull Requests also offer a View changed files / diff prompt — showing a file summary table and inline patch view with Monokai highlighting.
Every issue and PR has Open in Browser and Copy URL at the bottom of its detail view.
📊 Repo stats
Browse → select repo → Stats & Insights
Three views, available individually or all at once:
| View | What it shows |
|---|---|
| Top Contributors | Ranked list of up to 20 contributors with commit counts and proportional fill bars |
| Language Breakdown | Bytes per language, percentage of total, colour-coded bars |
| Commit Activity | 26-week histogram, bars heat-coded by activity level (bright = high, dim = low) |
🔎 Search repositories
Main menu → Search Repositories
Search GitHub globally for repositories by keyword. Results are sorted by your choice of stars, recently updated, forks, or best match. An optional language filter appends language:<lang> to the query.
Selecting a result drops you into the same repo action loop as Browse — Browse Files, Search Code, Issues & PRs, Actions, Stats, and more.
⭐ Starred repos
Main menu → Starred Repos
Fetches up to 500 of your starred repositories (requires a token). Supports the same live keyword filter as Browse — matches name, description, full name, and topics. All repo actions available after selection.
🕓 Clone history
Main menu → Clone History
Every successful sparse checkout is recorded in ~/.octo_clones.json (up to 20 entries, deduplicated by path). Entries whose directories no longer exist are pruned automatically.
From the Clone History menu you can:
| Action | What it does |
|---|---|
| Commit & Push | Stage, commit, and push without leaving Octo |
| Open in Editor | Launch $EDITOR pointed at the directory |
| Open in Browser | Open the source GitHub repo URL in your browser |
| Copy path | Copy the local directory path to clipboard |
| Remove entry | Remove from history without deleting the directory |
⬇️ Pull / sync
Main menu → Pull / Sync
Pull latest remote changes into any local clone. Picks from your clone history or accepts a manual path. Shows the current branch before pulling.
± View local diff
Main menu → View Local Diff
Renders git diff output inline with Monokai syntax highlighting before you commit. Four modes:
| Mode | Equivalent command |
|---|---|
| Unstaged changes | git diff |
| Staged changes | git diff --staged |
| All changes vs HEAD | git diff HEAD |
| Between commits / branches | git diff <ref-a> <ref-b> |
🌿 Branch manager
Main menu → Branch Manager
Manage local branches for any clone in your history. The current branch is shown at the top of every action.
| Action | Detail |
|---|---|
| Switch branch | Checkout any local branch |
| Create branch | Name and base branch — creates and checks out immediately |
| Delete branch | Safe delete (-d) or force delete (-D) with confirmation |
| List all branches | Local and remote, with current branch highlighted |
🗂️ Stash manager
Main menu → Stash Manager
Full stash workflow without leaving Octo.
| Action | Detail |
|---|---|
| Stash | Stash current changes with an optional message. Includes untracked files (-u). |
| Pop | Apply and remove the latest stash |
| Apply | Apply a selected stash without removing it |
| Drop | Delete a selected stash |
| List | Show all stashes in a table with their messages |
⚡ GitHub Actions
Browse → select repo → GitHub Actions
Shows the last 30 workflow runs for a repository. Each run displays a status icon, branch, trigger event, and duration.
| Icon | Meaning |
|---|---|
✓ | Success |
✗ | Failure or timed out |
⟳ | In progress |
⊘ | Cancelled |
Selecting a run shows all jobs in a table. Failed jobs expand to show which individual step failed, so you can identify the problem without opening the browser.
✏️ Create an issue
Browse → select repo → Create an Issue
Opens a short form: title (required), optional body, and optional labels fetched from the repository. A preview panel shows the issue before you submit. On success, the new issue URL is displayed and you can open it in the browser immediately.
repo scope. Add one via Setup Token in the main menu.
👤 Profiles
Octo supports multiple named token profiles so you can switch between GitHub accounts without re-entering tokens.
Add a profile
Main menu → Setup Token
Profile name: work
Switch profiles
Main menu → Manage Profiles → Switch active profile
Storage
Profiles are stored in ~/.octo_profiles.json. The file is created with 0600 permissions on Unix. Writes are atomic — a crash or interrupt during save cannot leave the file corrupt.
{
"default": "ghp_...",
"work": "ghp_...",
"oss": "ghp_..."
}
🔒 Security
v1.1.0 is a dedicated security release. The following issues were identified in v1.0.0 and patched.
$, backticks, !, or \ could trigger arbitrary command execution. Fixed by writing the token to a 0600 temp file and passing only the helper script path to git — the token value never enters any shell evaluation context.Ctrl-C mid-write could leave ~/.octo_profiles.json truncated with no recovery path. Writes now use mkstemp + os.replace(), which is atomic on POSIX and Win32.chmod command if the permissions are too broad.requests calls now pass verify=True explicitly, making intent clear and preventing environment-variable overrides from silently disabling certificate verification.Credential security model
When Octo needs to authenticate a git operation (clone, push), it:
# 1. Token written to a temp file (0600, never in any string interpolation)
token_file = mkstemp() # e.g. /tmp/.octo_tok_abc123
# 2. A helper script written (0700) that reads from the token file
helper = "#!/bin/sh\necho username=token\ncat TOKEN_FILE\n"
# 3. Git receives only the SCRIPT PATH — never the token value
git -c credential.helper=HELPER_PATH clone ...
# 4. Both temp files deleted in a finally block — even on failure
os.unlink(token_file)
os.unlink(helper)
◈ Platform support
| Platform | Runs | Credential helper | Editor launch | File permissions |
|---|---|---|---|---|
| macOS | ✓ | ✓ | ✓ | ✓ 0600 |
| Linux | ✓ | ✓ | ✓ | ✓ 0600 |
| Windows (CMD / PowerShell) | ✓ | ✓ .bat helper | ✓ | ⚠ ACL-based |
| Windows (WSL) | ✓ | ✓ | ⚠ depends on setup | ✓ 0600 |
◈ Dependencies
| Package | Version | Purpose |
|---|---|---|
| requests | ≥ 2.31 | GitHub API HTTP calls |
| rich | ≥ 13.7 | Terminal UI — panels, tables, syntax highlighting |
| InquirerPy | ≥ 0.3.4 | Interactive menus and prompts |
| pyperclip | ≥ 1.8 (optional) | Cross-platform clipboard support. Falls back to pbcopy / xclip / clip if not installed. |
The first three are required. pyperclip is optional — uncomment it in requirements.txt to install.
◈ Project structure
Octo-Git/
├── octo/
│ ├── __init__.py # package metadata, version
│ ├── __main__.py # enables python -m octo
│ └── core.py # all application logic
├── docs/
│ └── preview_main.txt # ASCII preview reference
├── tests/ # test suite
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── pyproject.toml # build config + entry point
└── requirements.txt # runtime dependencies
◈ Changelog
- Pull / Sync — pull latest remote changes from the main menu, with detached HEAD detection
- Branch Manager — list, switch, create, and delete local branches
- View Local Diff — git diff rendered inline with Monokai highlighting, four scope modes
- Stash Manager — stash, pop, apply, drop, and list stashes interactively
- File download — download any previewed file to disk via GitHub raw URL
- Language filter in global repo search — appends
language:<lang>to query - Fixed: detached HEAD warning in commit & push flow
- Fixed: pull-before-push prompt prevents remote divergence errors
- Fixed: issue body input replaced with
inquirer.text()— consistent with all other inputs
- Clone History — tracks up to 20 clones in
~/.octo_clones.jsonwith commit, open, copy, and remove actions - Repository Search — global GitHub search with sort and language filter
- Starred Repositories — browse up to 500 starred repos with live filter
- Recent Usernames — last 10 usernames as quick-pick in Browse flow
- GitHub Actions viewer — runs, jobs, step-level failure details
- PR Diff viewer — file summary table and inline patch with Monokai highlighting
- Create Issue — title, body, labels, preview before submit
- Open in Browser and Copy URL — available across all repo, file, and issue views
- Shared
_repo_action_loop()— all entry points (Browse, Search, Starred) behave identically - Topic filtering in Browse — filter matches repository topics field
- Fixed: private repos now visible when browsing your own username
- Fixed: non-JSON GitHub error responses no longer crash
_get_json - Fixed: file browser shows correct error when path points to a file not a directory
- Fixed:
datetime.utcfromtimestampdeprecated in Python 3.12 — replaced - Fixed: rate limit tracking via
X-RateLimit-Remainingheader on every response - Fixed: repo action menu now loops — no longer ejects to username prompt after an action
- Fixed: profile manager reachable even when no profiles are saved
- Fixed shell injection vulnerability in git credential helper
- Token now validated before saving — invalid tokens are rejected at entry
- Atomic profile writes via
mkstemp+os.replace() - Loose permissions warning on startup and in profile manager
- Explicit
verify=Trueon all outbound requests - File browser no longer exits after cloning a path
$EDITORwith arguments (e.g.code --wait) now works correctly- Git presence check before clone and push — clear error if not on PATH
- Empty file selection in interactive staging now aborts cleanly
- Repo path validated before commit — distinct errors for missing path vs. non-repo
- Corrupt profiles file shows a warning instead of silently resetting
- Interactive repository browser with real-time filter
- Syntax-highlighted file preview — 40+ types, Monokai theme
- Sparse checkout — clone any subdirectory via git sparse-checkout
- Commit & push workflow with interactive file staging
- GitHub code search scoped to a repository
- Issues & Pull Requests viewer with comment threads
- Repo stats: contributors, language breakdown, 26-week activity
- Multi-account token profiles in
~/.octo_profiles.json - Full pagination — no 100-repo or 100-branch cap
- Cyan / electric-blue terminal theme
◈ Roadmap
Planned and considered features for future releases.
~/.octorc)◈ Contributing
Contributions are welcome. Here's how to get a development environment running:
git clone https://github.com/threed2y/Octo-Git.git
cd Octo-Git
python -m venv .venv
source .venv/bin/activate
pip install -e .
python -m octo
Code style
| Rule | Detail |
|---|---|
| Type hints | Python 3.10+ style — str | None, not Optional[str] |
| Colour tokens | Always use the module-level constants (C, CD, OK, etc.) — never hardcode "cyan" inline |
| Output | Use _ok(), _err(), _warn(), _info(), _panel() — don't call console.print() directly in feature functions |
| Secrets | Never interpolate tokens into shell strings. Use _git_env_with_token() for all git operations that need auth |
Submitting a pull request
git checkout -b feat/my-featuremain