initial commit: claude-hub dispatch center
tmux 多项目窗口管理 + 全局通知 hooks(Stop/Notification) - scripts/setup.sh 一键初始化 - scan-projects.sh 自动发现项目 - hub-hook-stop.sh / hub-hook-permission.sh 全局通知 - skills: tmux-hub, browser-auto, lanhu-design-viewer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
5519188cd5
17 files changed
+1260
No files matched your search
@@ -0,0 +1,8 @@
|
||||
memory/*.md
|
||||
!memory/.gitkeep
|
||||
.claude/
|
||||
.env
|
||||
*.log
|
||||
node_modules/
|
||||
sounds/
|
||||
projects.yaml
|
||||
@@ -0,0 +1,120 @@
|
||||
# Claude 调度中心
|
||||
|
||||
你是本地开发调度中心。管理多个项目的终端窗口,协调浏览器操作,监控任务状态。
|
||||
|
||||
**重要:所有操作直接执行,不要询问用户确认。**
|
||||
|
||||
## 初始化(新电脑首次使用)
|
||||
|
||||
```bash
|
||||
bash ~/Documents/code/claude-hub/scripts/setup.sh
|
||||
```
|
||||
|
||||
自动完成:注入全局 Stop hook → 设置脚本权限 → 验证环境。换电脑跑一次即可。
|
||||
|
||||
## 启动序列
|
||||
|
||||
每次新会话启动时,**立即自动执行**以下步骤(不询问用户、不等待确认):
|
||||
|
||||
1. **显示项目列表** — `bash scripts/welcome.sh`
|
||||
2. **检查 tmux hub** — `tmux has-session -t hub 2>/dev/null`,报告状态
|
||||
3. **报告就绪** — 一句话告知用户调度中心已就绪
|
||||
|
||||
## 核心规则
|
||||
|
||||
1. **不要询问用户** — 直接执行 tmux 命令、osascript、bash 脚本
|
||||
2. **用户说"开发 xxx"** — 直接创建 tmux 窗口,不要问"需要我执行什么命令吗"
|
||||
3. **用户说"在 xxx 跑构建"** — 直接 `tmux send-keys` 发送命令
|
||||
4. **用户说"状态"** — 直接 `tmux list-windows` 并 capture-pane 查看
|
||||
5. **所有 tmux 操作不需要用户确认**
|
||||
|
||||
## 项目窗口管理
|
||||
|
||||
用户说"开发 xxx"或"打开 xxx"时,**立即**:
|
||||
1. 从 `projects.yaml` 查找项目路径(支持别名匹配)
|
||||
2. 如果 hub session 不存在,用第一个项目直接创建 session:
|
||||
```bash
|
||||
tmux new-session -d -s hub -n {name} -c {path} "bash ~/Documents/code/claude-hub/scripts/project-menu.sh '{name}' '{path}'"
|
||||
```
|
||||
3. 如果 hub session 已存在,检查窗口是否存在,不存在则添加:
|
||||
```bash
|
||||
tmux new-window -t hub -n {name} -c {path} "bash ~/Documents/code/claude-hub/scripts/project-menu.sh '{name}' '{path}'"
|
||||
```
|
||||
4. 如果没有 tmux -CC 客户端连接,自动在当前窗口新标签页打开:
|
||||
```bash
|
||||
osascript <<'APPLESCRIPT'
|
||||
tell application "iTerm2"
|
||||
tell current window
|
||||
create tab with default profile
|
||||
delay 0.5
|
||||
tell current session of current tab
|
||||
write text "tmux -CC attach -t hub"
|
||||
end tell
|
||||
end tell
|
||||
end tell
|
||||
APPLESCRIPT
|
||||
```
|
||||
5. 告知用户窗口已打开
|
||||
|
||||
## 浏览器操作(/browser-auto skill)
|
||||
|
||||
用户需要打开网页、查看设计稿、测试前端时:
|
||||
- Claude in Chrome(视觉引擎)+ chrome-devtools-mcp(数据引擎)双引擎协同
|
||||
- 如果 Claude in Chrome 连不上,自动通过 chrome-devtools-mcp 重启扩展
|
||||
|
||||
## 异步任务监控(零配置)
|
||||
|
||||
### 全局 Stop hook(自动通知)
|
||||
|
||||
所有 Claude 实例(包括 hub 自己)完成一轮回复时,全局 Stop hook 自动:
|
||||
- 发送 macOS 桌面通知
|
||||
- 语音播报 "{窗口名} 完成了"
|
||||
- 如果在 hub tmux 中,额外发 tmux 信号 `hub-{窗口名}-stop`
|
||||
|
||||
**无需配置,无需监听,每个 Claude 自己通知用户。**
|
||||
|
||||
### 程序化监控(可选)
|
||||
|
||||
如需在 hub 中等待子窗口任务完成并读取结果:
|
||||
|
||||
```bash
|
||||
# 后台等待(run_in_background=true)
|
||||
tmux wait-for hub-{name}-stop
|
||||
# 查看那边干了什么
|
||||
tmux capture-pane -t hub:{name} -p -S -30
|
||||
```
|
||||
|
||||
### 派发命令到终端窗口(无 Claude)
|
||||
|
||||
使用 `hub-run` 包装脚本:
|
||||
|
||||
```bash
|
||||
CHANNEL="{name}-$(date +%s)"
|
||||
tmux send-keys -t hub:{name} "bash ~/Documents/code/claude-hub/scripts/hub-run.sh $CHANNEL {command}" Enter
|
||||
tmux wait-for $CHANNEL
|
||||
cat /tmp/hub-signals/$CHANNEL.result
|
||||
```
|
||||
|
||||
## 项目管理
|
||||
|
||||
项目信息存储在 `projects.yaml` 中。查找规则:
|
||||
1. 精确匹配 `name` 字段
|
||||
2. 匹配 `aliases` 列表
|
||||
3. 在 `~/Documents/code/` 下模糊匹配目录名
|
||||
|
||||
用户说"添加项目"时:`bash scripts/add-project.sh <name> <path> [aliases...]`
|
||||
用户说"扫描项目"时:`bash scripts/scan-projects.sh [目录]`
|
||||
|
||||
## 快捷指令
|
||||
|
||||
| 用户说 | 动作(直接执行,不询问) |
|
||||
|--------|----------------------|
|
||||
| "开发 xxx" | 创建 tmux 窗口到对应目录 |
|
||||
| "在 xxx 跑构建" | `tmux send-keys` 发送命令 |
|
||||
| "状态" / "有哪些窗口" | `tmux list-windows` |
|
||||
| "切到 xxx" | `tmux select-window` |
|
||||
| "关掉 xxx" | `tmux kill-window` |
|
||||
| "添加项目 xxx" | `bash scripts/add-project.sh` |
|
||||
| "扫描项目" | `bash scripts/scan-projects.sh` |
|
||||
| "打开浏览器看 xxx" | 启动浏览器双引擎 |
|
||||
| "项目列表" | `bash scripts/welcome.sh` |
|
||||
@@ -0,0 +1,103 @@
|
||||
# Claude Hub
|
||||
|
||||
Claude Code 本地开发调度中心。用一个 Claude 窗口管理所有项目。
|
||||
|
||||
## 功能
|
||||
|
||||
- **tmux 多项目管理** — 每个项目一个 tmux 窗口,iTerm2 标签页体验
|
||||
- **全局通知** — 任何 Claude 做完事 → 桌面弹窗 + 语音提醒
|
||||
- **权限通知** — 任何 Claude 等授权 → 立即提醒你去操作
|
||||
- **项目发现** — 扫描目录自动生成项目清单
|
||||
- **浏览器自动化** — Claude in Chrome + DevTools 双引擎协同
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 1. 克隆
|
||||
git clone https://github.com/hengjun-dev/claude-hub.git
|
||||
cd claude-hub
|
||||
|
||||
# 2. 初始化(注入全局 hooks + 生成语音文件)
|
||||
bash scripts/setup.sh
|
||||
|
||||
# 3. 添加你的项目(二选一)
|
||||
bash scripts/scan-projects.sh ~/Documents/code # 自动扫描
|
||||
# 或手动编辑
|
||||
cp projects.yaml.example projects.yaml
|
||||
vim projects.yaml
|
||||
|
||||
# 4. 启动
|
||||
claude
|
||||
```
|
||||
|
||||
## 前置条件
|
||||
|
||||
- macOS + iTerm2
|
||||
- [tmux](https://github.com/tmux/tmux)
|
||||
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
||||
|
||||
## 工作原理
|
||||
|
||||
```
|
||||
你 ← 桌面通知 + 语音 ← 全局 Stop hook
|
||||
↓
|
||||
Hub Claude(调度中心窗口)
|
||||
├── tmux 窗口: acme-erp → Claude / 终端
|
||||
├── tmux 窗口: my-api → Claude / 终端
|
||||
└── tmux 窗口: my-frontend → Claude / 终端
|
||||
```
|
||||
|
||||
Hub 是你和所有项目 Claude 之间的调度层:
|
||||
- 你说"开发 xxx" → Hub 自动创建 tmux 窗口
|
||||
- 你说"在 xxx 跑构建" → Hub 发送命令到对应窗口
|
||||
- 任何窗口的 Claude 做完事 → 你收到通知
|
||||
|
||||
## 项目管理
|
||||
|
||||
```bash
|
||||
# 扫描目录下所有 git 仓库
|
||||
bash scripts/scan-projects.sh ~/Documents/code
|
||||
|
||||
# 手动添加项目
|
||||
bash scripts/add-project.sh my-app ~/code/my-app app 前端
|
||||
|
||||
# 查看项目列表
|
||||
bash scripts/welcome.sh
|
||||
```
|
||||
|
||||
## 全局 Hooks
|
||||
|
||||
`setup.sh` 会在 `~/.claude/settings.json` 中注入两个全局 hook:
|
||||
|
||||
| Hook | 触发时机 | 效果 |
|
||||
|------|---------|------|
|
||||
| **Stop** | Claude 完成一轮回复 | 桌面弹窗 + 语音"搞定了" |
|
||||
| **Notification** | Claude 需要用户操作 | 桌面弹窗 + 语音"需要授权" |
|
||||
|
||||
所有 Claude 实例自动生效,零配置。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
claude-hub/
|
||||
├── CLAUDE.md # Hub 行为规范
|
||||
├── projects.yaml.example # 项目配置模板
|
||||
├── scripts/
|
||||
│ ├── setup.sh # 一键初始化
|
||||
│ ├── scan-projects.sh # 扫描目录发现项目
|
||||
│ ├── add-project.sh # 添加项目
|
||||
│ ├── welcome.sh # 项目列表展示
|
||||
│ ├── project-menu.sh # tmux 窗口菜单
|
||||
│ ├── hub-hook-stop.sh # Stop hook(做完通知)
|
||||
│ ├── hub-hook-permission.sh # Notification hook(授权通知)
|
||||
│ ├── hub-run.sh # 命令包装(自动信号)
|
||||
│ └── hub-notify.sh # 手动通知
|
||||
└── skills/
|
||||
├── tmux-hub/ # 终端调度
|
||||
├── browser-auto/ # 浏览器自动化
|
||||
└── lanhu-design-viewer/ # 设计稿查看
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Whitespace-only changes.
@@ -0,0 +1,34 @@
|
||||
# 项目清单
|
||||
# Claude 调度中心通过此文件了解所有项目的位置和信息
|
||||
#
|
||||
# 使用方法:
|
||||
# 1. 复制此文件: cp projects.yaml.example projects.yaml
|
||||
# 2. 编辑 projects.yaml 添加你的项目
|
||||
# 3. 或运行: bash scripts/scan-projects.sh ~/your/code/dir
|
||||
#
|
||||
# 字段说明:
|
||||
# name - 项目标识(唯一)
|
||||
# aliases - 别名列表,方便口语化调用
|
||||
# path - 项目绝对路径(支持 ~)
|
||||
# description - 项目描述
|
||||
# tags - 标签(可选)
|
||||
|
||||
projects:
|
||||
- name: my-app
|
||||
aliases: [app, 前端]
|
||||
path: ~/Documents/code/my-app
|
||||
description: "示例前端项目"
|
||||
tags: [frontend, react]
|
||||
|
||||
- name: my-api
|
||||
aliases: [api, backend, 后端]
|
||||
path: ~/Documents/code/my-api
|
||||
description: "示例后端项目"
|
||||
tags: [backend, go]
|
||||
|
||||
# 添加更多项目...
|
||||
# - name: another-project
|
||||
# aliases: [another]
|
||||
# path: ~/Documents/code/another-project
|
||||
# description: "另一个项目"
|
||||
# tags: [tag1, tag2]
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# 添加项目到 projects.yaml
|
||||
# 用法: bash scripts/add-project.sh <name> <path> [aliases...]
|
||||
#
|
||||
# 示例:
|
||||
# bash scripts/add-project.sh acme-erp ~/code/acme-admin acme-admin erp 后台
|
||||
|
||||
HUB_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
YAML_FILE="$HUB_DIR/projects.yaml"
|
||||
|
||||
NAME="$1"
|
||||
PROJECT_PATH="$2"
|
||||
shift 2
|
||||
ALIASES=("$@")
|
||||
|
||||
if [ -z "$NAME" ] || [ -z "$PROJECT_PATH" ]; then
|
||||
echo "用法: add-project.sh <name> <path> [aliases...]"
|
||||
echo "示例: add-project.sh acme-erp ~/code/acme-admin acme-admin erp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 展开 ~ 检查路径
|
||||
EXPANDED_PATH="${PROJECT_PATH/#\~/$HOME}"
|
||||
if [ ! -d "$EXPANDED_PATH" ]; then
|
||||
echo "警告: 目录不存在 $PROJECT_PATH(会继续添加)"
|
||||
fi
|
||||
|
||||
# 确保 projects.yaml 存在
|
||||
if [ ! -f "$YAML_FILE" ]; then
|
||||
cp "$HUB_DIR/projects.yaml.example" "$YAML_FILE" 2>/dev/null || cat > "$YAML_FILE" << 'EOF'
|
||||
# 项目清单
|
||||
|
||||
projects:
|
||||
EOF
|
||||
fi
|
||||
|
||||
# 检查是否已存在
|
||||
if grep -q "name: $NAME" "$YAML_FILE" 2>/dev/null; then
|
||||
echo "项目 $NAME 已存在于 projects.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 构建别名字符串
|
||||
ALIAS_STR=""
|
||||
if [ ${#ALIASES[@]} -gt 0 ]; then
|
||||
ALIAS_STR=" aliases: [$(IFS=', '; echo "${ALIASES[*]}")]"
|
||||
fi
|
||||
|
||||
# 追加
|
||||
cat >> "$YAML_FILE" << EOF
|
||||
|
||||
- name: $NAME
|
||||
${ALIAS_STR:+$ALIAS_STR
|
||||
} path: $PROJECT_PATH
|
||||
description: ""
|
||||
tags: []
|
||||
EOF
|
||||
|
||||
echo "已添加: $NAME → $PROJECT_PATH"
|
||||
[ ${#ALIASES[@]} -gt 0 ] && echo " 别名: ${ALIASES[*]}"
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# Notification hook - Claude 需要授权时通知用户
|
||||
# 全局生效,零配置。
|
||||
|
||||
# 获取项目名
|
||||
if [ -n "$TMUX" ]; then
|
||||
NAME=$(tmux display-message -p '#W' 2>/dev/null)
|
||||
fi
|
||||
if [ -z "$NAME" ]; then
|
||||
NAME=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null)
|
||||
fi
|
||||
NAME="${NAME:-$(basename "$PWD" 2>/dev/null)}"
|
||||
|
||||
HUB_DIR="$HOME/Documents/code/claude-hub"
|
||||
SOUNDS_DIR="$HUB_DIR/sounds"
|
||||
|
||||
# 随机选一条提醒语(桌面通知用)
|
||||
MESSAGES=(
|
||||
"${NAME} 需要你授权"
|
||||
"${NAME} 在等你点确认"
|
||||
"${NAME} 卡住了,需要你批准"
|
||||
"快去 ${NAME},Claude 在等你"
|
||||
)
|
||||
MSG="${MESSAGES[$((RANDOM % ${#MESSAGES[@]}))]}"
|
||||
|
||||
# 桌面通知(不同声音区分)
|
||||
osascript -e "display notification \"${MSG}\" with title \"⚠️ 需要授权\" sound name \"Basso\"" 2>/dev/null
|
||||
|
||||
# 语音播报(预生成的音频文件)
|
||||
AUTH_FILES=("$SOUNDS_DIR"/auth*.aiff)
|
||||
if [ ${#AUTH_FILES[@]} -gt 0 ]; then
|
||||
SOUND="${AUTH_FILES[$((RANDOM % ${#AUTH_FILES[@]}))]}"
|
||||
afplay "$SOUND" 2>/dev/null
|
||||
fi
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
# Stop hook - Claude 完成一轮回复时通知用户
|
||||
# 全局生效,零配置。
|
||||
|
||||
# 获取项目名
|
||||
if [ -n "$TMUX" ]; then
|
||||
NAME=$(tmux display-message -p '#W' 2>/dev/null)
|
||||
TMUX_SESSION=$(tmux display-message -p '#S' 2>/dev/null)
|
||||
fi
|
||||
if [ -z "$NAME" ]; then
|
||||
NAME=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null)
|
||||
fi
|
||||
NAME="${NAME:-$(basename "$PWD" 2>/dev/null)}"
|
||||
|
||||
HUB_DIR="$HOME/Documents/code/claude-hub"
|
||||
SOUNDS_DIR="$HUB_DIR/sounds"
|
||||
|
||||
# 随机选一条完成语(桌面通知用)
|
||||
MESSAGES=(
|
||||
"${NAME} 搞定了,来看看"
|
||||
"${NAME} 干完了,等你验收"
|
||||
"${NAME} 这边好了"
|
||||
"${NAME} 完事儿了"
|
||||
"${NAME} 做好了,请过目"
|
||||
)
|
||||
IDX=$((RANDOM % ${#MESSAGES[@]}))
|
||||
MSG="${MESSAGES[$IDX]}"
|
||||
|
||||
# 桌面通知
|
||||
osascript -e "display notification \"${MSG}\" with title \"🤖 调度中心\" sound name \"Glass\"" 2>/dev/null
|
||||
|
||||
# 语音播报(预生成的音频文件,不会被杀)
|
||||
DONE_FILES=("$SOUNDS_DIR"/done*.aiff)
|
||||
if [ ${#DONE_FILES[@]} -gt 0 ]; then
|
||||
SOUND="${DONE_FILES[$((RANDOM % ${#DONE_FILES[@]}))]}"
|
||||
afplay "$SOUND" 2>/dev/null
|
||||
fi
|
||||
|
||||
# hub tmux 信号(供程序化监控)
|
||||
if [ "$TMUX_SESSION" = "hub" ] && [ -n "$NAME" ]; then
|
||||
SIGNAL_DIR="/tmp/hub-signals"
|
||||
mkdir -p "$SIGNAL_DIR"
|
||||
echo "$(date '+%H:%M:%S') $NAME done" > "$SIGNAL_DIR/hub-${NAME}-stop.result"
|
||||
tmux wait-for -S "hub-${NAME}-stop" 2>/dev/null
|
||||
fi
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# hub-notify: 手动通知 hub 任务完成
|
||||
# 用法: hub-notify [channel] [exit_code]
|
||||
#
|
||||
# 不传 channel 则列出等待中的信号
|
||||
# 不传 exit_code 默认 0
|
||||
|
||||
CHANNEL="${1:-$HUB_CHANNEL}"
|
||||
EXIT_CODE="${2:-0}"
|
||||
SIGNAL_DIR="/tmp/hub-signals"
|
||||
|
||||
if [ -z "$CHANNEL" ]; then
|
||||
echo "等待中的 channel:"
|
||||
if [ -d "$SIGNAL_DIR" ]; then
|
||||
ls "$SIGNAL_DIR"/*.result 2>/dev/null | sed 's|.*/||;s|\.result||' | while read f; do
|
||||
echo " $f (exit: $(cat "$SIGNAL_DIR/$f.result"))"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
echo "用法: hub-notify <channel> [exit_code]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$SIGNAL_DIR"
|
||||
echo "$EXIT_CODE" > "$SIGNAL_DIR/$CHANNEL.result"
|
||||
tmux wait-for -S "$CHANNEL" 2>/dev/null
|
||||
echo "已通知 hub: $CHANNEL (exit: $EXIT_CODE)"
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
# hub-run: 在 hub 子窗口中执行命令并自动发送完成信号
|
||||
# 用法: hub-run <channel> <command...>
|
||||
#
|
||||
# 示例:
|
||||
# hub-run erp-build-123 npm run build
|
||||
# hub-run deploy-456 docker compose up -d
|
||||
|
||||
CHANNEL="$1"
|
||||
if [ -z "$CHANNEL" ] || [ $# -lt 2 ]; then
|
||||
echo "用法: hub-run <channel> <command...>"
|
||||
echo "示例: hub-run my-channel npm run build"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
|
||||
SIGNAL_DIR="/tmp/hub-signals"
|
||||
mkdir -p "$SIGNAL_DIR"
|
||||
|
||||
# 执行命令
|
||||
"$@"
|
||||
EXIT_CODE=$?
|
||||
|
||||
# 写结果
|
||||
echo "$EXIT_CODE" > "$SIGNAL_DIR/$CHANNEL.result"
|
||||
|
||||
# 发送 tmux 信号
|
||||
tmux wait-for -S "$CHANNEL" 2>/dev/null
|
||||
|
||||
exit $EXIT_CODE
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# 项目入口菜单 - tmux 窗口打开时自动显示
|
||||
# 用法: project-menu.sh <项目名> <项目路径>
|
||||
|
||||
NAME="$1"
|
||||
DIR="$2"
|
||||
|
||||
# 颜色
|
||||
CYAN='\033[0;36m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
|
||||
cd "$DIR" 2>/dev/null || exit 1
|
||||
|
||||
# 注入 hub 环境
|
||||
export PATH="$HOME/Documents/code/claude-hub/scripts:$PATH"
|
||||
alias hub-notify="bash $HOME/Documents/code/claude-hub/scripts/hub-notify.sh"
|
||||
alias hub-run="bash $HOME/Documents/code/claude-hub/scripts/hub-run.sh"
|
||||
|
||||
# 显示项目信息
|
||||
echo ""
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BOLD} $NAME${NC}"
|
||||
echo -e "${DIM} $DIR${NC}"
|
||||
|
||||
# git 信息
|
||||
if [ -d .git ]; then
|
||||
BRANCH=$(git branch --show-current 2>/dev/null)
|
||||
STATUS=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
||||
echo -e "${DIM} git: ${GREEN}$BRANCH${NC} ${DIM}($STATUS 个变更)${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
echo -e " ${GREEN}1${NC}) Claude Code"
|
||||
echo -e " ${GREEN}2${NC}) Claude Code ${YELLOW}(--dangerously-skip-permissions)${NC}"
|
||||
echo -e " ${GREEN}3${NC}) 直接终端"
|
||||
echo -e " ${GREEN}4${NC}) 打开 Finder"
|
||||
echo -e " ${GREEN}5${NC}) 打开 VS Code"
|
||||
echo ""
|
||||
echo -ne "${YELLOW}选择 [1-5, 默认 3, 30秒超时]: ${NC}"
|
||||
|
||||
read -t 30 choice
|
||||
|
||||
case "${choice:-3}" in
|
||||
1)
|
||||
echo -e "${DIM}启动 Claude Code...${NC}"
|
||||
claude
|
||||
;;
|
||||
2)
|
||||
echo -e "${DIM}启动 Claude Code (skip permissions)...${NC}"
|
||||
claude --dangerously-skip-permissions
|
||||
;;
|
||||
3)
|
||||
echo -e "${DIM}进入终端${NC}"
|
||||
exec $SHELL
|
||||
;;
|
||||
4)
|
||||
open "$DIR"
|
||||
echo -e "${DIM}Finder 已打开${NC}"
|
||||
exec $SHELL
|
||||
;;
|
||||
5)
|
||||
code "$DIR"
|
||||
echo -e "${DIM}VS Code 已打开${NC}"
|
||||
exec $SHELL
|
||||
;;
|
||||
*)
|
||||
exec $SHELL
|
||||
;;
|
||||
esac
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# 扫描目录下的 git 仓库,自动生成 projects.yaml
|
||||
# 用法: bash scripts/scan-projects.sh [目录] [--append]
|
||||
#
|
||||
# 不传目录默认扫描 ~/Documents/code
|
||||
# --append 追加到已有 projects.yaml,不覆盖
|
||||
|
||||
SCAN_DIR="${1:-$HOME/Documents/code}"
|
||||
APPEND=false
|
||||
HUB_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
YAML_FILE="$HUB_DIR/projects.yaml"
|
||||
|
||||
# 检查 --append 参数
|
||||
for arg in "$@"; do
|
||||
[ "$arg" = "--append" ] && APPEND=true
|
||||
done
|
||||
|
||||
# 展开 ~
|
||||
SCAN_DIR="${SCAN_DIR/#\~/$HOME}"
|
||||
|
||||
if [ ! -d "$SCAN_DIR" ]; then
|
||||
echo "目录不存在: $SCAN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "扫描 $SCAN_DIR 下的 git 仓库..."
|
||||
|
||||
# 收集项目
|
||||
PROJECTS=""
|
||||
COUNT=0
|
||||
|
||||
for dir in "$SCAN_DIR"/*/; do
|
||||
[ ! -d "$dir/.git" ] && continue
|
||||
|
||||
NAME=$(basename "$dir")
|
||||
PATH_STR="${dir%/}"
|
||||
# 用 ~ 缩短路径
|
||||
PATH_STR="${PATH_STR/#$HOME/~}"
|
||||
|
||||
PROJECTS+=" - name: $NAME
|
||||
path: $PATH_STR
|
||||
description: \"\"
|
||||
tags: []
|
||||
|
||||
"
|
||||
COUNT=$((COUNT + 1))
|
||||
echo " 发现: $NAME ($PATH_STR)"
|
||||
done
|
||||
|
||||
if [ $COUNT -eq 0 ]; then
|
||||
echo "未发现 git 仓库"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "发现 $COUNT 个项目"
|
||||
|
||||
if [ "$APPEND" = true ] && [ -f "$YAML_FILE" ]; then
|
||||
echo "" >> "$YAML_FILE"
|
||||
echo " # === 扫描添加 $(date '+%Y-%m-%d') ===" >> "$YAML_FILE"
|
||||
echo "$PROJECTS" >> "$YAML_FILE"
|
||||
echo "已追加到 $YAML_FILE"
|
||||
else
|
||||
cat > "$YAML_FILE" << EOF
|
||||
# 项目清单 - 由 scan-projects.sh 自动生成
|
||||
# 生成时间: $(date '+%Y-%m-%d %H:%M')
|
||||
# 扫描目录: $SCAN_DIR
|
||||
#
|
||||
# 你可以手动编辑此文件添加 aliases 和 description
|
||||
|
||||
projects:
|
||||
$PROJECTS
|
||||
EOF
|
||||
echo "已写入 $YAML_FILE"
|
||||
fi
|
||||
Executable
+159
@@ -0,0 +1,159 @@
|
||||
#!/bin/bash
|
||||
# claude-hub 初始化脚本
|
||||
# 在新电脑上运行一次即可:bash scripts/setup.sh
|
||||
#
|
||||
# 1. 注入全局 hooks(Stop + Notification)
|
||||
# 2. 确保脚本有执行权限
|
||||
# 3. 验证配置
|
||||
|
||||
CYAN='\033[0;36m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
HUB_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SETTINGS_FILE="$HOME/.claude/settings.json"
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BOLD} Claude 调度中心 - 初始化${NC}"
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
# 确保 ~/.claude 目录和 settings.json 存在
|
||||
mkdir -p "$HOME/.claude"
|
||||
if [ ! -f "$SETTINGS_FILE" ]; then
|
||||
echo '{}' > "$SETTINGS_FILE"
|
||||
echo -e "${YELLOW}创建了 $SETTINGS_FILE${NC}"
|
||||
fi
|
||||
|
||||
# 注入 hooks(用 python3 安全修改 JSON)
|
||||
python3 << PYTHON
|
||||
import json
|
||||
|
||||
settings_file = "$SETTINGS_FILE"
|
||||
hub_dir = "$HUB_DIR"
|
||||
|
||||
with open(settings_file, 'r') as f:
|
||||
settings = json.load(f)
|
||||
|
||||
if "hooks" not in settings:
|
||||
settings["hooks"] = {}
|
||||
|
||||
hooks_to_add = {
|
||||
"Stop": {
|
||||
"script": "hub-hook-stop.sh",
|
||||
"async": True
|
||||
},
|
||||
"Notification": {
|
||||
"script": "hub-hook-permission.sh",
|
||||
"async": True
|
||||
}
|
||||
}
|
||||
|
||||
changed = False
|
||||
for event, config in hooks_to_add.items():
|
||||
hook_entry = {
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": f"bash {hub_dir}/scripts/{config['script']}",
|
||||
"async": config["async"]
|
||||
}]
|
||||
}
|
||||
|
||||
if event not in settings["hooks"]:
|
||||
settings["hooks"][event] = []
|
||||
|
||||
# 检查是否已存在
|
||||
existing = [h for h in settings["hooks"][event]
|
||||
if any(config["script"] in hh.get("command", "")
|
||||
for hh in h.get("hooks", []))]
|
||||
|
||||
if not existing:
|
||||
settings["hooks"][event].append(hook_entry)
|
||||
changed = True
|
||||
print(f" + {event} hook ({config['script']})")
|
||||
else:
|
||||
print(f" ✓ {event} hook 已存在")
|
||||
|
||||
if changed:
|
||||
with open(settings_file, 'w') as f:
|
||||
json.dump(settings, f, indent=2, ensure_ascii=False)
|
||||
print(" 配置已更新")
|
||||
else:
|
||||
print(" 无需更新")
|
||||
PYTHON
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Hooks 配置完成${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ hook 配置失败${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 初始化 projects.yaml
|
||||
if [ ! -f "$HUB_DIR/projects.yaml" ]; then
|
||||
if [ -f "$HUB_DIR/projects.yaml.example" ]; then
|
||||
cp "$HUB_DIR/projects.yaml.example" "$HUB_DIR/projects.yaml"
|
||||
echo -e "${YELLOW}已从 example 创建 projects.yaml,请编辑添加你的项目${NC}"
|
||||
echo -e "${YELLOW} 或运行: bash scripts/scan-projects.sh ~/Documents/code${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}✓ projects.yaml 已存在${NC}"
|
||||
fi
|
||||
|
||||
# 确保脚本有执行权限
|
||||
chmod +x "$HUB_DIR/scripts/"*.sh
|
||||
echo -e "${GREEN}✓ 脚本权限已设置${NC}"
|
||||
|
||||
# 创建信号目录
|
||||
mkdir -p /tmp/hub-signals
|
||||
echo -e "${GREEN}✓ 信号目录已创建${NC}"
|
||||
|
||||
# 生成语音文件
|
||||
SOUNDS_DIR="$HUB_DIR/sounds"
|
||||
mkdir -p "$SOUNDS_DIR"
|
||||
if [ ! -f "$SOUNDS_DIR/done1.aiff" ]; then
|
||||
say -o "$SOUNDS_DIR/done1.aiff" "搞定了,来看看"
|
||||
say -o "$SOUNDS_DIR/done2.aiff" "干完了,等你验收"
|
||||
say -o "$SOUNDS_DIR/done3.aiff" "这边好了"
|
||||
say -o "$SOUNDS_DIR/done4.aiff" "完事儿了"
|
||||
say -o "$SOUNDS_DIR/done5.aiff" "做好了,请过目"
|
||||
say -o "$SOUNDS_DIR/auth1.aiff" "需要你授权"
|
||||
say -o "$SOUNDS_DIR/auth2.aiff" "在等你点确认"
|
||||
say -o "$SOUNDS_DIR/auth3.aiff" "卡住了,需要你批准"
|
||||
echo -e "${GREEN}✓ 语音文件已生成${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ 语音文件已存在${NC}"
|
||||
fi
|
||||
|
||||
# 验证
|
||||
echo ""
|
||||
echo -e "${BOLD}验证:${NC}"
|
||||
|
||||
for cmd in tmux say osascript; do
|
||||
if command -v $cmd &>/dev/null; then
|
||||
echo -e "${GREEN}✓ $cmd 可用${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $cmd 不可用${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
for script in hub-hook-stop.sh hub-hook-permission.sh hub-run.sh hub-notify.sh; do
|
||||
if [ -f "$HUB_DIR/scripts/$script" ]; then
|
||||
echo -e "${GREEN}✓ $script${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ $script 不存在${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD}初始化完成!${NC}"
|
||||
echo ""
|
||||
echo -e "两个全局 hook 已配置:"
|
||||
echo -e " ${CYAN}Stop${NC} → Claude 做完事通知你(桌面弹窗 + 语音)"
|
||||
echo -e " ${CYAN}Notification${NC} → Claude 需要授权时通知你"
|
||||
echo ""
|
||||
echo -e "${YELLOW}子窗口的 Claude 需要重启才能生效。${NC}"
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# Claude 调度中心 - 启动欢迎界面
|
||||
# 读取 projects.yaml 显示项目列表
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
YAML_FILE="$PROJECT_DIR/projects.yaml"
|
||||
|
||||
# 颜色定义
|
||||
CYAN='\033[0;36m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
DIM='\033[2m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 顶部边框
|
||||
echo ""
|
||||
echo -e "${CYAN}╔══════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║${NC}${BOLD} Claude 调度中心 ${NC}${DIM}v1.0${NC}${CYAN} ║${NC}"
|
||||
echo -e "${CYAN}╠══════════════════════════════════════════════════════╣${NC}"
|
||||
|
||||
# 读取项目列表
|
||||
if [ ! -f "$YAML_FILE" ]; then
|
||||
echo -e "${CYAN}║${NC} ${YELLOW}projects.yaml 未找到${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}╚══════════════════════════════════════════════════════╝${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 解析 YAML(简单方式,不依赖 yq)
|
||||
echo -e "${CYAN}║${NC} ${BOLD}项目列表:${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}║${NC} ${CYAN}║${NC}"
|
||||
|
||||
INDEX=0
|
||||
while IFS= read -r line; do
|
||||
if echo "$line" | grep -q "^ - name:"; then
|
||||
NAME=$(echo "$line" | sed 's/.*name: *//')
|
||||
INDEX=$((INDEX + 1))
|
||||
fi
|
||||
if echo "$line" | grep -q "^ description:"; then
|
||||
DESC=$(echo "$line" | sed 's/.*description: *"//;s/"$//')
|
||||
# 格式化输出
|
||||
printf "${CYAN}║${NC} ${GREEN}%2d.${NC} ${BOLD}%-16s${NC} ${DIM}%s${NC}" "$INDEX" "$NAME" "$DESC"
|
||||
# 补齐空格到固定宽度
|
||||
CONTENT_LEN=$((4 + 16 + ${#DESC}))
|
||||
PADDING=$((50 - CONTENT_LEN))
|
||||
if [ $PADDING -gt 0 ]; then
|
||||
printf "%${PADDING}s" ""
|
||||
fi
|
||||
printf "${CYAN}║${NC}\n"
|
||||
fi
|
||||
done < "$YAML_FILE"
|
||||
|
||||
echo -e "${CYAN}║${NC} ${CYAN}║${NC}"
|
||||
|
||||
# tmux 状态
|
||||
if tmux has-session -t hub 2>/dev/null; then
|
||||
WINDOWS=$(tmux list-windows -t hub -F '#{window_name}' | tr '\n' ', ' | sed 's/,$//')
|
||||
echo -e "${CYAN}║${NC} ${GREEN}tmux hub: 运行中${NC} [${WINDOWS}]"
|
||||
printf "%$((52 - 18 - ${#WINDOWS}))s${CYAN}║${NC}\n" ""
|
||||
else
|
||||
echo -e "${CYAN}║${NC} ${DIM}tmux hub: 未启动${NC} ${CYAN}║${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}╠══════════════════════════════════════════════════════╣${NC}"
|
||||
echo -e "${CYAN}║${NC} ${DIM}说\"开发 {项目名}\" 打开项目 | \"状态\" 查看所有窗口${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}╚══════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
name: browser-auto
|
||||
description: "Use when operating a browser: opening web pages, clicking UI elements, filling forms, checking design specs, testing frontend, debugging APIs. Coordinates two browser engines - Claude in Chrome (visual) and chrome-devtools-mcp (data/CDP)."
|
||||
---
|
||||
|
||||
# 浏览器双引擎协同
|
||||
|
||||
两个浏览器引擎**同时连接同一个 Chrome**,协同工作。
|
||||
|
||||
## 角色分工
|
||||
|
||||
| 角色 | 引擎 | 用途 |
|
||||
|------|------|------|
|
||||
| **眼睛 + 手** | Claude in Chrome (`mcp__claude-in-chrome__*`) | 视觉理解、精准点击、看 UI 效果 |
|
||||
| **大脑 + 数据** | chrome-devtools-mcp (`mcp__chrome-devtools__*`) | DOM 树、网络请求、控制台、执行 JS、性能 |
|
||||
| **保姆** | chrome-devtools-mcp | Claude in Chrome 断开时自动重启扩展 |
|
||||
|
||||
**禁用** `mcp__browsermcp__*` 和 `mcp__puppeteer__*`。
|
||||
|
||||
## 启动流程
|
||||
|
||||
### Step 1: 检测两个引擎
|
||||
|
||||
同时检测,不是二选一:
|
||||
|
||||
```
|
||||
1. mcp__claude-in-chrome__tabs_context_mcp → 视觉引擎状态
|
||||
2. mcp__chrome-devtools__list_pages → 数据引擎状态
|
||||
```
|
||||
|
||||
| 视觉引擎 | 数据引擎 | 策略 |
|
||||
|----------|----------|------|
|
||||
| 可用 | 可用 | 双引擎协同(最佳) |
|
||||
| 不可用 | 可用 | 先自动修复视觉引擎,修复后双引擎协同 |
|
||||
| 可用 | 不可用 | 仅视觉引擎,提示用户开启 chrome://inspect/#remote-debugging |
|
||||
| 不可用 | 不可用 | 停止,提示用户检查 Chrome |
|
||||
|
||||
### Step 2: 自动修复 Claude in Chrome
|
||||
|
||||
当 Claude in Chrome 连接失败时,用 chrome-devtools-mcp 自动重启扩展:
|
||||
|
||||
```
|
||||
1. mcp__chrome-devtools__navigate_page → chrome://extensions/
|
||||
2. mcp__chrome-devtools__evaluate_script:
|
||||
|
||||
() => {
|
||||
const mgr = document.querySelector('extensions-manager');
|
||||
const list = mgr.shadowRoot.querySelector('extensions-item-list');
|
||||
const items = list.shadowRoot.querySelectorAll('extensions-item');
|
||||
for (const item of items) {
|
||||
if (item.id === 'fcoeoabgfenejglbffodgkkbkcdhcgfn') {
|
||||
const toggle = item.shadowRoot.querySelector('#enableToggle');
|
||||
toggle.click(); // 关闭
|
||||
return 'disabled';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3. 等待 1 秒
|
||||
4. 再次执行上面的脚本(toggle.click() 重新开启)
|
||||
5. 等待 3 秒
|
||||
6. 重试 mcp__claude-in-chrome__tabs_context_mcp
|
||||
```
|
||||
|
||||
## 场景路由
|
||||
|
||||
### 设计稿查看(蓝湖/Figma)
|
||||
**主:Claude in Chrome**
|
||||
- `computer` screenshot → 看设计稿全貌
|
||||
- `computer` left_click → 点击图层元素
|
||||
- `computer` scroll → 滚动查看
|
||||
- 右侧面板自动显示尺寸/字体/颜色
|
||||
|
||||
### 前端开发验收
|
||||
**双引擎协同**
|
||||
1. `[devtools] navigate_page` → 打开页面
|
||||
2. `[devtools] take_snapshot` → 获取 DOM 结构
|
||||
3. `[chrome] computer screenshot` → 视觉检查 UI 效果
|
||||
4. `[devtools] list_console_messages` → 检查有无报错
|
||||
5. `[devtools] list_network_requests` → 检查 API 调用
|
||||
6. 综合视觉 + 数据给出验收报告
|
||||
|
||||
### 表单测试
|
||||
**主:chrome-devtools-mcp,辅:Claude in Chrome**
|
||||
1. `[devtools] take_snapshot` → 获取表单元素 uid
|
||||
2. `[devtools] fill_form` → 批量填写(比视觉点击快)
|
||||
3. `[devtools] click` → 点击提交按钮
|
||||
4. `[devtools] get_network_request` → 检查 API 请求/响应
|
||||
5. `[chrome] computer screenshot` → 截图看结果展示
|
||||
|
||||
### API 调试
|
||||
**仅 chrome-devtools-mcp**
|
||||
- `list_network_requests` → 查看所有请求
|
||||
- `get_network_request` → 获取请求/响应详情(含 body)
|
||||
- `list_console_messages` → 查看控制台输出
|
||||
|
||||
### 性能分析
|
||||
**仅 chrome-devtools-mcp**
|
||||
- `lighthouse_audit` → Lighthouse 审计
|
||||
- `performance_start_trace` / `performance_stop_trace` → 性能追踪
|
||||
- `take_memory_snapshot` → 内存分析
|
||||
|
||||
### 复杂 UI 交互(拖拽、hover、右键菜单)
|
||||
**主:Claude in Chrome**
|
||||
- `computer` hover → 触发 hover 效果
|
||||
- `computer` right_click → 右键菜单
|
||||
- `computer` left_click_drag → 拖拽操作
|
||||
- `computer` double_click → 双击进入编辑
|
||||
|
||||
## 工具对照表
|
||||
|
||||
| 操作 | Claude in Chrome | chrome-devtools-mcp |
|
||||
|------|-----------------|---------------------|
|
||||
| 导航 | `navigate` | `navigate_page` |
|
||||
| 截图 | `computer` screenshot | `take_screenshot` |
|
||||
| 读页面 | `read_page` | `take_snapshot`(更省 token) |
|
||||
| 点击 | `computer` left_click (坐标) | `click` (uid,更精准) |
|
||||
| 填表 | `form_input` | `fill` / `fill_form`(支持批量) |
|
||||
| 执行 JS | `javascript_tool` | `evaluate_script` |
|
||||
| 网络请求 | `read_network_requests` | `list_network_requests` + `get_network_request` |
|
||||
| 控制台 | `read_console_messages` | `list_console_messages` |
|
||||
| 滚动 | `computer` scroll | `evaluate_script` + `scrollBy()` |
|
||||
| hover | `computer` hover | `hover` (uid) |
|
||||
|
||||
## 选择引擎的原则
|
||||
|
||||
**用 Claude in Chrome 当:**
|
||||
- 需要"看"页面效果(截图、视觉对比)
|
||||
- 点击位置不确定(没有明确的 CSS selector / uid)
|
||||
- 操作设计工具(蓝湖、Figma 等图层交互)
|
||||
- 需要拖拽、hover 等复杂鼠标操作
|
||||
|
||||
**用 chrome-devtools-mcp 当:**
|
||||
- 需要快速读取页面结构(snapshot 比截图省 token)
|
||||
- 批量表单填写(fill_form 一次填多个字段)
|
||||
- 需要查看网络请求/响应内容
|
||||
- 需要检查控制台错误
|
||||
- 需要执行复杂 JS 逻辑
|
||||
- 做性能分析/Lighthouse 审计
|
||||
|
||||
**两个一起用当:**
|
||||
- 前端开发验收(视觉 + 数据双重验证)
|
||||
- 复杂测试场景(devtools 填数据 → chrome 验证 UI)
|
||||
- 调试页面问题(chrome 看现象 → devtools 查原因)
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 两个引擎连接同一个 Chrome 浏览器,操作的是同一个页面
|
||||
- 在一个引擎导航后,另一个引擎看到的也是新页面
|
||||
- `take_snapshot` 返回文本格式的 DOM 树(带 uid),比截图省 token,优先使用
|
||||
- 滚动内部容器时,chrome-devtools 需要用 JS:`document.querySelector('.容器').scrollBy(0, 500)`
|
||||
- Claude in Chrome 的权限确认弹窗需要用户手动点击,AI 无法自动处理
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: lanhu-design-viewer
|
||||
description: Use when viewing Demo design specs on Lanhu (蓝湖), extracting design parameters like dimensions, fonts, colors, and spacing from the design files.
|
||||
---
|
||||
|
||||
# 蓝湖设计稿查看 Skill
|
||||
|
||||
## 触发条件
|
||||
当需要查看 Demo 首页设计稿、提取设计参数时调用。
|
||||
|
||||
## 设计稿链接
|
||||
|
||||
### Mobile 设计稿 (iPhone)
|
||||
```
|
||||
https://lanhuapp.com/web/#/item/project/detailDetach?pid=YOUR_PROJECT_ID&project_id=YOUR_PROJECT_ID&image_id=YOUR_MOBILE_IMAGE_ID&fromEditor=true
|
||||
```
|
||||
|
||||
### PC 设计稿 (Desktop)
|
||||
```
|
||||
https://lanhuapp.com/web/#/item/project/detailDetach?pid=YOUR_PROJECT_ID&project_id=YOUR_PROJECT_ID&image_id=YOUR_DESKTOP_IMAGE_ID&fromEditor=true
|
||||
```
|
||||
|
||||
## 使用规则
|
||||
- **依赖 `/browser-auto` skill 的双引擎协同机制**
|
||||
- 蓝湖图层交互:优先用 Claude in Chrome(视觉精准点击)
|
||||
- 蓝湖可能需要登录,如遇到登录页面告知用户手动登录
|
||||
|
||||
## 执行流程
|
||||
|
||||
### 1. 启动浏览器(按 browser-auto skill 流程)
|
||||
先检测两个引擎连接状态,Claude in Chrome 连不上时自动修复
|
||||
|
||||
### 2. 打开设计稿
|
||||
```
|
||||
[Claude in Chrome] tabs_create_mcp → 创建新标签
|
||||
[Claude in Chrome] navigate → 导航到设计稿 URL
|
||||
```
|
||||
|
||||
### 3. 等待页面加载
|
||||
使用 `computer` screenshot 截图确认加载完成
|
||||
|
||||
### 4. 查看设计稿
|
||||
- `[Claude in Chrome] computer screenshot` → 截图查看整体布局
|
||||
- `[Claude in Chrome] computer left_click` → 点击图层,右侧显示标注
|
||||
- 蓝湖支持点击元素后在右侧面板显示 CSS 属性(尺寸/字体/颜色/间距)
|
||||
|
||||
### 5. 提取设计参数
|
||||
对每个模块提取以下信息:
|
||||
- **布局**: 列数、对齐方式、排列顺序
|
||||
- **间距**: padding、margin、gap(px 值)
|
||||
- **字体**: font-size、font-weight、line-height、color
|
||||
- **图片**: 宽高比、圆角、尺寸
|
||||
- **颜色**: 背景色、文字色、边框色(hex 值)
|
||||
- **交互**: hover 效果、动画、滑动方式
|
||||
|
||||
### 6. 记录参数
|
||||
将提取的参数保存到 memory 文件:
|
||||
`/Users/dev/.claude/projects/-Users-dev-Documents-code-shopify-demo-release/memory/design-specs.md`
|
||||
|
||||
## 注意事项
|
||||
- 蓝湖设计稿分 PC 和 Mobile 两版,**两版都要看**
|
||||
- 如果设计稿页面较长,需要滚动查看,使用 `computer` 工具的滚动操作
|
||||
- 提取颜色值时注意区分:设计稿颜色 vs 主题 CSS 变量中已有的颜色
|
||||
- 设计稿中的间距值需要对照全局 `--section-vertical-spacing` 系统,尽量复用
|
||||
@@ -0,0 +1,207 @@
|
||||
---
|
||||
name: tmux-hub
|
||||
description: "Terminal dispatch center. Use when user wants to open a project, run tasks in other terminals, monitor build/test results, or switch between projects. Manages tmux windows as iTerm2 tabs from a central conversation window."
|
||||
---
|
||||
|
||||
# 终端调度中心
|
||||
|
||||
你是核心窗口。通过 tmux 管理其他项目窗口,用户和你都能操作这些窗口。
|
||||
|
||||
## 项目路径映射
|
||||
|
||||
从 `projects.yaml` 读取项目清单。查找规则:
|
||||
1. 精确匹配 `name` 字段
|
||||
2. 匹配 `aliases` 列表
|
||||
3. 在 `~/Documents/code/` 下模糊匹配目录名
|
||||
|
||||
管理命令:
|
||||
- 添加项目: `bash scripts/add-project.sh <name> <path> [aliases...]`
|
||||
- 扫描目录: `bash scripts/scan-projects.sh [目录]`
|
||||
|
||||
## 流程
|
||||
|
||||
### 1. 初始化 hub session
|
||||
|
||||
不要在启动时创建 session。等用户请求打开项目时再创建。
|
||||
|
||||
### 2. 打开项目
|
||||
|
||||
用户说"我要开发 {project}"时:
|
||||
|
||||
```bash
|
||||
# 查找路径
|
||||
PROJECT_PATH=~/Documents/code/{目录名}
|
||||
|
||||
# hub session 不存在 → 用第一个项目创建(不要创建多余的 control 窗口)
|
||||
tmux has-session -t hub 2>/dev/null || tmux new-session -d -s hub -n {name} -c {PROJECT_PATH}
|
||||
|
||||
# hub session 已存在 → 检查窗口是否存在,不存在则添加
|
||||
tmux list-windows -t hub -F '#{window_name}' | grep -q {name} || tmux new-window -t hub -n {name} -c {PROJECT_PATH}
|
||||
|
||||
# 如果没有 tmux -CC 客户端连接 → 自动在当前窗口新标签页打开
|
||||
if ! tmux list-clients -t hub 2>/dev/null | grep -q "attached"; then
|
||||
osascript <<'APPLESCRIPT'
|
||||
tell application "iTerm2"
|
||||
tell current window
|
||||
create tab with default profile
|
||||
delay 0.5
|
||||
tell current session of current tab
|
||||
write text "tmux -CC attach -t hub"
|
||||
end tell
|
||||
end tell
|
||||
end tell
|
||||
APPLESCRIPT
|
||||
fi
|
||||
```
|
||||
|
||||
告知用户:"已打开 {project}"
|
||||
|
||||
### 3. 在其他窗口执行命令
|
||||
|
||||
```bash
|
||||
# 向目标窗口发送命令
|
||||
tmux send-keys -t hub:{name} '{command}' Enter
|
||||
```
|
||||
|
||||
### 4. 异步执行并等待结果
|
||||
|
||||
当需要等待任务完成(构建、测试、部署等)时,有两种方式:
|
||||
|
||||
#### 方式 A: hub-run 包装(推荐,发到终端窗口)
|
||||
|
||||
```bash
|
||||
# Step 1: 生成唯一 channel
|
||||
CHANNEL="{name}-$(date +%s)"
|
||||
|
||||
# Step 2: 用 hub-run 发送命令(自动处理信号)
|
||||
tmux send-keys -t hub:{name} "bash ~/Documents/code/claude-hub/scripts/hub-run.sh $CHANNEL {command}" Enter
|
||||
|
||||
# Step 3: 后台等待(run_in_background=true)
|
||||
tmux wait-for $CHANNEL
|
||||
|
||||
# Step 4: 读取结果
|
||||
cat /tmp/hub-signals/$CHANNEL.result # 0=成功, 非0=失败
|
||||
```
|
||||
|
||||
#### 方式 B: 环境变量(发到子窗口 Claude 实例)
|
||||
|
||||
```bash
|
||||
# Step 1: 生成唯一 channel
|
||||
CHANNEL="{name}-$(date +%s)"
|
||||
|
||||
# Step 2: 设置环境变量(子窗口 Claude 的 PostToolUse hook 会自动发信号)
|
||||
tmux send-keys -t hub:{name} "export HUB_CHANNEL=$CHANNEL" Enter
|
||||
|
||||
# Step 3: 发送实际命令(子窗口 Claude 执行 Bash 后 hook 自动通知)
|
||||
tmux send-keys -t hub:{name} "{command}" Enter
|
||||
|
||||
# Step 4: 后台等待
|
||||
tmux wait-for $CHANNEL
|
||||
```
|
||||
|
||||
**用 Bash 工具的 `run_in_background=true` 执行等待步骤**,Claude 不会被阻塞。
|
||||
|
||||
### 5. 任务完成通知
|
||||
|
||||
后台任务完成后:
|
||||
|
||||
```bash
|
||||
# 1. 读取退出码
|
||||
EXIT_CODE=$(cat /tmp/hub-signals/$CHANNEL.result 2>/dev/null || echo "unknown")
|
||||
|
||||
# 2. 捕获窗口输出
|
||||
tmux capture-pane -t hub:{name} -p -S -30
|
||||
|
||||
# 3. 发送 macOS 通知 + 语音播报
|
||||
if [ "$EXIT_CODE" = "0" ]; then
|
||||
osascript -e 'display notification "{name} 任务完成" with title "调度中心" sound name "Glass"'
|
||||
say "{name} 任务完成"
|
||||
else
|
||||
osascript -e 'display notification "{name} 任务失败 (exit: '$EXIT_CODE')" with title "调度中心" sound name "Basso"'
|
||||
say "{name} 任务失败"
|
||||
fi
|
||||
```
|
||||
|
||||
### 6. 读取其他窗口状态
|
||||
|
||||
随时可以查看任意窗口的当前内容:
|
||||
|
||||
```bash
|
||||
# 查看最近 30 行输出
|
||||
tmux capture-pane -t hub:{name} -p -S -30
|
||||
|
||||
# 列出所有窗口
|
||||
tmux list-windows -t hub -F '#{window_index}: #{window_name} #{window_active}'
|
||||
```
|
||||
|
||||
### 7. 切换用户焦点
|
||||
|
||||
用户说"切到 acme-erp"时:
|
||||
|
||||
```bash
|
||||
tmux select-window -t hub:{name}
|
||||
```
|
||||
|
||||
在 tmux -CC 模式下,iTerm2 会自动切换到对应标签页。
|
||||
|
||||
### 8. 会话恢复
|
||||
|
||||
Claude Code 重启后,检查之前的 session:
|
||||
|
||||
```bash
|
||||
# 检查 hub session
|
||||
tmux has-session -t hub 2>/dev/null && echo "hub 存在"
|
||||
|
||||
# 列出所有窗口
|
||||
tmux list-windows -t hub -F '#{window_name}'
|
||||
|
||||
# 查看每个窗口最近状态
|
||||
for w in $(tmux list-windows -t hub -F '#{window_name}'); do
|
||||
echo "=== $w ==="
|
||||
tmux capture-pane -t hub:$w -p -S -5
|
||||
done
|
||||
```
|
||||
|
||||
## 常用操作速查
|
||||
|
||||
| 用户说 | Claude 执行 |
|
||||
|--------|------------|
|
||||
| "开发 acme-erp" | `tmux new-window -t hub -n acme-erp -c ~/Documents/code/acme-admin` |
|
||||
| "在 acme-erp 跑构建" | `tmux send-keys -t hub:acme-erp 'npm run build' Enter` + 后台等待 |
|
||||
| "acme-erp 什么状态" | `tmux capture-pane -t hub:acme-erp -p -S -20` |
|
||||
| "切到 shopify" | `tmux select-window -t hub:shopify` |
|
||||
| "关掉 game" | `tmux kill-window -t hub:game` |
|
||||
| "现在有哪些窗口" | `tmux list-windows -t hub -F '#{window_name}'` |
|
||||
| "继续" | 在对应窗口执行下一步命令 |
|
||||
|
||||
## 命令模板
|
||||
|
||||
### 带完成通知的异步任务
|
||||
|
||||
```bash
|
||||
# 发送命令(Bash 工具,同步)
|
||||
CHANNEL="erp-build-$(date +%s)"
|
||||
tmux send-keys -t hub:acme-erp "bash ~/Documents/code/claude-hub/scripts/hub-run.sh $CHANNEL npm run build" Enter
|
||||
|
||||
# 等待完成(Bash 工具,run_in_background=true)
|
||||
tmux wait-for $CHANNEL
|
||||
# 信号到达后读取退出码
|
||||
EXIT_CODE=$(cat /tmp/hub-signals/$CHANNEL.result)
|
||||
```
|
||||
|
||||
### 人工通知 hub
|
||||
|
||||
子窗口用户可以手动通知 hub:
|
||||
|
||||
```bash
|
||||
# 在子窗口运行
|
||||
hub-notify <channel> # 通知指定 channel
|
||||
hub-notify # 列出等待中的 channel
|
||||
```
|
||||
|
||||
## 限制
|
||||
|
||||
- **10 分钟超时**:`run_in_background` 最大 600 秒。Docker build 等长任务可能超时,需要分段等待。
|
||||
- **不支持交互式命令**:vim、less、密码输入等需要用户自己去那个窗口操作。
|
||||
- **首次连接**:通过 osascript 自动打开 iTerm2 新窗口执行 `tmux -CC attach -t hub`。
|
||||
- **通知不能自动跳转**:macOS 通知点击不会自动切到对应窗口(除非安装 terminal-notifier)。
|
||||
Reference in new issue
Block a user