npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mcculloughrt-svh/spaces

v1.2.0

Published

CLI tool for managing GitHub repository workspaces using git worktrees, tmux sessions, and Linear integration

Readme

Spaces CLI

A powerful CLI tool for managing GitHub repository workspaces using git worktrees, tmux sessions, and optional Linear integration. Work on multiple features/tasks simultaneously, each in its own isolated workspace with dedicated tmux sessions.

Features

  • 🚀 Git Worktrees: Work on multiple branches simultaneously without stashing
  • 🖥️ Tmux Integration: Automatic session management for each workspace
  • 📋 Linear Integration: Create workspaces directly from Linear issues with automatic markdown documentation
  • 🔄 Smart Branch Management: Automatic detection of remote branches with fuzzy matching
  • 📊 Workspace Status: Track uncommitted changes, stale workspaces, and more
  • ⚙️ Custom Scripts: Convention-based scripts for setup, select, pre-setup, and removal phases

Prerequisites

The following tools must be installed and available in your PATH:

  • GitHub CLI (gh) - for listing repositories
  • Git - for worktree management
  • tmux - for session management
  • Bun - to run the CLI (or node.js... if you don't mind startup lag)

GitHub Authentication: You must authenticate the GitHub CLI before using Spaces:

gh auth login

Installation

bun install -g @mcculloughrt-svh/spaces

Verify installation:

spaces --version

To update:

bun update -g @mcculloughrt-svh/spaces

Quick Start

1. Add Your First Project

# This will list all your GitHub repositories
spaces add project

Select a github repository, and Spaces will:

  • Clone the repository to ~/spaces/<project-name>/base
  • Detect the default branch
  • Create project configuration
  • Optionally set up Linear integration

2. Create a Workspace

# Create a workspace from a Linear issue (if configured)
spaces add

# Or create a workspace with a custom name
spaces add my-feature

This will:

  • Create a git worktree in ~/spaces/<project-name>/workspaces/<workspace-name>
  • Create a new branch (or use an existing one)
  • Create a tmux session
  • Run setup scripts (if present)

3. Switch Between Workspaces

# List and select a workspace interactively with switch (or sw)
spaces switch

# Switch to a specific workspace, workspaces are fuzzy matched
spaces switch my-feature

4. List Workspaces

# List workspaces in the current project with list (or ls)
spaces list

# List projects
spaces list projects

Commands

spaces add project

Add a new project from GitHub.

spaces add project [options]

Options:
  --no-clone           Create project structure without cloning
  --org <org>          Filter repos to specific organization
  --linear-key <key>   Provide Linear API key via flag

Example:

# Add a project with Linear integration
spaces add project --linear-key lin_api_...

spaces add [workspace-name]

Create a new workspace in the current project. Omit workspace-name to interactively choose from github branches and linear issues to create from.

spaces add [workspace-name] [options]

Options:
  --branch <name>      Specify different branch name from workspace name
  --from <branch>      Create from specific branch instead of base
  --no-tmux            Don't create/attach tmux session
  --no-setup           Skip setup commands

Example:

# Create workspace from Linear issue (interactive selection)
spaces add

# Create workspace with custom name
spaces add fix-bug-123

# Create workspace from specific branch
spaces add hotfix --from production

Linear Integration: When creating a workspace from a Linear issue, Spaces will:

  • Generate a workspace name from the issue identifier and title
  • Save the full issue details (description, metadata, attachments) to .prompt/issue.md in the workspace
  • This markdown file can be used with LLM assistants for context about the task

spaces switch project [project-name]

Switch to a different project.

spaces switch project [project-name]

Example:

# Interactive selection
spaces switch project

# Direct switch
spaces switch project my-app

spaces switch [workspace-name]

Switch to a workspace in the current project. Workspace names are fuzzy-matched, so you don't need to type the exact name. Or leave workspace-name blank for interactive selection.

spaces switch [workspace-name] [options]
# Alias
spaces sw [workspace-name]

Options:
  --no-tmux            Just cd to workspace without tmux
  --new-window         Create new window in existing session instead of attaching
  -f, --force          Jump to first fuzzy match without confirmation

Fuzzy Matching:

  • Workspace names are matched approximately, so feat can match feature-branch
  • Shorter workspace names and active tmux sessions score higher
  • If multiple matches are found, you'll be prompted to choose (unless using -f)

Example:

# Interactive selection
spaces switch

# Direct switch (exact match)
spaces switch my-feature

# Fuzzy match - will find 'my-feature-branch'
spaces switch my-feat

# Force first fuzzy match without confirmation
spaces switch feat -f

spaces list [subcommand]

List projects or workspaces.

spaces list [subcommand] [options]
# Alias
spaces ls [subcommand] [options]

Subcommands:
  projects             List all projects
  workspaces           List workspaces in current project (default)

Options:
  --json               Output in JSON format
  --verbose            Show additional details

Example:

# List workspaces
spaces list
# Or using alias
spaces ls

# List projects
spaces list projects
# Or using alias
spaces ls projects

# Verbose output
spaces list --verbose

spaces remove workspace [workspace-name]

Remove a workspace.

spaces remove workspace [workspace-name] [options]
# Alias
spaces rm workspace [workspace-name] [options]

Options:
  --force              Skip confirmation prompts
  --keep-branch        Don't delete git branch when removing workspace

Example:

# Interactive removal
spaces remove workspace
# Or using alias
spaces rm workspace

# Force remove without confirmation
spaces remove workspace my-feature --force
# Or using alias
spaces rm workspace my-feature --force

spaces remove project [project-name]

Remove a project.

spaces remove project [project-name] [options]
# Alias
spaces rm project [project-name] [options]

Options:
  --force              Skip confirmation prompts

Example:

# Interactive removal (requires typing project name to confirm)
spaces remove project
# Or using alias
spaces rm project

# Direct removal
spaces remove project my-app
# Or using alias
spaces rm project my-app

spaces directory

Print the current project directory path. Useful for shell integration.

spaces directory
# Alias
spaces dir

Example:

# Print current project directory
spaces directory
# Output: /Users/username/spaces/my-app

# Shell integration - cd to project directory
cd $(spaces dir)

# Or use in scripts
PROJECT_DIR=$(spaces directory)
echo "Working in: $PROJECT_DIR"

Configuration

Global Configuration

Located at ~/spaces/.config.json:

{
	"currentProject": "my-app",
	"projectsDir": "/Users/username/spaces",
	"defaultBaseBranch": "main",
	"staleDays": 30
}
  • currentProject: Currently active project (can be overridden by SPACES_CURRENT_PROJECT env var)
  • projectsDir: Path to the spaces directory
  • defaultBaseBranch: Default base branch for new projects
  • staleDays: Days before a workspace is considered stale

Project Configuration

Located at ~/spaces/<project-name>/.config.json:

{
	"name": "my-app",
	"repository": "myorg/my-app",
	"baseBranch": "main",
	"linearApiKey": "lin_api_...",
	"linearTeamKey": "ENG",
	"llmAssistant": "claude",
	"createdAt": "2025-10-06T12:00:00Z",
	"lastAccessed": "2025-10-06T12:00:00Z"
}
  • linearApiKey: Optional Linear API key for issue integration
  • linearTeamKey: Optional Linear team filter
  • llmAssistant: Optional command to run in a split tmux pane (e.g., "claude", "aider", "cursor")

Custom Scripts

Spaces uses convention over configuration for custom scripts. Instead of listing commands in the config file, you create executable scripts in phase-specific directories within each project:

~/spaces/<project-name>/scripts/
├── pre/                   # Run before immediately after worktree creation
│   ├── 01-copy-env.sh
│   └── 02-setup-dirs.sh
├── setup/                 # Run on workspace creation after pre scripts have finished (will execute in new tmux session if tmux is present)
│   ├── 01-install.sh
│   └── 02-build.sh
├── select/                # Run every time the workspace is opened (will execute in a new tmux session if tmux is present)
│   ├── 01-fetch.sh
│   └── 02-status.sh
└── remove/                # Run before workspace deletion (in terminal)
    ├── 01-cleanup.sh
    └── 02-notify.sh

Script Execution Rules

  1. Discovery: Spaces walks each phase directory and finds all executable scripts
  2. Execution Order: Scripts run alphabetically (hence the 01-, 02- prefixes)
  3. Working Directory: All scripts execute with the workspace directory as their current working directory (e.g., ~/spaces/my-app/workspaces/my-feature/), so you can use relative paths
  4. Arguments: Every script receives two arguments:
    • $1: Workspace name (e.g., my-feature)
    • $2: Repository name (e.g., myorg/my-app)

Example script (~/spaces/my-app/scripts/setup/01-install.sh):

#!/bin/bash
# Current working directory: ~/spaces/my-app/workspaces/<workspace-name>/
WORKSPACE_NAME=$1
REPOSITORY=$2

echo "Installing dependencies for $WORKSPACE_NAME ($REPOSITORY)..."
npm install
npm run build

Make scripts executable:

chmod +x ~/spaces/my-app/scripts/setup/01-install.sh

Script Phases

1. Pre Scripts (pre/): Run once in the current terminal, before tmux session creation. Perfect for:

  • Copying environment templates (cp .env.example .env)
  • Creating directories (mkdir -p tmp/uploads)
  • File preparation that setup scripts need

These run in the terminal so you can see their output immediately.

2. Setup Scripts (setup/): Run once inside the tmux session when a workspace is first created. Perfect for:

  • Installing dependencies (npm install)
  • Initial builds (npm run build)
  • Database initialization
  • Custom setup tasks

After running, Spaces creates a .spaces-setup marker file. This prevents pre and setup scripts from running again, even if you destroy and recreate the tmux session.

3. Select Scripts (select/): Run inside the tmux session every time you create a new session for an existing workspace. Perfect for:

  • Fetching latest changes (git fetch --all)
  • Checking workspace state (git status)
  • Environment checks
  • Quick status updates

4. Remove Scripts (remove/): Run in the terminal before a workspace is deleted. Perfect for:

  • Cleaning up temporary files or caches
  • Notifying external services
  • Backing up important data
  • Logging or auditing workspace removal

These run before the worktree is removed, so you can still access workspace files.

Example workflow:

# First time: creates workspace, runs pre scripts → setup scripts, marks setup complete
spaces add my-feature
# → Creates worktree
# → Running pre scripts...
#   $ 01-copy-env.sh my-feature myorg/my-app
#   $ 02-setup-dirs.sh my-feature myorg/my-app
# → Creating tmux session
# → Running setup scripts in tmux...
#   $ 01-install.sh my-feature myorg/my-app
#   $ 02-build.sh my-feature myorg/my-app
# → (marker created)

# Later: tmux session destroyed, recreate it
spaces switch my-feature
# → Running select scripts in tmux...
#   $ 01-fetch.sh my-feature myorg/my-app
#   $ 02-status.sh my-feature myorg/my-app
# → (NO pre or setup scripts - they already ran!)

Environment Variables

# Set the current project (overrides global config)
export SPACES_CURRENT_PROJECT="my-app"

Add this to your shell profile (~/.zshrc, ~/.bashrc, etc.) to persist.

Custom Tmux Layouts

You can create a .tmux.conf file in any workspace root to define a custom tmux layout:

# ~/spaces/my-app/workspaces/my-feature/.tmux.conf

# Split window horizontally (30% on right)
split-window -h -p 30

# Select left pane
select-pane -t 0

# Split left pane vertically
split-window -v -p 50

# Select top-left pane
select-pane -t 0

When you create or switch to this workspace, Spaces will automatically source this .tmux.conf file.

Advanced Workflows

Working on Multiple Features

# Create workspaces for different features
spaces add feature-a
# Work on feature-a...
# Ctrl+B, D to detach from tmux

spaces add feature-b
# Work on feature-b...
# Ctrl+B, D to detach

# Switch back to feature-a
spaces switch feature-a

Stale Workspace Detection

Workspaces that haven't had commits in more than staleDays (default: 30) will be marked as stale:

spaces list
# Output:
#   my-feature  main  clean
#   old-feature main  clean  [stale: 45 days]

Remote Branch Handling

When creating a workspace, if the branch exists on remote, Spaces will prompt:

Branch 'feature-x' exists on remote. Pull it down? (Y/n)

This allows you to safely pull down branches created by teammates.

Development

Build from Source

# Clone the repository
git clone https://github.com/mcculloughrt-svh/spaces.git
cd spaces

# Install dependencies
bun install

# Link for local development
bun link

# Type checking
bun run typecheck

# Build (optional - bun runs TypeScript directly)
bun run build

# Run linter
bun run lint

Project Structure

~/spaces/app/
├── src/
│   ├── index.ts                 # CLI entry point
│   ├── commands/                # Command implementations
│   │   ├── add.ts
│   │   ├── switch.ts
│   │   ├── list.ts
│   │   ├── remove.ts
│   │   └── directory.ts
│   ├── core/                    # Core functionality
│   │   ├── config.ts
│   │   ├── git.ts
│   │   ├── github.ts
│   │   ├── tmux.ts
│   │   └── linear.ts
│   ├── utils/                   # Utilities
│   │   ├── deps.ts
│   │   ├── fuzzy-match.ts
│   │   ├── logger.ts
│   │   ├── markdown.ts
│   │   ├── prompts.ts
│   │   ├── run-commands.ts
│   │   ├── run-scripts.ts
│   │   ├── sanitize.ts
│   │   ├── shell-escape.ts
│   │   └── workspace-state.ts
│   └── types/                   # Type definitions
│       ├── config.ts
│       ├── errors.ts
│       ├── workspace.ts
│       └── workspace-fuzzy.ts
└── bin/
    └── spaces                   # Executable

~/spaces/<project-name>/
├── .config.json                 # Project configuration
├── tmux.template.conf           # Template tmux config (copied to workspaces)
├── base/                        # Base repository clone
├── workspaces/                  # Git worktrees (one per feature/task)
│   └── my-feature/
│       ├── .spaces-setup        # Marker file (setup completed)
│       ├── .tmux.conf           # Workspace tmux config (copied from template)
│       └── .prompt/             # Linear integration (optional)
│           └── issue.md         # Linear issue details
└── scripts/                     # Custom scripts
    ├── pre/                     # Run before tmux (terminal)
    ├── setup/                   # Run once in tmux (first time)
    ├── select/                  # Run in tmux (every session)
    └── remove/                  # Run before workspace deletion (terminal)

Troubleshooting

GitHub CLI not authenticated

Error: GitHub CLI is not authenticated

Please run: gh auth login

Solution: Run gh auth login and follow the prompts.

Missing dependencies

Error: Missing required dependencies:
  fzf (fzf)
    Install: https://github.com/junegunn/fzf

Solution: Install the missing dependencies using the provided URLs.

Workspace already exists

Error: Workspace "my-feature" already exists

To switch to this workspace:
  spaces switch my-feature

Solution: Use spaces switch my-feature to switch to the existing workspace, or choose a different name.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.