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:
CODEBLOCK0
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:
CODEBLOCK1
✅ Correct:
CODEBLOCK2
Rule: If a command can be ambiguous, make it explicit.
Pattern 2: Validate Before Use
❌ Wrong:
CODEBLOCK3
✅ Correct:
CODEBLOCK4
Rule: Never assume data has expected structure.
Pattern 3: Fallback Chains
❌ Wrong:
CODEBLOCK5
✅ Correct:
CODEBLOCK6
Rule: Provide automatic fallbacks for common variations.
Pattern 4: Helpful Errors
❌ Wrong:
CODEBLOCK7
✅ Correct:
CODEBLOCK8
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:
# 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)
CODEBLOCK11
Problems:
- - Returns first alphabetical match (wrong entry)
- No validation (could be empty/malformed)
- No fallback (fails if account name differs)
Fixed (Portable)
CODEBLOCK12
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"
CODEBLOCK13
Fix: Use $HOME, detect OS, or make configurable
Anti-Pattern 2: "Hope It's There"
CODEBLOCK14
Fix: Validate before using
TOKEN=$(cat config.json | jq -r '.token // empty')
[[ -z "$TOKEN" ]] && error "No token in config"
Anti-Pattern 3: "First Match Is Right"
CODEBLOCK16
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
Anti-Pattern 4: "Silent Failures"
CODEBLOCK18
Fix: Fail loudly with context
process_data || error "Failed to process
Data: $DATA
Expected: { ... }
Check: command_to_verify"
Integration With Existing Workflows
With sprint-plan.md
Add to testing section:
CODEBLOCK20
With PRIVACY-CHECKLIST.md
Add before publishing:
CODEBLOCK21
With skill-creator
When building new skills:
- 1. List what varies between devices
- Make it configurable or auto-discoverable
- Test with wrong config
- Document troubleshooting
Quick Reference Card
Before writing code:
- 1. What varies between devices?
- How do I prove this works?
- What happens when it breaks?
Mandatory patterns:
- - Explicit over implicit
- Validate before use
- Fallback chains
- Helpful errors
Testing:
- - Correct config → Works
- Wrong config → Fallback or helpful error
- Missing data → Clear diagnostic
Documentation:
- - Data flow diagram
- Common variations
- Troubleshooting guide
Success Criteria
A tool is portable when:
- 1. ✅ Works on different devices without modification
- ✅ Auto-discovers common variations in setup
- ✅ Fails gracefully with actionable error messages
- ✅ Can be debugged by reading the error output
- ✅ Documentation covers "what if my setup differs"
Test: Give it to someone with a different setup. If they need to ask you questions, the tool isn't portable yet.
Origin Story
This methodology emerged from debugging the OAuth refresher (2026-01-23):
- - Script read wrong keychain entry (didn't specify account)
- Assumed single entry existed (multiple did)
- No validation (used empty data)
- No fallback (failed on different account names)
Patrick's approach:
- 1. Asked for exact data (how many entries, which has tokens)
- Demanded proof (show BEFORE/AFTER tokens)
- Thought cross-device (what if naming differs?)
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.
When To Use This Skill
Use when:
- - Building tools that read system configuration
- Working with keychains, credentials, environment variables
- Creating scripts that run on multiple machines
- Publishing skills to ClawdHub (others will use them)
Apply:
- 1. Before implementing: Answer the three questions
- During implementation: Use mandatory patterns
- Before testing: Run pre-flight checklist
- After testing: Document variations and troubleshooting
Remember: Your device is just one case. Build for the general case.
便携工具 - 跨设备开发方法论
构建可在不同设备、命名方案和配置下工作的工具的方法论。基于OAuth刷新调试会话(2026-01-23)的经验总结。
核心原则
永远不要假设你的设备是唯一的设备。
你的本地设置只是众多可能配置中的一种。为通用情况构建,而非特定实例。
三个问题(编写代码前)
1. 不同设备之间哪些因素会变化?
在编写任何读取配置、数据或凭据的代码之前:
询问:
- - 文件路径?(macOS与Linux,不同的主目录)
- 账户名称?(user123 vs default vs oauth)
- 服务名称?(拼写/大小写的细微差异)
- 数据结构?(不同版本,不同格式)
- 环境?(不同的Shell,不同的可用工具)
来自OAuth刷新的示例:
- - ❌ 假设:账户始终是claude
- ✅ 现实:可能是claude、Claude Code、default等
行动: 列出变量,使其可配置或可自动发现
2. 如何证明这有效?
在声称成功之前:
要求:
- - 具体的BEFORE状态(精确值)
- 具体的AFTER状态(精确值)
- 证明它们不同(并排比较)
来自OAuth刷新的示例:
BEFORE:
- - 访问令牌: POp5z1fi...eSN9VAAA
- 过期时间: 1769189639000
AFTER:
- - 访问令牌: 01v0RrFG...eOE9QAA ✅ 不同
- 过期时间: 1769190268000 ✅ 已延长
行动: 始终使用真实值展示数据转换
3. 当它出问题时会发生什么?
在推送到生产环境之前:
测试:
- - 错误配置(故意破坏配置)
- 缺失数据(移除期望字段)
- 多个条目(歧义情况)
- 边界情况(空值,特殊字符)
来自OAuth刷新的示例:
- - 使用keychain_account: wrong-name测试 → 回退应能工作
- 使用不完整的钥匙串数据测试 → 应优雅失败并给出有用错误
行动: 测试失败模式,而不仅仅是正常路径
强制模式
模式1:显式优于隐式
❌ 错误:
bash
歧义 - 返回第一个匹配项
security find-generic-password -s Service -w
✅ 正确:
bash
显式 - 返回特定条目
security find-generic-password -s Service -a account -w
规则: 如果命令可能产生歧义,请使其显式化。
模式2:使用前验证
❌ 错误:
bash
DATA=$(read_config)
USE_VALUE=$DATA # 希望它是有效的
✅ 正确:
bash
DATA=$(read_config)
if ! validate_structure $DATA; then
error 无效的数据结构
fi
USE_VALUE=$DATA
规则: 永远不要假设数据具有预期的结构。
模式3:回退链
❌ 错误:
bash
ACCOUNT=claude # 硬编码
✅ 正确:
bash
尝试配置的 → 尝试常见的 → 错误并附带帮助
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 未找到账户。已尝试:...
规则: 为常见变体提供自动回退。
模式4:有用的错误信息
❌ 错误:
bash
[[ -z $TOKEN ]] && error 无令牌
✅ 正确:
bash
[[ -z $TOKEN ]] && error 未找到令牌
已检查:
- - 配置:$CONFIGFILE
- 字段:$FIELDNAME
- 期望:{ \tokens\: { \refresh\: \...\ } }
使用以下命令验证:
cat $CONFIG_FILE | jq .tokens
规则: 错误消息应帮助用户诊断和修复问题。
调试方法论(Patrick的方法)
步骤1:获取精确数据
不要问: 它坏了吗?
要问: 你看到的具体值是什么?有多少条目存在?哪个有数据?
示例:
bash
模糊
检查钥匙串
具体
运行:security find-generic-password -l Service | grep acct
告诉我:1. 有多少条目 2. 哪个有令牌 3. 最后修改时间
步骤2:用具体示例证明
不要说: 现在应该能用了
要展示: 这是BEFORE令牌(POp5z...),这是AFTER令牌(01v0R...),它们不同
模板:
BEFORE:
AFTER:
- - 字段1:<新值> ✅ 已更改
- 字段2:<新值> ✅ 已更改
证明:值不同
步骤3:立即考虑跨设备
不要想: 在我的机器上能用
要想: 如果他们的设置在[X]方面不同怎么办?
检查清单:
- - [ ] 不同的账户名称?
- [ ] 不同的文件路径?
- [ ] 不同的工具/版本?
- [ ] 不同的权限?
- [ ] 不同的数据格式?
起飞前检查清单(发布前)
发现阶段
- - [ ] 列出所有外部依赖项(文件、命令、服务)
- [ ] 记录每个依赖项提供什么
- [ ] 识别哪些部分在不同设备间可能不同
实现阶段
- - [ ] 使变体可配置(带有合理的默认值)
- [ ] 为每个输入添加验证
- [ ] 为常见变体构建回退链
- [ ] 添加--dry-run或--test模式
测试阶段
- - [ ] 使用正确配置测试 → 应能工作
- [ ] 使用错误配置测试 → 应回退或优雅失败
- [ ] 使用缺失数据测试 → 应给出有用的错误
- [ ] 使用多个条目测试 → 应处理歧义
文档阶段
- - [ ] 记录默认假设
- [ ] 记录如何验证本地设置
- [ ] 记录常见变体及如何处理
- [ ] 包含数据流图
- [ ] 添加故障排除部分
真实世界示例:OAuth刷新器
原始版本(有问题的)
bash
假设单个条目,无验证,无回退
KEYCHAIN_DATA=$(security find-generic-password -s Service -w)
REFRESH
TOKEN=$(echo $KEYCHAINDATA | jq -r .refreshToken)
使用令牌(希望它是有效的)
问题:
- - 返回第一个字母匹配项(错误条目)
- 无验证(可能为空/格式错误)
- 无回退(如果账户名称不同则失败)
修复版本(便携的)
bash
显式账户,带验证和回退
validate_data() {
echo $1 | jq -e .claudeAiOauth.refreshToken > /dev/null 2>&1
}
尝试配置的账户
DATA=$(security find-generic-password -s $SERVICE -a $ACCOUNT -w 2>&1)
if validate_data $DATA; then
log ✓ 使用账户:$ACCOUNT
else
log ⚠ 尝试回退账户...
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 ✓ 在 $fallback 中找到数据
break
fi
done
fi
[[ -z $DATA ]] || ! validate_data $DATA && error 未找到有效数据
已尝试账户:$ACCOUNT, claude, Claude Code, default
使用以下命令验证:security find-generic-password -l $SERVICE
REFRESH_TOKEN=$(echo $DATA | jq -r .claudeAiOauth.refreshToken)
改进:
- - ✅ 显式账户参数
- ✅ 验证数据结构
- ✅ 自动回退到常见名称
- ✅ 带有