|
| 1 | +--- |
| 2 | +title: "When Setup Scripts Meet Reality: A Tale of TTY and API Keys" |
| 3 | +date: 2026-01-30 |
| 4 | +tags: [api-keys, automation, bash, devops, debugging] |
| 5 | +excerpt: "What happens when your perfectly crafted setup script meets the harsh reality of non-interactive environments? A debugging journey through TTY detection and API key configuration." |
| 6 | +--- |
| 7 | + |
| 8 | +# When Setup Scripts Meet Reality: A Tale of TTY and API Keys |
| 9 | + |
| 10 | +Every developer has been there: you craft what seems like the perfect setup script, test it locally, and everything works beautifully. Then you deploy it to the real world, and suddenly your users are staring at broken installations with no clear explanation. Today, I'm sharing one of those humbling moments from my work on the Vibe Coding blog generation pipeline. |
| 11 | + |
| 12 | +## The Mission: Streamline API Key Configuration |
| 13 | + |
| 14 | +I was working on integrating Anthropic's Claude API into our automated blog generation system. The goal was simple: make the installation process seamless by automatically prompting users for their API key during plugin installation. What could go wrong? |
| 15 | + |
| 16 | +## The Perfect Plan (That Wasn't) |
| 17 | + |
| 18 | +My setup script looked elegant in its simplicity: |
| 19 | + |
| 20 | +```bash |
| 21 | +# setup-api-key.sh |
| 22 | +if [ -t 0 ]; then |
| 23 | + echo "Setting up Anthropic API key..." |
| 24 | + read -p "Enter your API key: " api_key |
| 25 | + # ... save to config |
| 26 | +else |
| 27 | + echo "Non-interactive mode detected, skipping setup" |
| 28 | +fi |
| 29 | +``` |
| 30 | + |
| 31 | +The TTY check (`[ -t 0 ]`) seemed like the right approach—detect if we're running interactively and prompt accordingly. During local testing, everything worked flawlessly. |
| 32 | + |
| 33 | +## Reality Strikes: The Non-Interactive Wall |
| 34 | + |
| 35 | +When users started installing the plugin via `claude plugin install`, something strange happened. The setup hook would run, but users never saw the API key prompt. The script would silently skip the interactive setup, leaving users with a half-configured system. |
| 36 | + |
| 37 | +The culprit? **Plugin installation hooks run in non-interactive mode by default.** |
| 38 | + |
| 39 | +Even though users were running the install command from their terminal, the hook execution environment didn't have a TTY attached. My clever TTY detection was working exactly as designed—it was just detecting the wrong thing. |
| 40 | + |
| 41 | +## The Debugging Journey |
| 42 | + |
| 43 | +Here's what the investigation looked like: |
| 44 | + |
| 45 | +```bash |
| 46 | +# Testing the current status |
| 47 | +$ python3 blog_gen.py --status |
| 48 | +❌ API key not configured |
| 49 | + |
| 50 | +# Checking the config location |
| 51 | +$ ls ~/.config/letter-for-my-future-self/ |
| 52 | +# (directory didn't exist) |
| 53 | + |
| 54 | +# The moment of realization |
| 55 | +$ echo "test" | ./scripts/setup-api-key.sh |
| 56 | +# Non-interactive mode detected, skipping setup |
| 57 | +``` |
| 58 | + |
| 59 | +The script was behaving correctly according to its logic, but the user experience was broken. Users had no clear path forward after installation. |
| 60 | + |
| 61 | +## The Fix: Manual Configuration with a Plan |
| 62 | + |
| 63 | +For the immediate problem, I configured the API key manually: |
| 64 | + |
| 65 | +```bash |
| 66 | +# Create the config directory |
| 67 | +mkdir -p ~/.config/letter-for-my-future-self |
| 68 | + |
| 69 | +# Write the configuration |
| 70 | +cat > ~/.config/letter-for-my-future-self/config.json << EOF |
| 71 | +{ |
| 72 | + "anthropic_api_key": "sk-ant-api03-[YOUR_KEY_HERE]" |
| 73 | +} |
| 74 | +EOF |
| 75 | + |
| 76 | +# Secure the config file |
| 77 | +chmod 600 ~/.config/letter-for-my-future-self/config.json |
| 78 | +``` |
| 79 | + |
| 80 | +Then verified it worked: |
| 81 | + |
| 82 | +```bash |
| 83 | +$ python3 blog_gen.py --status |
| 84 | +✅ API key configured (ends with: ...HwAA) |
| 85 | +✅ Blog generation ready |
| 86 | +``` |
| 87 | + |
| 88 | +Success! But this was clearly a band-aid solution. |
| 89 | + |
| 90 | +## Lessons Learned: Better UX for Non-Interactive Installs |
| 91 | + |
| 92 | +This experience highlighted several key insights about building robust installation experiences: |
| 93 | + |
| 94 | +### 1. **Assume Non-Interactive by Default** |
| 95 | +Plugin hooks, CI/CD pipelines, and automated deployments often run without TTY access. Design your setup scripts with this as the primary use case, not the exception. |
| 96 | + |
| 97 | +### 2. **Provide Clear Post-Install Instructions** |
| 98 | +When interactive setup isn't possible, give users an obvious next step: |
| 99 | + |
| 100 | +```bash |
| 101 | +echo "⚠️ API key setup required!" |
| 102 | +echo "Run: claude setup api-key" |
| 103 | +echo "Or manually edit: ~/.config/letter-for-my-future-self/config.json" |
| 104 | +``` |
| 105 | + |
| 106 | +### 3. **Offer Multiple Configuration Paths** |
| 107 | +Consider supporting: |
| 108 | +- Interactive prompts (when possible) |
| 109 | +- Environment variables |
| 110 | +- Config files |
| 111 | +- Command-line flags |
| 112 | +- In-session setup commands |
| 113 | + |
| 114 | +### 4. **Test in Real Environments** |
| 115 | +Local testing with `./script.sh` doesn't replicate how your script runs in production. Test through the actual installation mechanism your users will encounter. |
| 116 | + |
| 117 | +## The Road Ahead |
| 118 | + |
| 119 | +This debugging session revealed several improvements for the next iteration: |
| 120 | + |
| 121 | +1. **Enhanced setup script** that detects non-interactive mode and provides helpful next steps |
| 122 | +2. **In-session configuration** via a `/letter-setup` command for users who need to configure keys later |
| 123 | +3. **Better error messaging** that guides users toward successful configuration |
| 124 | +4. **Documentation updates** with clear setup instructions for different environments |
| 125 | + |
| 126 | +## Final Thoughts |
| 127 | + |
| 128 | +Sometimes the most educational bugs aren't the complex algorithmic puzzles—they're the simple assumptions that don't hold up in the real world. This TTY detection issue was a good reminder that the gap between "works on my machine" and "works for users" often lies in the environmental details we take for granted. |
| 129 | + |
| 130 | +The next time you're writing a setup script, ask yourself: "What happens when this runs in a Docker container? In a GitHub Action? Through a package manager hook?" Your future self (and your users) will thank you. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +*Have you encountered similar installation gotchas? Share your debugging war stories—I'd love to hear how you've navigated the gap between development and deployment environments.* |
0 commit comments