Portable Tools
Build cross-device tools without hardcoding paths or account names
Build cross-device tools without hardcoding paths or account names
Real data. Real impact.
Growing
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Methodology for building tools that work across different devices, naming schemes, and configurations. Based on lessons from OAuth refresher debugging session (2026-01-23).
Never assume your device is the only device.
Your local setup is just one of many possible configurations. Build for the general case, not the specific instance.
Before writing any code that reads configuration, data, or credentials:
Ask:
Example from OAuth refresher:
Action: List variables, make them configurable or auto-discoverable
Before claiming success:
Require:
Example from OAuth refresher:
BEFORE: - Access Token: POp5z1fi...eSN9VAAA - Expires: 1769189639000AFTER:
Access Token: 01v0RrFG...eOE9QAA ✅ Different
Expires: 1769190268000 ✅ Extended
Action: Always show data transformation with real values
Before pushing to production:
Test:
Example from OAuth refresher:
keychain_account: "wrong-name" → Fallback should workAction: Test failure modes, not just happy path
❌ Wrong:
# Ambiguous - returns first match security find-generic-password -s "Service" -w
✅ Correct:
# Explicit - returns specific entry security find-generic-password -s "Service" -a "account" -w
Rule: If a command can be ambiguous, make it explicit.
❌ Wrong:
DATA=$(read_config) USE_VALUE="$DATA" # Hope it's valid
✅ Correct:
DATA=$(read_config) if ! validate_structure "$DATA"; then error "Invalid data structure" fi USE_VALUE="$DATA"
Rule: Never assume data has expected structure.
❌ Wrong:
ACCOUNT="claude" # Hardcoded
✅ Correct:
# Try configured → Try common → Error with help ACCOUNT="${CONFIG_ACCOUNT}" if ! has_data "$ACCOUNT"; then for fallback in "claude" "default" "oauth"; do if has_data "$fallback"; then ACCOUNT="$fallback" break fi done fi [[ -z "$ACCOUNT" ]] && error "No account found. Tried: ..."
Rule: Provide automatic fallbacks for common variations.
❌ Wrong:
[[ -z "$TOKEN" ]] && error "No token"
✅ Correct:
[[ -z "$TOKEN" ]] && error "No token foundChecked:
- Config: $CONFIG_FILE
- Field: $FIELD_NAME
- Expected: { "tokens": { "refresh": "..." } }
Verify with: cat $CONFIG_FILE | jq '.tokens' "
Rule: Error messages should help user diagnose and fix.
Don't ask: "Is it broken?"
Ask: "What exact values do you see? How many entries exist? Which one has the data?"
Example:
# Vague "Check keychain"Specific
"Run: security find-generic-password -l 'Service' | grep 'acct'" "Tell me: 1. How many entries 2. Which has tokens 3. Last modified"
Don't say: "It should work now"
Show: "Here's the BEFORE token (POp5z...), here's AFTER (01v0R...), they're different"
Template:
BEFORE: - Field1: <exact_value> - Field2: <exact_value>AFTER:
- Field1: <new_value> ✅ Changed
- Field2: <new_value> ✅ Changed
PROOF: Values are different
Don't think: "Works on my machine"
Think: "What if their setup differs in [X]?"
Checklist:
--dry-run or --test mode# Assumes single entry, no validation, no fallback KEYCHAIN_DATA=$(security find-generic-password -s "Service" -w) REFRESH_TOKEN=$(echo "$KEYCHAIN_DATA" | jq -r '.refreshToken') # Use token (hope it's valid)
Problems:
# Explicit account with validation and fallback validate_data() { echo "$1" | jq -e '.claudeAiOauth.refreshToken' > /dev/null 2>&1 }Try configured account
DATA=$(security find-generic-password -s "$SERVICE" -a "$ACCOUNT" -w 2>&1) if validate_data "$DATA"; then log "✓ Using account: $ACCOUNT" else log "⚠ Trying fallback accounts..." for fallback in "claude" "Claude Code" "default"; do DATA=$(security find-generic-password -s "$SERVICE" -a "$fallback" -w 2>&1) if validate_data "$DATA"; then ACCOUNT="$fallback" log "✓ Found data in: $fallback" break fi done fi
[[ -z "$DATA" ]] || ! validate_data "$DATA" && error "No valid data found Tried accounts: $ACCOUNT, claude, Claude Code, default Verify with: security find-generic-password -l '$SERVICE'"
REFRESH_TOKEN=$(echo "$DATA" | jq -r '.claudeAiOauth.refreshToken')
Improvements:
FILE="/Users/patrick/.config/app.json" # Hardcoded path
Fix: Use
$HOME, detect OS, or make configurable
TOKEN=$(cat config.json | jq -r '.token') # What if .token doesn't exist? Script continues with empty value
Fix: Validate before using
TOKEN=$(cat config.json | jq -r '.token // empty') [[ -z "$TOKEN" ]] && error "No token in config"
# If multiple entries exist, which one? ENTRY=$(find_entry "service")
Fix: Be explicit or enumerate all
ENTRY=$(find_entry "service" "account") # Specific # OR ALL=$(find_all_entries "service") for entry in $ALL; do validate_and_use "$entry" done
process_data || true # Ignore errors
Fix: Fail loudly with context
process_data || error "Failed to process Data: $DATA Expected: { ... } Check: command_to_verify"
Add to testing section:
## Cross-Device Testing - [ ] Test with different account names - [ ] Test with wrong config values - [ ] Test with missing data - [ ] Document fallback behavior
Add before publishing:
## Portability Check - [ ] No hardcoded paths (use $HOME, detect OS) - [ ] No hardcoded names (use config or fallback) - [ ] Validation on all inputs - [ ] Helpful errors for common issues
When building new skills:
Before writing code:
Mandatory patterns:
Testing:
Documentation:
A tool is portable when:
Test: Give it to someone with a different setup. If they need to ask you questions, the tool isn't portable yet.
This methodology emerged from debugging the OAuth refresher (2026-01-23):
Patrick's approach:
Result: Tool went from single-device/broken to universal/production-ready.
Key insight: The bugs weren't in the logic - they were in the assumptions.
Use when:
Apply:
Remember: Your device is just one case. Build for the general case.
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.