Claude
Skills
Sign in
Back

portable-tools

Included with Lifetime
$97 forever

Build cross-device tools without hardcoding paths or account names

General

What this skill does


# Portable Tools - Cross-Device Development Methodology

Methodology for building tools that work across different devices, naming schemes, and configurations. Based on lessons from OAuth refresher debugging session (2026-01-23).

## Core Principle

**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.

---

## The Three Questions (Before Writing Code)

### 1. "What varies between devices?"

Before writing any code that reads configuration, data, or credentials:

**Ask:**
- File paths? (macOS vs Linux, different home dirs)
- Account names? (user123 vs default vs oauth)
- Service names? (slight variations in spelling/capitalization)
- Data structure? (different versions, different formats)
- Environment? (different shells, different tools available)

**Example from OAuth refresher:**
- ❌ Assumed: Account is always "claude"
- ✅ Reality: Could be "claude", "Claude Code", "default", etc.

**Action:** List variables, make them configurable or auto-discoverable

---

### 2. "How do I prove this works?"

Before claiming success:

**Require:**
- Concrete BEFORE state (exact values)
- Concrete AFTER state (exact values)
- Proof they're different (side-by-side comparison)

**Example from OAuth refresher:**
```
BEFORE:
- Access Token: POp5z1fi...eSN9VAAA
- Expires: 1769189639000

AFTER:
- Access Token: 01v0RrFG...eOE9QAA ✅ Different
- Expires: 1769190268000 ✅ Extended
```

**Action:** Always show data transformation with real values

---

### 3. "What happens when it breaks?"

Before pushing to production:

**Test:**
- Wrong configuration (intentionally break config)
- Missing data (remove expected fields)
- Multiple entries (ambiguous case)
- Edge cases (empty values, special characters)

**Example from OAuth refresher:**
- Test with `keychain_account: "wrong-name"` → Fallback should work
- Test with incomplete keychain data → Should fail gracefully with helpful error

**Action:** Test failure modes, not just happy path

---

## Mandatory Patterns

### Pattern 1: Explicit Over Implicit

**❌ Wrong:**
```bash
# Ambiguous - returns first match
security find-generic-password -s "Service" -w
```

**✅ Correct:**
```bash
# Explicit - returns specific entry
security find-generic-password -s "Service" -a "account" -w
```

**Rule:** If a command can be ambiguous, make it explicit.

---

### Pattern 2: Validate Before Use

**❌ Wrong:**
```bash
DATA=$(read_config)
USE_VALUE="$DATA"  # Hope it's valid
```

**✅ Correct:**
```bash
DATA=$(read_config)
if ! validate_structure "$DATA"; then
    error "Invalid data structure"
fi
USE_VALUE="$DATA"
```

**Rule:** Never assume data has expected structure.

---

### Pattern 3: Fallback Chains

**❌ Wrong:**
```bash
ACCOUNT="claude"  # Hardcoded
```

**✅ Correct:**
```bash
# 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.

---

### Pattern 4: Helpful Errors

**❌ Wrong:**
```bash
[[ -z "$TOKEN" ]] && error "No token"
```

**✅ Correct:**
```bash
[[ -z "$TOKEN" ]] && error "No token found

Checked:
- 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.

---

## Debugging Methodology (Patrick's Approach)

### Step 1: Get Exact Data

**Don't ask:** "Is it broken?"  
**Ask:** "What exact values do you see? How many entries exist? Which one has the data?"

**Example:**
```bash
# 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"
```

---

### Step 2: Prove With Concrete Examples

**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
```

---

### Step 3: Think Cross-Device Immediately

**Don't think:** "Works on my machine"  
**Think:** "What if their setup differs in [X]?"

**Checklist:**
- [ ] Different account names?
- [ ] Different file paths?
- [ ] Different tools/versions?
- [ ] Different permissions?
- [ ] Different data formats?

---

## Pre-Flight Checklist (Before Publishing)

### Discovery Phase
- [ ] List all external dependencies (files, commands, services)
- [ ] Document what each dependency provides
- [ ] Identify which parts could vary between devices

### Implementation Phase
- [ ] Make variations configurable (with sensible defaults)
- [ ] Add validation for each input
- [ ] Build fallback chains for common variations
- [ ] Add `--dry-run` or `--test` mode

### Testing Phase
- [ ] Test with correct config → Should work
- [ ] Test with wrong config → Should fallback or fail gracefully
- [ ] Test with missing data → Should give helpful error
- [ ] Test with multiple entries → Should handle ambiguity

### Documentation Phase
- [ ] Document default assumptions
- [ ] Document how to verify local setup
- [ ] Document common variations and how to handle them
- [ ] Include data flow diagram
- [ ] Add troubleshooting section

---

## Real-World Example: OAuth Refresher

### Original (Broken)
```bash
# 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:**
- Returns first alphabetical match (wrong entry)
- No validation (could be empty/malformed)
- No fallback (fails if account name differs)

---

### Fixed (Portable)
```bash
# 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:**
- ✅ Explicit account parameter
- ✅ Validates data structure
- ✅ Automatic fallback to common names
- ✅ Helpful error with verification command

---

## Common Anti-Patterns

### Anti-Pattern 1: "Works On My Machine"
```bash
FILE="/Users/patrick/.config/app.json"  # Hardcoded path
```

**Fix:** Use `$HOME`, detect OS, or make configurable

---

### Anti-Pattern 2: "Hope It's There"
```bash
TOKEN=$(cat config.json | jq -r '.token')
# What if .token doesn't exist? Script continues with empty value
```

**Fix:** Validate before using
```bash
TOKEN=$(cat config.json | jq -r '.token // empty')
[[ -z "$TOKEN" ]] && error "No token in config"
```

---

### Anti-Pattern 3: "First Match Is Right"
```bash
# If multiple entries exist, which one?
ENTRY=$(find_entry "service")
```

**Fix:** Be explicit or enumerate all
```bash
ENTRY=$(find_entry "service" "account")  # Specific
# OR
ALL=$(find_all_entries "service")
for entry in $ALL; do
    validate_and_use "$entry"
done
```

---

Related in General