Build cross-device tools without hardcoding paths or account names
数据来源:ClawHub。 在 ClawSkills 查看
选择你使用的 Agent
方法一:命令行安装(推荐)
推荐(无需提前安装 clawhub)
npx clawhub@latest --dir ~/.claude/skills install portable-tools或使用 clawhub CLI(需提前安装)
clawhub --dir ~/.claude/skills install portable-tools⚠️ 需要 Node.js 18+,没有 Node?请使用下方方法二直接下载 ZIP。 安装 Node.js →
方法二:手动下载安装(无需 Node)
下载 ZIP,解压后将文件夹放到以下路径,重启 Agent 即可:
安装路径
~/.claude/skills/portable-tools/💡解压后将文件夹放到上方路径,重启 Agent 即可生效
--- name: portable-tools description: Build cross-device tools without hardcoding paths or account names ---
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: 1769189639000
AFTER:
- 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 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.
---
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")
...
安装 Portable Tools 后,可以对 AI 说这些话来触发它
Help me get started with Portable Tools
Explains what Portable Tools does, walks through the setup, and runs a quick demo based on your current project
Use Portable Tools to build cross-device tools without hardcoding paths or account names
Invokes Portable Tools with the right parameters and returns the result directly in the conversation
What can I do with Portable Tools in my developer & devops workflow?
Lists the top use cases for Portable Tools, with example commands for each scenario
将技能文件夹放到 ~/.claude/skills/portable-tools/ 目录(个人级,所有项目可用),或 .claude/skills/portable-tools/(项目级)。重启 AI 客户端后,用 /portable-tools 主动调用,或让 AI 根据上下文自动发现并使用。
Portable Tools 支持 Claude、Cursor、OpenClaw,可与这些 AI 平台无缝集成,扩展其能力。
Portable Tools 可免费安装使用。请查阅仓库了解许可证信息。
Build cross-device tools without hardcoding paths or account names
Portable Tools 属于「Developer & DevOps」分类,该分类的技能帮助 AI 智能体在此领域执行专业任务。
Automate my developer & devops tasks using Portable Tools
Identifies repetitive steps in your workflow and sets up Portable Tools to handle them automatically