612 lines
18 KiB
Bash
Executable file
612 lines
18 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# ccc - Smart tmux wrapper for Claude Code
|
|
#
|
|
# Runs Claude Code inside tmux for terminal crash resilience.
|
|
# Sessions persist even if the terminal crashes or is closed.
|
|
#
|
|
# Usage:
|
|
# ccc # Smart attach-or-create for current directory
|
|
# cccstatus # Show session status for current directory
|
|
# ccclist # Show all sessions with directories
|
|
# cccattach NAME # Attach to specific session
|
|
# ccctasks # Show persisted tasks
|
|
# cccreview # LLM-analyze tasks (run after reboot)
|
|
# cccresume [n] # Show/resume reviewed tasks
|
|
# ccckill [NAME] # Kill session (current dir if NAME omitted)
|
|
# cccnew # Force create new session
|
|
# ccchelp # Show help
|
|
#
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
GRAY='\033[0;90m'
|
|
NC='\033[0m'
|
|
|
|
TASKS_DIR="$HOME/.local/claude/tasks"
|
|
REVIEWS_DIR="$HOME/.local/claude/reviews"
|
|
YELLOW='\033[0;33m'
|
|
|
|
check_dependencies() {
|
|
if ! command -v tmux &>/dev/null; then
|
|
echo -e "${RED}Error: tmux is not installed${NC}"
|
|
exit 1
|
|
fi
|
|
if ! command -v claude &>/dev/null; then
|
|
echo -e "${RED}Error: claude is not installed${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
generate_session_name() {
|
|
local dir="${1:-$(pwd)}"
|
|
local hash=$(echo "$dir" | md5sum | cut -c1-4)
|
|
local last_component=$(basename "$dir")
|
|
# Sanitize for tmux (no dots, limited special chars)
|
|
last_component=$(echo "$last_component" | tr '.' '-' | tr -cd '[:alnum:]-@_')
|
|
echo "claude-${hash}-${last_component}"
|
|
}
|
|
|
|
session_exists() {
|
|
tmux has-session -t "$1" 2>/dev/null
|
|
}
|
|
|
|
show_session_info() {
|
|
local session="$1"
|
|
local info=$(tmux list-sessions -F "#{session_name}|#{session_created_string}" 2>/dev/null | grep "^${session}|" || true)
|
|
if [[ -n "$info" ]]; then
|
|
local created=$(echo "$info" | cut -d'|' -f2)
|
|
echo -e " ${GRAY}Created: $created${NC}"
|
|
fi
|
|
}
|
|
|
|
create_session() {
|
|
local dir="$1"
|
|
local session_name="$2"
|
|
|
|
# Create detached session in target directory
|
|
tmux new-session -d -s "$session_name" -c "$dir"
|
|
|
|
# Store the original directory as session environment variable
|
|
tmux set-environment -t "$session_name" CC_DIR "$dir"
|
|
|
|
# Configure for Claude Code
|
|
tmux set-option -t "$session_name" default-terminal "tmux-256color"
|
|
tmux set-option -t "$session_name" mouse on
|
|
|
|
# Start claude
|
|
tmux send-keys -t "$session_name" "claude" Enter
|
|
|
|
# Attach
|
|
tmux attach-session -t "$session_name"
|
|
}
|
|
|
|
get_session_dir() {
|
|
local session="$1"
|
|
tmux show-environment -t "$session" CC_DIR 2>/dev/null | cut -d'=' -f2- || echo ""
|
|
}
|
|
|
|
get_session_prompt() {
|
|
local dir="$1"
|
|
[[ -z "$dir" ]] && return
|
|
|
|
# Extract last component for simpler matching (e.g., @packages)
|
|
local last_component=$(basename "$dir")
|
|
|
|
# Find most recent task file matching this directory component
|
|
local task_file=$(ls -t "$TASKS_DIR"/*"${last_component}"*.txt 2>/dev/null | head -1)
|
|
[[ -z "$task_file" ]] && return
|
|
|
|
# Extract prompt (lines after the blank line, first 50 chars)
|
|
local prompt=$(awk '/^$/{found=1; next} found{print; exit}' "$task_file" 2>/dev/null)
|
|
echo "${prompt:0:50}"
|
|
}
|
|
|
|
smart_attach() {
|
|
local dir="${1:-$(pwd)}"
|
|
local session_name=$(generate_session_name "$dir")
|
|
|
|
if session_exists "$session_name"; then
|
|
echo -e "${GREEN}Attaching to existing session:${NC} $session_name"
|
|
show_session_info "$session_name"
|
|
tmux attach-session -t "$session_name"
|
|
else
|
|
echo -e "${BLUE}Creating new session:${NC} $session_name"
|
|
create_session "$dir" "$session_name"
|
|
fi
|
|
}
|
|
|
|
list_sessions() {
|
|
echo -e "${BLUE}=== Claude Code Sessions ===${NC}"
|
|
echo ""
|
|
|
|
local sessions=$(tmux list-sessions -F "#{session_name}|#{session_created_string}" 2>/dev/null | grep "^claude-" || true)
|
|
local current_session=$(generate_session_name "$(pwd)")
|
|
|
|
if [[ -z "$sessions" ]]; then
|
|
echo "No claude sessions found."
|
|
echo ""
|
|
echo -e "${GRAY}Current directory: $(pwd)${NC}"
|
|
echo -e "${GRAY}Would create: $current_session${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo "$sessions" | while IFS='|' read -r name created; do
|
|
local dir=$(get_session_dir "$name")
|
|
local short_dir="${dir/#$HOME/~}"
|
|
local prompt=$(get_session_prompt "$dir")
|
|
local marker=" "
|
|
local color=""
|
|
|
|
# Highlight current directory's session
|
|
if [[ "$name" == "$current_session" ]]; then
|
|
marker="*"
|
|
color="${GREEN}"
|
|
fi
|
|
|
|
# Show session with context
|
|
printf "${color}%s %s${NC}\n" "$marker" "$name"
|
|
printf " ${GRAY}%s${NC}\n" "$short_dir"
|
|
if [[ -n "$prompt" ]]; then
|
|
printf " ${GRAY}\"%s...\"${NC}\n" "$prompt"
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
echo -e "${GRAY}* = current directory${NC}"
|
|
}
|
|
|
|
show_tasks() {
|
|
echo -e "${BLUE}=== Persisted Tasks ===${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -d "$TASKS_DIR" ]] || [[ -z "$(ls -A "$TASKS_DIR" 2>/dev/null)" ]]; then
|
|
echo "No persisted tasks found."
|
|
return 0
|
|
fi
|
|
|
|
# Show most recent 10 tasks with prompts
|
|
for task_file in $(ls -t "$TASKS_DIR"/*.txt 2>/dev/null | head -10); do
|
|
[[ -f "$task_file" ]] || continue
|
|
local basename=$(basename "$task_file" .txt)
|
|
local timestamp=$(echo "$basename" | cut -d'_' -f1)
|
|
local project=$(echo "$basename" | cut -d'_' -f2- | cut -d'-' -f1)
|
|
local prompt=$(awk '/^$/{found=1; next} found{print; exit}' "$task_file" 2>/dev/null)
|
|
|
|
# Convert timestamp to human-readable
|
|
local date_str=$(date -d "@$((timestamp / 1000))" "+%m-%d %H:%M" 2>/dev/null || echo "$timestamp")
|
|
|
|
echo -e "${GREEN}$project${NC} ${GRAY}($date_str)${NC}"
|
|
if [[ -n "$prompt" ]]; then
|
|
echo -e " ${GRAY}\"${prompt:0:60}...\"${NC}"
|
|
fi
|
|
echo ""
|
|
done
|
|
}
|
|
|
|
kill_session() {
|
|
local target="$1"
|
|
|
|
if [[ -z "$target" ]]; then
|
|
target=$(generate_session_name)
|
|
fi
|
|
|
|
if session_exists "$target"; then
|
|
tmux kill-session -t "$target"
|
|
echo -e "${GREEN}Killed session:${NC} $target"
|
|
else
|
|
echo -e "${RED}Session not found:${NC} $target"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
review_tasks() {
|
|
echo -e "${BLUE}=== Reviewing Tasks with LLM ===${NC}"
|
|
echo ""
|
|
|
|
mkdir -p "$REVIEWS_DIR"
|
|
|
|
if [[ ! -d "$TASKS_DIR" ]] || [[ -z "$(ls -A "$TASKS_DIR" 2>/dev/null)" ]]; then
|
|
echo "No tasks to review."
|
|
return 0
|
|
fi
|
|
|
|
local task_files=$(ls -t "$TASKS_DIR"/*.txt 2>/dev/null | head -15)
|
|
local count=0
|
|
|
|
for task_file in $task_files; do
|
|
[[ -f "$task_file" ]] || continue
|
|
count=$((count + 1))
|
|
|
|
local basename=$(basename "$task_file" .txt)
|
|
local timestamp=$(echo "$basename" | cut -d'_' -f1)
|
|
local project=$(echo "$basename" | cut -d'_' -f2- | cut -d'-' -f1)
|
|
local review_file="$REVIEWS_DIR/${basename}.md"
|
|
|
|
# Skip if recently reviewed (within last hour)
|
|
if [[ -f "$review_file" ]]; then
|
|
local review_age=$(( $(date +%s) - $(stat -c %Y "$review_file") ))
|
|
if [[ $review_age -lt 3600 ]]; then
|
|
echo -e "${GRAY}[$count] $project - already reviewed${NC}"
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
echo -e "${YELLOW}[$count] Reviewing: $project...${NC}"
|
|
|
|
# Read task content
|
|
local task_content=$(cat "$task_file")
|
|
|
|
# Get age in hours
|
|
local now_ms=$(($(date +%s) * 1000))
|
|
local age_hours=$(( (now_ms - timestamp) / 1000 / 3600 ))
|
|
|
|
# Use Claude to analyze
|
|
local review=$(claude --print -p "You are analyzing a saved task for session recovery. Be concise.
|
|
|
|
TASK FILE:
|
|
$task_content
|
|
|
|
TASK AGE: ${age_hours} hours
|
|
|
|
Analyze and respond with EXACTLY this format (no other text):
|
|
|
|
STATUS: [ACTIVE|COOLING|STALE]
|
|
- ACTIVE = recent (<4h) and incomplete work
|
|
- COOLING = paused work (4-24h) or waiting on something
|
|
- STALE = old (>24h) or completed work
|
|
|
|
SUMMARY: [1 sentence describing what was being worked on]
|
|
|
|
NEXT_STEP: [1 sentence describing what to do next to resume, or 'N/A' if complete]
|
|
|
|
DIRECTORY: [extract from task file]" 2>/dev/null || echo "STATUS: UNKNOWN
|
|
SUMMARY: Failed to analyze
|
|
NEXT_STEP: Review manually
|
|
DIRECTORY: unknown")
|
|
|
|
# Save review
|
|
cat > "$review_file" << EOF
|
|
# Task Review: $project
|
|
Generated: $(date -Iseconds)
|
|
Task ID: $timestamp
|
|
|
|
$review
|
|
|
|
---
|
|
Source: $task_file
|
|
EOF
|
|
|
|
echo -e "${GREEN} Done${NC}"
|
|
done
|
|
|
|
echo ""
|
|
echo -e "Reviews saved to ${GRAY}$REVIEWS_DIR/${NC}"
|
|
echo -e "Run ${BLUE}ccc resume${NC} to see results and resume a task"
|
|
}
|
|
|
|
show_reviews() {
|
|
echo -e "${BLUE}=== Task Reviews ===${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -d "$REVIEWS_DIR" ]] || [[ -z "$(ls -A "$REVIEWS_DIR" 2>/dev/null)" ]]; then
|
|
echo "No reviews found. Run ${BLUE}ccc review${NC} first."
|
|
return 0
|
|
fi
|
|
|
|
local index=0
|
|
local active_tasks=()
|
|
local cooling_tasks=()
|
|
local stale_tasks=()
|
|
|
|
# Categorize reviews
|
|
for review_file in $(ls -t "$REVIEWS_DIR"/*.md 2>/dev/null); do
|
|
[[ -f "$review_file" ]] || continue
|
|
|
|
local status=$(grep "^STATUS:" "$review_file" | head -1 | cut -d: -f2 | tr -d ' ')
|
|
local summary=$(grep "^SUMMARY:" "$review_file" | head -1 | cut -d: -f2-)
|
|
local next_step=$(grep "^NEXT_STEP:" "$review_file" | head -1 | cut -d: -f2-)
|
|
local directory=$(grep "^DIRECTORY:" "$review_file" | head -1 | cut -d: -f2- | tr -d ' ')
|
|
local basename=$(basename "$review_file" .md)
|
|
local project=$(echo "$basename" | cut -d'_' -f2- | cut -d'-' -f1)
|
|
|
|
case "$status" in
|
|
ACTIVE) active_tasks+=("$project|$summary|$next_step|$directory|$review_file") ;;
|
|
COOLING) cooling_tasks+=("$project|$summary|$next_step|$directory|$review_file") ;;
|
|
*) stale_tasks+=("$project|$summary|$next_step|$directory|$review_file") ;;
|
|
esac
|
|
done
|
|
|
|
# Show active tasks
|
|
if [[ ${#active_tasks[@]} -gt 0 ]]; then
|
|
echo -e "${GREEN}ACTIVE (resume immediately)${NC}"
|
|
for task in "${active_tasks[@]}"; do
|
|
index=$((index + 1))
|
|
IFS='|' read -r project summary next_step directory file <<< "$task"
|
|
echo -e " ${GREEN}[$index]${NC} $project"
|
|
echo -e " ${GRAY}$summary${NC}"
|
|
echo -e " ${BLUE}Next:${NC}$next_step"
|
|
echo ""
|
|
done
|
|
fi
|
|
|
|
# Show cooling tasks
|
|
if [[ ${#cooling_tasks[@]} -gt 0 ]]; then
|
|
echo -e "${YELLOW}COOLING (can resume)${NC}"
|
|
for task in "${cooling_tasks[@]}"; do
|
|
index=$((index + 1))
|
|
IFS='|' read -r project summary next_step directory file <<< "$task"
|
|
echo -e " ${YELLOW}[$index]${NC} $project"
|
|
echo -e " ${GRAY}$summary${NC}"
|
|
echo ""
|
|
done
|
|
fi
|
|
|
|
# Show stale count
|
|
if [[ ${#stale_tasks[@]} -gt 0 ]]; then
|
|
echo -e "${GRAY}STALE: ${#stale_tasks[@]} tasks (run 'ccc tasks' to see all)${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "Resume with: ${BLUE}ccc resume <number>${NC}"
|
|
}
|
|
|
|
resume_task() {
|
|
local selector="$1"
|
|
|
|
if [[ -z "$selector" ]]; then
|
|
show_reviews
|
|
return 0
|
|
fi
|
|
|
|
# Build indexed list of reviews (same order as show_reviews)
|
|
local all_tasks=()
|
|
for review_file in $(ls -t "$REVIEWS_DIR"/*.md 2>/dev/null); do
|
|
[[ -f "$review_file" ]] || continue
|
|
|
|
local status=$(grep "^STATUS:" "$review_file" | head -1 | cut -d: -f2 | tr -d ' ')
|
|
local basename=$(basename "$review_file" .md)
|
|
local project=$(echo "$basename" | cut -d'_' -f2- | cut -d'-' -f1)
|
|
|
|
# Order: ACTIVE first, then COOLING, then STALE
|
|
case "$status" in
|
|
ACTIVE) all_tasks=("$project|$review_file" "${all_tasks[@]}") ;;
|
|
COOLING) all_tasks+=("$project|$review_file") ;;
|
|
*) all_tasks+=("$project|$review_file") ;;
|
|
esac
|
|
done
|
|
|
|
local target_task=""
|
|
|
|
# Check if selector is a number
|
|
if [[ "$selector" =~ ^[0-9]+$ ]]; then
|
|
local idx=$((selector - 1))
|
|
if [[ $idx -ge 0 ]] && [[ $idx -lt ${#all_tasks[@]} ]]; then
|
|
target_task="${all_tasks[$idx]}"
|
|
fi
|
|
else
|
|
# Search by project name
|
|
for task in "${all_tasks[@]}"; do
|
|
local project=$(echo "$task" | cut -d'|' -f1)
|
|
if [[ "$project" == *"$selector"* ]]; then
|
|
target_task="$task"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -z "$target_task" ]]; then
|
|
echo -e "${RED}Task not found: $selector${NC}"
|
|
echo "Run ${BLUE}ccc resume${NC} to see available tasks"
|
|
return 1
|
|
fi
|
|
|
|
IFS='|' read -r project review_file <<< "$target_task"
|
|
|
|
# Extract directory from the original task file
|
|
local task_basename=$(basename "$review_file" .md)
|
|
local task_file="$TASKS_DIR/${task_basename}.txt"
|
|
local directory=""
|
|
|
|
if [[ -f "$task_file" ]]; then
|
|
directory=$(grep "^\[Directory:" "$task_file" | head -1 | sed 's/\[Directory: \(.*\)\]/\1/')
|
|
fi
|
|
|
|
if [[ -z "$directory" ]] || [[ ! -d "$directory" ]]; then
|
|
echo -e "${RED}Directory not found: $directory${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Extract review content
|
|
local summary=$(grep "^SUMMARY:" "$review_file" | head -1 | cut -d: -f2-)
|
|
local next_step=$(grep "^NEXT_STEP:" "$review_file" | head -1 | cut -d: -f2-)
|
|
local status=$(grep "^STATUS:" "$review_file" | head -1 | cut -d: -f2 | tr -d ' ')
|
|
|
|
# Read original task content for context
|
|
local original_prompt=$(awk '/^$/{found=1; next} found && !/^---/{print}' "$task_file" 2>/dev/null | head -20)
|
|
|
|
# Create handoff file in project directory
|
|
local handoff_dir="$directory/.claude/handoffs"
|
|
local handoff_timestamp=$(date +%Y%m%d_%H%M%S)
|
|
local handoff_name=$(echo "$project" | tr -cd '[:alnum:]-_')
|
|
local handoff_file="$handoff_dir/${handoff_timestamp}_${handoff_name}.md"
|
|
|
|
mkdir -p "$handoff_dir"
|
|
|
|
cat > "$handoff_file" << EOF
|
|
# Session Handoff: $project
|
|
|
|
**Status**: $status
|
|
**Generated**: $(date -Iseconds)
|
|
**Directory**: $directory
|
|
|
|
## Context
|
|
$summary
|
|
|
|
## What Was Being Worked On
|
|
$original_prompt
|
|
|
|
## Next Step
|
|
$next_step
|
|
|
|
## Instructions
|
|
Continue the work described above. The previous session ended unexpectedly (system crash/reboot).
|
|
Pick up where we left off based on the context provided.
|
|
EOF
|
|
|
|
echo -e "${GREEN}=== Handoff Created ===${NC}"
|
|
echo ""
|
|
echo -e "Project: ${BLUE}$project${NC}"
|
|
echo -e "Directory: ${GRAY}$directory${NC}"
|
|
echo -e "Status: $status"
|
|
echo ""
|
|
echo -e "Context: $summary"
|
|
echo -e "Next: $next_step"
|
|
echo ""
|
|
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
echo -e "To resume, run these commands:"
|
|
echo ""
|
|
echo -e " ${GREEN}cd $directory${NC}"
|
|
echo -e " ${GREEN}ccc${NC}"
|
|
echo ""
|
|
echo -e "Then paste this into Claude:"
|
|
echo ""
|
|
echo -e " ${BLUE}Read the handoff at .claude/handoffs/${handoff_timestamp}_${handoff_name}.md and continue the work${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
|
|
# Copy the instruction to clipboard if xclip available
|
|
if command -v xclip &>/dev/null; then
|
|
echo "Read the handoff at .claude/handoffs/${handoff_timestamp}_${handoff_name}.md and continue the work" | xclip -selection clipboard
|
|
echo ""
|
|
echo -e "${GREEN}(Instruction copied to clipboard)${NC}"
|
|
fi
|
|
}
|
|
|
|
show_status() {
|
|
local dir="$(pwd)"
|
|
local session_name=$(generate_session_name "$dir")
|
|
local short_dir="${dir/#$HOME/~}"
|
|
local prompt=$(get_session_prompt "$dir")
|
|
|
|
echo -e "${BLUE}=== Current Directory ===${NC}"
|
|
echo -e " Path: $short_dir"
|
|
echo -e " Session: $session_name"
|
|
if [[ -n "$prompt" ]]; then
|
|
echo -e " Task: \"$prompt...\""
|
|
fi
|
|
echo ""
|
|
|
|
if session_exists "$session_name"; then
|
|
local info=$(tmux list-sessions -F "#{session_name}|#{session_created_string}|#{session_activity_string}" 2>/dev/null | grep "^${session_name}|" || true)
|
|
if [[ -n "$info" ]]; then
|
|
local created=$(echo "$info" | cut -d'|' -f2)
|
|
local activity=$(echo "$info" | cut -d'|' -f3)
|
|
echo -e "${GREEN}Session running${NC}"
|
|
echo -e " Created: $created"
|
|
echo -e " Activity: $activity"
|
|
echo ""
|
|
echo -e "Run ${BLUE}ccc${NC} to attach"
|
|
fi
|
|
else
|
|
echo -e "${GRAY}No active session${NC}"
|
|
echo ""
|
|
echo -e "Run ${BLUE}ccc${NC} to create"
|
|
fi
|
|
}
|
|
|
|
show_help() {
|
|
cat << 'EOF'
|
|
ccc - Smart tmux wrapper for Claude Code
|
|
|
|
USAGE:
|
|
ccc [command] [args]
|
|
|
|
COMMANDS:
|
|
(default) Smart attach-or-create session for current directory
|
|
status, s Show session status for current directory
|
|
list, ls Show all claude tmux sessions with directories
|
|
attach, a Attach to session by name
|
|
tasks, t Show persisted tasks from ~/.local/claude/tasks/
|
|
review, r Use LLM to analyze and categorize all tasks
|
|
resume [n] Show reviewed tasks, or resume task by number/name
|
|
kill, k Kill session (by name, or current dir if omitted)
|
|
new, n Force create new session even if one exists
|
|
help, -h Show this help message
|
|
|
|
RECOVERY WORKFLOW (after reboot):
|
|
ccc review # LLM analyzes all tasks, categorizes them
|
|
ccc resume # Shows: ACTIVE, COOLING, STALE tasks
|
|
ccc resume 1 # Creates handoff file, shows paste instruction
|
|
|
|
EXAMPLES:
|
|
ccc # Start/attach claude for current project
|
|
ccc status # Check if session exists for this dir
|
|
ccc list # See all sessions with their directories
|
|
ccc review # Analyze tasks after system reboot
|
|
ccc resume # See reviewed tasks
|
|
ccc resume 2 # Resume second task
|
|
ccc resume @packages # Resume by project name
|
|
|
|
KEYBINDINGS (in tmux):
|
|
Ctrl+B D Detach from session (keeps claude running)
|
|
Ctrl+B [ Enter scroll mode (arrows to scroll, q to exit)
|
|
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
check_dependencies
|
|
|
|
local cmd="${1:-}"
|
|
|
|
case "$cmd" in
|
|
"")
|
|
smart_attach "$(pwd)"
|
|
;;
|
|
status|s)
|
|
show_status
|
|
;;
|
|
list|ls)
|
|
list_sessions
|
|
;;
|
|
attach|a)
|
|
if [[ -n "${2:-}" ]]; then
|
|
tmux attach-session -t "$2"
|
|
else
|
|
echo -e "${RED}Usage: ccc attach SESSION_NAME${NC}"
|
|
exit 1
|
|
fi
|
|
;;
|
|
tasks|t)
|
|
show_tasks
|
|
;;
|
|
review|r)
|
|
review_tasks
|
|
;;
|
|
resume)
|
|
resume_task "${2:-}"
|
|
;;
|
|
kill|k)
|
|
kill_session "${2:-}"
|
|
;;
|
|
new|n)
|
|
local session_name="$(generate_session_name)-$(date +%s)"
|
|
create_session "$(pwd)" "$session_name"
|
|
;;
|
|
help|-h|--help)
|
|
show_help
|
|
;;
|
|
*)
|
|
echo -e "${RED}Unknown command:${NC} $cmd"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|