Setup wizard improvments.

This commit is contained in:
Aaron D. Lee
2025-12-15 21:11:15 -05:00
parent a257c07c67
commit 9d632c16ed
7 changed files with 641 additions and 40 deletions

View File

@@ -11,14 +11,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Core Features
- **Interactive Setup Wizard** (`setup-wizard.sh`) - Beautiful TUI installer using `gum` with fallback to basic prompts
- **Dynamic MOTD** (`motd.zsh`) - Compact system info on shell start (uptime, CPU, memory, docker, git status)
- **Command Palette** (`command-palette.zsh`) - Raycast-style fuzzy launcher triggered by Ctrl+Space or Ctrl+P
- **Smart Suggestions** (`smart-suggest.zsh`) - Typo correction for 100+ common mistakes + alias recommendations
- **Shell Analytics** (`shell-stats.sh`) - Dashboard showing command usage, suggestions, and activity heatmap
- **Secrets Vault** (`vault.sh`) - Encrypted storage for API keys using age/gpg
- **Password Manager Integration** (`password-manager.zsh`) - Unified CLI for 1Password, LastPass, Bitwarden
- **Dotfiles Sync** (`dotfiles-sync.sh`) - Multi-machine synchronization with watch mode
- **Dotfiles Doctor** (`dotfiles-doctor.sh`) - Health checker with auto-fix capability
- **Version Tracking** (`dotfiles-version.sh`) - Compare local vs remote versions
#### Password Manager Support
- 1Password CLI (`op`) installation and integration
- LastPass CLI (`lpass`) installation and integration
- Bitwarden CLI (`bw`) installation and integration
- Unified `pw` command with fuzzy search support
#### Configuration
- Centralized `dotfiles.conf` for all settings
- Git identity configuration (generated, not hardcoded)

View File

@@ -15,11 +15,13 @@ Personal configuration files for a fast, consistent dev environment across Linux
| Feature | Description |
|---------|-------------|
| **Setup Wizard** | Beautiful TUI installer with feature selection |
| **Dynamic MOTD** | System info on shell start (uptime, CPU, memory, updates) |
| **Zsh Theme** | Git status, command timer, root detection |
| **Command Palette** | Raycast-style fuzzy launcher (Ctrl+Space) |
| **Smart Suggestions** | Typo correction + alias recommendations |
| **Shell Analytics** | Track command usage, get insights |
| **Secrets Vault** | Encrypted storage for API keys |
| **Password Managers** | Unified CLI for 1Password, LastPass, Bitwarden |
| **Dotfiles Sync** | Auto-sync across machines |
| **Espanso** | 100+ text expansion snippets |
| **Snapper** | Btrfs snapshot helpers (Arch/CachyOS) |
@@ -184,6 +186,45 @@ eval $(vault shell) # Load all secrets
Uses `age` or `gpg` encryption. Secrets auto-load on shell start.
## 🔑 Password Manager Integration
Unified CLI for 1Password, LastPass, and Bitwarden:
```bash
pw list # List all items
pw get github # Get password
pw get github username # Get specific field
pw otp github # Get TOTP code
pw copy aws # Copy password to clipboard
pw search mail # Search items
pwf # Fuzzy search + copy (requires fzf)
```
Supports:
- **1Password** (`op`) - `INSTALL_1PASSWORD="true"`
- **LastPass** (`lpass`) - `INSTALL_LASTPASS="true"`
- **Bitwarden** (`bw`) - `INSTALL_BITWARDEN="true"`
## 🖥️ Dynamic MOTD
System info displayed on shell start:
```
┌──────────────────────────────────────────────────────────────┐
│ ✦ alee@battlestation Mon Dec 15 14:30│
├──────────────────────────────────────────────────────────────┤
│ ▲ up:4d 7h ◆ cpu:12% ◇ mem:8.2/32G ⊡ 234G free │
├──────────────────────────────────────────────────────────────┤
│ ◉4 containers ⎇2 dirty ↑3 updates ●dotfiles:✓ │
└──────────────────────────────────────────────────────────────┘
```
Configure in `dotfiles.conf`:
```bash
ENABLE_MOTD="true"
MOTD_STYLE="compact" # compact, mini, or off
```
## 🔄 Dotfiles Sync
Keep dotfiles synchronized across machines:

View File

@@ -312,19 +312,70 @@ step_features() {
wizard_header "📦 Feature Selection"
echo "Select which features to install."
echo "(Use space to select, enter to confirm)"
echo
local features=(
"zsh-plugins: Autosuggestions + Syntax Highlighting"
"fzf: Fuzzy finder for files and history"
"bat: Better cat with syntax highlighting"
"eza: Modern ls replacement with icons"
"espanso: Text expander (100+ snippets)"
"vault: Encrypted secrets storage"
"sync: Auto-sync dotfiles across machines"
"zsh-plugins"
"fzf"
"bat"
"eza"
"espanso"
"vault"
"motd"
"1password"
"lastpass"
"bitwarden"
)
WIZARD_FEATURES=$(wizard_multichoose "Select features to install:" "${features[@]}")
local descriptions=(
"Autosuggestions + Syntax Highlighting"
"Fuzzy finder for files and history"
"Better cat with syntax highlighting"
"Modern ls replacement"
"Text expander (100+ snippets)"
"Encrypted secrets storage"
"Dynamic system info on startup"
"1Password CLI integration"
"LastPass CLI integration"
"Bitwarden CLI integration"
)
if [[ "$HAS_GUM" == true ]]; then
# Build display options
local display_opts=()
for i in "${!features[@]}"; do
display_opts+=("${features[$i]}: ${descriptions[$i]}")
done
WIZARD_FEATURES=$(printf '%s\n' "${display_opts[@]}" | gum choose --no-limit --height=15 \
--selected="zsh-plugins: Autosuggestions + Syntax Highlighting,fzf: Fuzzy finder for files and history,vault: Encrypted secrets storage,motd: Dynamic system info on startup")
else
echo "Available features:"
for i in "${!features[@]}"; do
echo " $((i+1)). ${features[$i]}: ${descriptions[$i]}"
done
echo
echo "Enter numbers separated by commas (e.g., 1,2,3) or 'all':"
read -p "> " choices
choices=${choices:-"1,2,5,7"}
WIZARD_FEATURES=""
if [[ "$choices" == "all" ]]; then
for i in "${!features[@]}"; do
WIZARD_FEATURES+="${features[$i]}: ${descriptions[$i]}"$'\n'
done
else
IFS=',' read -ra selected <<< "$choices"
for idx in "${selected[@]}"; do
idx=$(echo "$idx" | tr -d ' ')
local i=$((idx - 1))
if [[ $i -ge 0 && $i -lt ${#features[@]} ]]; then
WIZARD_FEATURES+="${features[$i]}: ${descriptions[$i]}"$'\n'
fi
done
fi
fi
echo
echo -e "${GREEN}${NC} Features selected"
@@ -405,12 +456,27 @@ step_review() {
echo "Please review your configuration:"
echo
# Format features list - extract just the short names
local features_short=""
local features_list=""
while IFS= read -r feature; do
[[ -z "$feature" ]] && continue
local short_name="${feature%%:*}"
if [[ -z "$features_short" ]]; then
features_short="$short_name"
else
features_short="$features_short, $short_name"
fi
features_list="${features_list}${feature}\n"
done <<< "$WIZARD_FEATURES"
if [[ "$HAS_GUM" == true ]]; then
gum style \
--border normal \
--border-foreground 240 \
--padding "1 2" \
--margin "0 2" \
--width 60 \
"Identity:" \
" Name: $WIZARD_NAME" \
" Email: $WIZARD_EMAIL" \
@@ -420,9 +486,13 @@ step_review() {
" Branch: $WIZARD_GIT_BRANCH" \
" Editor: $WIZARD_GIT_EDITOR" \
"" \
"Theme: $WIZARD_THEME" \
"" \
"Features: $(echo "$WIZARD_FEATURES" | tr '\n' ', ' | sed 's/,$//')"
"Theme: $WIZARD_THEME"
echo
echo -e "${CYAN}Selected Features:${NC}"
echo "$WIZARD_FEATURES" | while IFS= read -r feature; do
[[ -n "$feature" ]] && echo -e " ${GREEN}${NC} ${feature%%:*}"
done
else
echo " Identity:"
echo " Name: $WIZARD_NAME"
@@ -435,7 +505,10 @@ step_review() {
echo
echo " Theme: $WIZARD_THEME"
echo
echo " Features: $(echo "$WIZARD_FEATURES" | tr '\n' ', ' | sed 's/,$//')"
echo " Features:"
echo "$WIZARD_FEATURES" | while IFS= read -r feature; do
[[ -n "$feature" ]] && echo "${feature%%:*}"
done
fi
echo
@@ -510,6 +583,11 @@ step_complete() {
}
generate_config() {
# Helper to check if feature is selected
_has_feature() {
echo "$WIZARD_FEATURES" | grep -qi "^$1:" || echo "$WIZARD_FEATURES" | grep -qi "^$1$"
}
# Generate dotfiles.conf with wizard selections
cat > "$DOTFILES_DIR/dotfiles.conf.wizard" << EOF
# ============================================================================
@@ -532,21 +610,30 @@ GIT_CREDENTIAL_HELPER="${WIZARD_GIT_CRED}"
# --- Feature Toggles ---
INSTALL_DEPS="${WIZARD_INSTALL_DEPS}"
INSTALL_ZSH_PLUGINS="$(echo "$WIZARD_FEATURES" | grep -q "zsh-plugins" && echo "true" || echo "false")"
INSTALL_FZF="$(echo "$WIZARD_FEATURES" | grep -q "fzf" && echo "true" || echo "false")"
INSTALL_BAT="$(echo "$WIZARD_FEATURES" | grep -q "bat" && echo "true" || echo "false")"
INSTALL_EZA="$(echo "$WIZARD_FEATURES" | grep -q "eza" && echo "true" || echo "false")"
INSTALL_ESPANSO="$(echo "$WIZARD_FEATURES" | grep -q "espanso" && echo "true" || echo "false")"
INSTALL_ZSH_PLUGINS="$(echo "$WIZARD_FEATURES" | grep -qi "zsh-plugins" && echo "true" || echo "false")"
INSTALL_FZF="$(echo "$WIZARD_FEATURES" | grep -qi "fzf" && echo "true" || echo "false")"
INSTALL_BAT="$(echo "$WIZARD_FEATURES" | grep -qi "bat" && echo "true" || echo "false")"
INSTALL_EZA="$(echo "$WIZARD_FEATURES" | grep -qi "eza" && echo "true" || echo "false")"
INSTALL_ESPANSO="$(echo "$WIZARD_FEATURES" | grep -qi "espanso" && echo "true" || echo "false")"
SET_ZSH_DEFAULT="${WIZARD_SET_DEFAULT_SHELL}"
# --- Password Manager Integration ---
INSTALL_1PASSWORD="$(echo "$WIZARD_FEATURES" | grep -qi "1password" && echo "true" || echo "false")"
INSTALL_LASTPASS="$(echo "$WIZARD_FEATURES" | grep -qi "lastpass" && echo "true" || echo "false")"
INSTALL_BITWARDEN="$(echo "$WIZARD_FEATURES" | grep -qi "bitwarden" && echo "true" || echo "false")"
# --- MOTD ---
ENABLE_MOTD="$(echo "$WIZARD_FEATURES" | grep -qi "motd" && echo "true" || echo "false")"
MOTD_STYLE="compact"
# --- Theme ---
ZSH_THEME_NAME="${WIZARD_THEME}"
# --- Advanced Features ---
ENABLE_SHELL_ANALYTICS="${WIZARD_ANALYTICS}"
ENABLE_SMART_SUGGESTIONS="${WIZARD_SUGGESTIONS}"
ENABLE_VAULT="$(echo "$WIZARD_FEATURES" | grep -q "vault" && echo "true" || echo "false")"
ENABLE_SYNC="$(echo "$WIZARD_FEATURES" | grep -q "sync" && echo "true" || echo "false")"
ENABLE_VAULT="$(echo "$WIZARD_FEATURES" | grep -qi "vault" && echo "true" || echo "false")"
ENABLE_COMMAND_PALETTE="true"
EOF
}

View File

@@ -295,6 +295,89 @@ dotfiles-sync.sh --log # Show sync history
**Auto-check:** On shell start, you'll be notified of available updates.
### Password Manager Integration
Unified interface for 1Password, LastPass, and Bitwarden:
```bash
pw list # List all items
pw get <item> # Get password
pw get <item> username # Get specific field
pw otp <item> # Get TOTP/2FA code
pw copy <item> # Copy password to clipboard
pw search <query> # Search items
pw provider # Show which CLI is being used
pw lock # Lock session
```
**Interactive selection (requires fzf):**
```bash
pwf # Fuzzy search items, copy password
pwof # Fuzzy search items, copy OTP
```
**Configuration in `dotfiles.conf`:**
```bash
INSTALL_1PASSWORD="ask" # Install 1Password CLI (op)
INSTALL_LASTPASS="ask" # Install LastPass CLI (lpass)
INSTALL_BITWARDEN="ask" # Install Bitwarden CLI (bw)
PASSWORD_MANAGER="auto" # auto, 1password, lastpass, or bitwarden
```
**Manual CLI installation:**
```bash
# 1Password
brew install --cask 1password-cli # macOS
# See: https://1password.com/downloads/command-line/
# LastPass
brew install lastpass-cli # macOS
sudo apt install lastpass-cli # Ubuntu
# Bitwarden
brew install bitwarden-cli # macOS
npm install -g @bitwarden/cli # Any platform
```
### Dynamic MOTD
System information displayed on shell start:
```
┌──────────────────────────────────────────────────────────────┐
│ ✦ user@hostname Mon Dec 15 14:30│
├──────────────────────────────────────────────────────────────┤
│ ▲ up:4d 7h ◆ cpu:12% ◇ mem:8.2/32G ⊡ 234G free │
├──────────────────────────────────────────────────────────────┤
│ ◉4 containers ⎇2 dirty ↑3 updates ●dotfiles:✓ │
└──────────────────────────────────────────────────────────────┘
```
**Shows:**
- Uptime, CPU usage, memory, disk space
- Docker containers running
- Git repos with uncommitted changes
- Available system updates
- Dotfiles sync status
**Configuration:**
```bash
ENABLE_MOTD="true" # Enable MOTD
MOTD_STYLE="compact" # compact (box), mini (single line), or off
```
**Manual commands:**
```bash
show_motd # Show compact MOTD
show_motd_mini # Show single-line MOTD
motd # Alias for show_motd
```
---
## Customization

View File

@@ -44,6 +44,16 @@ INSTALL_BAT="ask"
INSTALL_EZA="ask"
SET_ZSH_DEFAULT="ask"
# --- Password Manager Integration ---
# Enables CLI plugins for password managers (requires respective CLI tools)
INSTALL_1PASSWORD="ask" # 1Password CLI (op) - https://1password.com/downloads/command-line/
INSTALL_LASTPASS="ask" # LastPass CLI (lpass) - https://github.com/lastpass/lastpass-cli
INSTALL_BITWARDEN="ask" # Bitwarden CLI (bw) - https://bitwarden.com/help/cli/
# --- MOTD (Message of the Day) ---
ENABLE_MOTD="true" # Show system info on shell start
MOTD_STYLE="compact" # "compact" (box), "mini" (single line), or "off"
# --- Theme Settings ---
ZSH_THEME_NAME="adlee"
THEME_TIMER_THRESHOLD=10 # Show elapsed time for commands longer than N seconds

View File

@@ -60,7 +60,7 @@ export EDITOR='vim'
export VISUAL='vim'
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export PATH="$HOME/.local/bin:$HOME/.dotfiles/bin:$PATH"
export PATH="$HOME/.local/bin:$PATH"
# --- Aliases ---
@@ -210,25 +210,14 @@ bindkey "^[[3~" delete-char # Delete
# --- Custom Widgets ---
reload-zshrc() {
echo -n "Re-sourcing ~/.zshrc ... "
source ~/.zshrc
echo "Done."
_adlee_build_prompt
zle reset-prompt
}
zle -N reload-zshrc
bindkey "^X@s^[^R" reload-zshrc # Ctrl+Super+Alt+R
grab-fastfetch() {
echo "fastfetch"
fastfetch
_adlee_build_prompt
zle reset-prompt
}
zle -N grab-fastfetch
bindkey "^X@s^[^F" grab-fastfetch # Ctrl+Super+Alt+F
# Alt+R to reload zsh config
reload-zsh() {
source ~/.zshrc
echo "✓ zsh configuration reloaded"
zle reset-prompt
}
zle -N reload-zsh
bindkey "^[r" reload-zsh
# Alt+G to show git status
git-status-widget() {
@@ -330,7 +319,7 @@ fi
if [[ "${DOTFILES_AUTO_SYNC_CHECK:-true}" == "true" ]]; then
# Quick async check for dotfiles updates
($HOME/.dotfiles/bin/dotfiles-sync.sh --auto 2>/dev/null &)
(dotfiles-sync.sh --auto 2>/dev/null &)
fi
# --- Vault Integration ---
@@ -340,6 +329,26 @@ if command -v vault.sh &>/dev/null && [[ -f "$HOME/.dotfiles/vault/secrets.enc"
eval "$(vault.sh shell 2>/dev/null)" || true
fi
# --- Password Manager Integration ---
if [[ -f "$HOME/.dotfiles/zsh/functions/password-manager.zsh" ]]; then
source "$HOME/.dotfiles/zsh/functions/password-manager.zsh"
fi
# --- MOTD (Message of the Day) ---
if [[ -f "$HOME/.dotfiles/zsh/functions/motd.zsh" ]]; then
source "$HOME/.dotfiles/zsh/functions/motd.zsh"
# Show MOTD based on style setting
case "${MOTD_STYLE:-compact}" in
compact) show_motd ;;
mini) show_motd_mini ;;
off|false|no) ;;
*) show_motd ;;
esac
fi
# --- Local Configuration ---
[ -f ~/.zshrc.local ] && source ~/.zshrc.local

View File

@@ -0,0 +1,363 @@
# ============================================================================
# Password Manager Integration for Zsh
# ============================================================================
# Unified interface for 1Password, LastPass, and Bitwarden CLIs
#
# Usage:
# pw list # List items (auto-detects provider)
# pw get <item> # Get password
# pw otp <item> # Get OTP/TOTP code
# pw search <query> # Search items
# pw copy <item> # Copy password to clipboard
#
# Supported: 1Password (op), LastPass (lpass), Bitwarden (bw)
#
# Add to .zshrc:
# source ~/.dotfiles/zsh/functions/password-manager.zsh
# ============================================================================
# ============================================================================
# Configuration
# ============================================================================
# Auto-detect preferred password manager (override in dotfiles.conf)
typeset -g PASSWORD_MANAGER="${PASSWORD_MANAGER:-auto}"
# Session timeout (seconds) - for managers that support it
typeset -g PW_SESSION_TIMEOUT=1800
# ============================================================================
# Provider Detection
# ============================================================================
_pw_detect_provider() {
if [[ "$PASSWORD_MANAGER" != "auto" ]]; then
echo "$PASSWORD_MANAGER"
return
fi
# Auto-detect based on installed CLI
if command -v op &>/dev/null; then
echo "1password"
elif command -v lpass &>/dev/null; then
echo "lastpass"
elif command -v bw &>/dev/null; then
echo "bitwarden"
else
echo "none"
fi
}
_pw_check_provider() {
local provider=$(_pw_detect_provider)
if [[ "$provider" == "none" ]]; then
echo "Error: No password manager CLI found" >&2
echo "Install one of: op (1Password), lpass (LastPass), bw (Bitwarden)" >&2
return 1
fi
echo "$provider"
}
# ============================================================================
# 1Password Functions
# ============================================================================
_1p_ensure_session() {
# Check if signed in
if ! op account list &>/dev/null 2>&1; then
echo "Signing into 1Password..." >&2
eval $(op signin)
fi
}
_1p_list() {
_1p_ensure_session
op item list --format=json | jq -r '.[] | "\(.title)\t\(.category)"' 2>/dev/null || \
op item list 2>/dev/null
}
_1p_get() {
local item="$1"
local field="${2:-password}"
_1p_ensure_session
op item get "$item" --fields "$field" 2>/dev/null
}
_1p_otp() {
local item="$1"
_1p_ensure_session
op item get "$item" --otp 2>/dev/null
}
_1p_search() {
local query="$1"
_1p_ensure_session
op item list --format=json | jq -r ".[] | select(.title | test(\"$query\"; \"i\")) | .title" 2>/dev/null
}
# ============================================================================
# LastPass Functions
# ============================================================================
_lp_ensure_session() {
if ! lpass status -q 2>/dev/null; then
echo "Signing into LastPass..." >&2
lpass login "${LASTPASS_EMAIL:-}"
fi
}
_lp_list() {
_lp_ensure_session
lpass ls --format="%an\t%ag" 2>/dev/null
}
_lp_get() {
local item="$1"
local field="${2:-password}"
_lp_ensure_session
case "$field" in
password) lpass show --password "$item" 2>/dev/null ;;
username) lpass show --username "$item" 2>/dev/null ;;
url) lpass show --url "$item" 2>/dev/null ;;
notes) lpass show --notes "$item" 2>/dev/null ;;
*) lpass show --field="$field" "$item" 2>/dev/null ;;
esac
}
_lp_otp() {
local item="$1"
_lp_ensure_session
lpass show --otp "$item" 2>/dev/null
}
_lp_search() {
local query="$1"
_lp_ensure_session
lpass ls 2>/dev/null | grep -i "$query"
}
# ============================================================================
# Bitwarden Functions
# ============================================================================
_bw_ensure_session() {
# Check if locked
if [[ -z "$BW_SESSION" ]]; then
if bw status 2>/dev/null | grep -q '"status":"locked"'; then
echo "Unlocking Bitwarden..." >&2
export BW_SESSION=$(bw unlock --raw)
elif bw status 2>/dev/null | grep -q '"status":"unauthenticated"'; then
echo "Signing into Bitwarden..." >&2
bw login
export BW_SESSION=$(bw unlock --raw)
fi
fi
}
_bw_list() {
_bw_ensure_session
bw list items --session "$BW_SESSION" 2>/dev/null | jq -r '.[] | "\(.name)\t\(.type)"'
}
_bw_get() {
local item="$1"
local field="${2:-password}"
_bw_ensure_session
local item_json=$(bw get item "$item" --session "$BW_SESSION" 2>/dev/null)
case "$field" in
password) echo "$item_json" | jq -r '.login.password // empty' ;;
username) echo "$item_json" | jq -r '.login.username // empty' ;;
url) echo "$item_json" | jq -r '.login.uris[0].uri // empty' ;;
notes) echo "$item_json" | jq -r '.notes // empty' ;;
*) echo "$item_json" | jq -r ".fields[]? | select(.name==\"$field\") | .value" ;;
esac
}
_bw_otp() {
local item="$1"
_bw_ensure_session
bw get totp "$item" --session "$BW_SESSION" 2>/dev/null
}
_bw_search() {
local query="$1"
_bw_ensure_session
bw list items --search "$query" --session "$BW_SESSION" 2>/dev/null | jq -r '.[].name'
}
# ============================================================================
# Unified Interface
# ============================================================================
pw() {
local cmd="${1:-help}"
shift
local provider=$(_pw_check_provider) || return 1
case "$cmd" in
list|ls|l)
case "$provider" in
1password) _1p_list ;;
lastpass) _lp_list ;;
bitwarden) _bw_list ;;
esac
;;
get|g|show)
local item="$1"
local field="${2:-password}"
[[ -z "$item" ]] && { echo "Usage: pw get <item> [field]"; return 1; }
case "$provider" in
1password) _1p_get "$item" "$field" ;;
lastpass) _lp_get "$item" "$field" ;;
bitwarden) _bw_get "$item" "$field" ;;
esac
;;
otp|totp|2fa)
local item="$1"
[[ -z "$item" ]] && { echo "Usage: pw otp <item>"; return 1; }
case "$provider" in
1password) _1p_otp "$item" ;;
lastpass) _lp_otp "$item" ;;
bitwarden) _bw_otp "$item" ;;
esac
;;
search|find|s)
local query="$1"
[[ -z "$query" ]] && { echo "Usage: pw search <query>"; return 1; }
case "$provider" in
1password) _1p_search "$query" ;;
lastpass) _lp_search "$query" ;;
bitwarden) _bw_search "$query" ;;
esac
;;
copy|cp|c)
local item="$1"
local field="${2:-password}"
[[ -z "$item" ]] && { echo "Usage: pw copy <item> [field]"; return 1; }
local value
case "$provider" in
1password) value=$(_1p_get "$item" "$field") ;;
lastpass) value=$(_lp_get "$item" "$field") ;;
bitwarden) value=$(_bw_get "$item" "$field") ;;
esac
if [[ -n "$value" ]]; then
echo -n "$value" | pbcopy 2>/dev/null || \
echo -n "$value" | xclip -selection clipboard 2>/dev/null || \
echo -n "$value" | xsel --clipboard 2>/dev/null || \
{ echo "Could not copy to clipboard"; return 1; }
echo "Copied to clipboard"
else
echo "Item not found or empty"
return 1
fi
;;
provider|which)
echo "Using: $provider"
case "$provider" in
1password) op --version 2>/dev/null ;;
lastpass) lpass --version 2>/dev/null ;;
bitwarden) bw --version 2>/dev/null ;;
esac
;;
lock)
case "$provider" in
1password) op signout 2>/dev/null ;;
lastpass) lpass logout -f 2>/dev/null ;;
bitwarden) bw lock 2>/dev/null; unset BW_SESSION ;;
esac
echo "Session locked"
;;
help|--help|-h|*)
echo "Password Manager CLI (using: $provider)"
echo
echo "Usage: pw <command> [args]"
echo
echo "Commands:"
echo " list List all items"
echo " get <item> [field] Get field (default: password)"
echo " otp <item> Get OTP/TOTP code"
echo " search <query> Search items"
echo " copy <item> [field] Copy to clipboard"
echo " provider Show current provider"
echo " lock Lock/sign out"
echo " help Show this help"
echo
echo "Fields: password, username, url, notes, or custom field name"
echo
echo "Examples:"
echo " pw get github"
echo " pw get github username"
echo " pw otp github"
echo " pw copy aws"
echo " pw search mail"
;;
esac
}
# ============================================================================
# Aliases
# ============================================================================
alias pwl='pw list'
alias pwg='pw get'
alias pwc='pw copy'
alias pws='pw search'
# ============================================================================
# FZF Integration (if available)
# ============================================================================
if command -v fzf &>/dev/null; then
# Interactive password selection
pwf() {
local provider=$(_pw_check_provider) || return 1
local item
case "$provider" in
1password) item=$(_1p_list | fzf --height=40% --reverse | cut -f1) ;;
lastpass) item=$(_lp_list | fzf --height=40% --reverse | cut -f1) ;;
bitwarden) item=$(_bw_list | fzf --height=40% --reverse | cut -f1) ;;
esac
[[ -n "$item" ]] && pw copy "$item"
}
# Interactive OTP selection
pwof() {
local provider=$(_pw_check_provider) || return 1
local item
case "$provider" in
1password) item=$(_1p_list | fzf --height=40% --reverse | cut -f1) ;;
lastpass) item=$(_lp_list | fzf --height=40% --reverse | cut -f1) ;;
bitwarden) item=$(_bw_list | fzf --height=40% --reverse | cut -f1) ;;
esac
if [[ -n "$item" ]]; then
local otp=$(pw otp "$item")
if [[ -n "$otp" ]]; then
echo -n "$otp" | pbcopy 2>/dev/null || \
echo -n "$otp" | xclip -selection clipboard 2>/dev/null
echo "OTP copied: $otp"
fi
fi
}
fi