mirror of
https://github.com/tradecatlabs/vibe-coding-cn.git
synced 2026-08-03 06:07:46 +00:00
feat: skills - upgrade auto-tmux automation
This commit is contained in:
@@ -166,7 +166,7 @@ git push origin develop
|
||||
│ ├── README.md # skills 总览与索引
|
||||
│ ├── AGENTS.md # skills/ 目录规则
|
||||
│ ├── auto-skill/ # 元技能核心
|
||||
│ ├── auto-tmux/ # tmux 自动化操控与多终端协作
|
||||
│ ├── auto-tmux/ # tmux 自动化脚本、pane 巡检、救援与多终端协作
|
||||
│ └── claude-official-skills/ # Claude 官方 skills 软链接入口
|
||||
│
|
||||
├── assets/ # 静态资产与外部资源入口
|
||||
@@ -239,7 +239,7 @@ git push origin develop
|
||||
- `docs/references/README.md#reference-engineering-practice` - 项目架构、代码组织、开发经验、底层程序逻辑、AI 编程质量门禁与常见坑的统一入口
|
||||
- `docs/references/README.md#reference-technology-stack` - 常见软件系统技术栈、选型维度、组合案例与初学者学习路径
|
||||
- `skills/auto-skill/` - Skills 生成、重构与校验的元技能
|
||||
- `skills/auto-tmux/` - tmux 自动化操控、pane 巡检、按键注入与多终端协作技能
|
||||
- `skills/auto-tmux/` - tmux 自动化操控、脚本化 pane 巡检、按键注入、日志录制与多终端协作技能
|
||||
|
||||
### docs README 结构契约
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ pip install -r tools/prompts-library/scripts/requirements.txt
|
||||
* [**Skills 制作器**](https://github.com/yusufkaraaslan/Skill_Seekers): 可根据需求生成定制化 Skills 的工具。
|
||||
* [**元提示词**](https://docs.google.com/spreadsheets/d/1Ifk_dLF25ULSxcfGem1hXzJsi7_RBUNAki8SBCuvkJA/edit?gid=1254297203#gid=1254297203): 用于生成提示词的高级提示词。
|
||||
* [**元技能:Auto Skill**](./skills/auto-skill/SKILL.md): 用于生成、重构与校验 Skills 的元技能。
|
||||
* [**auto-tmux**](./skills/auto-tmux/SKILL.md): tmux 自动化操控、pane 巡检、按键注入与多终端协作技能。
|
||||
* [**auto-tmux**](./skills/auto-tmux/SKILL.md): tmux 自动化操控、脚本化 pane 巡检、按键注入、日志录制与多终端协作技能。
|
||||
* [**Mermaid Chart**](https://www.mermaidchart.com/): 用于将文本描述转换为架构图、序列图等可视化图表。
|
||||
* [**NotebookLM**](https://notebooklm.google.com/): 一款用于 AI 解读资料、音频和生成思维导图的工具。
|
||||
* [**Zread**](https://zread.ai/): AI 驱动的 GitHub 仓库阅读工具,有助于快速理解项目代码。
|
||||
@@ -587,7 +587,7 @@ pip install -r tools/prompts-library/scripts/requirements.txt
|
||||
├── prompts/ # 提示词库入口(指向云端表格)
|
||||
├── skills/ # 技能库入口
|
||||
│ ├── auto-skill/ # 元技能核心
|
||||
│ ├── auto-tmux/ # tmux 自动化操控与多终端协作
|
||||
│ ├── auto-tmux/ # tmux 自动化脚本、pane 巡检、救援与多终端协作
|
||||
│ └── claude-official-skills/ # Claude 官方 skills 软链接入口
|
||||
├── tools/ # 辅助工具、外部仓库与工具配置
|
||||
├── scripts/ # 自动化脚本
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ skills/
|
||||
│ ├── assets/ # (可选) 模板/样例/静态资源
|
||||
│ └── agents/ # (可选) Agent 元数据(如 openai.yaml)
|
||||
├── auto-skill/ # 元技能:生成/校验/脚手架化其它技能
|
||||
└── auto-tmux/ # tmux 自动化操控、pane 巡检与多终端协作
|
||||
└── auto-tmux/ # tmux 自动化脚本、pane 巡检、救援与多终端协作
|
||||
```
|
||||
|
||||
## 外部仓库引用(约定)
|
||||
@@ -47,6 +47,6 @@ skills/
|
||||
## 快速定位(常用技能)
|
||||
|
||||
- `skills/auto-skill/`:元技能(技能生成/校验/脚手架)
|
||||
- `skills/auto-tmux/`:tmux 自动化技能;通过 `assets/` 软链接复用 `tools/external/.tmux` 与 `tools/external/tmux`
|
||||
- `skills/auto-tmux/`:tmux 自动化技能;通过 `scripts/auto-tmux.sh` 提供安全执行层,并通过 `assets/` 软链接复用 `tools/external/.tmux` 与 `tools/external/tmux`
|
||||
- `skills/claude-official-skills/`:Claude 官方 skills 仓库(Anthropic)的软链接入口
|
||||
- `skills/auto-skill/scripts/Skill_Seekers-development`:Skill Seekers submodule 的软链接入口
|
||||
|
||||
+3
-3
@@ -7,13 +7,13 @@
|
||||
| 技能 | 说明 |
|
||||
|:---|:---|
|
||||
| [auto-skill](./auto-skill/SKILL.md) | 生成、重构与校验 Skills 的元技能 |
|
||||
| [auto-tmux](./auto-tmux/SKILL.md) | tmux 自动化操控、pane 巡检、按键注入、蜂群协作和 oh-my-tmux 参考入口 |
|
||||
| [auto-tmux](./auto-tmux/SKILL.md) | tmux 自动化操控、脚本化 pane 巡检、按键注入、日志录制、蜂群协作和 oh-my-tmux 参考入口 |
|
||||
| [claude-official-skills](./claude-official-skills/) | Claude 官方 skills 仓库的软链接入口,事实来源在 `tools/external/claude-official-skills/` |
|
||||
|
||||
## 维护口径
|
||||
|
||||
- `auto-skill/` 是本仓库的 Skill 生产与升级入口,继续保留。
|
||||
- `auto-tmux/` 是 tmux 自动化与多 AI 终端协作入口,通过软链接复用 `tools/external/.tmux` 与 `tools/external/tmux`。
|
||||
- `auto-tmux/` 是 tmux 自动化与多 AI 终端协作入口,包含 `scripts/auto-tmux.sh` 执行层,并通过软链接复用 `tools/external/.tmux` 与 `tools/external/tmux`。
|
||||
- `claude-official-skills/` 是外部官方仓库展示入口,通过 submodule 管理事实来源。
|
||||
- 领域型/工具型 Skill 如交易、数据库、代理、Telegram、EPUB 等不在主仓库维护;如需引入,优先使用独立仓库、submodule 或重新生成的方式。
|
||||
|
||||
@@ -51,6 +51,6 @@ git submodule update --init --recursive
|
||||
## 相关资源
|
||||
|
||||
- [元技能文件](./auto-skill/SKILL.md) - 生成、重构与校验 Skills
|
||||
- [auto-tmux](./auto-tmux/SKILL.md) - tmux 自动化操控与多终端协作
|
||||
- [auto-tmux](./auto-tmux/SKILL.md) - tmux 自动化操控、脚本化巡检与多终端协作
|
||||
- [提示词库](../prompts/) - 更细粒度的提示词集合
|
||||
- [文档库](../docs/) - 方法论与开发经验
|
||||
|
||||
@@ -11,10 +11,15 @@ skills/auto-tmux/
|
||||
├── assets/ # 外部 tmux 相关仓库的只读软链接入口
|
||||
│ ├── oh-my-tmux -> ../../../tools/external/.tmux
|
||||
│ └── tmux-src -> ../../../tools/external/tmux
|
||||
├── scripts/ # 安全封装的 tmux 自动化脚本
|
||||
│ ├── auto-tmux.sh
|
||||
│ ├── README.md
|
||||
│ └── AGENTS.md
|
||||
└── references/ # 长文档、示例与故障排查
|
||||
├── index.md
|
||||
├── getting_started.md
|
||||
├── api.md
|
||||
├── automation.md
|
||||
├── examples.md
|
||||
└── troubleshooting.md
|
||||
```
|
||||
@@ -23,6 +28,7 @@ skills/auto-tmux/
|
||||
|
||||
- `assets/oh-my-tmux` 只暴露 gpakosz/oh-my-tmux submodule,不在技能目录内复制配置源码。
|
||||
- `assets/tmux-src` 只暴露 tmux/tmux submodule,供需要查看源码或上游文档时定位。
|
||||
- `scripts/auto-tmux.sh` 是技能执行层,封装 topology/capture/send/scan/rescue/record/hub/wait。
|
||||
- 技能文档可以引用软链接入口;更新上游内容必须通过 `tools/external/` 下的 submodule 指针完成。
|
||||
- 不在本目录直接修改 submodule 内容;如需改造,先 fork 上游并更新 submodule 来源。
|
||||
|
||||
@@ -31,3 +37,4 @@ skills/auto-tmux/
|
||||
- 上游来源:`tools/external/.tmux` 与 `tools/external/tmux`。
|
||||
- 下游使用:`SKILL.md` 和 `references/` 中的命令示例。
|
||||
- 验证入口:`skills/auto-skill/scripts/validate-skill.sh skills/auto-tmux --strict`。
|
||||
- 脚本验证:`bash -n skills/auto-tmux/scripts/auto-tmux.sh`。
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
## 入口
|
||||
|
||||
- [SKILL.md](./SKILL.md) - 技能触发条件、命令片段、规则和质量门禁。
|
||||
- [scripts/](./scripts/) - 安全封装的 tmux 自动化脚本。
|
||||
- [references/](./references/) - tmux / oh-my-tmux 参考、示例和故障排查。
|
||||
- [assets/](./assets/) - 指向仓库外部 submodule 的只读软链接入口。
|
||||
|
||||
@@ -18,12 +19,15 @@
|
||||
## 快速验证
|
||||
|
||||
```bash
|
||||
bash -n skills/auto-tmux/scripts/auto-tmux.sh
|
||||
skills/auto-tmux/scripts/auto-tmux.sh help
|
||||
skills/auto-skill/scripts/validate-skill.sh skills/auto-tmux --strict
|
||||
```
|
||||
|
||||
## 使用边界
|
||||
|
||||
- 发送按键前必须先 `capture-pane` 复核目标上下文。
|
||||
- 优先使用 `scripts/auto-tmux.sh`,需要低层调试时再直接使用 tmux 原生命令。
|
||||
- 发送按键前必须先 `capture-pane` 复核目标上下文;脚本会默认打印最近上下文。
|
||||
- 批量操作前必须先列出 session/window/pane 白名单。
|
||||
- 不在未知 pane 中发送破坏性命令。
|
||||
- 不在本目录复制或修改上游 tmux / oh-my-tmux 源码。
|
||||
|
||||
+59
-27
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: auto-tmux
|
||||
description: "tmux 自动化操控:读取/广播/救援 session|window|pane,基于 oh-my-tmux 配置安全地让 AI 自控/互控终端。触发:需要 capture-pane/send-keys、批量巡检、蜂群 AI 协作、卡死救援或快速查看快捷键。"
|
||||
description: "tmux 自动化操控:用 scripts/auto-tmux.sh 安全读取、发送、巡检、救援、录制 session|window|pane,并基于 oh-my-tmux 组织多 AI 终端协作。触发:capture-pane、send-keys、批量巡检、蜂群 AI 协作、卡死救援、tmux 工作台初始化。"
|
||||
---
|
||||
|
||||
# auto-tmux Skill
|
||||
|
||||
让 AI 像熟练运维一样操作 tmux:读取终端输出、发送按键、批量巡检、协作/救援其他终端,默认兼容 `tools/external/.tmux`(gpakosz/oh-my-tmux),并在本技能内通过 `assets/oh-my-tmux` 与 `assets/tmux-src` 暴露上游 submodule。
|
||||
让 AI 像熟练运维一样操作 tmux:优先使用 `scripts/auto-tmux.sh` 安全封装读取终端输出、发送按键、批量巡检、协作/救援其他终端;必要时再回退到 tmux 原生命令。默认兼容 `tools/external/.tmux`(gpakosz/oh-my-tmux),并在本技能内通过 `assets/oh-my-tmux` 与 `assets/tmux-src` 暴露上游 submodule。
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
@@ -13,6 +13,7 @@ description: "tmux 自动化操控:读取/广播/救援 session|window|pane,
|
||||
- 需要远程读取/复制某个 tmux pane 的最新输出(日志、提示、错误)。
|
||||
- 需要向指定 pane 发送按键/命令(确认 `y`、`Enter`、`Ctrl+C`、广播同一窗口)。
|
||||
- 需要批量巡检/接管多 AI 终端(蜂群协作、自动救援卡死任务)。
|
||||
- 需要初始化 AI 多终端工作台、等待输出 pattern、录制 pane 日志或保存巡检证据。
|
||||
- 需要快速回忆 oh-my-tmux 快捷键、前缀或同步面板操作。
|
||||
- 需要在当前仓库复用 `tools/external/.tmux` 配置并避免修改主配置。
|
||||
- 需要从技能目录内快速查看 oh-my-tmux 配置或 tmux 上游源码入口。
|
||||
@@ -22,30 +23,54 @@ description: "tmux 自动化操控:读取/广播/救援 session|window|pane,
|
||||
- 不用于执行非 tmux 环境(目标不存在时先创建 session/window/pane)。
|
||||
- 不直接编辑 `~/.tmux.conf` / `.tmux.conf`(遵循 oh-my-tmux:修改请放到 `.tmux.conf.local`)。
|
||||
- 不盲发破坏性按键:`kill-server`、`rm -rf` 等必须先 capture-pane 确认目标上下文。
|
||||
- 不把脚本当成权限绕过器;敏感会话、密码输入、生产数据库操作仍需人工确认。
|
||||
- 如果 tmux 版本 < 2.6,需先升级再使用本技能的命令组合。
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### 常用模式(≤20 条,直接复制即用)
|
||||
|
||||
**脚本帮助与语法检查**
|
||||
```bash
|
||||
bash -n skills/auto-tmux/scripts/auto-tmux.sh
|
||||
skills/auto-tmux/scripts/auto-tmux.sh help
|
||||
```
|
||||
|
||||
**查看快捷键帮助**(oh-my-tmux 双前缀:主 `C-a`,备用 `C-b`,前缀后按 `?`)
|
||||
```text
|
||||
[按] <prefix> ?
|
||||
```
|
||||
|
||||
**枚举拓扑(session/window/pane)**
|
||||
**枚举拓扑(脚本优先)**
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology --session ai-hub
|
||||
```
|
||||
|
||||
**枚举拓扑(原生命令兜底)**
|
||||
```bash
|
||||
tmux list-sessions
|
||||
tmux list-windows -a -F '#S:#I:#W#F'
|
||||
tmux list-panes -a -F '#S:#I.#P #{pane_current_command} #{pane_title}'
|
||||
```
|
||||
|
||||
**读取指定 pane 最近 120 行**
|
||||
**读取指定 pane 最近输出**
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh capture -t <session>:<window>.<pane> -n 120
|
||||
```
|
||||
|
||||
**原生读取指定 pane 最近 120 行**
|
||||
```bash
|
||||
tmux capture-pane -t <session>:<window>.<pane> -p -S -120
|
||||
```
|
||||
|
||||
**向指定 pane 发送确认键**
|
||||
**向指定 pane 发送命令或确认键**
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh send -t <session>:<window>.<pane> --text "make test" --enter
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t <session>:<window>.<pane> --pattern "(y/n)" --reply y
|
||||
```
|
||||
|
||||
**原生发送确认键**
|
||||
```bash
|
||||
tmux send-keys -t <session>:<window>.<pane> "y" Enter
|
||||
```
|
||||
@@ -61,27 +86,23 @@ tmux set-window-option synchronize-panes on
|
||||
tmux set-window-option synchronize-panes off
|
||||
```
|
||||
|
||||
**一次扫描全部窗口并截取最近输出(蜂群巡检骨架)**
|
||||
**一次扫描全部窗口并截取最近输出(蜂群巡检)**
|
||||
```bash
|
||||
for w in $(tmux list-windows -a -F '#S:#I'); do
|
||||
tmux capture-pane -t "$w" -p -S -80 | sed "s/^/[$w] /";
|
||||
done
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub -n 80
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback" --save-dir /tmp/auto-tmux-scan
|
||||
```
|
||||
|
||||
**远程救援:发现等待输入即发送 y**
|
||||
```bash
|
||||
target="0:3.0" # 示例
|
||||
tmux capture-pane -t "$target" -p -S -50 | grep -qi "(y/n)" && \
|
||||
tmux send-keys -t "$target" "y" Enter
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t "$target" --pattern "(y/n)" --reply y --dry-run
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t "$target" --pattern "(y/n)" --reply y
|
||||
```
|
||||
|
||||
**新建 AI 蜂群工作台(1 指挥 + 3 工人)**
|
||||
```bash
|
||||
tmux new-session -d -s ai-hub -n commander 'bash'
|
||||
tmux new-window -t ai-hub -n worker1 'kiro-cli chat'
|
||||
tmux new-window -t ai-hub -n worker2 'kiro-cli chat'
|
||||
tmux new-window -t ai-hub -n worker3 'kiro-cli chat'
|
||||
tmux attach -t ai-hub
|
||||
skills/auto-tmux/scripts/auto-tmux.sh hub --session ai-hub --workers 3 --cmd "codex"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology --session ai-hub
|
||||
```
|
||||
|
||||
**启用 oh-my-tmux 配置(仓库内版本)**
|
||||
@@ -93,11 +114,18 @@ cp -n "$repo_root/skills/auto-tmux/assets/oh-my-tmux/.tmux.conf.local" ~/.tmux.c
|
||||
|
||||
**记录 pane 输出到文件**
|
||||
```bash
|
||||
tmux pipe-pane -t <session>:<window>.<pane> -o 'cat >> /tmp/tmux-<session>-<window>-<pane>.log'
|
||||
skills/auto-tmux/scripts/auto-tmux.sh record start -t <session>:<window>.<pane> --dir /tmp/auto-tmux-records
|
||||
skills/auto-tmux/scripts/auto-tmux.sh record stop -t <session>:<window>.<pane>
|
||||
```
|
||||
|
||||
**等待 pane 出现完成信号**
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh wait -t <session>:<window>.<pane> --pattern "Tests passed|BUILD SUCCESS" --timeout 300
|
||||
```
|
||||
|
||||
## Rules & Constraints
|
||||
|
||||
- MUST:优先使用 `scripts/auto-tmux.sh`;直接使用 tmux 原生命令时必须保留同等安全约束。
|
||||
- MUST:在发送按键前用 `capture-pane` 复核目标上下文;按键操作必须带 `<session>:<window>.<pane>` 绝对定位。
|
||||
- MUST:遵循 oh-my-tmux 约定,不修改主配置文件;自定义写入 `~/.tmux.conf.local`。
|
||||
- MUST:批量操作前先 `list-windows`/`list-panes` 建立白名单,避免误控用户窗口。
|
||||
@@ -110,17 +138,18 @@ tmux pipe-pane -t <session>:<window>.<pane> -o 'cat >> /tmp/tmux-<session>-<wind
|
||||
## Examples
|
||||
|
||||
### Example 1:自动给卡住的安装脚本回车确认
|
||||
- 输入:安装脚本停在 `Proceed? (y/n)`,位置在 `0:3.0`。
|
||||
- 输入:安装脚本停在 `Proceed? (y/n)`,先用 `topology` 找到真实 target。
|
||||
- 步骤:
|
||||
1) `tmux capture-pane -t 0:3.0 -p -S -50 | grep -qi "(y/n)"` 确认等待输入。
|
||||
2) `tmux send-keys -t 0:3.0 "y" Enter` 发送确认。
|
||||
1) `skills/auto-tmux/scripts/auto-tmux.sh topology` 找到目标 `<session>:<window>.<pane>`。
|
||||
2) `skills/auto-tmux/scripts/auto-tmux.sh rescue -t <target> --pattern "(y/n)" --reply y --dry-run` 确认命中。
|
||||
3) `skills/auto-tmux/scripts/auto-tmux.sh rescue -t <target> --pattern "(y/n)" --reply y` 发送确认。
|
||||
- 验收:脚本继续运行,无额外多余输入。
|
||||
|
||||
### Example 2:蜂群巡检 + 自动救援
|
||||
- 输入:4 个 AI 终端分布在 session `ai-hub` 的多个窗口。
|
||||
- 步骤:
|
||||
1) 运行 Quick Reference 巡检循环收集最近 80 行并观察状态。
|
||||
2) 对匹配 `"(y/n)"` 的 pane 触发「远程救援」片段自动发 `y`。
|
||||
1) `skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub -n 80 --save-dir /tmp/auto-tmux-scan` 收集状态。
|
||||
2) 对匹配 `"(y/n)"` 的 pane 运行 `rescue --dry-run`,确认后再真实发送。
|
||||
3) 对输出包含 `Traceback` 或 `ERROR` 的 pane,先 `capture-pane` 记录再人工介入。
|
||||
- 验收:等待确认的终端恢复,未误伤正常任务;巡检日志保存在本地。
|
||||
|
||||
@@ -128,8 +157,8 @@ tmux pipe-pane -t <session>:<window>.<pane> -o 'cat >> /tmp/tmux-<session>-<wind
|
||||
- 输入:需要 1 个指挥 + 3 个 worker,并使用仓库内 oh-my-tmux 配置。
|
||||
- 步骤:
|
||||
1) 运行「启用 oh-my-tmux 配置」片段完成软链和本地副本(若已存在跳过)。
|
||||
2) 运行「新建 AI 蜂群工作台」片段创建 session 并 attach。
|
||||
3) 在 commander pane 使用「枚举拓扑」确认 worker 窗口存在。
|
||||
2) `skills/auto-tmux/scripts/auto-tmux.sh hub --session ai-hub --workers 3 --cmd "codex"` 创建 session。
|
||||
3) `skills/auto-tmux/scripts/auto-tmux.sh topology --session ai-hub` 确认 worker 窗口存在。
|
||||
- 验收:前缀键为 `C-a`,状态栏正常;4 个窗口可互相捕获/发送按键。
|
||||
|
||||
## FAQ
|
||||
@@ -153,15 +182,17 @@ tmux pipe-pane -t <session>:<window>.<pane> -o 'cat >> /tmp/tmux-<session>-<wind
|
||||
- `references/index.md`: 导航与文件速览
|
||||
- `references/getting_started.md`: 术语、oh-my-tmux 最小接入步骤
|
||||
- `references/api.md`: tmux/oh-my-tmux 常用命令、选项与 gpakosz 特色键位
|
||||
- `references/automation.md`: `scripts/auto-tmux.sh` 子命令、安全模型与 AI 蜂群协作流程
|
||||
- `references/examples.md`: 蜂群协议脚本与长示例
|
||||
- `references/troubleshooting.md`: 典型故障到修复路径
|
||||
- `scripts/auto-tmux.sh`: 安全封装的 tmux 自动化脚本入口
|
||||
- `assets/oh-my-tmux`: gpakosz/oh-my-tmux submodule 的相对软链接入口
|
||||
- `assets/tmux-src`: tmux/tmux submodule 的相对软链接入口
|
||||
|
||||
## Maintenance
|
||||
|
||||
- Sources: `skills/auto-tmux/assets/oh-my-tmux`、`skills/auto-tmux/assets/tmux-src`、蜂群实战提示词、Skill Seeker 生成的抓取材料(如需扩展自动化)。
|
||||
- Last updated: 2026-04-28
|
||||
- Last updated: 2026-05-19
|
||||
- Known limits: 未涵盖 tmux 插件管理(tpm)及多用户共享权限配置;需时再补充。
|
||||
|
||||
## Quality Gate
|
||||
@@ -173,4 +204,5 @@ tmux pipe-pane -t <session>:<window>.<pane> -o 'cat >> /tmp/tmux-<session>-<wind
|
||||
4. ≥3 个端到端示例,含输入/步骤/验收。
|
||||
5. 长文档放在 `references/` 并可导航;无文档堆砌。
|
||||
6. 不确定项给出验证路径;禁止虚构 tmux 行为。
|
||||
7. 运行 `skills/auto-skill/scripts/validate-skill.sh skills/auto-tmux` 通过。
|
||||
7. `bash -n skills/auto-tmux/scripts/auto-tmux.sh` 通过。
|
||||
8. 运行 `skills/auto-skill/scripts/validate-skill.sh skills/auto-tmux --strict` 通过。
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
- 参考资料只能引用仓库相对路径,不写个人机器绝对路径。
|
||||
- 上游来源统一写为 `tools/external/.tmux`、`tools/external/tmux` 或 `tools/external/Skill_Seekers-development`。
|
||||
- 示例命令必须优先使用可复制的占位符或 `git rev-parse --show-toplevel` 动态定位仓库根目录。
|
||||
- 脚本类说明优先引用 `skills/auto-tmux/scripts/auto-tmux.sh`,再给原生命令兜底。
|
||||
- 不确定的 tmux 行为必须给出验证命令,不编造。
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
| [index.md](./index.md) | 参考资料索引 |
|
||||
| [getting_started.md](./getting_started.md) | 术语、最短路径和工作姿势 |
|
||||
| [api.md](./api.md) | tmux / oh-my-tmux 常用命令与配置速查 |
|
||||
| [automation.md](./automation.md) | `auto-tmux.sh` 脚本子命令、安全模型和 AI 蜂群协作流程 |
|
||||
| [examples.md](./examples.md) | 巡检、救援、审计和多 AI 工作台示例 |
|
||||
| [troubleshooting.md](./troubleshooting.md) | 常见故障与修复路径 |
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# auto-tmux Script Automation
|
||||
|
||||
`scripts/auto-tmux.sh` 是 `auto-tmux` 的安全执行层:把高频 tmux 操作封装为可审计命令,减少手写 `capture-pane` / `send-keys` 时的误控风险。
|
||||
|
||||
## 设计目标
|
||||
|
||||
- **先观察再控制**:所有发送动作都要求目标 pane 存在,并优先输出最近上下文。
|
||||
- **默认脱敏**:读取 pane 输出时自动遮蔽常见 token、API key、password、Bearer token。
|
||||
- **拒绝危险动作**:`rm -rf`、`git reset --hard`、`git clean -fd`、`tmux kill-server` 等命令默认拒绝,必须显式 `--force`。
|
||||
- **批量动作可解释**:scan/rescue 都基于明确 session、pane、pattern 和 reply,不做无条件广播。
|
||||
- **不改用户配置**:脚本只操作 tmux server,不写 `~/.tmux.conf`;oh-my-tmux 配置仍走 `assets/oh-my-tmux` 参考入口。
|
||||
|
||||
## 子命令总览
|
||||
|
||||
| 子命令 | 用途 | 风险控制 |
|
||||
|:---|:---|:---|
|
||||
| `topology` | 列出 session/window/pane 拓扑 | 只读 |
|
||||
| `capture` | 读取指定 pane 最近输出 | 默认脱敏,可保存到文件 |
|
||||
| `send` | 向指定 pane 发送文本或按键 | 先打印上下文,危险文本需 `--force` |
|
||||
| `rescue` | pattern 命中后发送确认 | 不命中不发送 |
|
||||
| `scan` | 批量巡检 pane 输出 | 可按 session 限定范围 |
|
||||
| `record` | 开启/停止 pane 输出审计日志 | 文件写入到显式目录 |
|
||||
| `hub` | 初始化 AI 多终端工作台 | 已存在 session 不覆盖 |
|
||||
| `wait` | 等待 pane 输出出现某个 pattern | 超时失败 |
|
||||
|
||||
## 典型流程
|
||||
|
||||
### 1. 建立 AI 工作台
|
||||
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh hub --session ai-hub --workers 3 --cmd "codex"
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology --session ai-hub
|
||||
```
|
||||
|
||||
### 2. 巡检全部 worker
|
||||
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub -n 100
|
||||
```
|
||||
|
||||
只看错误:
|
||||
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback|failed"
|
||||
```
|
||||
|
||||
保存巡检日志:
|
||||
|
||||
```bash
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --save-dir /tmp/auto-tmux-scan
|
||||
```
|
||||
|
||||
### 3. 安全发送命令
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --enter
|
||||
```
|
||||
|
||||
如果要清空当前输入行后发送:
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --clear --text "make lint" --enter
|
||||
```
|
||||
|
||||
### 4. 自动救援等待确认的 pane
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t "$target" --pattern "(y/n)" --reply y
|
||||
```
|
||||
|
||||
先演练不发送:
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t "$target" --pattern "(y/n)" --reply y --dry-run
|
||||
```
|
||||
|
||||
### 5. 等待任务完成信号
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh wait -t "$target" --pattern "BUILD SUCCESS|Tests passed" --timeout 300
|
||||
```
|
||||
|
||||
### 6. 开启审计日志
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh record start -t "$target" --dir /tmp/auto-tmux-records
|
||||
```
|
||||
|
||||
停止:
|
||||
|
||||
```bash
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
skills/auto-tmux/scripts/auto-tmux.sh record stop -t "$target"
|
||||
```
|
||||
|
||||
## AI 蜂群协作建议
|
||||
|
||||
推荐结构:
|
||||
|
||||
```text
|
||||
ai-hub
|
||||
├── commander # 人类或主 Agent:分配任务、巡检、验收、回收证据
|
||||
├── worker1 # 子任务 1
|
||||
├── worker2 # 子任务 2
|
||||
└── worker3 # 子任务 3
|
||||
```
|
||||
|
||||
最小协议:
|
||||
|
||||
1. commander 只通过明确 pane 目标发送任务,不盲发广播。
|
||||
2. worker 输出完成状态、失败原因、修改文件和验证结果。
|
||||
3. commander 用 `scan` 收集状态,用 `capture` 保留证据。
|
||||
4. 需要确认输入时,用 `rescue --dry-run` 先验证 pattern,再真实发送。
|
||||
5. 长任务用 `record start` 留日志,结束后 `record stop`。
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
bash -n skills/auto-tmux/scripts/auto-tmux.sh
|
||||
skills/auto-tmux/scripts/auto-tmux.sh help
|
||||
skills/auto-skill/scripts/validate-skill.sh skills/auto-tmux --strict
|
||||
```
|
||||
@@ -1,5 +1,25 @@
|
||||
# Long Examples
|
||||
|
||||
## 用例 0:使用脚本入口完成巡检、发送和救援
|
||||
|
||||
```bash
|
||||
# 查看拓扑
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology --session ai-hub
|
||||
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
|
||||
# 读取 worker 输出
|
||||
skills/auto-tmux/scripts/auto-tmux.sh capture -t "$target" -n 100
|
||||
|
||||
# 发送测试命令
|
||||
skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --enter
|
||||
|
||||
# 对等待确认的 pane 执行救援
|
||||
skills/auto-tmux/scripts/auto-tmux.sh rescue -t "$target" --pattern "(y/n)" --reply y
|
||||
```
|
||||
|
||||
- 推荐优先使用脚本入口;下面的 Bash 片段保留为理解 tmux 原生命令的参考。
|
||||
|
||||
## 用例 1:巡检 + 自动救援脚本(bash)
|
||||
|
||||
```bash
|
||||
@@ -11,7 +31,7 @@ for w in $(tmux list-windows -a -F '#S:#I'); do
|
||||
panes=$(tmux list-panes -t "$w" -F '#S:#I.#P')
|
||||
for p in $panes; do
|
||||
log=$(tmux capture-pane -t "$p" -p -S -80)
|
||||
printf '--- [%s] ---\n%s\n' "$p" "$log"
|
||||
printf -- '--- [%s] ---\n%s\n' "$p" "$log"
|
||||
if echo "$log" | grep -qi "(y/n)"; then
|
||||
tmux send-keys -t "$p" "y" Enter
|
||||
echo "[action] sent y to $p"
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
- `getting_started.md`:术语、最小安装、前缀说明
|
||||
- `api.md`:tmux/oh-my-tmux 常用命令、同步广播、安全写法
|
||||
- `automation.md`:`scripts/auto-tmux.sh` 子命令、安全模型与 AI 蜂群协作流程
|
||||
- `examples.md`:蜂群巡检脚本、自动救援脚本、Skill Seeker 抓取示例
|
||||
- `troubleshooting.md`:常见报错与修复路径
|
||||
|
||||
## Notes
|
||||
|
||||
- 长文档、脚本细节放这里,`SKILL.md` 只保留可立即执行的片段。
|
||||
- 脚本入口统一为 `skills/auto-tmux/scripts/auto-tmux.sh`,原生命令作为兜底参考。
|
||||
- 配置来源:仓库内 `tools/external/.tmux`(gpakosz/oh-my-tmux)。
|
||||
- 大规模文档抓取/刷新可用 `tools/external/Skill_Seekers-development`,示例见 `examples.md`。
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# skills/auto-tmux/scripts
|
||||
|
||||
本目录承载 `auto-tmux` 的可执行脚本层。脚本必须安全封装 tmux 常用操作,减少手写 `capture-pane` / `send-keys` 时的误控风险。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
scripts/
|
||||
├── AGENTS.md # 本文件:脚本维护规则
|
||||
├── README.md # 面向人类的脚本入口说明
|
||||
└── auto-tmux.sh # 统一 tmux 自动化命令入口
|
||||
```
|
||||
|
||||
## 维护规则
|
||||
|
||||
- 脚本必须使用 Bash 标准能力,不引入额外运行时依赖。
|
||||
- 发送按键类功能必须先验证目标 pane 存在,并优先打印上下文。
|
||||
- 捕获输出默认脱敏;只有显式 `--no-redact` 才允许输出原文。
|
||||
- 批量救援必须通过 pattern 匹配触发,不允许无条件向所有 pane 发送输入。
|
||||
- 新增脚本后必须更新 `README.md`、`SKILL.md`、`references/index.md`,并运行 `bash -n` 与 skill strict 校验。
|
||||
@@ -0,0 +1,35 @@
|
||||
# auto-tmux scripts
|
||||
|
||||
`scripts/` 存放 `auto-tmux` 的可执行辅助脚本,把常见 tmux 操作封装成安全、可复用、可审计的命令。
|
||||
|
||||
## 入口
|
||||
|
||||
- [`auto-tmux.sh`](./auto-tmux.sh) - 统一命令入口,支持拓扑、读取、发送、巡检、救援、录制、等待和 AI 工作台初始化。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 查看帮助
|
||||
skills/auto-tmux/scripts/auto-tmux.sh help
|
||||
|
||||
# 查看 tmux 拓扑
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology
|
||||
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
|
||||
# 读取指定 pane 最近 80 行
|
||||
skills/auto-tmux/scripts/auto-tmux.sh capture -t "$target" -n 80
|
||||
|
||||
# 发送命令并回车
|
||||
skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --enter
|
||||
|
||||
# 扫描某个 session 的错误
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback"
|
||||
```
|
||||
|
||||
## 安全约束
|
||||
|
||||
- 默认对捕获输出做密钥脱敏。
|
||||
- `send` 会先打印目标 pane 最近上下文,再发送按键。
|
||||
- 危险命令默认拒绝,必须显式 `--force`。
|
||||
- 批量操作必须使用明确的 session/window/pane 目标。
|
||||
Executable
+428
@@ -0,0 +1,428 @@
|
||||
#!/usr/bin/env bash
|
||||
# auto-tmux: safe tmux automation wrapper for Codex/AI operator workflows.
|
||||
set -euo pipefail
|
||||
|
||||
PROG="${0##*/}"
|
||||
DEFAULT_LINES=120
|
||||
DEFAULT_RESCUE_PATTERN='(\(y/n\)|\[y/N\]|\[Y/n\]|continue\?|proceed\?|confirm|确认|是否继续)'
|
||||
DANGEROUS_PATTERN='(^|[[:space:];|&])(rm[[:space:]]+-rf|mkfs|dd[[:space:]]+if=|shutdown|reboot|halt|poweroff|tmux[[:space:]]+kill-server|git[[:space:]]+reset[[:space:]]+--hard|git[[:space:]]+clean[[:space:]]+-fd|DROP[[:space:]]+(DATABASE|TABLE))'
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
auto-tmux: safe tmux automation wrapper
|
||||
|
||||
Usage:
|
||||
auto-tmux.sh help
|
||||
auto-tmux.sh topology [--session NAME]
|
||||
auto-tmux.sh capture -t TARGET [-n LINES] [--save FILE] [--no-redact]
|
||||
auto-tmux.sh send -t TARGET (--text TEXT | --key KEY) [--enter] [--clear] [--force] [--dry-run]
|
||||
auto-tmux.sh rescue -t TARGET [--pattern REGEX] [--reply TEXT] [-n LINES] [--force] [--dry-run]
|
||||
auto-tmux.sh scan [--session NAME] [-n LINES] [--pattern REGEX] [--rescue] [--reply TEXT] [--save-dir DIR]
|
||||
auto-tmux.sh record start -t TARGET [--dir DIR]
|
||||
auto-tmux.sh record stop -t TARGET
|
||||
auto-tmux.sh hub [--session NAME] [--workers N] [--cmd CMD] [--commander-cmd CMD] [--attach]
|
||||
auto-tmux.sh wait -t TARGET --pattern REGEX [--timeout SEC] [--interval SEC] [-n LINES]
|
||||
|
||||
Target format:
|
||||
<session>:<window>.<pane>
|
||||
|
||||
Examples:
|
||||
auto-tmux.sh topology
|
||||
auto-tmux.sh capture -t <target-from-topology> -n 80
|
||||
auto-tmux.sh send -t <target-from-topology> --text "make test" --enter
|
||||
auto-tmux.sh rescue -t <target-from-topology> --pattern "(y/n)" --reply y
|
||||
auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback"
|
||||
auto-tmux.sh hub --session ai-hub --workers 3 --cmd "codex"
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf 'warn: %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
require_tmux() {
|
||||
command -v tmux >/dev/null 2>&1 || die "tmux is not installed or not in PATH"
|
||||
tmux list-sessions >/dev/null 2>&1 || die "no tmux server/session found"
|
||||
}
|
||||
|
||||
target_exists() {
|
||||
local target="$1"
|
||||
tmux display-message -pt "$target" '#{pane_id}' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
require_target() {
|
||||
local target="$1"
|
||||
[[ -n "$target" ]] || die "missing -t TARGET"
|
||||
target_exists "$target" || die "tmux target not found: $target"
|
||||
}
|
||||
|
||||
redact() {
|
||||
sed -E \
|
||||
-e 's/(OPENAI_API_KEY|ANTHROPIC_API_KEY|GITHUB_TOKEN|GH_TOKEN|API_KEY|TOKEN|PASSWORD|SECRET)=([^[:space:]]+)/\1=<redacted>/g' \
|
||||
-e 's/(Bearer[[:space:]]+)[A-Za-z0-9._~+\/=-]+/\1<redacted>/g' \
|
||||
-e 's/(sk-[A-Za-z0-9_-]{12,})/<redacted-openai-key>/g'
|
||||
}
|
||||
|
||||
capture_raw() {
|
||||
local target="$1"
|
||||
local lines="$2"
|
||||
tmux capture-pane -t "$target" -p -S "-$lines"
|
||||
}
|
||||
|
||||
capture_print() {
|
||||
local target="$1"
|
||||
local lines="$2"
|
||||
local no_redact="$3"
|
||||
if [[ "$no_redact" == "1" ]]; then
|
||||
capture_raw "$target" "$lines"
|
||||
else
|
||||
capture_raw "$target" "$lines" | redact
|
||||
fi
|
||||
}
|
||||
|
||||
is_dangerous_text() {
|
||||
local text="$1"
|
||||
echo "$text" | grep -Eiq "$DANGEROUS_PATTERN"
|
||||
}
|
||||
|
||||
cancel_copy_mode_if_needed() {
|
||||
local target="$1"
|
||||
local in_mode
|
||||
in_mode="$(tmux display-message -pt "$target" '#{pane_in_mode}' 2>/dev/null || echo 0)"
|
||||
if [[ "$in_mode" == "1" ]]; then
|
||||
tmux send-keys -t "$target" -X cancel
|
||||
fi
|
||||
}
|
||||
|
||||
pane_list() {
|
||||
local session="$1"
|
||||
if [[ -n "$session" ]]; then
|
||||
tmux list-panes -a -F '#S:#I.#P' | awk -F: -v s="$session" '$1 == s {print}'
|
||||
else
|
||||
tmux list-panes -a -F '#S:#I.#P'
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_topology() {
|
||||
local session=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown topology option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
if [[ -n "$session" ]]; then
|
||||
tmux has-session -t "$session" 2>/dev/null || die "session not found: $session"
|
||||
printf 'session: %s\n' "$session"
|
||||
tmux list-windows -t "$session" -F ' window #{window_index}: #{window_name} #{window_flags}'
|
||||
tmux list-panes -a -F '#S #S:#{window_index}.#{pane_index} #{pane_current_command} #{pane_current_path}' |
|
||||
awk -v s="$session" '$1 == s {print " pane target=" $2 " cmd=" $3 " path=" $4}'
|
||||
else
|
||||
tmux list-sessions -F 'session: #{session_name} windows=#{session_windows} attached=#{session_attached}'
|
||||
tmux list-windows -a -F ' window #S:#{window_index}: #{window_name} #{window_flags}'
|
||||
tmux list-panes -a -F ' pane #S:#{window_index}.#{pane_index} cmd=#{pane_current_command} path=#{pane_current_path}'
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_capture() {
|
||||
local target=""
|
||||
local lines="$DEFAULT_LINES"
|
||||
local save_file=""
|
||||
local no_redact="0"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--target) target="${2:-}"; shift 2 ;;
|
||||
-n|--lines) lines="${2:-}"; shift 2 ;;
|
||||
--save) save_file="${2:-}"; shift 2 ;;
|
||||
--no-redact) no_redact="1"; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown capture option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
require_target "$target"
|
||||
if [[ -n "$save_file" ]]; then
|
||||
mkdir -p "$(dirname "$save_file")"
|
||||
capture_print "$target" "$lines" "$no_redact" | tee "$save_file"
|
||||
else
|
||||
capture_print "$target" "$lines" "$no_redact"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_send() {
|
||||
local target=""
|
||||
local text=""
|
||||
local key=""
|
||||
local enter="0"
|
||||
local clear="0"
|
||||
local force="0"
|
||||
local dry_run="0"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--target) target="${2:-}"; shift 2 ;;
|
||||
--text) text="${2:-}"; shift 2 ;;
|
||||
--key) key="${2:-}"; shift 2 ;;
|
||||
--enter) enter="1"; shift ;;
|
||||
--clear) clear="1"; shift ;;
|
||||
--force) force="1"; shift ;;
|
||||
--dry-run) dry_run="1"; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown send option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
require_target "$target"
|
||||
[[ -n "$text" || -n "$key" ]] || die "send requires --text TEXT or --key KEY"
|
||||
[[ -z "$text" || -z "$key" ]] || die "send accepts only one of --text or --key"
|
||||
|
||||
printf -- '--- current context: %s ---\n' "$target" >&2
|
||||
capture_print "$target" 40 0 >&2 || true
|
||||
|
||||
if [[ -n "$text" && "$force" != "1" ]] && is_dangerous_text "$text"; then
|
||||
die "refusing dangerous text without --force: $text"
|
||||
fi
|
||||
|
||||
if [[ "$dry_run" == "1" ]]; then
|
||||
printf '[dry-run] target=%s clear=%s text=%q key=%q enter=%s\n' "$target" "$clear" "$text" "$key" "$enter"
|
||||
return 0
|
||||
fi
|
||||
|
||||
cancel_copy_mode_if_needed "$target"
|
||||
if [[ "$clear" == "1" ]]; then
|
||||
tmux send-keys -t "$target" C-u
|
||||
fi
|
||||
if [[ -n "$text" ]]; then
|
||||
tmux send-keys -t "$target" -l "$text"
|
||||
else
|
||||
tmux send-keys -t "$target" "$key"
|
||||
fi
|
||||
if [[ "$enter" == "1" ]]; then
|
||||
tmux send-keys -t "$target" Enter
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_rescue() {
|
||||
local target=""
|
||||
local pattern="$DEFAULT_RESCUE_PATTERN"
|
||||
local reply="y"
|
||||
local lines=80
|
||||
local force="0"
|
||||
local dry_run="0"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--target) target="${2:-}"; shift 2 ;;
|
||||
--pattern) pattern="${2:-}"; shift 2 ;;
|
||||
--reply) reply="${2:-}"; shift 2 ;;
|
||||
-n|--lines) lines="${2:-}"; shift 2 ;;
|
||||
--force) force="1"; shift ;;
|
||||
--dry-run) dry_run="1"; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown rescue option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
require_target "$target"
|
||||
local output
|
||||
output="$(capture_raw "$target" "$lines")"
|
||||
printf '%s\n' "$output" | redact
|
||||
if printf '%s\n' "$output" | grep -Eiq "$pattern"; then
|
||||
printf '[match] target=%s pattern=%s reply=%q\n' "$target" "$pattern" "$reply" >&2
|
||||
if [[ "$dry_run" == "1" ]]; then
|
||||
printf '[dry-run] would send reply to %s\n' "$target"
|
||||
else
|
||||
if [[ "$force" == "1" ]]; then
|
||||
cmd_send -t "$target" --text "$reply" --enter --force
|
||||
else
|
||||
cmd_send -t "$target" --text "$reply" --enter
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf '[skip] no rescue pattern matched for %s\n' "$target" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_scan() {
|
||||
local session=""
|
||||
local lines=80
|
||||
local pattern=""
|
||||
local rescue="0"
|
||||
local reply="y"
|
||||
local save_dir=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
-n|--lines) lines="${2:-}"; shift 2 ;;
|
||||
--pattern) pattern="${2:-}"; shift 2 ;;
|
||||
--rescue) rescue="1"; shift ;;
|
||||
--reply) reply="${2:-}"; shift 2 ;;
|
||||
--save-dir) save_dir="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown scan option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
if [[ -n "$save_dir" ]]; then
|
||||
mkdir -p "$save_dir"
|
||||
fi
|
||||
|
||||
local pane output safe_name
|
||||
while IFS= read -r pane; do
|
||||
[[ -n "$pane" ]] || continue
|
||||
printf '\n===== [%s] =====\n' "$pane"
|
||||
output="$(capture_raw "$pane" "$lines")"
|
||||
printf '%s\n' "$output" | redact
|
||||
if [[ -n "$save_dir" ]]; then
|
||||
safe_name="${pane//[:.]/-}.log"
|
||||
printf '%s\n' "$output" | redact > "$save_dir/$safe_name"
|
||||
fi
|
||||
if [[ -n "$pattern" ]] && printf '%s\n' "$output" | grep -Eiq "$pattern"; then
|
||||
printf '[match] %s matched %s\n' "$pane" "$pattern" >&2
|
||||
if [[ "$rescue" == "1" ]]; then
|
||||
cmd_send -t "$pane" --text "$reply" --enter
|
||||
fi
|
||||
fi
|
||||
done < <(pane_list "$session")
|
||||
}
|
||||
|
||||
cmd_record() {
|
||||
local action="${1:-}"
|
||||
[[ -n "$action" ]] || die "record requires start or stop"
|
||||
shift || true
|
||||
|
||||
local target=""
|
||||
local dir="/tmp/auto-tmux-records"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--target) target="${2:-}"; shift 2 ;;
|
||||
--dir) dir="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown record option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
require_target "$target"
|
||||
case "$action" in
|
||||
start)
|
||||
mkdir -p "$dir"
|
||||
local safe_name="${target//[:.]/-}.log"
|
||||
local file="$dir/$safe_name"
|
||||
tmux pipe-pane -t "$target" -o "cat >> '$file'"
|
||||
printf 'recording %s -> %s\n' "$target" "$file"
|
||||
;;
|
||||
stop)
|
||||
tmux pipe-pane -t "$target"
|
||||
printf 'recording stopped for %s\n' "$target"
|
||||
;;
|
||||
*)
|
||||
die "record action must be start or stop"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_hub() {
|
||||
local session="ai-hub"
|
||||
local workers=3
|
||||
local worker_cmd="bash"
|
||||
local commander_cmd="bash"
|
||||
local attach="0"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
--workers) workers="${2:-}"; shift 2 ;;
|
||||
--cmd) worker_cmd="${2:-}"; shift 2 ;;
|
||||
--commander-cmd) commander_cmd="${2:-}"; shift 2 ;;
|
||||
--attach) attach="1"; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown hub option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
command -v tmux >/dev/null 2>&1 || die "tmux is not installed or not in PATH"
|
||||
if tmux has-session -t "$session" 2>/dev/null; then
|
||||
warn "session already exists: $session"
|
||||
tmux list-windows -t "$session" -F '#S:#I #{window_name}'
|
||||
else
|
||||
tmux new-session -d -s "$session" -n commander "$commander_cmd"
|
||||
local i
|
||||
for i in $(seq 1 "$workers"); do
|
||||
tmux new-window -t "$session" -n "worker$i" "$worker_cmd"
|
||||
done
|
||||
tmux select-window -t "$session:commander"
|
||||
printf 'created tmux AI hub: %s workers=%s cmd=%q\n' "$session" "$workers" "$worker_cmd"
|
||||
fi
|
||||
if [[ "$attach" == "1" ]]; then
|
||||
tmux attach -t "$session"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_wait() {
|
||||
local target=""
|
||||
local pattern=""
|
||||
local timeout=60
|
||||
local interval=2
|
||||
local lines=80
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--target) target="${2:-}"; shift 2 ;;
|
||||
--pattern) pattern="${2:-}"; shift 2 ;;
|
||||
--timeout) timeout="${2:-}"; shift 2 ;;
|
||||
--interval) interval="${2:-}"; shift 2 ;;
|
||||
-n|--lines) lines="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown wait option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
require_target "$target"
|
||||
[[ -n "$pattern" ]] || die "wait requires --pattern REGEX"
|
||||
|
||||
local start now output
|
||||
start="$(date +%s)"
|
||||
while true; do
|
||||
output="$(capture_raw "$target" "$lines")"
|
||||
if printf '%s\n' "$output" | grep -Eiq "$pattern"; then
|
||||
printf '%s\n' "$output" | redact
|
||||
printf '[matched] target=%s pattern=%s\n' "$target" "$pattern" >&2
|
||||
return 0
|
||||
fi
|
||||
now="$(date +%s)"
|
||||
if (( now - start >= timeout )); then
|
||||
printf '%s\n' "$output" | redact
|
||||
die "timeout waiting for pattern: $pattern"
|
||||
fi
|
||||
sleep "$interval"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-help}"
|
||||
shift || true
|
||||
case "$cmd" in
|
||||
help|-h|--help) usage ;;
|
||||
topology) cmd_topology "$@" ;;
|
||||
capture) cmd_capture "$@" ;;
|
||||
send) cmd_send "$@" ;;
|
||||
rescue) cmd_rescue "$@" ;;
|
||||
scan) cmd_scan "$@" ;;
|
||||
record) cmd_record "$@" ;;
|
||||
hub) cmd_hub "$@" ;;
|
||||
wait) cmd_wait "$@" ;;
|
||||
*) die "unknown command: $cmd" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user