Skip to content

Instantly share code, notes, and snippets.

@elijahr
Forked from nazq/claude-session.sh
Last active September 7, 2025 01:55
Show Gist options
  • Select an option

  • Save elijahr/96b5c31e98a721326ad64735334186b1 to your computer and use it in GitHub Desktop.

Select an option

Save elijahr/96b5c31e98a721326ad64735334186b1 to your computer and use it in GitHub Desktop.
Claude Session Manager - tmux session manager for Claude Code
#!/bin/bash
# Claude Session Manager for Git Worktrees (Zellij Edition)
# Fork of tmux-based Claude Session Manager by nazq https://gist.github.com/nazq/3d179988627b15119944da573c91f1ab
# Usage: cs [session_name]
# See 'cs help' for details
# Initialize Claude session if environment variable is set
_cs_session_init() {
if [[ -z "$CLAUDE_SESSION_ZELLIJ_INIT" ]]; then
return 0;
fi
local session_data="$CLAUDE_SESSION_ZELLIJ_INIT"
# Unset immediately to prevent nested initialization
unset CLAUDE_SESSION_ZELLIJ_INIT
# Parse session data (format: session_name|working_directory)
local session_name="${session_data%%|*}"
local working_dir="${session_data##*|}"
# Change to working directory
if [[ -n "$working_dir" && -d "$working_dir" ]]; then
cd "$working_dir"
fi
echo "πŸ€– Starting Claude Code session for: $session_name"
echo "πŸ“ Directory: $PWD"
# Check if claude command exists
if ! command -v claude >/dev/null 2>&1; then
echo "⚠️ Warning: 'claude' command not found"
echo " Make sure Claude Code is installed and in your PATH"
echo " You can install it from: https://claude.ai/download"
else
# Start Claude
echo "πŸš€ Starting Claude Code..."
claude
fi
}
_cs_session_init
# --- Core Utility Functions (Unix Philosophy) ---
# Get a random animal name from the predefined list
_cs_get_random_animal() {
local animals=(
"albatross" "alligator" "alpaca" "ant" "antelope" "ape" "armadillo" "donkey"
"baboon" "badger" "bat" "bear" "beaver" "bee" "beetle" "binturong"
"boar" "bobcat" "bull" "butterfly" "camel" "cardinal" "cat" "cattle"
"cheetah" "chicken" "chimpanzee" "chinchilla" "chough" "cobra" "cockroach" "cod"
"cormorant" "cow" "crab" "crane" "crocodile" "crow" "deer" "dog"
"dogfish" "dolphin" "donkey" "dove" "duck" "dunlin" "eagle" "echidna"
"eel" "elephant" "elk" "emu" "falcon" "ferret" "finch" "flamingo"
"fly" "fox" "frog" "gecko" "gerbil" "panda" "giraffe" "gnat"
"gnu" "goat" "goldfinch" "goosander" "goose" "gorilla" "goshawk" "grasshopper"
"grouse" "guanaco" "guinea_fowl" "guinea_pig" "hamster" "hare" "hawk" "hedgehog"
"heron" "herring" "hippopotamus" "hornet" "horse" "human" "hyena" "jaguar"
"jay" "jellyfish" "junglefowl" "kangaroo" "kingbird" "kinkajou" "koala" "ladybug"
"lapwing" "lark" "lemur" "leopard" "lion" "lizard" "llama" "lobster"
"locust" "lyrebird" "mallard" "meerkat" "mole" "mongoose" "monkey" "moose"
"mosquito" "moth" "mouse" "narwhal" "newt" "nightingale" "octopus" "opossum"
"otter"
)
# Seed random number generator with current time and process ID for better randomness
RANDOM=$(( $(date +%s) + $$ + $(date +%N | sed 's/^0*//') ))
local random_index=$(( RANDOM % ${#animals[@]} ))
echo "${animals[$random_index]}"
}
# Get the current directory's session name pattern
_cs_get_current_dir_pattern() {
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
local repo_name=""
local remote_url=$(git remote get-url origin 2>/dev/null)
if [[ -n "$remote_url" ]]; then
repo_name=$(basename "$remote_url" .git)
else
repo_name=$(basename "$(git rev-parse --show-toplevel)")
fi
local branch_name=$(git branch --show-current 2>/dev/null)
if [[ -z "$branch_name" ]]; then
branch_name=$(git rev-parse --short HEAD 2>/dev/null || echo "detached")
fi
# Return sanitized pattern (same as session creation logic)
echo "${repo_name}:${branch_name}" | sed 's/:/_/g' | sed 's/[^a-zA-Z0-9._-]/_/g'
else
# Return sanitized directory name pattern
basename "$PWD" | sed 's/:/_/g' | sed 's/[^a-zA-Z0-9._-]/_/g'
fi
}
# Generate an auto session name for current directory
_cs_generate_auto_session_name() {
local pattern=$(_cs_get_current_dir_pattern)
local animal=$(_cs_get_random_animal)
echo "${pattern}-${animal}"
}
# Sanitize a session name for zellij compatibility
_cs_sanitize_session_name() {
local name="$1"
echo "$name" | sed 's/:/_/g' | sed 's/[^a-zA-Z0-9._-]/_/g'
}
# Check if a session exists (active or dead)
_cs_session_exists() {
local session_name="$1"
zellij list-sessions | grep -q "^$session_name "
}
# Get all session names as an array
_cs_get_all_sessions() {
zellij list-sessions --short 2>/dev/null | cut -d' ' -f1 | tr -d "'" | sort
}
# Get session by index number
_cs_get_session_by_number() {
local number="$1"
# Validate input
if [[ -z "$number" ]]; then
echo "❌ No session number provided" >&2
return 1
fi
if ! [[ "$number" =~ ^[0-9]+$ ]]; then
echo "❌ Invalid session number: '$number' (must be a positive integer)" >&2
return 1
fi
local index_offset=0; [[ -z "$ZSH_VERSION" ]] && index_offset=1
local sessions=($(_cs_get_all_sessions))
if [[ ${#sessions[@]} -eq 0 ]]; then
echo "❌ No active sessions found" >&2
return 1
fi
if [[ "$number" -lt 1 || "$number" -gt ${#sessions[@]} ]]; then
echo "❌ Invalid session number: $number (valid range: 1-${#sessions[@]})" >&2
echo "πŸ“‹ Available sessions:" >&2
_cs_list_all_sessions >&2
return 1
fi
echo "${sessions[$((number - index_offset))]}"
}
# List all sessions with numbers
_cs_list_all_sessions() {
echo "πŸ“‹ Active Zellij sessions:"
local sessions_info=$(zellij list-sessions --short 2>/dev/null | sort)
if [[ -z "$sessions_info" ]]; then
echo " No active sessions found"
return 0
fi
local i=1
while IFS= read -r session_line; do
printf "%2d. %s\n" "$i" "$session_line"
((i++))
done <<< "$sessions_info"
}
# List sessions for current directory only
_cs_list_current_dir_sessions() {
local pattern=$(_cs_get_current_dir_pattern)
local sessions_info=$(zellij list-sessions --short 2>/dev/null | sort)
if [[ -z "$sessions_info" ]]; then
echo "πŸ“‹ No active sessions found"
return 0
fi
echo "πŸ“‹ Sessions for current directory ($pattern):"
local i=1
local found_any=false
while IFS= read -r session_line; do
local session_name=$(echo "$session_line" | cut -d' ' -f1 | tr -d "'")
if [[ "$session_name" =~ ^${pattern}(-[a-z]+)?$ ]]; then
printf "%2d. %s\n" "$i" "$session_line"
found_any=true
fi
((i++))
done <<< "$sessions_info"
if [[ "$found_any" == false ]]; then
echo " No sessions found for current directory"
echo " Use 'cs la' to see all sessions"
fi
}
# Resolve session identifier (number or name) to session name
_cs_resolve_session_identifier() {
local identifier="$1"
if [[ -z "$identifier" ]]; then
echo "❌ No session identifier provided" >&2
return 1
fi
if [[ "$identifier" =~ ^[0-9]+$ ]]; then
# Resolve by number
_cs_get_session_by_number "$identifier"
else
# Resolve by name - validate it exists
if _cs_session_exists "$identifier"; then
echo "$identifier"
else
echo "❌ Session '$identifier' does not exist" >&2
return 1
fi
fi
}
# Attach to a session by number or name
_cs_attach_session() {
local identifier="$1"
local session_name
session_name=$(_cs_resolve_session_identifier "$identifier")
if [[ $? -ne 0 ]]; then
echo "Use 'cs list' to see available sessions" >&2
return 1
fi
echo "πŸ”— Attaching to session: $session_name"
exec zellij attach "$session_name"
}
# Kill a session by number or name
_cs_kill_session() {
local identifier="$1"
local session_name
session_name=$(_cs_resolve_session_identifier "$identifier")
if [[ $? -ne 0 ]]; then
echo "Use 'cs list' to see available sessions" >&2
return 1
fi
echo "πŸ’€ Killing session: $session_name"
if zellij kill-session "$session_name" 2>/dev/null; then
echo "βœ… Session killed successfully"
return 0
else
echo "❌ Failed to kill session '$session_name'" >&2
return 1
fi
}
# Kill all Claude-related sessions
_cs_kill_all_sessions() {
local all_sessions=$(zellij list-sessions -n --no-formatting 2>/dev/null)
local claude_sessions=""
while IFS= read -r session; do
if [[ "$session" =~ (claude|_|-) ]]; then
claude_sessions+="$session"$'\n'
fi
done <<< "$all_sessions"
claude_sessions=$(echo -n "$claude_sessions")
if [[ -n "$claude_sessions" ]]; then
echo "πŸ’€ Found likely Claude sessions:"
echo "$claude_sessions" | sed 's/^/ /'
echo ""
read -p "Kill these sessions? (y/N): " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
echo "$claude_sessions" | xargs -I {} zellij kill-session {}
echo "βœ… Sessions terminated"
else
echo "❌ Cancelled"
fi
else
echo "ℹ️ No Claude sessions to kill"
fi
}
# Find an available session name (handles conflicts for auto-generated names)
_cs_find_available_session_name() {
local base_name="$1"
local is_custom="$2" # "true" if custom name, "false" if auto-generated
local session_name="$base_name"
local attempt_count=0
local max_attempts=10
while [[ $attempt_count -lt $max_attempts ]]; do
if _cs_session_exists "$session_name"; then
if [[ "$is_custom" == "true" ]]; then
# For custom names, prompt the user
echo "⚑ Session '$session_name' already exists!"
read -p "Do you want to [a]ttach or [k]ill and recreate? (a/k): " choice
case $choice in
a|A|"")
_cs_attach_session "$session_name"
return $? # This will exec, so we won't return normally
;;
k|K)
_cs_kill_session "$session_name"
break
;;
*)
echo "❌ Invalid choice. Please choose 'a' to attach or 'k' to kill and recreate."
;;
esac
else
# For auto-generated names, try a different animal
((attempt_count++))
echo "πŸ”„ Session '$session_name' exists, trying with different animal (attempt $attempt_count/$max_attempts)..."
local new_animal=$(_cs_get_random_animal)
# Extract the base pattern (everything before the last hyphen and animal)
local pattern_part="${base_name%-*}"
session_name="${pattern_part}-${new_animal}"
fi
else
# Session name is available
break
fi
done
if [[ $attempt_count -ge $max_attempts ]]; then
echo "❌ Could not find an available session name after $max_attempts attempts" >&2
return 1
fi
echo "$session_name"
}
# Create and enter a new session
_cs_create_session() {
local session_name="$1"
local use_exec="${2:-false}"
shift 2
local zellij_args=("$@") # Additional arguments to pass to zellij
# Validate input
if [[ -z "$session_name" ]]; then
echo "❌ No session name provided" >&2
return 1
fi
echo "πŸš€ Creating Zellij session: $session_name"
echo "πŸ“ Directory: $PWD"
if [[ ${#zellij_args[@]} -gt 0 ]]; then
echo "βš™οΈ Additional args: ${zellij_args[*]}"
echo "πŸ”§ Command: zellij ${zellij_args[*]} --session $session_name"
fi
if [[ "$use_exec" == "true" ]]; then
# Execute zellij session directly (this replaces current process)
echo "πŸ”— Replacing current shell with Zellij session..."
if [[ ${#zellij_args[@]} -gt 0 ]]; then
CLAUDE_SESSION_ZELLIJ_INIT="${session_name}|${PWD}" exec zellij "${zellij_args[@]}" --session "$session_name"
else
CLAUDE_SESSION_ZELLIJ_INIT="${session_name}|${PWD}" exec zellij --session "$session_name"
fi
else
# Run zellij session normally (can be exited without closing parent)
echo "πŸ”— Starting Zellij session with CLAUDE_SESSION_ZELLIJ_INIT=${session_name}|${PWD}..."
local exit_status=0
if [[ ${#zellij_args[@]} -gt 0 ]]; then
CLAUDE_SESSION_ZELLIJ_INIT="${session_name}|${PWD}" zellij "${zellij_args[@]}" --session "$session_name" || exit_status=$?
else
CLAUDE_SESSION_ZELLIJ_INIT="${session_name}|${PWD}" zellij --session "$session_name" || exit_status=$?
fi
if [[ $exit_status -eq 0 ]]; then
echo "πŸ“€ Exited Zellij session: $session_name with status $exit_status"
return 0
else
echo "❌ Failed to start Zellij session: $session_name (exit code $exit_status)" >&2
return 1
fi
fi
}
# --- Help and Utility Functions ---
# Show main help message
_cs_help() {
echo "cs - Claude Session Manager for Zellij"
echo ""
echo "Usage:"
echo " cs [session_name] Create session with name (or auto-generate) and enter it"
echo " cs replace [name] Create session and replace current shell (exec)"
echo " cs list List sessions for current directory"
echo " cs listall List all active sessions with numbers"
echo " cs attach <number|name> Attach to session by number or name"
echo " cs kill <number|name> Kill session by number or name"
echo " cs killall Kill all Claude-related sessions"
echo " cs help Show this help message"
echo ""
echo "Passing arguments to zellij:"
echo " cs [session_name] -- [zellij_args] Pass additional args to zellij"
echo " cs replace [name] -- [zellij_args] Pass additional args to zellij"
echo ""
echo "Short forms:"
echo " cs l = cs list"
echo " cs la = cs listall"
echo " cs r = cs replace"
echo " cs a N = cs attach N"
echo " cs k N = cs kill N"
echo " cs ka = cs killall"
echo " cs h = cs help"
echo ""
echo "Session naming:"
echo " - Auto-generated: REPO:BRANCH-ANIMAL or FOLDER-ANIMAL"
echo " - If auto-generated name exists, tries different animals"
echo " - If custom name exists, prompts to attach or recreate"
echo ""
echo "Examples:"
echo " cs # Create session with auto-generated name"
echo " cs my-project # Create session named 'my-project'"
echo " cs r # Create session and replace current shell"
echo " cs r my-project # Create 'my-project' session and replace shell"
echo " cs l # List sessions for current directory"
echo " cs la # List all sessions"
echo " cs a 3 # Attach to session #3"
echo " cs a my-session # Attach to session named 'my-session'"
echo " cs k 2 # Kill session #2"
echo " cs k my-session # Kill session named 'my-session'"
echo " cs ka # Kill all Claude sessions"
echo ""
echo "Examples with zellij arguments:"
echo " cs -- --new-session-with-layout compact # Create session with compact layout"
echo " cs my-proj -- --new-session-with-layout /path/layout # Create 'my-proj' with custom layout"
echo " cs r -- --config /path/config # Replace shell with custom config"
}
# Handle attach command with validation
_cs_handle_attach() {
local identifier="$1"
if [[ -n "$identifier" ]]; then
_cs_attach_session "$identifier"
else
echo "❌ Usage: cs attach <number|name>"
echo "Use 'cs list' to see numbered sessions"
return 1
fi
}
# Handle kill command with validation
_cs_handle_kill() {
local identifier="$1"
if [[ -n "$identifier" ]]; then
_cs_kill_session "$identifier"
else
echo "❌ Usage: cs kill <number|name>"
echo "Use 'cs list' to see numbered sessions"
return 1
fi
}
# Handle replace command
_cs_replace() {
local custom_name="$1"
shift
local zellij_args=("$@")
local session_name
local is_custom="false"
if [[ -n "$custom_name" ]]; then
# Custom session name provided
session_name=$(_cs_sanitize_session_name "$custom_name")
is_custom="true"
echo "🎯 Using custom session name: $session_name"
else
# Auto-generate session name
session_name=$(_cs_sanitize_session_name "$(_cs_generate_auto_session_name)")
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "πŸ“ Git worktree detected: $session_name"
else
echo "πŸ“‚ Using directory name: $session_name"
fi
fi
# Find available session name (handles conflicts)
session_name=$(_cs_find_available_session_name "$session_name" "$is_custom")
if [[ $? -ne 0 ]]; then
return 1
fi
# Create and replace with the session
_cs_create_session "$session_name" "true" "${zellij_args[@]}"
}
# Handle session creation (default behavior)
_cs_handle_new() {
local custom_name="$1"
shift
local zellij_args=("$@")
local session_name
local is_custom="false"
if [[ -n "$custom_name" ]]; then
session_name=$(_cs_sanitize_session_name "$custom_name")
is_custom="true"
echo "🎯 Using custom session name: $session_name"
else
session_name=$(_cs_sanitize_session_name "$(_cs_generate_auto_session_name)")
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "πŸ“ Git worktree detected: $session_name"
else
echo "πŸ“‚ Using directory name: $session_name"
fi
fi
# Find available session name (handles conflicts)
session_name=$(_cs_find_available_session_name "$session_name" "$is_custom")
if [[ $? -ne 0 ]]; then
return 1
fi
# Create and enter the session (without exec)
_cs_create_session "$session_name" "false" "${zellij_args[@]}"
}
# --- Main CS Command ---
# Main cs function with subcommand support and session creation
cs() {
# Handle `--` separator for passing args to zellij
local zellij_args=()
local cs_args=()
local found_separator=false
# Split arguments at `--`
while [[ $# -gt 0 ]]; do
if [[ "$1" == "--" ]]; then
found_separator=true
shift
break
fi
cs_args+=("$1")
shift
done
# Everything after `--` goes to zellij
if [[ "$found_separator" == "true" ]]; then
zellij_args=("$@")
fi
# Process cs arguments
set -- "${cs_args[@]}"
case "${1:-}" in
# List sessions for current directory
l|list)
_cs_list_current_dir_sessions
;;
# List all sessions globally
la|listall)
_cs_list_all_sessions
;;
# Attach to session by number or name
a|attach)
_cs_handle_attach "$2"
;;
# Kill session by number or name
k|kill)
_cs_handle_kill "$2"
;;
# Kill all sessions
ka|killall)
_cs_kill_all_sessions
;;
# Replace current shell with new session (exec behavior)
r|replace)
_cs_replace "$2" "${zellij_args[@]}"
;;
# Help
h|help|--help|-h)
_cs_help
;;
# Default: create new session
*)
_cs_handle_new "$1" "${zellij_args[@]}"
;;
esac
}
@elijahr
Copy link
Author

elijahr commented Sep 6, 2025

Claude Session Manager Setup & Usage Guide (Zellij Edition)

πŸš€ Quick Setup (for Bash and Zsh)

1. Download the script

# Create the directory if it doesn't exist
mkdir -p ~/.bash_functions

# Download the universal script (works for bash and zsh)
curl -o ~/.bash_functions/claude-session-zellij.sh https://gist.githubusercontent.com/elijahr/96b5c31e98a721326ad64735334186b1/raw/claude-session-zellij.sh

2. Add to your shell's config file

Add the following line to your configuration file.

  • For Zsh users, add below to ~/.zshrc (It doesn't matter that the script is in ~/.bash_functions)
  • For Bash users, add below to ~/.bashrc or ~/.bash_profile
# Source Claude Session Manager (Zellij Edition)
[ -f ~/.bash_functions/claude-session-zellij.sh ] && source ~/.bash_functions/claude-session-zellij.sh

3. Reload your shell

For Zsh:

source ~/.zshrc

For Bash:

source ~/.bashrc

πŸ“– Usage

Core Commands

Session Creation

  • cs - Create a new Claude session with an auto-generated name (REPO:BRANCH-ANIMAL or FOLDER-ANIMAL)
  • cs my-project - Create a session with a custom name
  • cs replace or cs r - Create session and replace current shell (exec behavior)
  • cs r my-project - Create custom-named session and replace current shell

Session Management

  • cs list or cs l - List sessions for current directory only
  • cs listall or cs la - List all active sessions with numbers
  • cs attach 3 or cs a 3 - Attach to session #3 from the list
  • cs kill 2 or cs k 2 - Kill session #2 from the list
  • cs killall or cs ka - Kill all Claude-related sessions (with confirmation)

Help

  • cs help or cs h - Show detailed help message

Session Naming Logic

The script uses intelligent auto-naming with animal suffixes:

  • In a git worktree: REPO:BRANCH-ANIMAL (e.g., webapp:feature-auth-dolphin)
  • Outside git: FOLDER-ANIMAL (e.g., my-notes-elephant)
  • Conflict resolution: If auto-generated name exists, tries different animals automatically
  • Custom names: If custom name exists, prompts to attach or recreate

Animal Names

Sessions get random animal suffixes from a curated list of 80+ animals:
albatross, dolphin, elephant, falcon, giraffe, hedgehog, jaguar, koala, llama, octopus, penguin, etc.

Examples

# Auto-generated session in git repo
cd ~/projects/webapp
cs
# Creates: webapp:main-dolphin (or similar with random animal)

# If that session exists, automatically tries a different animal
cs
# Creates: webapp:main-elephant (different animal)

# Custom session name
cs backend-api
# Creates: backend-api
# If exists, prompts: [a]ttach or [k]ill and recreate?

# Replace current shell (exec behavior)
cs r
# Creates session and replaces current shell process

# List sessions for current directory
cs l
# Shows only sessions matching current directory pattern

# List all sessions with numbers
cs la
#  1. webapp:main-dolphin     [RUNNING]
#  2. backend-api            [RUNNING]  
#  3. notes-elephant         [RUNNING]

# Attach to session by number
cs a 2
# Attaches to backend-api

# Kill session by number
cs k 3
# Kills notes-elephant session

# Kill all Claude sessions (with confirmation)
cs ka
# Shows list and prompts for confirmation

Short Forms

All commands have convenient short forms:

  • cs l = cs list
  • cs la = cs listall
  • cs r = cs replace
  • cs a N = cs attach N
  • cs k N = cs kill N
  • cs ka = cs killall
  • cs h = cs help

🎯 Key Features

Smart Naming

  • Git-aware: Auto-detects repository name and current branch
  • Animal suffixes: Random animals prevent naming conflicts
  • Automatic conflict resolution: Tries different animals for auto-generated names
  • Interactive conflict handling: Prompts for custom name conflicts
  • Sanitization: Converts special characters to shell-safe alternatives

Session Management

  • Directory-scoped listing: cs l shows only relevant sessions
  • Global session overview: cs la shows all sessions with numbers
  • Number-based operations: Attach/kill by session number for convenience
  • Bulk operations: Kill all Claude sessions with confirmation

Execution Models

  • Normal mode: Default cs creates session you can exit normally
  • Replace mode: cs r replaces current shell (exec behavior)
  • Persistent sessions: Zellij sessions survive disconnection

Unix Philosophy Design

  • Small focused functions: Each function does one thing well
  • Composable: Functions work together seamlessly
  • Reliable error handling: Graceful failure modes
  • No global side effects: Safe for interactive shell use

πŸ“‹ Requirements

  • Zellij terminal multiplexer
  • Claude Code CLI (claude command)
  • bash 4.0+ or zsh
  • git (optional, for git-aware naming)

πŸ’‘ Pro Tips

Workflow Optimization

  • Use cs l frequently to see sessions for your current project
  • Use cs la to get the big picture of all active sessions
  • Leverage numbered operations (cs a 3, cs k 2) for speed
  • Use cs r when you want to fully commit to a session

Naming Strategy

  • Let auto-naming handle most cases - animals make sessions memorable
  • Use custom names for long-term sessions you'll reference often
  • The directory-scoped listing (cs l) keeps things organized

Session Lifecycle

  • Sessions persist between shell restarts and disconnections
  • Each session maintains independent Claude context
  • Use cs ka periodically to clean up unused sessions
  • Git worktree changes automatically create appropriately named sessions

Troubleshooting

  • If session creation fails, check that zellij and claude commands are available
  • Use cs la to verify session state before operations
  • Session names are sanitized automatically - special characters become underscores
  • The script handles edge cases like detached git heads and missing remotes gracefully

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment