Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ source ~/.bashrc # or source ~/.zshrc
## Usage

```
tmux-ws create <name> [--detach|-d] Create and attach to a workspace
tmux-ws create <name> [--detach|-d] [--force|-f] Create and attach to a workspace
tmux-ws attach <name> Attach to a running workspace
tmux-ws list List available and running workspaces
tmux-ws kill <name> Kill a workspace session
tmux-ws edit <name> Open workspace config in $EDITOR
Expand Down
77 changes: 47 additions & 30 deletions bin/tmux-ws
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,36 @@ NC='\033[0m'

# --- Usage ---
usage() {
cat <<EOF
${BOLD}$SCRIPT_NAME${NC} — tmux workspace manager

${BOLD}Usage:${NC}
$SCRIPT_NAME create <name> [--detach|-d] Create and attach to a workspace
$SCRIPT_NAME list List available and running workspaces
$SCRIPT_NAME kill <name> Kill a workspace session
$SCRIPT_NAME edit <name> Open workspace config in \$EDITOR
$SCRIPT_NAME init Create config directory structure
$SCRIPT_NAME help Show this help

${BOLD}Options:${NC}
--detach, -d Create workspace without attaching
--force, -f Kill existing session before creating

${BOLD}Config:${NC}
$CONFIG_DIR/<name>/workspace.conf
$CONFIG_DIR/_templates/default.conf Base template (auto-inherited)

${BOLD}Config format:${NC}
# workspace.conf — EXECUTED as shell code. Do not untrusted sources.
_BASE="default"
DEFAULT_DIR="\$HOME/projects/myapp"
WINDOWS=(
"editor::vim"
"server::npm run dev"
"logs::tail -f /var/log/app.log"
"scratch::"
)
EOF
echo -e "${BOLD}$SCRIPT_NAME${NC} — tmux workspace manager"
echo -e ""
echo -e "${BOLD}Usage:${NC}"
echo -e " $SCRIPT_NAME create <name> [--detach|-d] Create and attach to a workspace"
echo -e " $SCRIPT_NAME attach <name> Attach to a running workspace"
echo -e " $SCRIPT_NAME list List available and running workspaces"
echo -e " $SCRIPT_NAME kill <name> Kill a workspace session"
echo -e " $SCRIPT_NAME edit <name> Open workspace config in \$EDITOR"
echo -e " $SCRIPT_NAME init Create config directory structure"
echo -e " $SCRIPT_NAME version Show version"
echo -e " $SCRIPT_NAME help Show this help"
echo -e ""
echo -e "${BOLD}Options:${NC}"
echo -e " --detach, -d Create workspace without attaching"
echo -e " --force, -f Kill existing session before creating"
echo -e ""
echo -e "${BOLD}Config:${NC}"
echo -e " $CONFIG_DIR/<name>/workspace.conf"
echo -e " $CONFIG_DIR/_templates/default.conf Base template (auto-inherited)"
echo -e ""
echo -e "${BOLD}Config format:${NC}"
echo -e " # workspace.conf — EXECUTED as shell code. Do not untrusted sources."
echo -e " _BASE=\"default\""
echo -e " DEFAULT_DIR=\"\$HOME/projects/myapp\""
echo -e " WINDOWS=("
echo -e " \"editor::vim\""
echo -e " \"server::npm run dev\""
echo -e " \"logs::tail -f /var/log/app.log\""
echo -e " \"scratch::\""
echo -e " )"
}

# --- Logging ---
Expand Down Expand Up @@ -345,6 +345,22 @@ cmd_edit() {
"${EDITOR:-vim}" "$config_file"
}

cmd_attach() {
local name="${1:-}"
if [[ -z "$name" ]]; then
error "Workspace name required"
exit 1
fi

if ! tmux has-session -t "$name" 2>/dev/null; then
error "Session '$name' is not running"
echo " Create it with: $SCRIPT_NAME create $name"
exit 1
fi

tmux attach -t "$name"
}

cmd_init() {
if [[ -d "$CONFIG_DIR" ]] && [[ "$(ls -A "$CONFIG_DIR" 2>/dev/null)" ]]; then
warn "Config directory already exists at $CONFIG_DIR"
Expand Down Expand Up @@ -416,6 +432,7 @@ main() {

case "$command" in
create) cmd_create "$@" ;;
attach) cmd_attach "$@" ;;
list) cmd_list ;;
kill) cmd_kill "$@" ;;
edit) cmd_edit "$@" ;;
Expand Down
24 changes: 24 additions & 0 deletions tests/test_tmux_ws.bats
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ EOF
tmux kill-session -t bad-format 2>/dev/null || true
tmux kill-session -t no-template 2>/dev/null || true
tmux kill-session -t fail-hook 2>/dev/null || true
tmux kill-session -t test-attach 2>/dev/null || true
}

teardown() {
Expand All @@ -104,6 +105,7 @@ teardown() {
tmux kill-session -t bad-format 2>/dev/null || true
tmux kill-session -t no-template 2>/dev/null || true
tmux kill-session -t fail-hook 2>/dev/null || true
tmux kill-session -t test-attach 2>/dev/null || true
rm -rf "$TEST_CONFIG_DIR"
rm -rf "$BATS_TEST_TMPDIR/test-workspace-dir"
rm -rf "$BATS_TEST_TMPDIR/myapp-dir"
Expand Down Expand Up @@ -272,6 +274,28 @@ teardown() {
[[ "${output}" == *"not running"* ]]
}

# --- Attach ---

@test "attach requires a name" {
run tmux-ws attach
[ "$status" -eq 1 ]
[[ "${output}" == *"Workspace name required"* ]]
}

@test "attach fails for nonexistent session" {
run tmux-ws attach nonexistent
[ "$status" -eq 1 ]
[[ "${output}" == *"not running"* ]]
}

@test "attach passes validation for running session" {
tmux new-session -d -s test-attach
# tmux attach requires a tty, so we can't test the actual attach.
# Verify the session exists (would pass the has-session check).
run tmux has-session -t test-attach
[ "$status" -eq 0 ]
}

# --- Edit ---

@test "edit requires a name" {
Expand Down
Loading