关于 setup
setup 是 claude-ops 插件的交互式配置向导,面向希望配置命令行工具、通信渠道、MCP 和项目注册表的用户。它先检测现有状态,再按用户选择引导配置,并要求所有 Bash 调用在后台运行。
主要功能
- 01检测 CLI、渠道、MCP 和项目配置状态
- 02引导安装命令行工具与配置集成
- 03设置渠道凭据及偏好设置
- 04配置项目注册表和后台守护进程
- 05将独立诊断任务并行执行
适用场景
- 01首次配置 claude-ops 插件
- 02补齐尚未配置的通信渠道或 MCP
- 03只重新运行特定配置部分
- 04为多个项目建立管理注册表
MCP 相关
向导检测已配置的 MCP 服务,并提供配置 Linear、Sentry、Vercel 和 Gmail MCP 服务的选项。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill setup安装后在 Claude Code 中输入/setup确认已加载。
详细介绍
OPS ► 设置向导
你正在为 claude-ops 插件运行交互式配置向导。用户希望你引导他们完成启用插件所需的每个步骤:安装 CLI、设置环境变量、配置频道、填充项目注册表,以及保存偏好设置。
规则零——每次 Bash 调用都必须使用 run_in_background: true
这是不可妥协的。本设置向导中的每一次 Bash 工具调用都必须设置 run_in_background: true。绝无例外。适用于:
- 凭据扫描、CLI 安装、OAuth 流程、npm/brew 安装
- 启动守护进程、重新加载守护进程、launchctl 命令
- 写入钥匙串、查询 Doppler、查询 Chrome 历史记录
- 自动链接脚本、同步/回填、冒烟测试
- 文件写入、配置写入、追加环境变量
- 任何命令,无论你认为它执行得多快
后台命令运行期间,立即继续执行下一个独立步骤,或向用户提出下一个问题。收到 <task-notification> 后再处理结果。设置向导绝不能显示 (ctrl+b to run in background)——如果用户看到此提示,就说明你违反了此规则。
规则一——静默调用 Bash
每次 Bash 工具调用都必须包含简短的 description 参数(5-10 个词,例如「安装缺失的 CLI」「扫描钥匙串中的 Telegram 凭据」「重新加载守护进程」)。用户看到的是这段描述,而不是原始命令。保持设置过程简洁安静——用户应看到进度标题,而非 shell 脚本。
其他硬性规则:
- 这是一次_对话_,不是脚本输出。每项决定都使用
AskUserQuestion,如果结构化选择器可以完成,就绝不要用普通文字提问。 - 如果用户尚未明确同意某项操作,请通过
AskUserQuestion确认(例如,“全部配置”涵盖所有内容,之后无需逐项确认)。 - 跳过用户拒绝的部分。不要反复催促。
- 绝不要自动跳过任何频道或集成。 用户选中的每个频道/服务都必须通过
AskUserQuestion明确询问,并将跳过作为选项之一。如果找不到凭据,请提供 [手动粘贴] / [深度搜寻] / [跳过] 选项。如果冒烟测试失败,请询问用户是重试、重新配置还是跳过。用户选择“跳过”选项是唯一可接受的跳过方式。扫描未找到任何内容时,不要悄悄略过某项服务——此时更应该询问用户。 - 先展示已配置的内容,让用户只需补齐缺漏。
- 除非用户在本次会话中明确提供,否则绝不要在输出中显示用户的真实姓名或电子邮件。 不要从记忆、现有配置或环境变量中读取信息来填写显示名称。
- 每次
AskUserQuestion调用最多提供 4 个选项。工具架构要求options数组中的项目数<=4。如果某个步骤列出的选项超过 4 个,先过滤掉已配置的项目,再将其余选项分成多个连续调用,每次最多 4 个选项,并按逻辑分组。使用[更多选项...]作为最后一个选项,以衔接下一组选项。 - 尽可能并行运行所有诊断/探测命令。一次消息中使用多次 Bash 工具调用。独立的探测不得顺序运行(例如,
gog auth status、wacli doctor和钥匙串侦测应同时运行)。 - 所有写入只能写入以下路径,不得写入其他位置:
$PREFS_PATH— 每用户偏好设置和密钥。解析为${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json。位于 Claude Code 的插件数据目录,因此在重新安装插件和升级版本后仍会保留。绝不会提交到 git。${CLAUDE_PLUGIN_ROOT}/scripts/registry.json— 每用户项目注册表(在源代码仓库中被 git 忽略)。如果父目录不存在,请运行mkdir -p创建。${CLAUDE_PLUGIN_ROOT}/.mcp.json— 仅用于添加${user_config.*}占位符,绝不可写入硬编码令牌。- 用户的 shell 配置文件(
~/.zshrc等)— 只能追加,不得重写。
- 每个向导步骤开始时,确保
$PREFS_PATH的父目录存在:mkdir -p "$(dirname "$PREFS_PATH")"。Claude Code 安装插件时会创建~/.claude/plugins/data/ops-ops-marketplace/,但不要依赖这一点。
Agent Teams 支持
如果已设置 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1,且需要同时启动多个“深度搜寻”凭据 Agent,请使用 Agent Teams。这可以:
- 并行安排凭据侦察 Agent 搜寻 Doppler、钥匙串、浏览器配置和密码管理器
- 让 Agent 共享发现结果(例如,Doppler Agent 找到不完整的配置后,钥匙串 Agent 就知道可跳过该服务)
- 在搜寻过程中调整指示:“已找到 Telegram 令牌,停止搜寻该项”
团队设置(仅在标志已启用且触发了多个深度搜寻时):
TeamCreate("setup-hunters")
Agent(team_name="setup-hunters", name="hunt-telegram", model="haiku", ...)
Agent(team_name="setup-hunters", name="hunt-sentry", model="haiku", ...)
Agent(team_name="setup-hunters", name="hunt-shopify", model="haiku", ...)
每个 Agent 都会汇报发现结果。汇总结果后,呈现给用户确认。
如果未设置该标志,请使用彼此独立的后台子 Agent,并设置 run_in_background: true。
设置 Agent 委派模式
如果用户在设置期间提出复杂的集成相关问题(例如,“/ops:ecom 如何处理多店铺设置?”),设置 Agent 可以加载相关技能的 SKILL.md,以获取更深入的背景信息:
cat "${CLAUDE_PLUGIN_ROOT}/skills/ops-ecom/SKILL.md"
下面的每个子步骤都包含一个指向相关技能文件的 > **深入了解:** 指引。请遵循这些指引,而不是在本向导中重复操作细节。
步骤 0 — 预检(在你阅读时于后台运行)
${CLAUDE_PLUGIN_ROOT}/bin/ops-setup-preflight &>/dev/null &
预检数据:所有探测结果都缓存在 /tmp/ops-preflight/。运行任何诊断命令之前,请检查结果是否已存在于此处:
- CLI 状态:
cat /tmp/ops-preflight/clis.txt - Slack:
cat /tmp/ops-preflight/slack.json - Telegram:
cat /tmp/ops-preflight/telegram.txt - gog/Gmail:
cat /tmp/ops-preflight/gog-gmail.json - gog/Calendar:
cat /tmp/ops-preflight/gog-cal.json - WhatsApp:
cat /tmp/ops-preflight/wacli-doctor.json和wacli-chats.json - MCP 服务:
cat /tmp/ops-preflight/mcp-servers.txt - GitHub:
cat /tmp/ops-preflight/gh-auth.txt - AWS:
cat /tmp/ops-preflight/aws-identity.json - 项目:
cat /tmp/ops-preflight/projects.txt - 现有注册表:
cat /tmp/ops-preflight/existing-registry.json - 现有偏好设置:
cat /tmp/ops-preflight/existing-prefs.json - Doppler:
cat /tmp/ops-preflight/doppler.json
读取之前,请等待 /tmp/ops-preflight/.complete 存在(通常会在 2–3 秒内准备就绪)。绝不要重新运行已有缓存结果的探测——请改为读取缓存文件。
步骤 0b — 检测当前状态
运行检测器并解析其 JSON 输出(或在有预检缓存时读取缓存):
${CLAUDE_PLUGIN_ROOT}/bin/ops-setup-detect 2>/dev/null
如果未设置 CLAUDE_PLUGIN_ROOT,则回退到最新安装的缓存目录 ~/.claude/plugins/cache/ops-marketplace/ops/<latest-version>/。将解析出的路径存储为 PLUGIN_ROOT,以供本次会话后续使用。
同时解析一次 PREFS_PATH,并在所有地方重复使用:
PREFS_PATH="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json"
mkdir -p "$(dirname "$PREFS_PATH")"
向用户打印简洁的状态摘要,每个类别占一行:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OPS ► SETUP WIZARD
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Shell: zsh → ~/.zshrc
Core CLIs: ✓ jq ✓ git ✓ gh ✓ aws ✓ node
Channels: ✓ wacli ✓ gog ○ telegram (no token)
Secrets: ✓ doppler (project: my-app, config: dev)
MCPs: ✓ linear ✓ sentry ○ slack ○ vercel
Registry: 19 projects
Preferences: not set
──────────────────────────────────────────────────────
已存在/已设置时使用 ✓,缺失/未设置时使用 ○,损坏时使用 ✗。
第 1 步 — 询问要配置哪些部分
首先,提供一个快速的“全部设置”选项:
How would you like to run setup?
[Set up everything — install CLIs, configure all channels, MCPs, registry, daemon, preferences (Recommended)]
[Pick sections — choose which parts to configure]
[Re-run a specific section — I know what I need]
如果用户选择“全部设置”,请选择所有批次中的全部部分,并按顺序运行(第 2 步 → 2b → 2c → 3 → 4 → 5 → 5b → 6 → 7),跳过任何已全部配置的部分。在每个步骤中,尽可能使用“全部配置”快速路径。
如果用户选择“重新运行特定部分”,显示一个 AskUserQuestion,列出各部分名称(cli、daemon、channels、mcp、registry、prefs、env、ecom、mktg、voice、revenue),并直接跳转到对应步骤。
如果用户选择“选择部分”,继续执行下面的分批选择。
使用 AskUserQuestion 并设置 multiSelect: true。仅提供需要处理的部分(跳过已显示为绿色的部分)。由于 AskUserQuestion 最多允许 4 个选项,请按逻辑分组:
批次 1 — 核心设置(尽早运行,以便 daemon 在你完成其余设置时预热缓存):
| 选项 | 标题 | 说明 |
|---|---|---|
| 安装 CLI | cli | 通过 Homebrew 安装缺失的命令行工具 |
| 后台 daemon | daemon | 尽早安装 ops-daemon — 在其余设置运行时预热简报缓存 |
| 配置 MCP | mcp | 启用 Linear、Sentry、Vercel、Gmail MCP 服务 |
| 构建注册表 | registry | 注册 Claude 应管理的项目 |
批次 2 — 渠道与插件:
| 选项 | 标题 | 说明 |
|---|---|---|
| 配置渠道 | channels | 设置 Telegram、WhatsApp、Email、Slack 的令牌 |
| 配套插件 | plugins | 安装 GSD,以跟踪项目路线图 |
| 保存偏好设置 | prefs | 所有者姓名、时区、默认优先级 |
| Shell 环境 | env | 在 shell 配置文件中导出 CLAUDE_PLUGIN_ROOT |
批次 3 — 附加项(仅在尚未配置时显示):
| 选项 | 标题 | 说明 |
|---|---|---|
| 配置电商 | ecom | 设置 Shopify 商店 URL 和管理员令牌、ShipBob |
| 配置营销 | mktg | 设置 Klaviyo、Meta Ads、GA4、Search Console 密钥 |
| 配置语音 | voice | 设置 Bland AI、ElevenLabs、Groq API 密钥 |
| 配置收入 | revenue | 设置 Stripe 和 RevenueCat 密钥,以实时跟踪 MRR |
将每个批次作为单独的 AskUserQuestion 调用呈现。跳过所有项目均已显示为绿色的批次。收集所有批次中的选择,并按顺序运行每个已选部分。
第 2 步 — 安装 CLI(如果已选择)
如果缺少多个 CLI,先提供批量安装选项:
Missing CLIs detected: jq, gh, wacli. What would you like to do?
[Install all missing CLIs (Recommended)]
[Pick which to install]
[Skip CLI installation]
如果用户选择“全部安装”,则按顺序安装所有缺失的工具,无需进一步询问。如果选择“选择要安装的工具”,则逐个询问:
Install jq? [Yes, install now] [Skip]
Install gh? [Yes, install now] [Skip]
Install wacli? [Yes, install now] [Skip — manual install required]
对于每个 Yes,运行:
${PLUGIN_ROOT}/bin/ops-setup-install <tool>
报告成功或失败。如果 macOS 上缺少 Homebrew,请停止并告知用户先从 https://brew.sh 安装;不要尝试自动安装 brew。
安装后,重新运行 ops-setup-detect,以刷新状态后再继续。
第 2b 步 — 配套插件(如果已选择)
GSD(Get Shit Done)
GSD 是一款第三方 Claude Code 插件,可添加项目路线图跟踪功能。安装后,claude-ops 仪表板(/ops:go、/ops:projects、/ops:next、/ops:yolo)会自动显示每个项目的活动阶段、进度和后续操作。未安装时,这些部分会被省略。
检查 GSD 是否已安装:
find ~/.claude -name "gsd-progress" -path "*/skills/*" 2>/dev/null | head -1 | grep -q . && echo "installed" || echo "not_installed"
如果尚未安装,通过 AskUserQuestion 询问:
GSD adds project roadmap tracking to your ops dashboards.
/ops:go shows active phases and progress per project
/ops:projects shows GSD state alongside CI/PR status
/ops:next factors in GSD work priority
[Install GSD (latest)] [Skip — I don't need roadmap tracking]
安装时,直接运行命令——不要让用户手动运行:
# Install GSD in one shot — no user intervention needed
claude plugin marketplace add gsd-build/get-shit-done 2>/dev/null && \
claude plugin install gsd@gsd-build-get-shit-done 2>/dev/null
如果 claude CLI 不在路径中,则使用插件缓存机制作为备用方案:
# Direct marketplace clone fallback
GSD_MARKETPLACE_DIR="$HOME/.claude/plugins/marketplaces/gsd-build-get-shit-done"
if [ ! -d "$GSD_MARKETPLACE_DIR" ]; then
git clone https://github.com/gsd-build/get-shit-done.git "$GSD_MARKETPLACE_DIR" 2>/dev/null
fi
报告成功或失败。在 $PREFS_PATH 中记录 plugins.gsd = "installed"。
如果用户跳过:
Skipped GSD. Install later with: /plugin marketplace add gsd-build/get-shit-done
步骤 2c — 后台守护进程(提前安装,预热缓存)
为什么要这么早安装守护进程? 在其余设置步骤进行的同时运行守护进程,可让它开始预热简报缓存(ops-gather 收集的基础设施、git、PR 和 CI 结果),这样用户到达步骤 7 并运行 /ops:go 时,简报已缓存,加载时间会从 10 秒缩短到 3 秒以内。依赖渠道的服务(wacli-sync、message-listener、inbox-digest、store-health)会在步骤 5b 中,待相应渠道配置完成后再添加。
平台支持
后台守护进程自带 launchd 集成(仅限 macOS)。尝试安装前先检测平台:
case "$(uname -s)" in
Darwin) OS=macos ;;
Linux) grep -qi microsoft /proc/version 2>/dev/null && OS=wsl || OS=linux ;;
MINGW*|MSYS*|CYGWIN*) OS=windows ;;
*) OS=unknown ;;
esac
-
macOS(
OS=macos):继续执行下方完整的launchctl bootstrap流程。 -
Linux / WSL(
OS=linux|wsl):无法使用launchctl。守护进程脚本(${CLAUDE_PLUGIN_ROOT}/scripts/ops-daemon.sh)可在bash下正常运行,但要将其安装为用户服务,需要使用systemd --user(Linux)或自定义的 cron/at 包装程序(WSL)。这项工作不在本补丁范围内——列为后续工作。目前请打印:○ Background daemon — skipped (Linux/WSL install via systemd --user is pending; see docs/daemon-guide.md). You can still launch it manually with: nohup ${CLAUDE_PLUGIN_ROOT}/scripts/ops-daemon.sh &在
$PREFS_PATH中写入daemon.enabled = false和daemon.skip_reason = "platform:<os>",然后继续执行步骤 3。 -
Windows(原生,
OS=windows):不会安装守护进程。打印○ Background daemon — not supported on native Windows. Use WSL or run ops-daemon.sh manually.,然后继续。
如果 OS=macos,检查守护进程是否已安装:
launchctl print gui/$(id -u)/com.claude-ops.daemon 2>/dev/null | head -1
如果已经加载,打印 ✓ Background daemon already running — will reconcile services in Step 5b.,然后跳至步骤 3。
否则,通过 AskUserQuestion 询问:
Install the ops background daemon now?
Starts pre-warming briefing cache while you finish the rest of setup.
Auto-heals on failure. Single launchd agent (com.claude-ops.daemon).
Channel services (wacli-sync, message-listener) added after channels are set up.
[Yes — install now] [Skip — I'll run it manually later]
如果选择 Yes,运行安装(使用 run_in_background: true——RULE ZERO):
# Always resolve CLAUDE_PLUGIN_ROOT to the CURRENT installed version — never hardcode a version number.
# If CLAUDE_PLUGIN_ROOT is not set, detect it from the plugin cache:
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d "$HOME/.claude/plugins/cache/ops-marketplace/ops"/*/ 2>/dev/null | sort -V | tail -1)}"
DAEMON_SCRIPT="${PLUGIN_ROOT}/scripts/ops-daemon.sh"
chmod +x "$DAEMON_SCRIPT"
PLIST_TEMPLATE="${PLUGIN_ROOT}/scripts/com.claude-ops.daemon.plist"
PLIST_DEST="$HOME/Library/LaunchAgents/com.claude-ops.daemon.plist"
DATA_DIR="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}"
LOG_DIR="$DATA_DIR/logs"
mkdir -p "$LOG_DIR"
# Resolve bash 4+ (required for associative arrays in ops-daemon.sh).
# macOS ships bash 3; Homebrew installs bash 5 at /opt/homebrew/bin/bash.
BASH_PATH="/bin/bash"
if [[ -x /opt/homebrew/bin/bash ]]; then
BASH_PATH="/opt/homebrew/bin/bash"
elif [[ -x /usr/local/bin/bash ]]; then
BASH_PATH="/usr/local/bin/bash"
fi
# Generate plist
sed -e "s|__DAEMON_SCRIPT_PATH__|$DAEMON_SCRIPT|g" \
-e "s|__BASH_PATH__|$BASH_PATH|g" \
-e "s|__PLUGIN_ROOT__|$PLUGIN_ROOT|g" \
-e "s|__LOG_DIR__|$LOG_DIR|g" \
-e "s|__HOME__|$HOME|g" \
"$PLIST_TEMPLATE" > "$PLIST_DEST"
# Write a minimal initial services config — only the services that don't depend on
# channels yet. `briefing-pre-warm` runs ops-gather every 2 minutes so the next
# /ops:go is instant. `memory-extractor` runs but stays idle until channels exist.
SERVICES_CONFIG="$DATA_DIR/daemon-services.json"
cat > "$SERVICES_CONFIG" <<JSON
{
"services": {
"briefing-pre-warm": {
"enabled": true,
"command": "${CLAUDE_PLUGIN_ROOT}/bin/ops-gather",
"cron": "*/2 * * * *",
"_note": "Pre-warms /ops:go cache. Runs every 2 minutes."
},
"memory-extractor": {
"enabled": true,
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-memory-extractor.sh",
"cron": "*/30 * * * *",
"health_file": "~/.claude/plugins/data/ops-ops-marketplace/memories/.health",
"_note": "Idle until channels are configured; will extract profiles once wacli/gog are live."
}
}
}
JSON
# Remove the old standalone wacli keepalive if present
launchctl bootout gui/$(id -u)/com.claude-ops.wacli-keepalive 2>/dev/null || true
rm -f "$HOME/Library/LaunchAgents/com.claude-ops.wacli-keepalive.plist"
# Load daemon in background — does NOT block the wizard
launchctl bootout gui/$(id -u) "$PLIST_DEST" 2>/dev/null || true
launchctl bootstrap gui/$(id -u) "$PLIST_DEST"
在 $PREFS_PATH 中写入 daemon.enabled = true 和 daemon.installed_at_step = "2c",以便步骤 5b 知道应协调服务,而不是重新安装。
打印:
✓ Background daemon — installed. Pre-warming briefing cache in parallel while you finish setup.
Channel-dependent services will be added after channels are configured (Step 5b).
立即继续执行步骤 3——不要等待守护进程确认启动。健康文件检查推迟到步骤 5b。
深入了解: 如需了解后台守护进程的完整操作说明、CLI 参考和故障排除方法,请参阅
${CLAUDE_PLUGIN_ROOT}/docs/daemon-guide.md。如果设置 Agent 需要比此向导更深入的信息,可直接加载该文件。
步骤 3 — 配置频道(如已选择)
首先,在逐项选择前,先提供快速“配置全部”选项。使用 AskUserQuestion:
How would you like to configure channels and integrations?
[Configure all — set up every available channel and service (Recommended)]
[Pick individually — choose which channels to configure]
[Skip all — configure channels later]
如果用户选择“配置全部”,则按顺序运行以下所有频道子流程(Telegram → WhatsApp → Email → Slack → Notion → Calendar → Doppler → Vault),跳过已配置的频道。如果用户选择“全部跳过”,则进入步骤 4。
如果用户选择“逐项选择”,则使用带有 multiSelect: true 的 AskUserQuestion 询问要配置哪些频道。由于 AskUserQuestion 最多允许 4 个选项,请分成两组。跳过已配置的频道(仅显示需要处理的频道)。
第 1 组 — 消息:
| 选项 | 标题 | 描述 |
|---|---|---|
| Telegram | telegram | 用于 /ops-comms telegram 的 Bot token + 所有者 ID |
| wacli doctor + 自动修复 + 回填 | ||
用于 /ops-inbox email 的 gog CLI → Gmail MCP 备用方案 | ||
| Slack | slack | Slack MCP 服务(由 Claude Code 管理) |
第 2 组 — 知识与服务:
| 选项 | 标题 | 描述 |
|---|---|---|
| Notion | notion | Notion MCP — 工作区搜索、评论、任务、知识库 |
| Calendar | calendar | gog calendar → Google Calendar MCP 备用方案 — 为简报提供日程上下文 |
| Doppler | doppler | 密钥管理器 — 为所有 ops 技能设置默认项目 + 配置 |
| Vault | vault | 密码管理器 — 1Password、Dashlane、Bitwarden 或 macOS Keychain |
将每组作为单独的 AskUserQuestion 调用呈现。若某组中的所有项目均已配置,则跳过该组。对于每个选中的频道,运行对应的子流程。
通用:建议安装前检测主机操作系统
claude-ops 向导可在 macOS、Linux(所有主流发行版 + WSL)和 Windows(原生环境 + WSL)上运行。打印任何安装命令前,技能 MUST 检测主机操作系统并选择适用于该操作系统的命令。绝不要向 Windows 用户打印 brew install … 命令,也绝不要向 macOS 用户打印 winget install … 命令。
最简检测代码片段(bash — 适用于 macOS、Linux、WSL、MSYS/Cygwin):
case "$(uname -s)" in
Darwin*) OS=macos ;;
Linux*)
if grep -qi microsoft /proc/version 2>/dev/null; then OS=wsl
elif [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
arch|manjaro) OS=arch ;;
fedora|rhel|centos|rocky|almalinux) OS=fedora ;;
debian|ubuntu|pop|linuxmint) OS=debian ;;
alpine) OS=alpine ;;
opensuse*|sles) OS=suse ;;
*) OS=linux ;;
esac
else OS=linux; fi ;;
MINGW*|MSYS*|CYGWIN*) OS=windows ;;
*) OS=unknown ;;
esac
按以下顺序选择包管理器(选第一个可用的):
brew(macOS + Linuxbrew)— macOS 上的首选。- 原生操作系统包管理器 —
apt-get(debian/ubuntu)、dnf(fedora/rhel)、pacman(arch)、zypper(suse)、apk(alpine)。 - Windows 上依次使用
winget(Windows 10 1809+)→scoop→choco→ 最后才从源代码构建。
如果首选包管理器未安装,则改用下一个可用选项,不要中止流程。以下每个 AskUserQuestion 的“[立即安装 — …]”提示都使用适配操作系统的命令表 — 仅打印与检测到的操作系统相匹配的行。
权威的跨操作系统检测逻辑请复用 bin/ops-setup-detect(其 JSON 输出包含 os、pkg_mgr、arch、keyring_backend、shell、browser_profiles_found)。
通用:优先使用 OAuth,而非手动输入令牌
只要某个渠道支持基于浏览器的 OAuth 流程,就应优先提供该选项,并将手动输入令牌作为后备方案。OAuth 更安全(权限范围明确、可撤销,且无需将密钥存入 dotfiles),而且通常能让用户更快完成操作。
| 渠道 | OAuth 方式 | 手动后备方案 |
|---|---|---|
| Email (gog) | gog auth add <email> --services gmail,calendar,drive,contacts,docs,sheets(浏览器) | 不适用 — gog 仅支持 OAuth |
| Calendar (gog) | 使用相同的 gog auth add,并在 --services 中包含 calendar | 不适用 |
| Slack | claude mcp add slack(通过 Claude Code 处理 OAuth) | 通过自动扫描获取 Bot 令牌 + 手动粘贴 |
| Linear | claude mcp add linear | API 密钥 |
| Sentry | claude mcp add sentry | DSN / 身份验证令牌 |
| Vercel | claude mcp add vercel | 个人访问令牌 |
| Telegram | ❌ 不支持 OAuth(Bot API 的设计决定其只能使用令牌) | 自动扫描 + 手动粘贴(唯一选项) |
通过 wacli auth 扫描二维码配对(使用体验类似 OAuth) | 不适用 — 仅支持配对会话 |
当渠道支持 OAuth 时,默认的 AskUserQuestion 应优先引导用户使用该方式:
[Connect via OAuth (recommended)] [Enter a token manually] [Skip]
只有当用户选择“手动”时,或渠道(Telegram、本地工具)不支持 OAuth 时,才继续执行下方的凭据自动扫描流程。
通用凭据自动扫描
在向用户索取任何凭据之前,先按以下顺序执行扫描。此要求适用于所有步骤——渠道、电商、营销、语音和 MCP。绝不能让用户去查找已经存在于其系统上的密钥。
重要 — 搜索所有来源后再报告。 在一个批次中运行所有扫描来源(下方的 1-10 项),然后再分析汇总结果。只检查环境变量和 Dashlane 后,不要就报告“未找到凭据”——Chrome 历史记录、.env 文件、Doppler 和钥匙串中可能已经有答案。如果缺少 API 令牌,但已找到商店或服务身份信息(例如在 Chrome 历史记录中发现商店 URL,或在 Dashlane 中找到登录信息),请报告发现的内容,并跳转到令牌步骤,同时预填身份信息。用户说“找一下”或“检查所有可用来源”,意味着你的搜索还不够彻底——绝不要让用户查找你能通过程序找到的信息。
对于每个变量名(例如 TELEGRAM_BOT_TOKEN、SHOPIFY_ACCESS_TOKEN、KLAVIYO_API_KEY):
- 当前 shell 环境 —
printenv <VAR>。运行中的 shell 会继承导出的变量、Doppler 注入的变量以及通过 dotenv 加载的文件。这里最有可能获取正确值。 - Shell 配置文件 — 在
~/.zshrc、~/.bashrc、~/.zprofile、~/.config/fish/config.fish、~/.envrc(direnv)中搜索<VAR>=或export <VAR>=。在值旁边显示文件路径,让用户知道它的来源。 - Doppler(所有项目) — 如果
command -v doppler执行成功:
如果 prd 失败,也尝试默认项目配置(dev/staging)。显示来源信息for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do doppler secrets --project "$proj" --config prd --json 2>/dev/null | \ jq -r --arg var "$VAR" --arg proj "$proj" \ 'to_entries[] | select(.key == $var) | "\(.value.computed) (doppler:\($proj)/prd)"' done(project: <slug>, config: <config>)。 - Dashlane CLI — 如果
command -v dcli执行成功:
映射服务关键词:dcli password "$SERVICE_KEYWORD" --output json 2>/dev/nullshopify→ SHOPIFY 变量,klaviyo→ KLAVIYO 变量,bland/bland-ai→ BLAND 变量,等等。 - macOS 钥匙串 — 针对特定服务:
使用符合常见模式的服务名称(例如security find-generic-password -s "$SERVICE" -w 2>/dev/nullshopify-admin-token、klaviyo-api-key、bland-ai-api-key)。 - OpenClaw 配置 — 如果
~/.openclaw/openclaw.json存在:jq -r --arg var "$VAR" '.agents.defaults.env[$var] // empty' ~/.openclaw/openclaw.json 2>/dev/null - 已安装的 MCP 配置 — 读取检测器找到的每个
.mcp.json。对于每个服务条目,检查.env和.args中是否包含该变量名,或是否有看起来像目标值的字面值。将 MCP 服务名称显示为来源。 - 插件偏好设置 — 在现有的
$PREFS_PATH中检查相关区段下的键(例如.ecom.shopify.admin_token、.marketing.klaviyo.api_key)。如果找到且不是doppler:引用,则将其显示为来源。 - Chrome 历史记录 — 对于具有网页管理界面的服务(Shopify、Klaviyo 等),查询 Chrome 的 History SQLite 数据库,查找可用于识别账户/商店的管理页面 URL:
从 URL 中提取标识符(例如sqlite3 ~/Library/Application\ Support/Google/Chrome/Default/History \ "SELECT DISTINCT url FROM urls WHERE url LIKE '%<service_domain>%' ORDER BY last_visit_time DESC LIMIT 10" 2>/dev/null*.myshopify.com商店标识和账户 ID)。 - 项目
.env文件 — 扫描~/Projects/*/.env*,查找变量名或服务域名模式。这些文件通常包含可复用于其他项目的凭据。
自动扫描时的环境变量 → 服务关键词映射:
| 变量名称 | 服务关键词(Dashlane/Keychain) |
|---|---|
SHOPIFY_ACCESS_TOKEN、SHOPIFY_ADMIN_TOKEN、SHOPIFY_STORE_URL | shopify |
KLAVIYO_API_KEY、KLAVIYO_PRIVATE_KEY | klaviyo |
META_ACCESS_TOKEN、FACEBOOK_ACCESS_TOKEN、META_AD_ACCOUNT_ID | meta、facebook |
GA4_PROPERTY_ID、GA_MEASUREMENT_ID | google-analytics、ga4 |
BLAND_AI_API_KEY、BLAND_API_KEY | bland-ai、bland |
ELEVENLABS_API_KEY | elevenlabs |
GROQ_API_KEY | groq |
SHIPBOB_ACCESS_TOKEN | shipbob |
TELEGRAM_BOT_TOKEN、TELEGRAM_API_ID、TELEGRAM_API_HASH | telegram |
SLACK_BOT_TOKEN、SLACK_MCP_XOXC_TOKEN | slack |
使用 AskUserQuestion 呈现发现结果(每次调用最多 4 个选项):
Found credential for SHOPIFY_ACCESS_TOKEN:
[A] shell env + ~/.zshrc + Dashlane — shpat_508b...682e (matched across 3 sources)
[B] Doppler (project: mystore, config: prd) — shpat_9f2c...a17b (different!)
[C] Enter a different one
提示规则:
- 显示任何令牌的前 8 个和后 4 个字符,绝不显示完整值。
- 始终将匹配的来源合并为一个选项,并附上
(matched in env + ~/.zshrc + Dashlane)。这对于将选项数控制在 4 个以内至关重要。 - 如果来源之间的值不一致,则将每个不同的值显示为单独选项。如果不同的值超过 3 个(很少见),则分多次调用,并使用
[More sources...]。 ${user_config.*}、<your-token>、CHANGE_ME等占位值或空字符串均视为未找到。- 始终将
[Enter a different one]作为最后一个选项。 - 如果没有任何来源包含值,则通过
AskUserQuestion向用户提供以下选项:
<SERVICE_NAME> — no credential found after scanning all sources.
[I have it — let me paste it]
[Deep hunt — spawn an agent to find it]
[Skip this service]
-
"I have it" → 说明如何在服务的控制面板中找到凭据,然后接受自由文本输入。
-
"Deep hunt" → 在后台启动一个 Haiku 子 Agent,并向其下达以下任务:
查找 <CREDENTIAL_NAME>(<SERVICE_NAME>)。请全面搜索: 1. 所有 Doppler 项目和配置(dev/stg/prd/ci) 2. ~/Projects/ 下所有 .env* 文件(递归搜索) 3. macOS 钥匙串(使用 security find-generic-password,并尝试多种服务名称模式) 4. Dashlane CLI(dcli password <service>,以及相关关键词) 5. Chrome 浏览器——通过 Kapture/Playwright MCP 前往 <service_admin_url>,如有需要则登录,并从设置页面提取凭据 6. 所有 shell 配置文件(~/.zshrc、~/.bashrc、~/.zprofile、~/.envrc、~/.config/fish/*) 7. 1Password CLI(如果可用,使用 op item list --tags <service>) 8. AWS Secrets Manager / SSM Parameter Store(如果 aws cli 已通过身份验证) 如果找到,请返回凭据值;否则,请详细报告检查过的所有位置及发现的内容(部分匹配、过期令牌、格式错误的值)。使用
Agent(subagent_type: "general-purpose", model: "haiku"),并设置run_in_background: true。在搜索进行时,继续处理下一个服务。Agent 返回后,向用户展示搜索结果并请其确认。 -
“跳过” → 在
$PREFS_PATH中记录为已跳过,然后继续。
用户选择后,以选定的值为准,并在征得用户同意后,可选择将其同步到其他来源(例如:“也要更新 ~/.zshrc 和 Doppler 以保持一致吗?”)。默认不进行同步,除非用户主动选择同步。
共享:凭据自动扫描
本节专门适用于频道令牌(Telegram、Slack)。 其他所有步骤请参阅上方的通用凭据自动扫描部分——所有地方都适用相同的模式。
在提示用户粘贴任何令牌之前,请先使用上方的通用凭据自动扫描流程进行扫描。向用户展示找到的内容,并请其确认或覆盖。未经确认,切勿直接使用令牌。
3a — Telegram(通过 ops-telegram-autolink 使用用户账号进行身份验证)
启动 Telegram 流程前,务必先询问——即使用户选择了“所有频道”也不例外。使用 AskUserQuestion:
Set up Telegram personal account access?
[Yes — enter my phone number and authenticate]
[Skip Telegram]
如果用户跳过,请在 $PREFS_PATH 中记录 channels.telegram = "skipped",然后继续。切勿默默将 Telegram 标记为未配置——明确记录为跳过,可防止后续运行时状态标题将 ○ telegram (no token) 显示为待处理事项。
速率限制保护:启动前,检查 $PREFS_PATH 中的 channels.telegram 是否为对象,且 .status == "rate_limited"——使用类型守卫:if (channels.telegram | type) == "object" and .channels.telegram.status == "rate_limited"(jq:if (.channels.telegram | type) == "object" then .channels.telegram.status else "skipped" end)。如果 retry_after 的时间尚未到,请使用 AskUserQuestion 向用户展示:
Telegram is rate-limited until [time]. What would you like to do?
[Wait and retry after cooldown — re-run /ops:setup telegram after [time]]
[Skip Telegram for now]
在速率限制期间,切勿尝试 send_password——它会立即失败,并可能延长冷却时间。如果用户选择跳过,请在 $PREFS_PATH 中记录为跳过,然后继续下一个频道。
机器人无法读取用户的私信,因此 /ops-inbox telegram 需要使用个人账号的 MCP。该插件提供了 bin/ops-telegram-autolink.mjs,其功能如下:
- 扫描各个探查源(钥匙串 → ~/.claude.json → shell 配置文件 → Doppler),查找之前提取的
TELEGRAM_API_ID/TELEGRAM_API_HASH/TELEGRAM_SESSION。 - 如果未找到,则向
my.telegram.org发送普通 HTTP 请求(不使用浏览器——my.telegram.org使用服务端 HTML,无需 JS),使用桥接文件中的手机验证码登录,按需创建应用,并提取 api_id + api_hash。 - 运行 gram.js
client.start()生成会话字符串,通过同一文件传递第二个验证码。 - 在 stdout 上输出最终 JSON:
{api_id, api_hash, phone, session}。
子流程(仅在用户在上方选择“是”时运行):
-
先探查。 检查钥匙串中是否已有提取过的 Telegram 凭据:
for svc in telegram-api-id telegram-api-hash telegram-phone telegram-session; do security find-generic-password -s "$svc" -w 2>/dev/null && echo "FOUND: $svc" done另请检查
~/.claude.json mcpServers.telegram.env.TELEGRAM_API_ID。如果这 4 项都已找到,且存储的TELEGRAM_SESSION可解码为 StringSession,请告知用户"✓ Telegram already configured (api_id=XXXXXXX, phone=+XX...)"并跳至第 8 步。 -
通过
AskUserQuestion询问用户的电话号码,并提供一个自由文本选项。不要提供特定国家/地区的预设选项或示例号码,只提供一个提示用户直接输入的选项:Enter your Telegram phone number (include country code, e.g. +31612345678): [Enter phone number — type your full number starting with +]用户会选择此选项,并在“其他”自由文本字段中输入号码。验证号码是否匹配
^\+\d{7,15}$。说明该手机号仅在首次运行时提取过程中使用一次,并且只会存储在本地。 -
提醒用户有 2 个验证码。 通过
AskUserQuestion告知用户:"Telegram will send TWO codes to your Telegram app — one for my.telegram.org web login, then a second one for gram.js auth. Have your Telegram app ready."。选项:[I'm ready]、[Cancel]。 -
在后台以严格的文件权限启动自动关联脚本:
(umask 077 && node "${CLAUDE_PLUGIN_ROOT}/bin/ops-telegram-autolink.mjs" --phone "$PHONE" 2>/tmp/ops-telegram-autolink.log 1>/tmp/ops-telegram-autolink.out &) echo $! > /tmp/ops-telegram-autolink.pid使用 Bash 工具的
run_in_background: true。umask 077会将所有桥接文件(日志、输出文件)的权限设为 0600。.out文件包含完整的凭据 JSON,其中包括 gram.js 会话字符串——如果该文件对所有用户可读,任何本地进程都能窃取长期有效的 Telegram 账户访问权限。 -
轮询标准错误日志,查找
need_code事件。 每 3 秒读取一次/tmp/ops-telegram-autolink.log,查找尚未处理的最新一行{"type":"need_code", ...}。看到该行时:- 判断验证码类型:
channel: "web_login"(第一个)或channel: "gram_auth"(第二个)。 - 使用
AskUserQuestion提供自由文本输入:"Enter the code Telegram just sent to your Telegram app:"。不要说“只能输入数字”——Telegram Web 登录验证码可能包含字母、连字符和下划线(例如Zv_-ef77YSU)。自动关联脚本的桥接文件接受任意 3-20 位的字母数字、连字符和下划线字符串。 - 以严格的权限将验证码写入
/tmp/telegram-code.txt:Bash: (umask 077 && printf '%s' "$CODE" > /tmp/telegram-code.txt)。umask 077至关重要——没有它,在 macOS 上创建的文件将对所有用户可读(/tmp的权限为drwxrwxrwt),任何本地进程都能在 2 秒轮询窗口内抢先读取验证码。 - 在 10 秒内确认验证码已被读取:
ls /tmp/telegram-code.txt 2>/dev/null。如果 10 秒后文件仍存在,说明脚本的验证正则表达式拒绝了该验证码。查看日志中的错误。不要重新运行脚本或索取新验证码——这会消耗一次登录尝试。 - 等待下一个事件。
- 如果看到
{"type":"need_password"},则处理两步验证(2FA):通过AskUserQuestion询问用户,并使用(umask 077 && printf '%s' "$PW" > /tmp/telegram-password.txt)写入/tmp/telegram-password.txt。与验证码文件一样,必须加强权限保护。2FA 密码比一次性验证码敏感得多。
- 判断验证码类型:
-
等待脚本退出。 持续轮询,直到进程不再运行(
ps -p "$(cat /tmp/ops-telegram-autolink.pid)")。读取/tmp/ops-telegram-autolink.out——其中应包含一行 JSON,含有api_id、api_hash、phone和session。安全提示:设置技能应使用(umask 077 && node "${CLAUDE_PLUGIN_ROOT}/bin/ops-telegram-autolink.mjs" --phone "$PHONE" 2>/tmp/ops-telegram-autolink.log 1>/tmp/ops-telegram-autolink.out &)启动自动关联脚本,以确保.log和.out文件的权限为 0600。使用stat -f '%Lp' /tmp/ops-telegram-autolink.out验证,结果必须为600。将凭据读入内存后,立即使用shred -u(Linux)或rm -P(macOS)删除.out文件。错误恢复——至关重要:不要消耗登录尝试。 每次
send_password调用都会计入 Telegram 的速率限制(每 8 小时约 3-5 次)。如果自动关联失败:"could not extract ... after 6 extraction strategies"/ 提取失败:HTML 解析失败,但登录成功。不要重新运行脚本。请检查错误是否包含html_snippet——该片段显示了剥离后的页面文本。如果其中在 “api_id” 附近包含一个 5–12 位数字,并在 “api_hash” 附近包含一个 32 个字符的十六进制字符串,请直接从片段中用 grep/regex 提取。如果片段显示的是登录页面或重定向,则会话在提取过程中已过期。rate-limited:在$PREFS_PATH中记录channels.telegram.status = "rate_limited"和channels.telegram.retry_after(当前时间 + 8 小时)。继续处理下一个频道。在后续运行/ops:setup时,检查retry_after;如果冷却时间尚未结束,则跳过 Telegram。- 代码文件未被读取:如果写入
/tmp/telegram-code.txt后 10 秒以上该文件仍然存在,说明验证正则表达式拒绝了它。读取文件内容和日志。不要向用户索要另一个代码——原代码仍然有效,你只需要修复桥接程序。 - 一般规则:每个设置会话最多可尝试
send_password2 次。如果第一次尝试因非限流原因失败,请先诊断根本原因,再重试。如果第二次尝试失败,则保存状态并继续。
-
保存到钥匙串 + 偏好设置。 仅限 macOS:
security add-generic-password -U -s telegram-api-id -a "$USER" -w "$API_ID" security add-generic-password -U -s telegram-api-hash -a "$USER" -w "$API_HASH" security add-generic-password -U -s telegram-phone -a "$USER" -w "$PHONE" security add-generic-password -U -s telegram-session -a "$USER" -w "$SESSION"然后更新
$PREFS_PATH,写入channels.telegram = {backend: "gram.js", api_id: "...", phone: "...", status: "configured"}。绝不要将 api_hash 或会话写入 preferences.json——这些信息只能保存在钥匙串中。preferences.json 仅保存非敏感元数据。 -
自动配置 MCP 服务。 将凭据直接写入插件的用户配置,这样用户就不必手动粘贴任何内容:
# Read existing user config or create empty USER_CONFIG="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/user-config.json" mkdir -p "$(dirname "$USER_CONFIG")" # Write Telegram credentials to user config jq -n \ --arg api_id "$API_ID" \ --arg api_hash "$API_HASH" \ --arg phone "$PHONE" \ --arg session "$SESSION" \ '{telegram_api_id: $api_id, telegram_api_hash: $api_hash, telegram_phone: $phone, telegram_session: $session}' \ > "${USER_CONFIG}.tmp" # Merge with existing config if present if [ -f "$USER_CONFIG" ]; then jq -s '.[0] * .[1]' "$USER_CONFIG" "${USER_CONFIG}.tmp" > "${USER_CONFIG}.new" && mv "${USER_CONFIG}.new" "$USER_CONFIG" rm -f "${USER_CONFIG}.tmp" else mv "${USER_CONFIG}.tmp" "$USER_CONFIG" fi chmod 600 "$USER_CONFIG"如果
~/.claude.json中存在 telegram 服务条目,也要更新 MCP 服务配置——将凭据注入为环境变量:# Update .claude.json mcpServers.telegram.env with actual values CLAUDE_JSON="$HOME/.claude.json" if [ -f "$CLAUDE_JSON" ] && jq -e '.mcpServers.telegram' "$CLAUDE_JSON" >/dev/null 2>&1; then jq --arg id "$API_ID" --arg hash "$API_HASH" --arg phone "$PHONE" --arg session "$SESSION" \ '.mcpServers.telegram.env.TELEGRAM_API_ID = $id | .mcpServers.telegram.env.TELEGRAM_API_HASH = $hash | .mcpServers.telegram.env.TELEGRAM_PHONE = $phone | .mcpServers.telegram.env.TELEGRAM_SESSION = $session' \ "$CLAUDE_JSON" > "${CLAUDE_JSON}.tmp" && mv "${CLAUDE_JSON}.tmp" "$CLAUDE_JSON" fi打印:
✓ Telegram configured automatically. API ID: [api_id] Phone: [phone] Session: stored in keychain + MCP config Restart Claude Code to activate the Telegram MCP server. -
冒烟测试(可选)。 在环境变量内联设置的情况下,启动
node ${CLAUDE_PLUGIN_ROOT}/telegram-server/index.js并运行 3 秒。如果没有打印身份验证错误,则会话可用。
给用户的隐私说明(开始时显示一次):
- 电话号码和所有凭据都会保留在你的设备上。除 HTTP 登录流程期间与 Telegram 自有服务器通信外,向导绝不会将它们传输到任何其他地方。
- 如果你已有用于其他项目的 gram.js / Telethon 会话,可以跳过此步骤,手动将这些值粘贴到
/plugin settings。 - 如果 Telegram 回复 “Sorry, too many tries. Please try again later.”,说明你的账号已被限流约 8 小时——向导无法绕过此限制。请等待后重试。
**深入了解:**有关此集成的完整操作说明、CLI 参考和故障排除信息,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-comms/SKILL.md。如果设置 Agent 需要比本向导更深入的信息,可以直接加载该文件。
3b — WhatsApp(诊断 + 自我修复 + 回填)
WhatsApp 是最常在无提示的情况下出故障的频道。向导必须自动诊断、检查并修复,而不只是报告状态后放弃。请从头到尾运行整个子流程,只有在系统运行正常或用户拒绝修复时才停止。
步骤 3b.1 — 检查是否存在
运行 command -v wacli。如果未找到,请通过 AskUserQuestion 询问:[Show install docs]、[Skip WhatsApp]。如果选择安装文档,请打印:
wacli is not on Homebrew. Install:
git clone https://github.com/Lifecycle-Innovations-Limited/wacli ~/src/wacli
cd ~/src/wacli && go build -o /usr/local/bin/wacli ./cmd/wacli
然后停止此子流程。
步骤 3b.2 — 收集状态
并行运行以下命令:
wacli doctor --json 2>&1
wacli auth status --json 2>&1
wacli messages list --after="$(date -v-1d +%Y-%m-%d 2>/dev/null || date -d '1 day ago' +%Y-%m-%d)" --limit=5 --json 2>&1
wacli chats list --json 2>&1 | head -c 4000
解析:
doctor.data.authenticated(布尔值)doctor.data.lock_held+doctor.data.lock_info(PID + acquired_at 时间戳)doctor.data.fts_enabled(布尔值——如果为 false,则搜索功能降级,但不属于致命问题)messages.data.messages长度 → 这是关键的健康信号。已验证身份但过去 24 小时内没有任何消息 = 已损坏。chats数量及其是否已填充
步骤 3b.3 — 诊断并分类
按顺序应用以下规则。遇到第一个匹配项时停止。
A. 未通过身份验证
如果 doctor.authenticated: false → 打印 "WhatsApp 需要通过 QR 码配对。请在单独的终端中运行 wacli auth,并用手机扫描 QR 码(WhatsApp → 已关联的设备 → 关联设备),然后重新运行 /ops:setup whatsapp。" 子流程结束。不要尝试自动扫描 QR 码——这需要用户将手机摄像头对准终端(规则 2 的例外)。
B. 同步卡住(锁已过期)
如果 lock_held: true,且 lock_info.acquired_at 超过 2 分钟前,且持有锁的 PID 仍在运行(ps -p <pid>):
- 通过现有进程的 stderr 尾部输出,在后台运行
wacli sync(timeout 15 wacli sync 2>&1)——或者,如果无法通过 tee 接入,则回退到: - 询问
AskUserQuestion:"wacli sync 进程 (pid=N) 已持有存储锁 Xm。很可能已卡住。要终止它吗?"→[终止 pid N 并重启同步]、[让它继续运行]。 - 选择终止时:运行
kill <pid>(不要先用 -9)。等待 3 秒。如果仍在运行,运行kill -9 <pid>。通过ps -p <pid>确认。 - 终止后,重新运行
wacli doctor --json,确认锁已释放。继续下一条规则。
C. 应用状态密钥不同步(最常见问题)
运行 timeout 15 wacli sync 2>&1 | tee /tmp/wacli-sync-probe.log(必须在 B 之后执行,以确保锁已释放)。在输出中搜索:
didn't find app state key→ 会话密钥不同步,需要重新配对failed to decode app state→ 同类错误Failed to do initial fetch of app state→ 同类错误
如果有任一项匹配:
- 原样打印诊断结果:
⚠ WhatsApp session is authenticated but the app-state decryption keys are out of sync with your primary device. This happens when the linked-device session is partially wiped on the phone side. Symptom: sync runs but 0 messages come through. Fix: logout this session and re-pair via QR. - 询问
AskUserQuestion:[退出登录并引导我重新配对]、[跳过 — 我会手动修复]。 - 选择退出登录时:运行
wacli auth logout --json并显示结果。然后打印:Now run `wacli auth` in a separate terminal (QR-based auth — requires your phone camera). A QR code will appear — scan it from WhatsApp → Settings → Linked Devices → Link a device. When it says "Connected", come back and type "done". - 等待用户通过
AskUserQuestion确认:[完成 — 已重新配对]、[取消]。 - 选择“完成”后,重新运行步骤 3b.2 以重新收集状态,并继续规则 D。
D. 已通过身份验证、锁已释放、没有近期消息、没有密钥错误 这通常表示缓存为空。转到步骤 3b.4(回填)。
E. 状态正常(消息流动中)
如果 messages.data.messages 中有 ≥1 条来自最近 24 小时的消息,则打印 ✓ 摘要并跳至步骤 3b.5。
步骤 3b.4 — 历史回填(后台静默运行)
每次重新配对后都要运行此步骤;符合规则 D 时也要运行。除非用户明确拒绝,否则绝不跳过。
回填是后台优化操作——不应输出冗长内容或令人担忧的状态消息。请静默运行,并忽略非致命错误。
- 按最近活跃时间加载排名前 10 的聊天:
wacli chats list --json 2>&1 | jq -r '[.data[] | select(.jid) | {jid, name, last_msg: .last_message_ts}] | sort_by(.last_msg) | reverse | .[0:10]' - 告知用户:
"正在对最近的 10 个聊天运行历史回填。此操作将在后台运行。"不要打印每个聊天的进度或无消息结果。 - 对每个聊天 JID,按顺序运行(回填共享存储锁,无法并行):
wacli history backfill --chat="<jid>" --count=50 --requests=2 --wait=30s --idle-exit=5s --json 2>&1 - 禁止输出任何单个聊天的结果。 如果命令以非零状态退出,则静默忽略错误——回填失败不属于面向用户的事件。不要打印“已同步 0 条消息”、错误回溯或关于设备连接的说明。
- 循环完成后,只打印最终健康状态摘要(步骤 3b.6)。
步骤 3b.5 — FTS 索引检查(可选)
如果 doctor.fts_enabled: false,则打印:
ℹ Full-text search is disabled — `wacli messages search` will use SQL LIKE (slower).
This is a non-fatal known-limitation. See wacli docs to enable FTS5.
不要因此阻塞。
步骤 3b.6 — 记录状态
将 channels.whatsapp = "wacli" 写入 $PREFS_PATH,并打印最终的 ✓ 摘要:
✓ WhatsApp — wacli authenticated, N chats
摘要行中绝不包含消息数量或回填结果。
步骤 3b.7 — 持久连接(保持连接)
完成身份验证和历史记录回填后,设置持久连接,让 wacli 保持连接并自动同步。这能确保 WhatsApp 在不同会话间稳定运行——如果没有持久连接,关联设备会在闲置约 14 天后断开,且 @lid JID 会返回空消息。
如果已配置 ops-daemon(步骤 5b),wacli 将作为守护服务运行——请跳过下方独立的 launchd 路径,并告知用户 wacli 同步由守护服务管理。守护服务会集中处理初始化、自动回填和运行状况报告。
独立 launchd 备用方案(仅当未设置 ops-daemon 时):
1. 安装保持连接脚本:
KEEPALIVE_SCRIPT="${CLAUDE_PLUGIN_ROOT}/scripts/wacli-keepalive.sh"
chmod +x "$KEEPALIVE_SCRIPT"
2. 根据模板生成 launchd plist:
PLIST_TEMPLATE="${CLAUDE_PLUGIN_ROOT}/scripts/com.claude-ops.wacli-keepalive.plist"
PLIST_DEST="$HOME/Library/LaunchAgents/com.claude-ops.wacli-keepalive.plist"
LOG_DIR="$HOME/.claude/plugins/data/ops-ops-marketplace/logs"
mkdir -p "$LOG_DIR" "$HOME/Library/LaunchAgents"
# Resolve bash 4+ (same logic as daemon plist)
BASH_PATH="/bin/bash"
if [[ -x /opt/homebrew/bin/bash ]]; then
BASH_PATH="/opt/homebrew/bin/bash"
elif [[ -x /usr/local/bin/bash ]]; then
BASH_PATH="/usr/local/bin/bash"
fi
sed -e "s|__KEEPALIVE_SCRIPT_PATH__|$KEEPALIVE_SCRIPT|g" \
-e "s|__BASH_PATH__|$BASH_PATH|g" \
-e "s|__LOG_DIR__|$LOG_DIR|g" \
-e "s|__HOME__|$HOME|g" \
"$PLIST_TEMPLATE" > "$PLIST_DEST"
3. 加载 Agent:
# Unload if already loaded (idempotent)
launchctl bootout gui/$(id -u) "$PLIST_DEST" 2>/dev/null || true
launchctl bootstrap gui/$(id -u) "$PLIST_DEST"
4. 验证是否正在运行:
等待 3 秒,然后检查:
launchctl print gui/$(id -u)/com.claude-ops.wacli-keepalive 2>&1 | head -5
cat "$HOME/.wacli/.health" 2>/dev/null
如果运行状况文件显示 status=connected 或 status=needs_reauth,说明守护服务正在运行。输出:
✓ WhatsApp keepalive — launchd agent installed and running
Persistent sync active. Auto-restarts on disconnect.
Health: ~/.wacli/.health | Logs: ~/.claude/plugins/data/ops-ops-marketplace/logs/
如果为 status=needs_reauth,请立即按照步骤 3b.3 规则 C 触发重新配对流程。
5. 保持连接脚本如何自动修复问题:
保持连接脚本(wacli-keepalive.sh)会自动处理以下故障:
| 故障 | 自动修复 |
|---|---|
| 孤立的 wacli 进程占用锁 | 终止过期的 PID,清除锁 |
| 连接中断(WhatsApp 服务重启) | launchd 会在 60 秒内重启 |
| App-state 密钥不同步 | 将 needs_reauth 写入运行状况文件——ops 技能会检测到并提示扫描 QR 码 |
| 身份验证过期 | 写入 needs_auth——使用相同的提示流程 |
| 脚本崩溃 | launchd 的 KeepAlive=true 会立即重启(限流为 60 秒) |
6. 供其他 ops 技能使用的运行状况文件约定:
所有使用 WhatsApp 的 ops 技能(ops-inbox、ops-comms、ops-go)在尝试执行 wacli 命令前,MUST 检查 ~/.wacli/.health。如果状态为 status=needs_auth 或 status=needs_reauth:
- 向用户输出诊断信息:
⚠ WhatsApp needs re-authentication. Run `wacli auth` in a separate terminal and scan the QR code with your phone (QR-based auth — exception to Rule 2). Then type "done" to continue. - 使用
AskUserQuestion:[已完成 — 已重新配对]、[跳过 WhatsApp]。 - 用户选择“已完成”后:通过
launchctl kickstart -k gui/$(id -u)/com.claude-ops.wacli-keepalive重启保持连接守护服务,并等待 5 秒,让运行状况文件更新。
这样可确保用户不会在不知情的情况下遇到 WhatsApp 连接故障——每个 ops 技能都会提示问题并引导用户完成修复。
**深入了解:**有关此集成的完整操作说明、CLI 参考和故障排除信息,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-comms/SKILL.md和${CLAUDE_PLUGIN_ROOT}/skills/ops-inbox/SKILL.md。如果设置 Agent 需要比本向导更深入的信息,可以直接加载这些文件。
3c — 电子邮件
电子邮件有两种可能的后端,按以下顺序尝试:
首选:gog CLI
gog 是 ops-inbox 和 ops-comms 默认调用的电子邮件和日历 CLI。它是一个独立二进制文件,使用自己的 OAuth 令牌(位于 ~/.gog/token.json)——拥有完整的读取和发送权限,无需 Claude Desktop 配置。
- 使用
command -v gog检查gog是否位于 PATH 中。 - 如果已安装,运行
gog auth status 2>&1 || true并显示输出。- 如果身份验证状态为红色(未通过身份验证/令牌已过期/退出代码 != 0),请通过 Bash 工具运行
gog auth add "$USER_EMAIL" --services gmail,calendar,drive,contacts,docs,sheets,并设置run_in_background: true(这会打开浏览器进行 OAuth 流程)。告知用户:“正在打开浏览器进行 Gmail OAuth——请在那里完成登录,然后输入‘完成’。”使用AskUserQuestion:[已完成 — 已通过身份验证]、[跳过电子邮件]。 - 如果身份验证状态为绿色,请运行以下命令进行探测:
如果返回的 JSON 包含gog gmail labels list --json 2>&1 | head -5labels数组,则表示 gog 已通过身份验证且 Gmail API 正常工作。报告 ✓。如果输出为错误或为空,则视为不可用,并指导用户重新运行gog auth add <email> --services gmail,calendar,drive,contacts,docs,sheets。 - 在
$PREFS_PATH中记录channels.email = "gog",然后到此为止。
- 如果身份验证状态为红色(未通过身份验证/令牌已过期/退出代码 != 0),请通过 Bash 工具运行
备用方案:Claude Gmail MCP 连接器
如果 gog 不在 PATH 中,请查看检测器的 mcp_configured 数组,寻找与 gmail、google-mail 或 claude_ai_Gmail 匹配的条目(不区分大小写)——这些是 Anthropic Gmail 连接器或用户安装的 Gmail MCP 服务的常见名称。
-
如果已配置 Gmail MCP,请询问
AskUserQuestion:[使用 Gmail MCP(只读备用方案)][改为安装 gog — 显示文档][跳过电子邮件]
-
如果选择“使用 Gmail MCP”,在
$PREFS_PATH中记录channels.email = "mcp:<name>"(其中<name>是找到的实际 MCP 服务名称),并原样打印此警告:⚠ Using the Gmail MCP connector as a fallback. Read operations (list inbox, search, fetch) will work. SEND operations will fail until you explicitly grant send permissions in Claude Desktop → Settings → Connectors → Gmail → Permissions. The ops plugin cannot grant those permissions for you — it's a Claude Desktop-side setting tied to your account. If you want unattended sending from ops-comms, install `gog` instead. -
如果选择“改为安装 gog”,打印适用于相应操作系统的安装命令:
操作系统 命令 macOS / Linuxbrew brew install gogcliWindows winget install -e --id steipete.gogcliArch Linux yay -S gogcli从源代码安装 git clone https://github.com/steipete/gogcli.git && cd gogcli && make文档:https://gogcli.sh/ · 仓库:https://github.com/steipete/gogcli
安装后,每个账户授权一次:
gog auth credentials /path/to/client_secret.json gog auth add [email protected] --services gmail,calendar,drive,contacts,docs,sheets刷新令牌存储在操作系统密钥环中(macOS 上为 Keychain,Linux 上为 Secret Service / libsecret,Windows 上为 Credential Manager)。然后结束此子流程,并等待用户重新运行
/ops:setup email。
两者均不可用
- 如果缺少
gog且未配置 Gmail MCP,请询问AskUserQuestion:[安装 gog — 显示文档][添加 Gmail MCP — 显示文档]→ 打印claude mcp add gmail,并告知用户之后重新运行/ops:setup email[暂时跳过电子邮件]
- 无论用户选择哪项,都要在
$PREFS_PATH中记录最终状态(设置channels.email = "gog"、channels.email = "mcp:<name>",或完全省略此键)。
**深入了解:**完整的操作说明、CLI 参考和此集成的故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-inbox/SKILL.md和${CLAUDE_PLUGIN_ROOT}/skills/ops-comms/SKILL.md。如果配置 Agent 需要比此向导更深入的信息,可以直接加载这些文件。
3d — Slack(scout + ops-slack-autolink)
Slack 官方 API 的大多数实用权限范围都需要工作区管理员批准。slack-mcp-server MCP 使用浏览器会话令牌(xoxc + xoxd),这些令牌按用户区分——无需管理员批准。此插件提供 bin/ops-slack-autolink.mjs,它会:
- 阶段 1 — scout — 检查以下位置是否已有提取的令牌:
~/.claude.json mcpServers.slack.env(Claude Code 存储令牌的位置)- 进程环境变量(
SLACK_MCP_XOXC_TOKEN/SLACK_MCP_XOXD_TOKEN/SLACK_BOT_TOKEN) - macOS 钥匙串(
slack-xoxc、slack-xoxd) - Shell 配置文件(
~/.zshrc、~/.bashrc、~/.zprofile、~/.envrc) - Doppler(
doppler secrets --json)
- 阶段 2 — Playwright 提取 — 仅在未找到任何令牌时,启动使用持久配置文件的 Chromium,打开
https://app.slack.com/client/,请用户登录(或在无头运行时使用现有会话),然后从localStorage.localConfig_v2.teams[teamId].token中提取xoxc-...,并从 Cookie 存储中提取d=...Cookie(xoxd-...)。
从 maorfr/slack-token-extractor 移植而来(Python → Node)。
子流程:
-
**先进行 scout 检查。**运行:
node "${CLAUDE_PLUGIN_ROOT}/bin/ops-slack-autolink.mjs" --scout-only 2>/tmp/ops-slack.log解析标准输出中的 JSON。如果结果非空,且包含
xoxc_token和xoxd_token,则报告"✓ Slack already configured (source=XXX)"并跳到步骤 5。 -
如果没有现有令牌,通过
AskUserQuestion提问:[通过 Playwright 提取令牌(推荐)]→ 以有头模式运行自动关联。[我会手动粘贴令牌]→ 通过两个自由文本AskUserQuestion分别收集xoxc-...和xoxd-...。[跳过 Slack]
-
使用 Playwright 路径:在后台启动 autolink:
(umask 077 && node "${CLAUDE_PLUGIN_ROOT}/bin/ops-slack-autolink.mjs" \ --workspace "https://app.slack.com/client/" \ 2>/tmp/ops-slack-autolink.log 1>/tmp/ops-slack-autolink.out &) echo $! > /tmp/ops-slack-autolink.pid轮询日志,查找
{"type":"need_login"}。看到后,使用AskUserQuestion:"A Chromium window should be open on your desktop. Log in to Slack there, then pick [Done]."。选择 Done 后,执行touch /tmp/slack-login-done。脚本会完成运行,并将提取的令牌写入/tmp/ops-slack-autolink.out。 -
如果未安装 Playwright(脚本退出并显示
playwright is not installed),请提供以下选项:[Install Playwright now]→ 运行cd ${CLAUDE_PLUGIN_ROOT}/telegram-server && npm install playwright && npx playwright install chromium(后台运行,下载约 150MB,并报告进度)。[Fall back to manual paste]→ 转到第 2 步的手动路径。
-
验证令牌。 使用准确语法调用 Slack 身份验证端点:
curl -s -H "Authorization: Bearer XOXC_TOKEN" -b "d=XOXD_TOKEN" "https://slack.com/api/auth.test"预期结果为
{"ok":true, "team_id":"T...", "user_id":"U...", "url":"https://<workspace>.slack.com/"}。如果ok:false,显示错误并重新询问。 -
持久化。
- Keychain:
security add-generic-password -U -s slack-xoxc -a "$USER" -w "$XOXC"; security add-generic-password -U -s slack-xoxd -a "$USER" -w "$XOXD"。 $PREFS_PATH→channels.slack = {backend: "mcp:slack", team_id: "...", source: "...", status: "configured"}。不要将原始令牌存入 preferences.json——仅存储在 Keychain 中。
- Keychain:
-
**接入 Claude Code 插件设置。**打印以下说明:
Slack tokens saved to keychain. To activate the MCP, Claude Code needs them in ~/.claude.json. Since this skill can't write to ~/.claude.json directly, either: a) Run: claude mcp add slack --transport stdio -- npx -y slack-mcp-server@latest --transport stdio and Claude Code will prompt for the env vars. b) Manually paste the xoxc + xoxd into /plugin settings for the Slack MCP.(不自动写入的原因:根据用户反馈,~/.claude.json 是 Claude Code 的内部文件,插件不得修改。MCP 注册由 Claude Code 负责。)
-
冒烟测试:使用令牌调用
https://slack.com/api/conversations.list?limit=1。预期返回ok:true,且响应中至少包含一个频道。
隐私说明:
- 只要浏览器会话保持有效,令牌就可以使用——如果定期使用 Slack,通常可持续数周到数月。如果 MCP 开始返回 401,请重新运行
/ops:setup slack。 - 退出 Slack 会使
dCookie 失效,导致 MCP 无法使用。请使用/ops:setup slack重新提取令牌。 - Slack 的服务条款允许将个人会话令牌用于自己的账户。请勿使用此流程访问不属于自己的账户。
**深入了解:**有关此集成的完整操作说明、CLI 参考和故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-comms/SKILL.md和${CLAUDE_PLUGIN_ROOT}/skills/ops-inbox/SKILL.md。当设置 Agent 需要比此向导更详细的信息时,可直接加载这些文件。
3e — Notion(MCP 集成)
开始 Notion 流程前务必先询问——即使用户选择了“所有频道”也不例外。使用 AskUserQuestion:
Set up Notion workspace integration?
[Yes — configure Notion]
[Skip Notion]
如果用户跳过,请在 $PREFS_PATH 中记录 channels.notion = "skipped",然后继续。
检测
-
**检查现有的 claude.ai Notion 集成。**扫描检测器的
mcp_configured数组,查找匹配Notion、claude_ai_Notion或notion的条目。如果找到,将NOTION_MCP_ENABLED=true,然后跳至验证步骤。 -
**检查自托管的 Notion MCP 服务。**在
~/.claude/settings.json中查找mcpServers.notion,或查找参数/命令中包含notion的条目。
设置路径
路径 A — Claude.ai 集成(推荐):
如果未检测到现有集成,请使用 AskUserQuestion:
How would you like to connect Notion?
[Claude.ai integration (Recommended) — add via claude.ai settings]
[Self-hosted MCP — use your own Notion API key]
[Skip Notion]
对于 claude.ai 集成:
- 告诉用户:“前往 claude.ai > Settings > Integrations > Notion 添加 Notion 集成。授权访问你的工作区,然后输入 ‘done’。”
- 使用
AskUserQuestion:[Done — connected]、[Skip Notion] - 用户选择“Done”后,使用简单查询测试
mcp__claude_ai_Notion__notion-search进行验证
路径 B — 自托管 MCP:
- 在 Keychain 中查找现有的 Notion API 密钥:
security find-generic-password -s "notion-api-key" -w 2>/dev/null || \ security find-generic-password -s "NOTION_API_KEY" -w 2>/dev/null || echo "" - 如果未找到,询问用户:
Enter your Notion integration token (starts with ntn_): Create one at https://www.notion.so/my-integrations [Paste token now] [Skip Notion] - 存储令牌:
security add-generic-password -s "notion-api-key" -a "claude-ops" -w "$TOKEN" -U - 在
~/.claude/settings.json的mcpServers.notion下添加 MCP 服务配置
验证
测试集成(在后台运行):
# For claude.ai: integration auto-detected — test via MCP tool call
# For self-hosted: verify API key works
if [ -n "$NOTION_API_KEY" ]; then
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $NOTION_API_KEY" -H "Notion-Version: 2022-06-28" https://api.notion.com/v1/users/me | grep -q "200" && echo "OK" || echo "FAIL"
else
echo "OK — claude.ai integration (verify via MCP tool call after restart)"
fi
完成配置
- 在
~/.claude/settings.json的 env 部分设置NOTION_MCP_ENABLED=true - 在
$PREFS_PATH中记录channels.notion = {"backend": "mcp:notion", "status": "configured", "source": "<claude-ai|self-hosted>"} - 将
"notion"添加到$PREFS_PATH中的default_channels数组 - 输出:
✓ Notion — workspace connected via [source]
深入了解: 完整的 Notion MCP 工具参考和故障排除说明,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-inbox/CHANNELS.md。
3f — 日历
日历不是消息渠道,但其他每项 ops 技能(简报、/ops-next、/ops-go)都能从了解用户的日程安排中获益良多——例如占用深度工作时间的会议、部署窗口和出行日。向导会以与电子邮件相同的方式进行配置:优先使用 gog calendar,其次使用 Google Calendar MCP 连接器。
首选:gog calendar
gog(即 gogcli 二进制文件——参见第 3c 步)已能处理电子邮件;同一二进制文件还提供 gog calendar,并使用相同的 OAuth 令牌。如果第 3c 步顺利完成,就不需要额外授权。注意:gog cal 不是有效别名——始终使用 gog calendar。
- 使用
command -v gog检查gog是否在 PATH 中。 - 如果已安装,且已在第 3c 步完成授权,则进行探测:
如果返回包含日历元数据的 JSON,则在gog calendar calendars --json 2>&1 | head -20$PREFS_PATH中记录channels.calendar = "gog",并输出✓ Calendar — gog calendar。到此为止。 - 如果已安装 gog,但缺少日历权限范围(常见错误:
insufficient scope或403 insufficient_permissions),则输出:
不要尝试通过技能重新授权——这是一个浏览器流程。Your gog OAuth token doesn't include the calendar scope. Re-add the account with the calendar service via Bash tool with `run_in_background: true`: gog auth add "$USER_EMAIL" --services gmail,calendar,drive,contacts,docs,sheets Tell the user: "Opening browser for Calendar OAuth — complete the sign-in there, then type 'done'." Use `AskUserQuestion`: `[Done — re-authorized]`, `[Skip calendar]`.
后备方案:Claude Google Calendar MCP 连接器
- 如果 gog 不在 PATH 中,则扫描检测器的
mcp_configured数组,查找匹配(不区分大小写)calendar、google-calendar或claude_ai_Calendar的条目。 - 如果找到,则询问
AskUserQuestion:[使用 Google Calendar MCP(只读后备方案)][改为安装 gog — 查看文档][跳过日历]
- 选择“使用 Google Calendar MCP”后,在
$PREFS_PATH中记录channels.calendar = "mcp:<name>",并逐字输出以下警告:⚠ Using the Google Calendar MCP connector as a fallback. Read operations (list calendars, fetch events, check free/busy) will work. WRITE operations (create events, decline meetings, reschedule) will fail until you explicitly grant write permissions in Claude Desktop → Settings → Connectors → Google Calendar → Permissions. The ops plugin cannot grant those permissions for you. If you want ops-next to auto-block focus time or ops-comms to confirm meetings, install `gog` instead. - 选择“改为安装 gog”后,通过 Bash 执行安装(规则 2),使用与第 3c 步相同的 npm → bun → source-clone 链。可以直接嵌入代码片段,也可以调用
${CLAUDE_PLUGIN_ROOT}/bin/ops-setup-install gog(根据规则 4 在后台运行)。只有四次尝试全部失败后,才改为输出手动操作说明。
两者都不可用
- 如果缺少
gog,且未配置 Calendar MCP,则询问:[安装 gog — 查看文档][添加 Google Calendar MCP — 查看文档]→ 输出claude mcp add google-calendar,并告知用户之后重新运行/ops:setup calendar[跳过日历]
为什么这很重要(技能背景)
下游技能(/ops-go、/ops-next、/ops-fires)会从 $PREFS_PATH 读取 channels.calendar,以决定是否将今天的日程与其输出进行交叉关联:
- 简报会提示“你下午 2 点有个站会,所以现在不要开始重构”
/ops-next会在距离会议不到 30 分钟时降低深度工作的优先级- 如果生产事故发生在预定通话期间,
/ops-fires会发出警告 因此,对于希望获得具备上下文感知能力的简报的用户,本节不可跳过。
深入了解: 有关此集成的完整操作说明、CLI 参考和故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-go/SKILL.md(日历上下文会提供给/ops:go简报)。如果设置 Agent 需要比本向导更详细的信息,可以直接加载该文件。
3g — Doppler(密钥管理)
Doppler 是一款密钥管理器,可在运行时注入环境变量。配置后,所有 ops 技能都可以通过 doppler secrets get 查询密钥,而不必从点文件或钥匙串读取。向导会检查其是否存在、授权状态以及默认项目上下文。
步骤 3g.1 — 检查是否已安装
command -v doppler
如果未安装,通过 uname -s / $OSTYPE / $OS 检测主机操作系统,并选择相应的安装命令。通过 AskUserQuestion 询问:
Doppler CLI is not installed.
[Install now — <os-specific command>]
[Skip Doppler]
操作系统对应的命令如下:
| 操作系统 | 安装命令 |
|---|---|
| macOS / Linuxbrew | brew install dopplerhq/cli/doppler |
| Debian / Ubuntu | curl -Ls https://cli.doppler.com/install.sh | sudo sh |
| Fedora / RHEL | sudo rpm --import https://packages.doppler.com/public.key && sudo dnf install -y doppler |
| Arch Linux | yay -S doppler-cli |
| Alpine | apk add --no-cache doppler-cli |
| Windows (winget) | winget install Doppler.doppler |
| Windows (scoop) | scoop bucket add doppler https://github.com/DopplerHQ/scoop-doppler.git; scoop install doppler |
在后台运行所选命令,捕获 stdout/stderr,并报告成功或失败。如果用户跳过,将 secrets_manager: "none" 写入 $PREFS_PATH,然后结束此子流程。
步骤 3g.2 — 身份验证状态
运行:
doppler me --json 2>&1
解析 JSON。如果输出包含 "error",或退出代码非零,则表示用户尚未通过身份验证。打印:
Doppler is not authenticated. Running `doppler login` now...
通过 Bash 工具运行 doppler login,并设置 run_in_background: true(此命令会打开浏览器以完成 OAuth 流程)。告诉用户:“正在打开浏览器进行 Doppler OAuth 身份验证——请在那里完成登录,然后输入 ‘done’。”使用 AskUserQuestion:[完成 — 已通过身份验证]、[跳过 Doppler]。用户选择“完成”后,重新运行 doppler me --json 进行验证。如果已通过身份验证,doppler me 将返回包含 name 和 email 的 JSON — 请确认:
✓ Doppler authenticated as <name> (<email>)
除非姓名或电子邮件来自本会话中 doppler me 的输出,否则绝不显示它们。
步骤 3g.3 — 项目上下文
如果已通过身份验证,列出可用项目:
doppler projects --json 2>&1
解析项目对象数组。通过 AskUserQuestion 并设置 singleSelect 展示这些项目。每次调用最多提供 4 个选项 — 如果项目超过 3 个,则分页显示:每页展示 3 个项目和 [更多项目...];最后一页始终将 [跳过 — 不设置默认项目] 作为最后一个选项。
Select your default Doppler project (page 1):
[ ] my-app
[ ] my-api
[ ] my-service
[ ] More projects...
如果用户选择了项目,获取该项目的配置:
doppler configs --project <selected_project> --json 2>&1
通过 AskUserQuestion 并设置 singleSelect 展示可用配置(最多 4 个选项 — 必要时分页):
Select the default config for <project>:
[ ] dev
[ ] staging
[ ] production
将所选内容写入 $PREFS_PATH(合并,不要覆盖):
{
"secrets_manager": "doppler",
"doppler": {
"project": "<selected>",
"config": "<selected>"
}
}
打印确认信息:
✓ Doppler default context set: <project>/<config>
步骤 3g.4 — 向 Agent 提供文档
打印以下说明,使其在会话中可见:
All ops skills can now query secrets via:
doppler secrets get <KEY> --plain --project <project> --config <config>
For example:
doppler secrets get TELEGRAM_BOT_TOKEN --plain --project my-app --config dev
The project and config above are the defaults saved to preferences.
Individual skills can override with --project / --config flags.
**深入了解:**Doppler 未提供专用技能 — 有关下游技能如何使用
$PREFS_PATH中的secrets_manager/doppler.*值,以及如何在运行时解析doppler:KEY_NAME引用,请参阅${CLAUDE_PLUGIN_ROOT}/docs/memories-system.md(Runtime Context 部分)。如果设置 Agent 需要比此向导更深入的信息,可以直接加载该文件。
步骤 3g.5 — Doppler MCP 服务
配置并完成 CLI 身份验证后,询问用户是否要设置官方 @dopplerhq/mcp-server MCP 集成。这样 Claude 无需调用 shell 命令,即可直接使用 Doppler 密钥工具。
-
检查是否可用:在后台运行
npx -y @dopplerhq/mcp-server --help 2>&1。如果退出码为 0,则表示该软件包可用。 -
生成服务令牌:如果用户在步骤 3g.3 中选择了项目/配置,则生成一个限定范围的令牌:
doppler configs tokens create mcp-server-token --project <project> --config <config> --plain 2>/dev/null如果命令失败,或用户未选择项目/配置,则询问:
Doppler MCP Server needs a token. Options: [Generate from CLI (requires project/config)] [Paste a token manually] [Skip MCP server] -
将令牌保存到 userConfig:将令牌写入插件的
userConfig中的doppler_token(运行时会通过${user_config.doppler_token}将其传入.mcp.json)。如果用户选择了项目/配置,也要保存doppler_project和doppler_config。 -
冒烟测试:验证 MCP 服务能否启动:
DOPPLER_TOKEN="<token>" timeout 10 npx -y @dopplerhq/mcp-server --help 2>&1如果退出码为 0,则表示服务正常运行。
-
确认:
✓ Doppler MCP Server configured — secrets accessible via MCP tools (mcp__doppler__*) -
给 Agent 的说明:
With the MCP server configured, skills can now query secrets directly via MCP tool calls (mcp__doppler__*) instead of shelling out to `doppler secrets get`. The Doppler CLI remains available as a fallback when the MCP server is unavailable.
3h — 密码管理器(凭证库)
Ops Agent 经常需要代用户查找凭证(API 密钥、数据库密码、服务令牌)。此步骤会配置密码管理器,以便通过存储在 $PREFS_PATH 中的标准命令模板自动执行这些查询。
步骤 3h.1 — 自动检测已安装的管理器
并行运行以下命令:
command -v op 2>/dev/null && op account list --format=json 2>&1 # 1Password CLI
command -v dcli 2>/dev/null && dcli sync 2>&1 # Dashlane CLI
command -v bw 2>/dev/null && bw status --raw 2>&1 # Bitwarden CLI
security find-generic-password -s "test" 2>&1 | head -1 # macOS Keychain (always available)
解析每项结果,并将其归类为 authenticated、needs_unlock、not_installed 或 available(Keychain 始终为 available)。
步骤 3h.2 — 展示检测结果
通过 AskUserQuestion 仅展示检测到的内容。**每次调用最多提供 4 个选项。**由于始终会显示 macOS Keychain 和 Skip,每次调用最多只能再显示 2 个已检测到的管理器。如果 3 个 CLI(1Password、Dashlane、Bitwarden)均已安装,则分两次调用:
如果检测到的 CLI 管理器不超过 2 个(常见情况,可在一次调用中展示):
Password managers found:
[1Password — authenticated as <account>]
[Dashlane — needs unlock]
[macOS Keychain — always available]
[Skip — don't connect a password manager]
如果检测到 3 个 CLI 管理器(罕见情况,分两次调用): 调用 1:
[1Password — authenticated as <account>]
[Dashlane — needs unlock]
[Bitwarden — <status>]
[More options...]
调用 2:
[macOS Keychain — always available]
[Skip — don't connect a password manager]
不要显示未安装的管理器。始终显示 macOS Keychain 和 Skip。如果没有安装任何 CLI,则跳过后续步骤,仅显示 [macOS Keychain — 始终可用] 和 [Skip]。
步骤 3h.3 — 配置所选管理器
1Password(op):
- 检查身份验证:
op account list --format=json - 如果输出为空或退出码非零(未登录),则打印:
结束此子流程。1Password CLI is installed but not signed in. Run `op signin` via Bash tool with `run_in_background: true`, then re-run /ops:setup vault. - 如果已完成身份验证,则列出保险库,供用户选择默认保险库:
使用op vault list --format=jsonAskUserQuestion(单选)展示保险库名称。用户选择的保险库将成为password_manager_config.vault。 - 记录查询语法:
op item get "{{name}}" --fields label=password --format=json
Dashlane(dcli):
- 检查身份验证:
dcli sync - 如果
dcli sync失败或返回未配置错误,则打印:
结束此子流程。Dashlane CLI is installed but not configured. Run `dcli configure` via Bash tool, then re-run /ops:setup vault. - 记录查询语法:
dcli password --filter "{{name}}" --output json - 无需选择保险库——Dashlane 使用扁平命名空间。
Bitwarden(bw):
- 检查身份验证:运行
bw status --raw并解析 JSON 的status字段。"unauthenticated"→ 打印:
结束此子流程。Bitwarden CLI 已安装,但尚未登录。 使用 Bash 工具并设置 `run_in_background: true`,运行 `bw login`,然后重新运行 /ops:setup vault。"locked"→ 打印:
结束此子流程。Bitwarden 保险库已锁定。 使用 Bash 工具运行 `bw unlock --raw`,捕获会话令牌,并将其导出为 `BW_SESSION`,供后续命令使用。然后继续运行 /ops:setup vault。"unlocked"→ 继续。
- 记录查询语法:
bw get item "{{name}}" --pretty - 无需选择保险库——Bitwarden 在每个会话中使用一个已解锁的保险库。
macOS Keychain:
- 无需检查身份验证——始终可用。
- 给用户的说明:
macOS Keychain is always available but is limited to items stored locally. No cross-device sync. Best for machine-specific secrets (API keys added via `security add-generic-password`). - 记录查询语法:
security find-generic-password -s "{{name}}" -w
第 3h.4 步 — 写入偏好设置
用户选择并配置管理器后,写入 $PREFS_PATH:
{
"password_manager": "<1password|dashlane|bitwarden|keychain>",
"password_manager_config": {
"vault": "<vault name, or omit if not applicable>",
"query_cmd": "<template with {{name}} placeholder>"
}
}
与现有文件合并(jq '. + { ... }')——绝不覆盖。以下是 1Password 的示例:
{
"password_manager": "1password",
"password_manager_config": {
"vault": "Private",
"query_cmd": "op item get \"{{name}}\" --fields label=password --format=json"
}
}
如果用户选择跳过,则写入 "password_manager": "none",这样后续运行时就不会再次询问,除非用户明确运行 /ops:setup vault。
第 3h.5 步 — 为 Agent 编写说明
保存后,打印以下说明一次:
All ops skills can now query credentials via your configured password manager.
The query command template is in preferences.json under password_manager_config.query_cmd.
Replace {{name}} with the item name — e.g. "GitHub PAT", "AWS root key", "my-project-db".
To query manually:
op item get "GitHub PAT" --fields label=password --format=json (1Password example)
security find-generic-password -s "my-project-db" -w (Keychain example)
仪表板显示
更新第 0b 步的状态标题,加入保险库状态:
Vault: ✓ 1password (vault: Private)
如果跳过,则使用 ○ none;如果管理器已安装但无法访问,则使用 ✗ locked。
完成摘要(第 8 步)
在最终摘要块中加入:
✓ Vault: 1password → Private vault
如果 password_manager 为 "none" 或未设置,则完全省略此行。
调用快捷方式
在快捷方式表中添加:vault、password-manager、pm → 第 3h 步
深入了解: 密码管理器集成不附带专用技能——如何由下游技能从
$PREFS_PATH解析password_manager和相关保险库引用,请参阅${CLAUDE_PLUGIN_ROOT}/docs/memories-system.md(Runtime Context 部分)。隐私与安全指南位于此 SKILL.md 中(仅在钥匙串中存储 API 哈希/会话字符串,桥接文件使用umask 077)。如果设置 Agent 需要比此向导提供的更深入的信息,可以直接加载该文件。
3i — 电商(Shopify + 动态合作方)
第 3i.1 步 — 自动扫描现有 Shopify 凭据
在询问任何信息之前,同时对所有 Shopify 相关变量运行通用凭据自动扫描:
# --- Token scan (API credentials) ---
# Scan shell env
printenv SHOPIFY_ACCESS_TOKEN SHOPIFY_ADMIN_TOKEN SHOPIFY_STORE_URL SHOPIFY_ADMIN_API_ACCESS_TOKEN 2>/dev/null
# Scan shell profiles
grep -h 'SHOPIFY\|myshopify' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'
# Scan Doppler across all projects
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do
doppler secrets --project "$proj" --config prd --json 2>/dev/null | \
jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("SHOPIFY|STORE")) | "\(.key)=\(.value.computed) (doppler:\($proj)/prd)"'
done
# Scan Dashlane for API tokens
dcli password shopify --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != "") | "\(.title): \(.url) → token found"'
# Scan macOS Keychain
security find-generic-password -s "shopify-admin-token" -w 2>/dev/null
security find-generic-password -s "shopify-access-token" -w 2>/dev/null
# Scan OpenClaw
jq -r '.agents.defaults.env | to_entries[] | select(.key | test("SHOPIFY")) | "\(.key)=\(.value)"' ~/.openclaw/openclaw.json 2>/dev/null
# Check existing prefs
jq -r '.ecom.shopify // empty' "$PREFS_PATH" 2>/dev/null
# --- Store URL discovery (even if no tokens found) ---
# Scan Chrome history for myshopify.com admin URLs
sqlite3 ~/Library/Application\ Support/Google/Chrome/Default/History \
"SELECT DISTINCT replace(replace(url, 'https://', ''), 'http://', '') FROM urls WHERE url LIKE '%myshopify.com/admin%' OR url LIKE '%admin.shopify.com/store/%' ORDER BY last_visit_time DESC LIMIT 10" 2>/dev/null | \
grep -oE '[a-z0-9-]+\.myshopify\.com|admin\.shopify\.com/store/[a-z0-9-]+' | sort -u
# Scan Dashlane URLs for myshopify.com store references
dcli password shopify --output json 2>/dev/null | jq -r '.[].url // empty' | grep -oE '[a-z0-9-]+\.myshopify\.com' | sort -u
# Scan project .env files for store URLs
grep -rhE 'myshopify\.com|SHOPIFY_STORE' ~/Projects/*/.env* 2>/dev/null | grep -v '^#' | head -5
重要:只有在检查完所有扫描来源后,才能报告“未找到 Shopify 凭据”。如果缺少令牌,但找到了商店 URL(例如,在 Chrome 历史记录或 Dashlane 中),则报告:"Found Shopify store(s): <stores>. No API token found — you'll need to create one.",并直接跳到第 3i.3 步(令牌),同时预填商店 URL。
如果已找到 store_url 和 admin_token,则显示:
✓ Shopify — already configured (<store_url>)
[Keep existing] [Reconfigure]
如果用户选择保留现有配置,则跳到第 3i.4 步。如果用户要重新配置或未找到任何值,则继续。
第 3i.2 步 — Shopify 商店 URL
如果在自动扫描中找到 SHOPIFY_STORE_URL,则使用通用凭据自动扫描提示格式显示该值。只有在未找到值时,才通过自由文本询问:
Enter your Shopify store URL:
Format: yourstore.myshopify.com
(Do not include https://)
验证输入:移除 https://,移除末尾斜杠,并检查结果是否以 .myshopify.com 结尾。如果无效,则附上更正说明并重新询问。
第 3i.3 步 — Shopify Admin API 令牌
如果在自动扫描中找到 SHOPIFY_ACCESS_TOKEN、SHOPIFY_ADMIN_TOKEN 或 SHOPIFY_ADMIN_API_ACCESS_TOKEN,请使用通用凭据自动扫描提示格式展示,并截断显示(shpat_508b...682e)。只有在未找到值时,才通过自由文本询问。
多店铺处理:发现多个店铺时,请分别处理每个店铺。对于没有令牌的店铺,请先尝试自动化方式:
- 检查所有项目中的 Doppler,查找特定店铺的 Shopify 令牌:
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do doppler secrets --project "$proj" --config prd --json 2>/dev/null | \ jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("SHOPIFY.*TOKEN|SHOPIFY.*ACCESS"; "i")) | "\(.key)=\(.value.computed | .[0:12])... (doppler:\($proj)/prd)"' done - 如果已安装 Shopify CLI(
command -v shopify),请尝试使用它:
成功完成身份验证后,通过 CLI 生成自定义应用令牌。这样可以避免手动浏览管理后台。shopify auth logout 2>/dev/null # Clear stale session shopify auth login --store <store>.myshopify.com 2>&1 # Opens browser OAuth - 浏览器自动化 — 如果 Kapture/Playwright MCP 可用,请访问
https://admin.shopify.com/store/<slug>/settings/apps/development,并自动执行“创建应用”→“配置范围”→“安装”→“显示令牌”流程。使用以下范围:read_orders,read_products,read_customers,read_inventory,read_fulfillments,read_analytics。 - 手动备用方案 — 仅在所有自动化方式均失败时使用:
No automated path available for <store>.myshopify.com. To generate a token manually: 1. Go to https://admin.shopify.com/store/<slug>/settings/apps/development 2. Create an app → Configure → grant scopes → Install → copy token Token starts with "shpat_"
不要仅仅因为没有找到令牌就跳过某个店铺——务必先尝试自动化。用户希望向导负责生成凭据,而不只是查找凭据。
保存到 $PREFS_PATH 下的 ecom.shopify。应用 Doppler 引用模式——如果已配置 Doppler,请运行:
doppler secrets set SHOPIFY_ADMIN_TOKEN="<token>" --project <project> --config <config>
并在偏好设置中存储 "admin_token": "doppler:SHOPIFY_ADMIN_TOKEN",而不是原始令牌。
冒烟测试:
STORE=$(jq -r '.ecom.shopify.store_url' "$PREFS_PATH")
TOKEN=$(jq -r '.ecom.shopify.admin_token' "$PREFS_PATH")
if [[ "$TOKEN" == doppler:* ]]; then
KEY="${TOKEN#doppler:}"
TOKEN=$(doppler secrets get "$KEY" --plain 2>/dev/null)
fi
curl -s -H "X-Shopify-Access-Token: $TOKEN" \
"https://$STORE/admin/api/2024-10/shop.json" | jq '.shop.name'
预期结果为店铺名称字符串。如果响应中包含 errors 或 {"shop":null},请显示错误并让用户检查令牌范围。打印 ✓ Shopify — 已连接(<shop name>)。
步骤 3i.4 — 动态电商合作伙伴
配置好 Shopify 后,通过 AskUserQuestion 提问(自由文本):
Do you use any other ecommerce tools you'd like to connect?
Examples: ShipBob (fulfillment), Recharge (subscriptions), Yotpo (reviews),
Shippo (shipping rates), Gorgias (support), Attentive (SMS), Loop (returns)
Type the names separated by commas, or leave blank to skip.
如果用户提供了合作伙伴名称,请循环处理每个名称:
对于每个合作伙伴:
-
查询凭据 — 网页搜索:
"<partner name> API authentication developer docs 2025"。确定:- 需要哪些凭据(API 密钥、OAuth 令牌、Webhook 密钥、基础 URL、账户 ID 等)
- API 基础 URL
- 适合用于冒烟测试的健康检查/身份验证端点(例如
/me、/account、/v1/ping,或限制为 1 条的列表端点) - 身份验证请求头格式(
Authorization: Bearer、X-Api-Key、自定义请求头等)
-
通过
AskUserQuestion询问每项凭据(每个凭据字段提一个问题),根据文档说明凭据的获取位置。以 ShipBob 为例: 在提问前,对SHIPBOB_ACCESS_TOKEN、SHIPBOB_API_TOKEN运行 Universal Credential Auto-Scan。如果找到凭据,请注明来源并显示。只有在未找到时才手动询问:Enter your ShipBob Personal Access Token: To generate: ShipBob dashboard → Integrations → API → Personal Access Tokens → Create Token -
冒烟测试,使用步骤 1 中找到的身份验证端点:
curl -s -H "<auth_header>: $TOKEN" "<base_url>/<health_endpoint>" | jq '.<identity_field>'显示结果。如果失败,显示原始响应,并提供
[重新输入凭据]/[跳过此合作伙伴]选项。 -
将信息保存到偏好设置,路径为
ecom.partners.<partner_slug>,其中partner_slug是合作伙伴名称转换为小写并以连字符连接后的形式:{ "ecom": { "partners": { "shipbob": { "api_base_url": "https://api.shipbob.com/v1", "auth_pattern": "Authorization: Bearer <token>", "credentials": { "api_token": "doppler:SHIPBOB_API_TOKEN" }, "health_endpoint": "/user", "configured_at": "<ISO timestamp>" } } } }配置了 Doppler 时,将实际密钥存储在 Doppler 中(密钥:
<PARTNER_SLUG_UPPER>_API_TOKEN),否则内联存储。auth_pattern和api_base_url字段是未来/ops:ecom调用用于访问合作伙伴的信息,务必根据查询到的文档填写。 -
打印确认信息:
✓ <Partner Name> — connected -
循环:处理完每个合作伙伴后,通过
AskUserQuestion提问:“还要连接其他电商工具吗?” →[是 — 添加另一个]/[完成]。这样用户可以逐个添加合作伙伴,而不必在最初用逗号分隔的列表一次性提供。
已知凭据格式的合作伙伴(直接使用以下信息,无需搜索,但仍需进行冒烟测试):
| 合作伙伴 | 身份验证请求头 | 基础 URL | 健康检查端点 |
|---|---|---|---|
| ShipBob | Authorization: Bearer <token> | https://api.shipbob.com/v1 | /user |
| Recharge | X-Recharge-Access-Token: <token> | https://api.rechargeapps.com/v1 | /shop |
| Yotpo | X-Api-Key: <app_key> | https://api.yotpo.com | /core/v3/stores/<id> |
| Shippo | Authorization: ShippoToken <token> | https://api.goshippo.com | /carrier_accounts |
| Gorgias | Authorization: Basic <base64> | https://<domain>.gorgias.com/api | /account |
| Loop | X-Authorization: <secret> | https://api.loopreturns.com/api/v1 | /warehouse |
| Attentive | Authorization: Bearer <token> | https://api.attentivemobile.com/v1 | /me |
对于此表中未列出的任何合作伙伴,在询问凭据前,务必先通过网页搜索查询最新的身份验证文档。
深入了解: 请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-ecom/SKILL.md,了解电商集成的完整操作说明、CLI 参考和故障排查信息(多店铺 Shopify、合作伙伴分发、店铺健康守护进程)。当设置 Agent 需要比本向导更深入的信息时,可以直接加载该文件。
3j — 营销(Klaviyo、Meta Ads、GA4、Search Console)
显示服务选择器之前,同时对所有营销变量运行 Universal Credential Auto-Scan:
# Shell env
printenv KLAVIYO_API_KEY KLAVIYO_PRIVATE_KEY META_ACCESS_TOKEN FACEBOOK_ACCESS_TOKEN META_AD_ACCOUNT_ID GA4_PROPERTY_ID GA_MEASUREMENT_ID 2>/dev/null
printenv GOOGLE_ADS_DEVELOPER_TOKEN GOOGLE_ADS_CLIENT_ID GOOGLE_ADS_CLIENT_SECRET GOOGLE_ADS_REFRESH_TOKEN GOOGLE_ADS_CUSTOMER_ID 2>/dev/null
# Shell profiles
grep -h 'KLAVIYO\|META_\|FACEBOOK\|GA4\|GA_MEASUREMENT\|GOOGLE_ADS' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'
# Doppler across all projects
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do
doppler secrets --project "$proj" --config prd --json 2>/dev/null | \
jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("KLAVIYO|META|FACEBOOK|GA4|GOOGLE|GOOGLE_ADS")) | "\(.key)=\(.value.computed) (doppler:\($proj)/prd)"'
done
# Dashlane
dcli password klaviyo --output json 2>/dev/null
dcli password facebook --output json 2>/dev/null
dcli password meta --output json 2>/dev/null
# OpenClaw
jq -r '.agents.defaults.env | to_entries[] | select(.key | test("KLAVIYO|META_|FACEBOOK|GA4")) | "\(.key)=\(.value)"' ~/.openclaw/openclaw.json 2>/dev/null
缓存这些结果,用于预填以下各个子步骤的答案。对于以下每项服务,在提示用户之前,检查是否已配置(先检查 $PREFS_PATH 中的 marketing.*,再检查上面的自动扫描结果)。如果已配置,显示 ✓ <service> — already configured,并提供 [Keep] / [Reconfigure] 选项。
通过两次连续的 AskUserQuestion 调用询问要配置哪些营销集成,使用 multiSelect: true(根据 Rule 1,最多 4 项):
第一次调用(主要集成):
| 选项 | 标题 | 描述 |
|---|---|---|
| Klaviyo | klaviyo | 邮件/SMS 营销 — 私有 API 密钥 |
| Meta Ads | meta | Facebook/Instagram 广告 — 访问令牌 + 广告账户 ID |
| Google Ads | google-ads | 付费搜索广告 — OAuth2 + 开发者令牌 |
| 更多... | more | Google Analytics 4、Search Console |
如果用户选择 [More...],则显示第二次调用:
| 选项 | 标题 | 描述 |
|---|---|---|
| Google Analytics 4 | ga4 | 网站分析 — GA4 属性 ID |
| Google Search Console | gsc | SEO 数据 — 网站 URL(使用 gcloud 身份验证) |
| WhatsApp Business API | waba | 大规模模板消息 — Business 令牌 + ID |
| 跳过 | skip | 营销设置已完成 |
按所选顺序运行下面选中的子步骤。
Klaviyo
如果自动扫描中找到了 KLAVIYO_API_KEY 或 KLAVIYO_PRIVATE_KEY,则按 Universal Credential Auto-Scan 提示格式显示。只有未找到时才通过自由文本询问:
Enter your Klaviyo Private API Key:
To generate one: Klaviyo dashboard → Settings → API Keys → Create Private Key
Key starts with "pk_"
冒烟测试:
curl -s -H "Authorization: Klaviyo-API-Key $KEY" \
-H "revision: 2024-10-15" \
"https://a.klaviyo.com/api/lists" | jq '.data | length'
预期结果为大于或等于 0 的数字。如果响应中包含带有身份验证错误的 "detail",则显示该错误并重新询问。
Meta Ads
如果自动扫描中找到了 META_ACCESS_TOKEN 或 FACEBOOK_ACCESS_TOKEN,则显示该令牌。如果找到了 META_AD_ACCOUNT_ID,也一并显示。只有未找到的值才通过自由文本询问。
询问以下内容:
- 访问令牌(说明:前往 Meta Business Suite → Settings → System Users,或你的个人账户 → Generate token,并授予
ads_read权限) - 广告账户 ID(格式:
act_XXXXXXXXXX— 可在 Business Manager → Ad Accounts 中找到)
冒烟测试:
curl -s "https://graph.facebook.com/v20.0/$AD_ACCOUNT_ID/campaigns?access_token=$TOKEN&limit=1" | jq '.data | length'
Google Ads
Google Ads 需要三组凭据。按顺序逐步引导用户完成每个步骤。
步骤 A — 开发者令牌:
如果自动扫描中找到了 GOOGLE_ADS_DEVELOPER_TOKEN,请展示该令牌,并提供 [Keep] / [Reconfigure] 选项。
否则,通过自由文本询问:
您的 Google Ads 开发者令牌(可在 Google Ads → Tools & Settings → API Center 中找到)。 注意:新开发者令牌初始处于“测试”模式,只能用于测试账号。要获取生产数据,请通过 https://ads.google.com/home/tools/manager-accounts/ 申请 Basic Access。
步骤 B — OAuth2 客户端凭据:
如果自动扫描中找到了 GOOGLE_ADS_CLIENT_ID 和 GOOGLE_ADS_CLIENT_SECRET,请展示并提供 [Keep] / [Reconfigure] 选项。
否则,通过自由文本询问(分两次提问):
- OAuth2 Client ID(从 Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client ID 中获取,类型:Desktop app;必须启用 Google Ads API)
- OAuth2 Client Secret(与客户端 ID 一同显示)
步骤 C — 刷新令牌(浏览器 OAuth 流程):
如果自动扫描中找到了 GOOGLE_ADS_REFRESH_TOKEN,请展示并提供 [Keep] / [Reconfigure] 选项。
否则,生成授权 URL 并运行 OAuth 流程:
AUTH_URL="https://accounts.google.com/o/oauth2/auth?client_id=${GADS_CLIENT_ID}&redirect_uri=http://localhost:8080&response_type=code&scope=https://www.googleapis.com/auth/adwords&access_type=offline&prompt=consent"
open "$AUTH_URL" # macOS
启动临时 localhost 服务以接收重定向:
# One-liner node HTTP server to capture the auth code
node -e "require('http').createServer((req,res)=>{const code=new URL(req.url,'http://localhost').searchParams.get('code');if(code){res.end('Authorization code received. You can close this tab.');process.stdout.write(code);process.exit(0)}else{res.end('Waiting for auth...')}}).listen(8080)"
通过 Bash 运行服务,并设置 run_in_background: true。等待授权码,最长 120 秒。
如果 localhost 方式失败,则请用户从浏览器地址栏中复制代码(code= 参数)。
使用代码交换刷新令牌:
TOKEN_RESP=$(curl -s -X POST https://oauth2.googleapis.com/token \
--data "code=${AUTH_CODE}" \
--data "client_id=${GADS_CLIENT_ID}" \
--data "client_secret=${GADS_CLIENT_SECRET}" \
--data "redirect_uri=http://localhost:8080" \
--data "grant_type=authorization_code")
GADS_REFRESH_TOKEN=$(echo "$TOKEN_RESP" | jq -r '.refresh_token')
如果 GADS_REFRESH_TOKEN 为 null 或为空,则打印错误并提供重试选项。
警告:如果用户的 Google Cloud 项目发布状态为“测试”,刷新令牌会在 7 天后过期。请警告用户:“您的 OAuth 应用处于测试模式,令牌将在 7 天后过期。要获取长期有效的令牌,请在 Google Cloud Console → OAuth consent screen → Publish App 中发布该应用。”
步骤 D — 客户 ID: 使用刷新令牌获取访问令牌,然后列出可访问的客户:
ACCESS_TOKEN=$(curl -s -X POST https://oauth2.googleapis.com/token \
--data "client_id=${GADS_CLIENT_ID}&client_secret=${GADS_CLIENT_SECRET}&refresh_token=${GADS_REFRESH_TOKEN}&grant_type=refresh_token" | jq -r '.access_token')
CUSTOMERS=$(curl -s -X GET \
"https://googleads.googleapis.com/v23/customers:listAccessibleCustomers" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "developer-token: ${GADS_DEV_TOKEN}")
如果返回多个账号,请以 AskUserQuestion 选项的形式展示(根据规则 1,最多 4 个;如果更多则分页)。如果只有一个账号,则自动选择。保存为 customer_id(去掉 customers/ 前缀和连字符)。
如果有任何账号是经理(MCC)账号,也要保存 login_customer_id。通过检查 listAccessibleCustomers 是否同时返回经理账号和客户账号来自动检测。
步骤 E — 冒烟测试:
curl -s -X GET "https://googleads.googleapis.com/v23/customers:listAccessibleCustomers" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "developer-token: ${GADS_DEV_TOKEN}"
预期返回包含 resourceNames 数组的 JSON。如果发生错误,则打印错误并提供 [Retry] / [Skip] 选项。
步骤 F — 保存到偏好设置:
{
"marketing": {
"google_ads": {
"developer_token": "<YOUR_DEVELOPER_TOKEN>",
"client_id": "<YOUR_CLIENT_ID>.apps.googleusercontent.com",
"client_secret": "GOCSPX-<YOUR_SECRET>",
"refresh_token": "1//<YOUR_REFRESH_TOKEN>",
"customer_id": "1234567890",
"login_customer_id": "9876543210"
}
}
}
使用与步骤 3i 相同的 Doppler 引用模式——如果已配置 Doppler,优先使用 doppler:KEY_NAME,而不是原始令牌。
打印:[Google Ads] ✓ connected — customer ID: XXXXXXXXXX
Google Analytics 4
如果自动扫描中找到了 GA4_PROPERTY_ID 或 GA_MEASUREMENT_ID,请展示该值。只有未找到时才通过自由文本询问。询问 GA4 Property ID(说明:GA4 信息中心 → Admin → Property Settings → Property ID,格式为数字,例如 123456789)。
如果已通过 gcloud 进行身份验证,则无需 API 密钥——GA4 Data API 使用 Application Default Credentials。检查:
gcloud auth application-default print-access-token 2>/dev/null | head -c 10
如果尚未设置 gcloud ADC,请说明 GA4 查询需要手动进行身份验证:gcloud auth application-default login。
Google Search Console
询问网站 URL(格式:https://example.com/ 或 sc-domain:example.com)。如果已通过 gcloud 进行身份验证,则无需 API 密钥。
冒烟测试:
ACCESS_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://searchconsole.googleapis.com/webmasters/v3/sites" | jq '.siteEntry | length'
WhatsApp Business API
**与个人 WhatsApp 的 wacli 服务分开。**用于大规模发送面向客户的 Business 模板消息。
自动扫描:
printenv WHATSAPP_BUSINESS_TOKEN WHATSAPP_PHONE_NUMBER_ID WHATSAPP_BUSINESS_ACCOUNT_ID 2>/dev/null
claude plugin config get whatsapp_business_token 2>/dev/null && echo "waba_token: already configured"
claude plugin config get whatsapp_phone_number_id 2>/dev/null && echo "waba_phone_id: already configured"
claude plugin config get whatsapp_business_account_id 2>/dev/null && echo "waba_account_id: already configured"
凭证位置:
WHATSAPP_BUSINESS_TOKEN:Meta Developer Portal → Your App → WhatsApp → API Setup → 临时令牌或 System User 令牌WHATSAPP_PHONE_NUMBER_ID:同一页面 →“From”电话号码 → Phone Number IDWHATSAPP_BUSINESS_ACCOUNT_ID:Meta Business Manager → Business Settings → WhatsApp Accounts → ID
如果未找到,使用 AskUserQuestion 逐项收集(自由文本)。保存到:
claude plugin config set whatsapp_business_token "$WABA_TOKEN"
claude plugin config set whatsapp_phone_number_id "$WABA_PHONE_ID"
claude plugin config set whatsapp_business_account_id "$WABA_ACCOUNT_ID"
冒烟测试:
curl -s "https://graph.facebook.com/v20.0/${WABA_PHONE_ID}" \
-H "Authorization: Bearer ${WABA_TOKEN}" | jq '.display_phone_number // empty'
如果返回电话号码:WhatsApp Business ✓ connected — Phone: <number>。否则显示错误。
保存到偏好设置
写入 $PREFS_PATH(合并):
{
"marketing": {
"klaviyo": { "api_key": "<pk_...>" },
"meta": { "access_token": "<token>", "ad_account_id": "act_XXXXXXXXXX" },
"ga4": { "property_id": "123456789" },
"gsc": { "site_url": "https://example.com/" },
"whatsapp_business": { "phone_number_id": "<ID>", "business_account_id": "<WABA_ID>" }
}
}
采用与步骤 3i 相同的 Doppler 引用模式 — 配置了 Doppler 时,优先使用 doppler:KEY_NAME,而不是原始令牌。
动态营销合作伙伴
处理完已知服务后,通过 AskUserQuestion 询问(自由文本):
Any other marketing tools you'd like to connect?
Examples: Postscript (SMS), Privy (popups), Triple Whale (attribution), Northbeam,
Hotjar, Heap, Segment, Mixpanel, Mailchimp, ActiveCampaign, HubSpot
Type names separated by commas, or leave blank to skip.
如果用户提供合作伙伴名称,采用与步骤 3i.4 相同的动态合作伙伴循环 — 对每个合作伙伴:
- 研究凭证,通过网页搜索:
"<partner name> API authentication developer docs 2025" - 询问凭证,通过
AskUserQuestion提供从文档中获取的说明 - 冒烟测试,针对身份验证/健康检查端点
- 保存到偏好设置,路径为
marketing.partners.<partner_slug>:{ "marketing": { "partners": { "triple-whale": { "api_base_url": "https://api.triplewhale.com/api/v2", "auth_pattern": "Authorization: Bearer <token>", "credentials": { "api_key": "doppler:TRIPLE_WHALE_API_KEY" }, "health_endpoint": "/attribution/get-attribution-data", "configured_at": "<ISO timestamp>" } } } } - 循环 — 处理完每个合作伙伴后,提供
[Add another]/[Done]选项。
已知凭证模式的合作伙伴(直接使用,但仍需冒烟测试):
| Partner | Auth header | Base URL | Health endpoint |
|---|---|---|---|
| HubSpot | Authorization: Bearer <token> | https://api.hubapi.com | /crm/v3/objects/contacts?limit=1 |
| Mailchimp | Authorization: Bearer <api_key> | https://<dc>.api.mailchimp.com/3.0 | /ping |
| Segment | Authorization: Basic <base64_key:> | https://api.segment.io/v1 | n/a — use write key |
| Mixpanel | Authorization: Basic <base64_secret:> | https://data.mixpanel.com/api/2.0 | /engage?limit=1 |
| Postscript | Authorization: ApiKey <key> | https://api.postscript.io/api/v2 | /shops |
| Triple Whale | Authorization: Bearer <token> | https://api.triplewhale.com/api/v2 | /attribution/get-attribution-data |
对于此表中未列出的合作伙伴,在询问凭证前,务必通过网页搜索查找最新的身份验证文档。
**深入了解:**有关营销集成(Klaviyo flows、Meta Ads、GA4、Search Console)的完整操作说明、CLI 参考和故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-marketing/SKILL.md。如果设置 Agent 需要比本向导更深入的信息,可以直接加载该文件。
3k — 语音(Bland AI、ElevenLabs、Groq)
显示服务选择器前,运行通用凭证自动扫描,同时扫描所有语音变量:
# Shell env
printenv BLAND_AI_API_KEY BLAND_API_KEY ELEVENLABS_API_KEY GROQ_API_KEY 2>/dev/null
# Shell profiles
grep -h 'BLAND\|ELEVENLABS\|GROQ' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'
# Doppler across all projects
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do
doppler secrets --project "$proj" --config prd --json 2>/dev/null | \
jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("BLAND|ELEVENLABS|GROQ")) | "\(.key)=\(.value.computed) (doppler:\($proj)/prd)"'
done
# Dashlane
dcli password "bland-ai" --output json 2>/dev/null
dcli password elevenlabs --output json 2>/dev/null
dcli password groq --output json 2>/dev/null
# OpenClaw (common location for AI service keys)
jq -r '.agents.defaults.env | to_entries[] | select(.key | test("BLAND|ELEVENLABS|GROQ")) | "\(.key)=\(.value)"' ~/.openclaw/openclaw.json 2>/dev/null
缓存这些结果 — 使用它们为以下各子步骤预填答案。同时检查 $PREFS_PATH 中 voice.* 下的现有配置。如果某个密钥已设置,显示 ✓ <service> — already configured,并提供 [Keep] / [Reconfigure] 选项。
通过 AskUserQuestion 询问要配置哪些语音服务,并设置 multiSelect: true:
| Option | Header | Description |
|---|---|---|
| Bland AI | bland | 外呼 AI 电话 — API 密钥 |
| ElevenLabs | elevenlabs | 文本转语音和语音克隆 — API 密钥 |
| Groq | groq | 快速 LLM 推理(Whisper、LLaMA)— API 密钥 |
Bland AI
如果自动扫描中发现了 BLAND_AI_API_KEY 或 BLAND_API_KEY,请使用通用凭据自动扫描提示格式展示。只有在未发现时,才通过自由文本询问:
Enter your Bland AI API Key:
To find it: https://app.bland.ai → Settings → API Key
冒烟测试:
curl -s -H "Authorization: $KEY" "https://api.bland.ai/v1/me" | jq '.user.id'
预期得到非空的用户 ID。
ElevenLabs
如果自动扫描中发现了 ELEVENLABS_API_KEY,请使用通用凭据自动扫描提示格式展示。只有在未发现时,才通过自由文本询问:
Enter your ElevenLabs API Key:
To find it: https://elevenlabs.io → Profile (top-right) → API Key
冒烟测试:
curl -s -H "xi-api-key: $KEY" "https://api.elevenlabs.io/v1/user" | jq '.subscription.tier'
预期得到订阅等级字符串(例如 "free"、"starter")。
Groq
如果自动扫描中发现了 GROQ_API_KEY,请使用通用凭据自动扫描提示格式展示。只有在未发现时,才通过自由文本询问:
Enter your Groq API Key:
To generate one: https://console.groq.com → API Keys → Create API Key
Key starts with "gsk_"
冒烟测试:
curl -s -H "Authorization: Bearer $KEY" \
"https://api.groq.com/openai/v1/models" | jq '.data | length'
预期得到一个正整数(可用模型的数量)。
保存到偏好设置
写入 $PREFS_PATH(合并):
{
"voice": {
"bland": { "api_key": "<key>" },
"elevenlabs": { "api_key": "<key>" },
"groq": { "api_key": "gsk_..." }
}
}
采用相同的 Doppler 引用模式——配置了 Doppler 时,优先使用 doppler:KEY_NAME,而不是原始令牌。
深入了解: 有关语音集成(Bland AI 呼叫流程、ElevenLabs TTS、Groq 转录)的完整操作说明、CLI 参考和故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-voice/SKILL.md。如果设置 Agent 需要比本向导更深入的信息,可直接加载该文件。
3l — 收入(Stripe + RevenueCat)
显示服务选择器之前,同时运行所有收入变量的通用凭据自动扫描(规则 4 — 在后台运行):
# Shell env
printenv STRIPE_SECRET_KEY STRIPE_API_KEY REVENUECAT_API_KEY REVENUECAT_SECRET_KEY REVENUECAT_PROJECT_ID 2>/dev/null
# Shell profiles + .envrc files
grep -h 'STRIPE\|REVENUECAT\|RC_API' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'
# Doppler across all projects
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do
doppler secrets --project "$proj" --config prd --json 2>/dev/null | \
jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("STRIPE|REVENUECAT|RC_API")) | "\(.key)=\(.value.computed) (doppler:\($proj)/prd)"'
done
# 1Password
op item list --categories "API Credential" --format json 2>/dev/null | \
jq -r '.[] | select(.title | test("stripe|revenuecat"; "i")) | .id' | \
while read id; do op item get "$id" --format json 2>/dev/null; done
# Dashlane
dcli password stripe --output json 2>/dev/null
dcli password revenuecat --output json 2>/dev/null
# Bitwarden
bw list items --search stripe 2>/dev/null | jq -r '.[] | select(.login.password) | .login.password' | head -1
bw list items --search revenuecat 2>/dev/null | jq -r '.[] | select(.login.password) | .login.password' | head -1
# macOS Keychain
security find-generic-password -s "stripe" -w 2>/dev/null
security find-generic-password -s "revenuecat" -w 2>/dev/null
# OpenClaw
jq -r '.agents.defaults.env | to_entries[] | select(.key | test("STRIPE|REVENUECAT")) | "\(.key)=\(.value)"' ~/.openclaw/openclaw.json 2>/dev/null
缓存这些结果。同时检查 $PREFS_PATH 中的 revenue.stripe.* 和 revenue.revenuecat.*——如果已设置,则显示 ✓ <service> — 已配置,并提供 [保留] / [重新配置] 选项。
使用 AskUserQuestion 并设置 multiSelect: true,询问要配置哪些收入集成:
| 选项 | 标题 | 描述 |
|---|---|---|
| Stripe | stripe | SaaS 收入 — 用于 MRR、收费和争议的密钥 |
| RevenueCat | revenuecat | 移动端订阅 — 用于移动端 MRR 的 API 密钥 + 项目 ID |
Stripe
如果自动扫描中发现了 STRIPE_SECRET_KEY,请使用通用凭据自动扫描提示格式展示,并提供 [使用此值] / [粘贴其他值] / [跳过] 选项。
根据规则 3——如果未发现任何结果,请提供(≤4 个选项):
No Stripe secret key found. How do you want to provide one?
[Paste Stripe secret key manually]
[Deep hunt — spawn agent]
[Skip — use registry.json values]
选择 [深入搜索 — 启动 Agent] 时,根据规则 3 启动后台研究 Agent:
Agent(
subagent_type: "general-purpose",
model: "haiku",
run_in_background: true,
prompt: "Grep the filesystem under $HOME (excluding node_modules, .git, Library/Caches) for Stripe secret key patterns: sk_live_[A-Za-z0-9]+ and sk_test_[A-Za-z0-9]+. Also scan ~/.config, ~/.aws, ~/.docker, any .env files, and known secrets directories. Return every hit with file path + line number + 6 chars of redacted prefix (e.g. sk_live_abc***). Do not print full keys."
)
Agent 运行期间,继续执行 RevenueCat 部分。Agent 报告结果后返回 Stripe;通过 AskUserQuestion 展示发现的结果(按规则 1 分页,每页 ≤4 项)。
选择 [手动粘贴 Stripe 密钥] 时:
Enter your Stripe Secret Key:
Format: sk_live_XXX (production) or sk_test_XXX (test mode)
Find it: Stripe Dashboard → Developers → API keys → Secret key → Reveal
Prefer a Doppler reference (e.g. doppler:STRIPE_SECRET_KEY) over the raw value.
冒烟测试:
curl -s -u "$STRIPE_SECRET_KEY:" "https://api.stripe.com/v1/balance" | jq '.available | length'
预期得到非零整数。如果返回 {"error": ...},请显示错误消息并重新询问。
RevenueCat
如果自动扫描中同时发现了 REVENUECAT_API_KEY 和 REVENUECAT_PROJECT_ID,请一并展示,并提供 [使用这些值] / [粘贴其他值] / [跳过] 选项。
根据规则 3——如果未发现,请提供:
No RevenueCat credentials found. How do you want to provide them?
[Paste RevenueCat API key manually]
[Deep hunt — spawn agent]
[Skip — mobile MRR will be omitted]
选择 [深入搜索 — 启动 Agent] 时,启动后台 Agent(规则 4):
Agent(
subagent_type: "general-purpose",
model: "haiku",
run_in_background: true,
prompt: "Grep the filesystem under $HOME (excluding node_modules, .git, Library/Caches) for RevenueCat credential patterns: rcb_[A-Za-z0-9]+ (V2 secret), sk_[A-Za-z0-9]+ near the literal string 'revenuecat', and any env vars matching REVENUECAT_* or RC_API_*. Also look for project IDs (32-char alphanum strings) adjacent to any revenuecat match. Return each hit with file path + line number + 6-char redacted prefix. Do not print full keys."
)
选择 [手动粘贴 RevenueCat API 密钥] 时:
Enter your RevenueCat API Key:
Find it: app.revenuecat.com → Project settings → API keys → Secret key
Format: rcb_XXX (V2 secret) or sk_XXX (legacy secret key)
Enter your RevenueCat Project ID:
Find it in the URL: app.revenuecat.com/projects/<project_id>/...
冒烟测试:
curl -s -H "Authorization: Bearer $REVENUECAT_API_KEY" \
"https://api.revenuecat.com/v2/projects/$REVENUECAT_PROJECT_ID/metrics/overview" | jq '.metrics // .object'
预期得到数值型 mrr 或对象描述符。如果响应为 {"code": 7243, ...}(身份验证错误),请重新询问。
保存到偏好设置
写入 $PREFS_PATH(合并):
{
"revenue": {
"stripe": {
"secret_key": "doppler:STRIPE_SECRET_KEY",
"configured_at": "<ISO timestamp>"
},
"revenuecat": {
"api_key": "doppler:REVENUECAT_API_KEY",
"project_id": "<project_id>",
"configured_at": "<ISO timestamp>"
}
}
}
配置了 Doppler 时,优先使用 Doppler 引用(doppler:STRIPE_SECRET_KEY、doppler:REVENUECAT_API_KEY),而不是原始令牌。对于任一服务,如果用户选择了 [Skip],则保存 {"revenue": {"<service>": "skipped"}},这样向导下次运行时就不会再次询问;不过,/ops:revenue 会回退到 scripts/registry.json 中的 revenue.mrr 值,具体说明见 revenue-tracker Agent。
深入了解: 完整的运营说明、CLI 参考,以及收入集成(Stripe MRR/ARR 查询、RevenueCat 订阅指标、注册表回退)的故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-revenue/SKILL.md。需要比本向导更深入的信息时,设置 Agent 可以直接加载该文件。
3n — 通知(fires-watcher 接收端)
为 CRITICAL/HIGH 级别的 fires 设置推送通知,让用户不必再手动轮询 /ops:fires。此功能依赖 fires-watcher 守护进程服务(默认禁用)。
询问用户之前,在后台运行接收端自动扫描(规则 4——全部并行):
# Already-configured sinks (env + $PREFS_PATH)
printenv TELEGRAM_BOT_TOKEN TELEGRAM_NOTIFY_CHAT_ID TELEGRAM_OWNER_ID \
DISCORD_WEBHOOK_URL NTFY_TOPIC PUSHOVER_USER PUSHOVER_TOKEN 2>/dev/null
jq -r 'to_entries
| map(select(.key | test("telegram_bot_token|telegram_notify_chat_id|discord_webhook_url|ntfy_topic|pushover_user_key|pushover_app_token")))
| .[] | "\(.key)=\(.value | if type == "string" and length > 6 then .[0:6] + "***" else . end)"' \
"$PREFS_PATH" 2>/dev/null
# Macro: is the Telegram bot already configured via /ops:setup telegram?
jq -r '.telegram.bot_token // empty, .telegram.owner_id // empty' "$PREFS_PATH" 2>/dev/null
缓存结果。然后将选项列表筛选为尚未配置的项目(规则 1——最多 4 项)。
通过 AskUserQuestion 呈现(≤4 个选项)。典型的首批选项:
| 选项 | 标题 | 描述 |
|---|---|---|
| 使用 Telegram(推荐) | telegram | 复用你在 3a 中配置的机器人。无需额外设置。 |
| 配置 ntfy.sh | ntfy | 免费,无需账户。选择一个随机主题,并在手机上安装应用。 |
| 配置 Pushover | pushover | 一次性约 $5。移动端投递可靠性最高,并支持 P0 优先级绕过。 |
| 跳过——改为轮询 /ops:fires | skip | 保持 fires-watcher 禁用。你仍需手动查询 fires。 |
如果 $PREFS_PATH 中尚未保存 Telegram 机器人令牌和所有者 ID,将 [Use Telegram] 替换为 [Configure Telegram bot — jump to 3a],返回此处前重新执行 3a。根据规则 3,不要默默跳过——始终明确提供 [Skip]。
对于希望额外使用 Discord 的用户(许多用户希望同时接收团队和个人通知),再运行一次 AskUserQuestion:
| 选项 | 标题 | 描述 |
|---|---|---|
| 另外添加 Discord webhook | discord | 除 X 外,也发送到 #incidents 频道。 |
| 不需要——仅使用我选择的接收端 | done | 仅配置一个接收端。 |
各接收端配置
Telegram——如果 $TELEGRAM_NOTIFY_CHAT_ID 为空,将其默认设为 $TELEGRAM_OWNER_ID(机器人会给所有者发送私信),并将 {"telegram_notify_chat_id": "<owner_id>"} 保存到 $PREFS_PATH。在后台进行冒烟测试(规则 4):
scripts/ops-notify.sh LOW "setup test" "fires-watcher Telegram sink is live" &
ntfy.sh——如果用户没有主题,则生成一个随机主题:
TOPIC="claude-ops-fires-$(openssl rand -hex 6 2>/dev/null || head -c 12 /dev/urandom | base64 | tr -dc 'a-z0-9' | head -c 12)"
echo "$TOPIC"
显示主题和安装说明(在浏览器中打开 https://ntfy.sh/<topic>,或在 ntfy 移动应用中扫描二维码)。将其以 ntfy_topic 为键保存到 $PREFS_PATH。
Pushover——根据规则 3 提供选项:[Paste user key + app token] / [Deep hunt — spawn agent] / [Skip]。用户粘贴后,在后台进行冒烟测试以验证(规则 4):
curl -s -X POST https://api.pushover.net/1/messages.json \
--data-urlencode "token=$PUSHOVER_TOKEN" \
--data-urlencode "user=$PUSHOVER_USER" \
--data-urlencode "title=claude-ops setup test" \
--data-urlencode "message=fires-watcher Pushover sink is live" &
应在 5 秒内返回 {"status":1,...}。
Discord——如果用户之前的运行(或 issue #20 的相关工作)已设置 discord_default_webhook_url,则复用;否则根据规则 3 询问 webhook URL(https://discord.com/api/webhooks/...)。
保存并启用守护进程服务
写入 $PREFS_PATH(合并,绝不覆盖无关键):
{
"notifications": {
"configured_sinks": ["telegram", "ntfy"],
"configured_at": "<ISO timestamp>"
},
"telegram_notify_chat_id": "<chat_id>",
"ntfy_topic": "<topic>"
}
然后在后台启用守护进程服务(规则 4):
jq '.services["fires-watcher"].enabled = true' \
~/.claude/plugins/data/ops-ops-marketplace/daemon-services.json \
> /tmp/daemon-services.json.new && \
mv /tmp/daemon-services.json.new ~/.claude/plugins/data/ops-ops-marketplace/daemon-services.json && \
scripts/ops-daemon.sh restart &
重启后确认监视器仍在运行:
cat ~/.claude/plugins/data/ops-ops-marketplace/fires-watcher.health 2>/dev/null
预期在 60 秒内获得 {"status": "ok", ...}。
**深入了解:**请参阅
${CLAUDE_PLUGIN_ROOT}/docs/notifications.md、${CLAUDE_PLUGIN_ROOT}/scripts/ops-fires-watcher.sh和${CLAUDE_PLUGIN_ROOT}/scripts/ops-notify.sh,了解接收端优先级的原因、去抖规则以及故障排查指南。
3m — Discord(webhook + 可选 bot)
Discord 是 v1 集成——基于 webhook 发送消息,并通过 REST 读取频道。私信和 gateway 支持推迟到 v2 问题中实现。发送端 webhook 也提供 scripts/ops-notify.sh 使用的 Discord 通知接收端,因此在此处配置即可同时支持 /ops:comms discord send 和 ops-fires 告警。
显示凭据提示之前,对 Discord 运行 Universal Credential Auto-Scan(规则 4——除非下一步依赖结果,否则每次 bash 调用都在后台运行):
# Shell env
printenv DISCORD_BOT_TOKEN DISCORD_WEBHOOK_URL DISCORD_GUILD_ID 2>/dev/null
# Shell profiles + .envrc
grep -hE 'DISCORD_(BOT_TOKEN|WEBHOOK|GUILD_ID)' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'
# Doppler across all projects
for proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do
doppler secrets --project "$proj" --config prd --json 2>/dev/null | \
jq -r --arg proj "$proj" 'to_entries[] | select(.key | test("DISCORD")) | "\(.key)=\(.value.computed) (doppler:\($proj)/prd)"'
done
# 1Password
op item list --categories "API Credential" --format json 2>/dev/null | \
jq -r '.[] | select(.title | test("discord"; "i")) | .id' | \
while read id; do op item get "$id" --format json 2>/dev/null; done
# Dashlane / Bitwarden
dcli password discord --output json 2>/dev/null
bw list items --search discord 2>/dev/null | jq -r '.[] | select(.login.password) | .login.password' | head -1
# macOS Keychain (Darwin only — the setup wizard already guards this at the OS level)
security find-generic-password -s "discord" -w 2>/dev/null
# Claude-ops credential store (cross-OS)
"${CLAUDE_PLUGIN_ROOT}/lib/credential-store.sh" get discord bot-token 2>/dev/null
缓存这些结果。还要检查 $PREFS_PATH 中的 discord.* 和 discord_webhook_url(与 ops-notify.sh 共用的扁平键)——如果已存在 discord.bot_token、discord.default_webhook_url 或 discord_webhook_url 中的任何一项,则显示 ✓ Discord — already configured,并提供 [Keep] / [Reconfigure] 选项。
如果找到了任何内容
按照 Universal Credential Auto-Scan 提示格式展示,并提供 [Use this value] / [Paste a different one] / [Skip] 选项。如果找到了 webhook URL,但没有 bot token,请说明读取和频道列表功能将不可用(仅支持发送)。
根据规则 3——如果没有找到任何内容,明确询问(根据规则 1,≤4 个选项)
No Discord credentials found. How do you want to configure Discord?
[Paste bot token]
[Paste webhook URL only]
[Deep hunt — spawn agent]
[Skip — I'll configure later]
选择 [Deep hunt — spawn agent] 时,启动一个后台研究 Agent(规则 4——run_in_background: true):
Agent(
subagent_type: "general-purpose",
model: "haiku",
run_in_background: true,
prompt: "Grep the filesystem under $HOME (excluding node_modules, .git, Library/Caches) for Discord credentials. Patterns: bot tokens look like base64 segments separated by dots (e.g. MTAxXXXX.YYYYYY.ZZZZZZZZZZZZ), webhook URLs start with https://discord.com/api/webhooks/ or https://discordapp.com/api/webhooks/, guild IDs are 17-20 digit snowflakes adjacent to the word 'guild' or 'server'. Also scan ~/.config, ~/.env, any .envrc files, and Doppler/1Password exports. Return every hit with file path + line number + 6-char redacted prefix. Do not print full tokens."
)
等待搜索期间,继续进行下一项设置;Agent 返回结果后,再回到 Discord,并通过 AskUserQuestion 展示结果(分页,每页 ≤4 项,遵循规则 1)。
选择 [Paste bot token] 时:
Enter your Discord Bot Token:
Find it: https://discord.com/developers/applications → your app → Bot → Reset Token
Format: MTAxXXXX.YYYYYY.ZZZZZZZZZZZZZZ (base64 dot-separated)
Prefer a Doppler reference (doppler:DISCORD_BOT_TOKEN) over the raw value.
Enter your Discord Guild ID (optional — needed for `channels` listing):
Enable Developer Mode in Discord → right-click server → Copy ID.
冒烟测试(根据规则 4,通过 Bash 在后台运行):
curl -sS "https://discord.com/api/v10/users/@me" \
-H "Authorization: Bot $DISCORD_BOT_TOKEN" \
-H "User-Agent: ops-discord (claude-ops, v1)" | jq '.id // .message'
预期得到一个数字形式的 snowflake id。如果响应为 {"message":"401: Unauthorized", ...},请重新询问。
选择 [Paste webhook URL only] 时:
Enter a default Discord Webhook URL:
Find it: Discord → Server Settings → Integrations → Webhooks → New Webhook → Copy URL
Format: https://discord.com/api/webhooks/<ID>/<TOKEN>
冒烟测试——设置期间不要向 webhook 发送内容。改为发送 GET 请求,确认该 URL 能返回 webhook 元数据:
curl -sS -o /dev/null -w '%{http_code}\n' -X GET "$DISCORD_WEBHOOK_URL"
预期返回 200(已返回 webhook 元数据)或 401(URL 有效,但 token 无效——用户需要重新复制)。
保存到偏好设置
写入 $PREFS_PATH(合并):
{
"discord": {
"bot_token": "doppler:DISCORD_BOT_TOKEN",
"guild_id": "<GUILD_ID>",
"default_webhook_url": "doppler:DISCORD_WEBHOOK_URL",
"configured_at": "<ISO timestamp>"
},
"discord_webhook_url": "doppler:DISCORD_WEBHOOK_URL"
}
将 webhook 同步到扁平的 discord_webhook_url 键,以便 scripts/ops-notify.sh(现有 fires 接收端)继续找到它。配置了 Doppler 时,优先使用 Doppler 引用,而不是原始 token。在配有原生密钥环的系统上,优先使用凭据存储(ops_cred_set discord bot-token <value>),而不是以明文写入偏好设置。如果用户选择了 [Skip],则保存 {"discord": "skipped"},避免向导下次运行时再次询问。
**深入了解:**请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-comms/SKILL.md(Discord 发送/读取部分)和${CLAUDE_PLUGIN_ROOT}/bin/ops-discord,了解完整的操作说明和子命令参考。
步骤 4 — 配置 MCP(如果已选择)
对于每个尚未添加到 mcp_configured 的 MCP,先提供批量设置选项:
Unconfigured MCPs: Linear, Sentry, Vercel. What would you like to do?
[Configure all MCPs — run claude mcp add for each (Recommended)]
[Pick which MCPs to add]
[Skip MCP configuration]
如果用户选择“全部配置”,依次为每个未配置的 MCP 运行 claude mcp add <name>。如果选择“选择要配置的项目”,则逐项列出:
Linear: claude mcp add linear
Sentry: claude mcp add sentry
Vercel: claude mcp add vercel
Slack: claude mcp add slack
Gmail: claude mcp add gmail (fallback only — prefer `gog` CLI, see Step 3c)
对每项提供 [立即添加]、[跳过]。不要尝试从技能中注册 MCP——插件无法安全地完成此操作。
**电子邮件说明:**ops 插件的主要电子邮件处理方式是使用 gog CLI(完整读取和发送功能,使用自有 OAuth)。Gmail MCP 连接器可用作备用方案,但若不在 Claude Desktop → Settings → Connectors 中进行额外权限配置,就无法发送邮件。向导会在步骤 3c 中检测这一情况;此步骤仅列出该连接器,供有意使用 MCP 的用户在此安装。
**深入了解:**有关 MCP 支持的集成(Linear 问题流、分流路由、Sentry/Vercel 故障)的完整操作说明、CLI 参考和故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-linear/SKILL.md、${CLAUDE_PLUGIN_ROOT}/skills/ops-triage/SKILL.md和${CLAUDE_PLUGIN_ROOT}/skills/ops-fires/SKILL.md。当设置 Agent 需要比此向导提供的更多信息时,可直接加载这些文件。
步骤 5 — 构建项目注册表(如果已选择)
模板:
${CLAUDE_PLUGIN_ROOT}/scripts/registry.templates/中提供了适用于常见技术栈的预设模板。若要从模板开始,运行cp "${CLAUDE_PLUGIN_ROOT}/scripts/registry.templates/<stack>.json" "${CLAUDE_PLUGIN_ROOT}/scripts/registry.json",然后进行编辑;也可以继续使用下方的向导进行交互式发现。
从文件系统自动发现
在要求用户手动输入项目之前,先扫描现有的 Git 仓库:
find ~ ~/Projects -maxdepth 2 -name ".git" -type d 2>/dev/null | sed 's|/.git||' | sort
通过 AskUserQuestion 向用户展示发现的路径,并设置 multiSelect: true。每次最多提供 4 个选项——每页显示 3 个项目,并将 [更多...] 或 [都没有 — 我将手动输入项目] 作为最后一个选项:
Found git repositories (page 1 of N):
[ ] ~/Projects/my-app
[ ] ~/Projects/my-api
[ ] ~/Projects/my-ai
[ ] More repositories...
在最后一页,将“更多仓库...”替换为 [以上都不是 / 已完成选择]。
对于每个选中的项目,每次通过一个 AskUserQuestion 收集以下字段:
alias(短名称,必填——建议使用目录名称作为默认值)org(GitHub 组织或所有者,例如your-org或your-username)infra.platform→ 选择[aws]、[vercel]、[cloudflare]、[other]revenue.model→ 分批选择,每批 4 项:[saas]、[subscription]、[marketplace]、[More...],然后是[internal]、[portfolio]、[other]
自动发现外部项目
真实用户的项目组合很少只有 Git 仓库。Shopify 商店、Linear 团队、Slack 工作区和 Notion 数据库都是一等项目,ops-external + ops-projects 知道如何展示它们——但前提是它们已添加到 registry.json。此子步骤会探测向导已配置的集成,并提供发现的项目供用户一键注册。
${CLAUDE_PLUGIN_ROOT}/bin/ops-discover-external 2>/dev/null || echo '[]'
该脚本从 $PREFS_PATH .ecom.shopify.* 和 SHOPIFY_* 环境变量读取 Shopify 凭据,从 LINEAR_API_KEY 读取 Linear 凭据,从 keychain 的 slack-xoxc/slack-xoxd 读取 Slack 凭据,并从 NOTION_API_KEY / keychain 的 notion-api-key 读取 Notion 凭据。脚本会返回候选项目数组,每个候选项目都包含可直接合并的 config 块。
解析这些候选项目,并针对每个尚未出现在 registry.json 中的项目(按 config.alias 或 source + source-specific ID 匹配),通过 AskUserQuestion 提供给用户。每次调用最多列出 3 个候选项目,并添加 [More...],以符合规则 1(最多 4 个选项)。批次示例:
Found external projects not yet in your registry (page 1 of N):
[Register "mystore" (shopify — basic plan, 142 products)]
[Register "linear-eng" (linear — Engineering, 42 open issues)]
[Register "notion-roadmap" (notion — Product Roadmap database)]
[More candidates...]
最后一页的最后一个选项改为 [None of the remaining — skip]。可以使用多选;如果提供多选,multiSelect: true 列表的大小不得超过 4。
对于每个接受的候选项目,使用 jq 将其 config 块直接合并到 registry.json .projects[] 中:
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
REG="$PLUGIN_ROOT/scripts/registry.json"
[ -f "$REG" ] || echo '{"version":"1.0","owner":"","projects":[]}' > "$REG"
jq --argjson new "$CONFIG_JSON" '.projects += [$new]' "$REG" > "$REG.tmp" && mv "$REG.tmp" "$REG"
状态处理:
discovered→ 按原样注册。auth_expired→ 显示警告,并引导用户前往/ops:setup,为受影响的渠道重新配置后再试。unreachable→ 提供[Register anyway (will show as unreachable in dashboards)]/[Skip]。
如果发现脚本返回 [],则打印一条信息提示(ℹ No external projects auto-discovered. You can add Shopify / Linear / Slack / Notion manually below.)并继续。切勿静默跳过——用户必须知道已执行发现操作。
如果候选项目的凭据值来自环境变量,而用户之后想改用 Doppler,则可以稍后通过 /ops:settings,将候选项目中存储的凭据键(如 SHOPIFY_ADMIN_TOKEN)安全地替换为 doppler: 引用。
现有注册表
如果 registry.json 中已有项目,先询问用户(4 个选项,可在一次调用中提供):[Keep existing N projects]、[Add more projects]、[Auto-detect from existing registry]、[Start from scratch]。
- “Keep existing” → 跳过此步骤。
- “Auto-detect from existing registry” → 重新读取注册表,显示现有项目摘要,并提供选项以添加缺失字段或新发现的仓库。
- “Start from scratch” → 先写入空骨架(
{"version":"1.0","owner":"","projects":[]})——覆盖前提示用户确认。 - “Add more” → 运行上述自动发现扫描,然后提供手动输入作为备选。
手动添加循环
自动发现后(或用户选择“我将手动输入项目”后):
- 询问
AskUserQuestion:“添加另一个项目?” →[Yes]、[Done]。 - 如果选择 Yes,每次只通过一个
AskUserQuestion收集以下字段:alias(短名称,必填)paths(逗号分隔的绝对路径,必填)repos(逗号分隔的org/repo,必填)type→ 选择[monorepo]、[multi-repo]infra.platform→ 选择[aws]、[vercel]、[cloudflare]、[other]revenue.model→ 每批选择 4 个:[saas]、[subscription]、[marketplace]、[More...],然后选择[internal]、[portfolio]、[other]revenue.stage→ 选择[pre-launch]、[development]、[growth]、[active]gsd→ 选择[Yes]、[No]priority(1-99,默认为 max+1)
- 确保注册表目录存在:
mkdir -p "${CLAUDE_PLUGIN_ROOT}/scripts"。如果因权限问题导致写入失败(插件缓存目录可能为只读),则改为写入$DATA_DIR/registry.json并创建符号链接。 - 使用
jq读取当前注册表,追加新项目,并以原子方式写回(jq ... > tmp && mv tmp registry.json)。 - 每次添加后,打印当前总数,并提供
[Add another]/[Done]选项。 - 注册表 Agent(如果已启动)必须有权写入目标路径。如果无法写入,设置向导应根据 Agent 返回的 JSON 自行写入文件——不要要求用户介入。
**深入了解:**完整操作说明、CLI 参考,以及项目注册表(自动发现、注册表架构、GSD 筛选器、优先级排序)的故障排除,请参阅
${CLAUDE_PLUGIN_ROOT}/skills/ops-projects/SKILL.md。当设置 Agent 需要比此向导更详细的信息时,可以直接加载该文件。
步骤 5b — 守护进程服务协调
此时,守护进程已在步骤 2c 中安装,并在用户配置频道期间于后台预热简报缓存。此步骤会在确认已配置的频道和集成后,添加依赖频道的服务(wacli-sync、message-listener、inbox-digest、store-health、competitor-intel)。
跳过条件:
- 如果用户在步骤 2c 中拒绝安装守护进程,则完全跳过此步骤。
- 如果
$PREFS_PATH中的daemon.enabled != true,则跳过。
否则继续——协调服务列表:
1. 确认守护进程正在运行:
DATA_DIR="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}"
PLIST_DEST="$HOME/Library/LaunchAgents/com.claude-ops.daemon.plist"
launchctl print gui/$(id -u)/com.claude-ops.daemon >/dev/null 2>&1 || {
# Daemon not running — install it now as a fallback
launchctl bootstrap gui/$(id -u) "$PLIST_DEST" 2>/dev/null
}
2. 5 秒后确认运行状况:
cat "$DATA_DIR/daemon-health.json" 2>/dev/null
解析 JSON。如果 action_needed 不为 null,则向用户显示所需操作。如果守护进程已写入运行状况文件,则打印:
✓ Background daemon — running (wacli-sync: connected, memory-extractor: scheduled)
如果运行状况文件缺失(守护进程可能仍在初始化),再等待 5 秒并重试一次。如果仍然缺失,则打印:
⚠ Daemon started but health file not yet written. Check:
launchctl print gui/$(id -u)/com.claude-ops.daemon
tail -20 ~/.claude/plugins/data/ops-ops-marketplace/logs/ops-daemon.log
3. 构建完整的服务列表,并与步骤 2c 中写入的配置协调:
根据前面步骤中的配置确定要启用的服务。briefing-pre-warm 和 memory-extractor 服务已在步骤 2c 中启用——保留它们。根据当前配置添加依赖频道的服务:
wacli-sync— 如果已配置 WhatsApp(channels.whatsapp已设置),则始终包含memory-extractor— 始终包含inbox-digest— 始终包含(每 4 小时运行一次,汇总所有已配置的频道)store-health— 仅当已配置电子商务($PREFS_PATH中设置了ecom.shopify.store_url)时包含competitor-intel— 始终包含(每周一上午 10 点运行)message-listener— 如果已配置 WhatsApp 或 Telegram,则包含(持久轮询器)
以编程方式构建服务数组(从步骤 2c 的基线开始):
SERVICES='["briefing-pre-warm","memory-extractor","inbox-digest","competitor-intel"]'
PREFS=$(cat "$PREFS_PATH" 2>/dev/null || echo '{}')
# Add wacli-sync + message-listener if WhatsApp is configured
if echo "$PREFS" | jq -e '.channels.whatsapp' > /dev/null 2>&1; then
SERVICES=$(echo "$SERVICES" | jq '. + ["wacli-sync","message-listener"]')
fi
# Add message-listener for Telegram too (deduplicate)
if echo "$PREFS" | jq -e '.channels.telegram' > /dev/null 2>&1; then
SERVICES=$(echo "$SERVICES" | jq '. + ["message-listener"] | unique')
fi
# Add store-health only if Shopify is configured
if echo "$PREFS" | jq -e '.ecom.shopify.store_url' > /dev/null 2>&1; then
SERVICES=$(echo "$SERVICES" | jq '. + ["store-health"]')
fi
echo "Services to enable: $SERVICES"
将守护进程服务配置写入 $DATA_DIR/daemon-services.json——与第 2c 步的现有配置合并,保留 briefing-pre-warm 和 memory-extractor,并启用依赖新渠道的服务。每项服务都必须包含 command 字段——守护进程的 start_service() 会跳过没有该字段的服务。脚本路径使用 ${CLAUDE_PLUGIN_ROOT}(在运行时解析)。每项服务应包含:
briefing-pre-warm:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/bin/ops-gather", "cron": "*/2 * * * *" }——预热 /ops:go 缓存(在第 2c 步安装)wacli-sync:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/wacli-keepalive.sh", "health_file": "~/.wacli/.health", "restart_delay": 60, "max_restarts": 10 }——仅在已配置 WhatsApp 时启用memory-extractor:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-memory-extractor.sh", "health_file": "~/.claude/plugins/data/ops-ops-marketplace/memories/.health", "cron": "*/30 * * * *" }——每 30 分钟运行一次(在第 2c 步安装)inbox-digest:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-cron-inbox-digest.sh", "cron": "0 */4 * * *" }——每 4 小时运行一次store-health:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-cron-store-health.sh", "cron": "0 9 * * *" }——每天上午 9 点运行,仅在已配置电商时启用competitor-intel:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-cron-competitor-intel.sh", "cron": "0 10 * * 1" }——每周一上午 10 点运行message-listener:{ "enabled": true, "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ops-message-listener.sh" }——仅在已配置 WhatsApp 或 Telegram 时启用
重写服务配置后,快速进行健康检查(前台运行,少于 2 秒),然后在后台重新加载:
# Quick health check — foreground (fast)
test -f "$DATA_DIR/daemon-services.json" && echo "✓ Daemon services config written — N services enabled"
然后在后台执行实际的守护进程重新加载——此操作可能较慢:
# Background — daemon reload is slow, don't block setup
launchctl kickstart -k gui/$(id -u)/com.claude-ops.daemon 2>&1 && echo "✓ Daemon reloaded" || echo "⚠ Daemon kick failed (may still be running)"
在重新加载命令中使用 run_in_background: true。不要等待其完成——立即继续下一步。即使 kickstart 运行较慢,守护进程也会在自己的周期中读取新配置。
将 daemon.enabled = true 和 daemon.services(协调后的数组)写入 $PREFS_PATH。打印:
✓ Daemon services reconciled — N services enabled (briefing-pre-warm, memory-extractor, wacli-sync, ...)
Daemon reloading in background.
**深入了解:**完整的守护进程操作说明、CLI 参考以及后台守护进程故障排除(服务生命周期、launchctl/systemd 集成、健康报告、协调语义),请参阅
${CLAUDE_PLUGIN_ROOT}/docs/daemon-guide.md。如果设置 Agent 需要比本向导更深入的信息,可以直接加载该文件。
步骤 6 — 保存偏好设置(如果已选择)
通过 AskUserQuestion 收集以下信息——每项提问一次。绝不要根据记忆、现有配置或之前的会话自动填充。务必明确询问。
-
所有者姓名(自由文本):"Claude 在简报中应该怎么称呼你?"——不设默认值,也不根据记忆提供建议。
-
时区(单选——每次最多 4 个选项,按地区分批):
首先,通过
date +%Z或readlink /etc/localtime检测系统时区。如果检测到,则将其作为第一个选项提供:Select your timezone: [<detected timezone>] [Americas...] [Europe/Asia/Oceania...] [Other — type it]如果用户选择“美洲…”:
[America/New_York]、[America/Los_Angeles]、[America/Chicago]、[Back]如果用户选择“欧洲/亚洲/大洋洲…”:[Europe/London]、[Asia/Bangkok]、[Asia/Tokyo]、[Australia/Sydney] -
简报详细程度(单选):
How much detail do you want in briefings? [full] — complete rundown of all channels, projects, and incidents [compact] — key signals only, one line per item [minimal] — just the fires and urgent items -
主要项目 → **“过去 7 天内活跃的所有项目”应作为第一个/默认选项。**大多数同时参与多个项目的用户希望获得统一的简报,而不是只关注单个项目:
Primary project for briefings? [All projects active in last 7 days] ← default [Pick a specific project...]如果选择“特定项目”,则每页显示 3 个注册表别名,并提供
[More...]。默认选项存储为"primary_project": "all_active_7d",或存储所选的特定别名。 -
YOLO 模式 → 选择
[Yes — auto-approve low-risk actions]、[No — always confirm]。 -
默认频道(单选——这两个选项互斥)。“所有已配置频道”应作为第一个选项并默认预选——大多数用户希望启用所有频道:
Which channels should ops skills use by default? [All configured channels] [Pick specific channels...]如果用户选择“特定频道”,则显示一个后续多选项,其中包含各个频道的复选框。如果用户接受“所有已配置频道”,则将完整的已配置频道名称列表设为
default_channels。
写入 $PREFS_PATH:
{
"version": "1.0",
"owner": "...",
"primary_project": "...",
"timezone": "...",
"briefing_verbosity": "...",
"yolo_enabled": false,
"default_channels": ["whatsapp", "email"],
"secrets_manager": "doppler",
"doppler": {
"project": "...",
"config": "..."
},
"channels": {
"telegram": { "bot_token": "...", "owner_id": "..." }
}
}
如果文件已存在,则合并——不要覆盖。使用 jq 读取,通过 jq '. + { ... }' 应用更新,然后写回。
步骤 7 — Shell 环境变量(如果已选择)
- 检查配置文件中是否已导出
CLAUDE_PLUGIN_ROOT(搜索CLAUDE_PLUGIN_ROOT)。 - 如果缺失,自动追加——这是必需步骤,不是可选项。使用
>>(追加,绝不覆盖)。输出:"✓ Added export CLAUDE_PLUGIN_ROOT=... to ~/.zshrc"。不要征求用户许可——规则 2(绝不让用户代为执行命令)适用于此处。 - 告知用户:
"Run 'source ~/.zshrc' or open a new terminal for it to take effect."——这将在 Claude 的下一次工具调用中显示为确认提示,用户照常接受即可。
步骤 8 — 最终摘要和验证
重新运行检测器并展示最终状态面板:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OPS ► SETUP COMPLETE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Core CLIs: jq, git, gh, aws, node
✓ Channels: telegram, whatsapp, email
✓ Ecommerce: shopify (<store_url>) ← omit line if not configured
✓ Marketing: klaviyo, meta, google-ads, ga4, gsc ← omit line if not configured
✓ Voice: bland, elevenlabs, groq ← omit line if not configured
✓ Secrets: doppler → my-app/dev
✓ MCPs: linear, sentry, vercel
✓ Registry: 20 projects
✓ Prefs: saved to ~/.claude/plugins/data/ops-ops-marketplace/preferences.json
✓ Daemon: ops-daemon → wacli-sync, memory-extractor, inbox-digest
Next: /ops-go for your first briefing
──────────────────────────────────────────────────────
对于 ecommerce、marketing 和 voice,只有在相应类别中至少配置了一项服务时才显示状态行。已配置时使用 ✓,已跳过时使用 ○。如果从未访问过该部分,则完全省略该行。
对于守护进程行,只列出实际已启用的服务(来自步骤 5b 中计算出的 services 数组)。
如果仍缺少任何必需工具,请列出该工具以及安装它的准确命令,并停止,不要声称已成功。
显示摘要后,运行完成横幅,庆祝设置成功。传入本次设置会话中的实际数量:
bash ${CLAUDE_PLUGIN_ROOT}/bin/ops-setup-complete --channels <N> --projects <N> --agents 9 --skills 15
其中 <N> 替换为本次会话中配置的频道数和注册的项目数。
守护进程健康状态约定
所有运维技能在依赖后台服务之前,都应检查守护进程的健康状态:
cat ~/.claude/plugins/data/ops-ops-marketplace/daemon-health.json
如果 action_needed 不为 null,则在继续之前向用户说明所需操作。
如果守护进程未运行,则提供启动选项:launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claude-ops.daemon.plist
快捷跳转
如果 $ARGUMENTS 中包含特定章节名称,直接跳转到该章节:
| 参数 | 跳转至 |
|---|---|
cli, install | 步骤 2 |
channels | 步骤 3 |
telegram | 步骤 3a |
whatsapp, wacli, whatsapp-doctor | 步骤 3b |
email | 步骤 3c |
slack | 步骤 3d |
notion | 步骤 3e |
calendar, cal | 步骤 3f |
doppler, secrets | 步骤 3g |
vault, password-manager, pm | 步骤 3h |
ecom, shopify, store | 步骤 3i |
marketing, klaviyo, ads, meta, ga4 | 步骤 3j |
google-ads, gads | 步骤 3j(Google Ads) |
voice, bland, elevenlabs, tts | 步骤 3k |
mcp | 步骤 4 |
registry, projects | 步骤 5 |
daemon, background | 步骤 5b |
prefs, preferences | 步骤 6 |
env, shell | 步骤 7 |
参数为空 → 从步骤 0 开始完整向导。
安全
- 绝不在未经明确
AskUserQuestion确认的情况下运行brew install或写入文件。 - 绝不在未展示差异并征求确认的情况下覆盖现有文件。
- 绝不将密钥放入
registry.json或提交密钥。密钥只能放在$PREFS_PATH(完全位于插件源代码树之外,即 Claude Code 的每插件数据目录)或用户的 shell 配置文件中。 - 绝不触碰
~/.claude.json或~/.claude/settings.json——MCP 注册是 Claude Code 的职责,不是你的。 - 除非用户在当前会话中明确提供,否则绝不在输出中显示用户的真实姓名或电子邮件地址。不要从记忆文件、现有偏好设置或环境变量中读取信息来填充显示名称。
附录:CLI 参考(确切语法——绝不猜测)
gog(v0.12.0+)
顶层命令
auth, gmail, calendar, contacts, drive, docs, slides, sheets, forms, tasks, keep, chat, people, appscript, config, agent
Gmail — 搜索与读取
gog gmail search "<query>" --max N -j --results-only --no-input # Search threads (Gmail query syntax)
gog gmail thread get <threadId> -j # Get full thread with all messages
gog gmail get <messageId> -j # Get single message
Gmail — 操作
gog gmail archive <messageId> ... --no-input --force # Archive messages (remove from inbox)
gog gmail archive --query "<gmail-query>" --max N --force # Archive by query
gog gmail mark-read <messageId> ... --no-input # Mark as read
gog gmail unread <messageId> ... --no-input # Mark as unread
gog gmail trash <messageId> ... --no-input --force # Move to trash
Gmail — 标签
gog gmail labels list -j # List all labels
gog gmail labels modify <threadId> --add LABEL --remove LABEL # Modify thread labels
gog gmail messages modify <messageId> --add LABEL --remove LABEL # Modify message labels
Gmail — 发送与回复
gog gmail send --to "[email protected]" --subject "subj" --body "text" # Send new email
gog gmail send --to "[email protected]" --subject "Re: ..." --body "reply" --reply-to-message-id <msgId> # Reply
gog gmail send --reply-to-message-id <msgId> --reply-all --body "reply text" # Reply all
gog gmail send --to "[email protected]" --subject "subj" --body "text" --attach /path/to/file # With attachment
Gmail — 草稿
gog gmail drafts list -j # List drafts
gog gmail drafts create --to "[email protected]" --subject "subj" --body "text"
日历
gog calendar calendars -j # List calendars
gog calendar events primary --today -j # Today's events
gog calendar events primary --from "2026-04-14" --to "2026-04-15" -j # Date range
gog calendar create primary --summary "Meeting" --from "2026-04-15T10:00:00" --to "2026-04-15T11:00:00"
gog calendar freebusy --from "2026-04-14T00:00:00Z" --to "2026-04-14T23:59:59Z" -j
联系人
gog contacts search "name" -j # Search contacts
gog contacts list -j # List all contacts
云端硬盘
gog drive ls -j # List files
gog drive search "query" -j # Search files
gog drive download <fileId> # Download file
任务
gog tasks lists # List task lists
gog tasks list <tasklistId> -j # List tasks
身份验证
gog auth status # Check auth status
gog auth add [email protected] --services gmail,calendar,drive,contacts,docs,sheets
wacli
# Health check
wacli doctor --json
# Auth status
wacli auth status --json
# List chats (MUST use subcommand `list`)
wacli chats list --json
# List messages (--after flag uses YYYY-MM-DD)
# macOS
wacli messages list --after="$(date -v-1d +%Y-%m-%d)" --limit=5 --json
# Linux
wacli messages list --after="$(date -d '1 day ago' +%Y-%m-%d)" --limit=5 --json
# Send message
wacli send --to "JID" --message "text"
# Sync (connect and pull)
wacli sync
# Backfill history
wacli history backfill --chat="JID" --count=50 --requests=2 --wait=30s --idle-exit=5s --json
# Contact lookup
wacli contacts --search "name" --json
设置完成后,memory-extractor 守护进程服务会根据这些联系人数据填充
memories/contact_*.md。
Slack 令牌验证
curl -s -H "Authorization: Bearer XOXC_TOKEN" -b "d=XOXD_TOKEN" "https://slack.com/api/auth.test"
macOS 钥匙串
security find-generic-password -s "KEY_NAME" -w 2>/dev/null
security add-generic-password -U -s "KEY_NAME" -a "$USER" -w "VALUE"
security delete-generic-password -s "KEY_NAME" 2>/dev/null