npx skills add https://github.com/akillness/oh-my-skills --skill jeo关键词:
jeo·annotate·UI-review·agentui (已弃用)| 平台: Claude Code · Codex CLI · Gemini CLI · OpenCode一个统一的技能,提供完全自动化的编排流程: 规划 (ralph+plannotator) → 执行 (team/bmad) → UI 反馈 (agentation/annotate) → 清理 (worktree cleanup)
JEO 使用一个跨平台的抽象层进行编排:
settings: 平台/运行时配置,例如 Claude 钩子、Codex config.toml、Gemini settings.json、MCP 注册和提示参数rules: 必须在每个平台上保持的策略约束广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
hooks: 在每个平台上强制执行这些规则的事件回调关键的 JEO 规则是:
plan_gate_status 重置为 pending权威状态存储在 .omc/state/jeo-state.json。钩子可以帮助推进工作流,但它们必须遵守状态文件。
jeo 关键词后立即遵循)以下是命令,而非描述。请按顺序执行。每个步骤仅在前一个步骤完成后才进行。
mkdir -p .omc/state .omc/plans .omc/logs
如果 .omc/state/jeo-state.json 不存在,则创建它:
{
"phase": "plan",
"task": "<检测到的任务>",
"plan_approved": false,
"plan_gate_status": "pending",
"plan_current_hash": null,
"last_reviewed_plan_hash": null,
"last_reviewed_plan_at": null,
"plan_review_method": null,
"team_available": null,
"retry_count": 0,
"last_error": null,
"checkpoint": null,
"created_at": "<ISO 8601>",
"updated_at": "<ISO 8601>",
"agentation": {
"active": false,
"session_id": null,
"keyword_used": null,
"submit_gate_status": "idle",
"submit_signal": null,
"submit_received_at": null,
"submitted_annotation_count": 0,
"started_at": null,
"timeout_seconds": 120,
"annotations": { "total": 0, "acknowledged": 0, "resolved": 0, "dismissed": 0, "pending": 0 },
"completed_at": null,
"exit_reason": null
}
}
通知用户:
"JEO 已激活。阶段: PLAN。如果需要 UI 反馈循环,请添加
annotate关键词。"
检查点记录 — 在进入每个步骤后立即执行:
# 在每个步骤开始时立即执行 (智能体直接更新 jeo-state.json)
python3 -c "
import json, datetime, os, subprocess, tempfile
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
import fcntl
with open(f, 'r+') as fh:
fcntl.flock(fh, fcntl.LOCK_EX)
try:
d = json.load(fh)
d['checkpoint']='<当前阶段>' # 'plan'|'execute'|'verify'|'cleanup'
d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'
fh.seek(0)
json.dump(d, fh, ensure_ascii=False, indent=2)
fh.truncate()
finally:
fcntl.flock(fh, fcntl.LOCK_UN)
" 2>/dev/null || true
last_error 记录 — 在预检失败或异常时:
python3 -c "
import json, datetime, os, subprocess, fcntl
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f, 'r+') as fh:
fcntl.flock(fh, fcntl.LOCK_EX)
try:
d = json.load(fh)
d['last_error']='<错误信息>'
d['retry_count']=d.get('retry_count',0)+1
d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'
fh.seek(0)
json.dump(d, fh, ensure_ascii=False, indent=2)
fh.truncate()
finally:
fcntl.flock(fh, fcntl.LOCK_UN)
" 2>/dev/null || true
基于检查点的重启恢复:
# 如果 jeo-state.json 已存在,则从检查点恢复
python3 -c "
import json, os, subprocess
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
d=json.load(open(f))
cp=d.get('checkpoint')
err=d.get('last_error')
if err: print(f'先前错误: {err}')
if cp: print(f'从以下位置恢复: {cp}')
" 2>/dev/null || true
规则 : 在预检中
exit 1之前,始终更新last_error并递增retry_count。如果retry_count >= 3,询问用户是否中止。
预检 (进入前必需):
# 记录检查点
python3 -c "
import json,datetime,os,subprocess,fcntl,tempfile
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'plan','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
# 注意: Claude Code — 跳过整个 bash 块。
# plannotator 是一个仅限钩子的二进制文件;直接调用它总是失败。
# 对于 Claude Code: 调用 EnterPlanMode → 编写计划 → 调用 ExitPlanMode。
# ExitPlanMode PermissionRequest 钩子会自动触发 plannotator。
# 以下脚本仅适用于 Codex / Gemini / OpenCode。
# 守卫: 通过计划哈希强制禁止重复 PLAN 审查。
# 相同哈希 + 终端门控状态 => 跳过重新打开计划门控
# 修订的 plan.md 内容 => 将门控重置为 pending 并重新审查
PLAN_GATE_STATUS=$(python3 -c "
import json, os
try:
s = json.load(open('.omc/state/jeo-state.json'))
print(s.get('plan_gate_status', 'pending'))
except Exception:
print('pending')
" 2>/dev/null || echo "pending")
HASH_MATCH=$(python3 -c "
import hashlib, json, os
try:
s = json.load(open('.omc/state/jeo-state.json'))
if not os.path.exists('plan.md'):
print('no-match')
else:
current_hash = hashlib.sha256(open('plan.md', 'rb').read()).hexdigest()
print('match' if current_hash == (s.get('last_reviewed_plan_hash') or '') else 'no-match')
except Exception:
print('no-match')
" 2>/dev/null || echo "no-match")
if [[ "$HASH_MATCH" == "match" && "$PLAN_GATE_STATUS" =~ ^(approved|manual_approved|feedback_required|infrastructure_blocked)$ ]]; then
echo "✅ 当前计划哈希已有门控结果: $PLAN_GATE_STATUS。不要重新打开 plannotator。"
exit 0
fi
# plannotator 对于 PLAN 步骤是强制性的 (Codex/Gemini/OpenCode)。
# 如果缺失,JEO 会在打开 PLAN 门控前自动安装它。
# 解析 JEO 脚本目录 (适用于任何 CWD)
_JEO_SCRIPTS=""
for _candidate in \
"${JEO_SKILL_DIR:-}/scripts" \
"$HOME/.agent-skills/jeo/scripts" \
"$HOME/.codex/skills/jeo/scripts" \
"$(pwd)/.agent-skills/jeo/scripts" \
"scripts" \
; do
if [ -f "${_candidate}/plannotator-plan-loop.sh" ]; then
_JEO_SCRIPTS="$_candidate"
break
fi
done
if [ -z "$_JEO_SCRIPTS" ]; then
echo "❌ 未找到 JEO 脚本。重新运行: bash setup-codex.sh (或 setup-gemini.sh)"
exit 1
fi
if ! bash "${_JEO_SCRIPTS}/ensure-plannotator.sh"; then
echo "❌ plannotator 自动安装失败: 无法继续 PLAN 步骤。"
echo " 重试: bash ${_JEO_SCRIPTS}/../scripts/install.sh --with-plannotator"
exit 1
fi
# 必需的 PLAN 门控 (Codex / Gemini / OpenCode):
# - 必须等待直到收到批准/反馈
# - 会话退出时自动重启 (最多 3 次)
# - 3 次退出后,询问用户是否结束 PLAN
FEEDBACK_DIR=$(python3 -c "import hashlib,os; h=hashlib.md5(os.getcwd().encode()).hexdigest()[:8]; d=f'/tmp/jeo-{h}'; os.makedirs(d,exist_ok=True); print(d)" 2>/dev/null || echo '/tmp')
FEEDBACK_FILE="${FEEDBACK_DIR}/plannotator_feedback.txt"
bash "${_JEO_SCRIPTS}/plannotator-plan-loop.sh" plan.md "$FEEDBACK_FILE" 3
PLAN_RC=$?
if [ "$PLAN_RC" -eq 0 ]; then
echo "✅ 计划已批准"
elif [ "$PLAN_RC" -eq 10 ]; then
echo "❌ 计划未批准 — 应用反馈,修订 plan.md,然后重试"
exit 1
elif [ "$PLAN_RC" -eq 32 ]; then
echo "⚠️ plannotator UI 不可用 (沙盒/CI)。进入对话批准模式:"
echo " 1. 在对话中向用户输出 plan.md 内容"
echo " 2. 询问用户: 'approve' 以继续或提供反馈"
echo " 3. 在用户明确批准之前,不要继续到 EXECUTE"
exit 32
elif [ "$PLAN_RC" -eq 30 ] || [ "$PLAN_RC" -eq 31 ]; then
echo "⛔ PLAN 退出决定 (或等待确认)。在重试前与用户确认。"
exit 1
else
echo "❌ plannotator PLAN 门控失败 (代码=$PLAN_RC)"
exit 1
fi
mkdir -p .omc/plans .omc/logs
plan.md (包含目标、步骤、风险和完成标准)Claude Code (钩子模式 — 唯一支持的方法) : plannotator 是一个仅限钩子的二进制文件。无法通过 MCP 工具或 CLI 直接调用。调用 EnterPlanMode,在计划模式下编写计划内容,然后调用 ExitPlanMode。ExitPlanMode PermissionRequest 钩子会自动触发 JEO Claude 计划门控包装器。该包装器在当前计划哈希已有终端审查结果时必须跳过重新进入。等待钩子返回后再继续 — 批准或反馈将通过钩子结果到达。
Codex / Gemini / OpenCode : 运行阻塞式 CLI (切勿使用 &):
# _JEO_SCRIPTS 必须首先通过上述预检中的动态路径发现块解析
bash "${_JEO_SCRIPTS}/plannotator-plan-loop.sh" plan.md /tmp/plannotator_feedback.txt 3
如果 plannotator 缺失,JEO 必须首先自动运行 bash "${_JEO_SCRIPTS}/ensure-plannotator.sh",并且仅在 CLI 可用后才继续。
3. 检查结果:
* approved: true (Claude Code: 钩子返回 approved) → 将 jeo-state.json 的 phase 更新为 "execute",plan_approved 更新为 true → 进入步骤 2
* 未批准 (Claude Code: 钩子返回反馈;其他: exit 10) → 读取反馈,修订 plan.md → 重复步骤 2
* 基础设施阻塞 (exit 32) → localhost 绑定不可用 (例如,沙盒/CI)。在 TTY 中使用手动门控;在非 TTY 的沙盒外与用户确认并重试
* 会话退出 3 次 (exit 30/31) → 询问用户是否结束 PLAN,并决定中止或恢复
切勿: 在没有 approved: true 的情况下进入 EXECUTE。切勿: 使用 & 后台运行。切勿: 在 approved、manual_approved、feedback_required 或 infrastructure_blocked 之后重新打开相同的未更改计划。
预检 (自动检测团队可用性):
# 记录检查点
python3 -c "
import json,datetime,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'execute','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
TEAM_AVAILABLE=false
if [[ "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-}" =~ ^(1|true|True|yes|YES)$ ]]; then
TEAM_AVAILABLE=true
elif python3 -c "
import json, os, sys
try:
s = json.load(open(os.path.expanduser('~/.claude/settings.json')))
val = s.get('env', {}).get('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS', '')
sys.exit(0 if str(val) in ('1', 'true', 'True', 'yes') else 1)
except Exception:
sys.exit(1)
" 2>/dev/null; then
TEAM_AVAILABLE=true
fi
export TEAM_AVAILABLE_BOOL="$TEAM_AVAILABLE"
python3 -c "
import json,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d['team_available']=os.environ.get('TEAM_AVAILABLE_BOOL','false').lower()=='true'
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
将 jeo-state.json 的 phase 更新为 "execute"
团队可用 (Claude Code + omc) :
/omc:team 3:executor "<任务>"
Claude Code 但无团队 :
echo "❌ JEO 需要 Claude Code 团队模式。重新运行 bash scripts/setup-claude.sh,重启 Claude Code,并确认 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1。"
exit 1
切勿在 Claude Code 中回退到单智能体执行。 4. 无 omc (BMAD 后备 — 仅限 Codex / Gemini / OpenCode) :
/workflow-init # 初始化 BMAD
/workflow-status # 检查当前步骤
将 jeo-state.json 的 phase 更新为 "verify"
使用 agent-browser 进行基本验证 (当浏览器 UI 存在时):
agent-browser snapshot http://localhost:3000
检测到 annotate 关键词 → 进入步骤 3.1
否则 → 进入步骤 4
annotate 关键词时)预检检查 (进入前必需):
if ! curl -sf --connect-timeout 2 http://localhost:4747/health >/dev/null 2>&1; then
echo "⚠️ agentation-mcp 服务器未运行 — 跳过 VERIFY_UI 并继续到 CLEANUP"
python3 -c "
import json,os,subprocess,fcntl,time try: root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip() except: root=os.getcwd() f=os.path.join(root,'.omc/state/jeo-state.json') if os.path.exists(f): with open(f,'r+') as fh: fcntl.flock(fh,fcntl.LOCK_EX) try: d=json.load(fh) d['last_error']='agentation-mcp 未运行; VERIFY_UI 已跳过' d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()) fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate() finally: fcntl.flock(fh,fcntl.LOCK_UN) " 2>/dev/null || true # 继续到步骤 4 CLEANUP (不 exit 1 — 优雅跳过) fi
2. 更新 `jeo-state.json`: `phase = "verify_ui"`, `agentation.active = true`, `agentation.submit_gate_status = "waiting_for_submit"`
3. 等待显式的人工提交:
- **Claude Code**: 等待用户在按下 **Send Annotations** / `onSubmit` 后的 `UserPromptSubmit`
- **Codex / Gemini / OpenCode**: 等待直到人类确认提交并且智能体发出 `ANNOTATE_READY` (或兼容别名 `AGENTUI_READY`)
4. 在该提交信号到达之前,不要读取 `/pending`,不要确认注释,也不要开始修复循环
5. 提交到达后,切换 `agentation.submit_gate_status = "submitted"` 并记录 `submit_signal`、`submit_received_at` 和 `submitted_annotation_count`
6. **Claude Code (MCP)**: 阻塞式调用 `agentation_watch_annotations` (`batchWindowSeconds:10`, `timeoutSeconds:120`)
7. **Codex / Gemini / OpenCode (HTTP)**: 通过 `GET http://localhost:4747/pending` 进行轮询循环
8. 处理每个注释: `acknowledge` → 通过 `elementPath` 导航代码 → 应用修复 → `resolve`
9. `count=0` 或超时 → 重置提交门控或完成子阶段 → **进入步骤 4**
**切勿: 在提交/onSubmit 之前处理草稿注释。**
---
### 步骤 4: 清理
**预检 (进入前检查):**
```bash
# 记录检查点
python3 -c "
import json,datetime,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'cleanup','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "⚠️ 不是 git 仓库 — 跳过 worktree 清理"
else
UNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
[[ "$UNCOMMITTED" -gt 0 ]] && echo "⚠️ ${UNCOMMITTED} 个未提交的更改 — 建议在清理前提交/暂存"
fi
将 jeo-state.json 的 phase 更新为 "cleanup"
工作树清理:
bash scripts/worktree-cleanup.sh || git worktree prune
将 jeo-state.json 的 phase 更新为 "done"
单一事实来源 :
https://github.com/akillness/oh-my-skills本地路径如~/.claude/skills/jeo/是通过npx skills add安装的副本。要更新到最新版本,请使用以下命令重新安装。
# 安装 JEO (npx skills add — 推荐)
npx skills add https://github.com/akillness/oh-my-skills --skill jeo
# 完整安装 (所有 AI 工具 + 所有组件)
bash scripts/install.sh --all
# 检查状态
bash scripts/check-status.sh
# 单个 AI 工具设置
bash scripts/setup-claude.sh # Claude Code 插件 + 钩子
bash scripts/setup-codex.sh # Codex CLI developer_instructions
bash scripts/setup-gemini.sh # Gemini CLI 钩子 + GEMINI.md
bash scripts/setup-opencode.sh # OpenCode 插件注册
JEO 安装和配置的工具:
| 工具 | 描述 | 安装命令 |
|---|---|---|
| omc (oh-my-claudecode) | Claude Code 多智能体编排 | /plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode |
| omx | OpenCode 的多智能体编排 | bunx oh-my-opencode setup |
| ohmg | Gemini CLI 的多智能体框架 | bunx oh-my-ag |
| bmad | BMAD 工作流编排 | 包含在技能中 |
| ralph | 自引用完成循环 | 包含在 omc 中或单独安装 |
| plannotator | 可视化计划/差异审查 | 在 PLAN 期间通过 bash scripts/ensure-plannotator.sh 自动安装 (或使用 bash scripts/install.sh --with-plannotator 预安装) |
| agentation | UI 注释 → 智能体代码修复集成 (annotate 关键词,保持 agentui 兼容性) | bash scripts/install.sh --with-agentation |
| agent-browser | 无头浏览器供 AI 智能体使用 — 用于浏览器行为验证的主要工具 | npm install -g agent-browser |
| playwriter | 基于 Playwright 的浏览器自动化 (可选) | npm install -g playwriter |
jeo "<任务>"
│
▼
[1] PLAN (ralph + plannotator)
使用 ralph 起草计划 → 使用 plannotator 进行可视化审查 → 批准/反馈
│
▼
[2] EXECUTE
├─ 团队可用? → /omc:team N:executor "<任务>"
│ 分阶段管道: plan→prd→exec→verify→fix
└─ 无团队? → /bmad /workflow-init → 运行 BMAD 步骤
│
▼
[3] VERIFY (agent-browser — 默认行为)
使用 agent-browser 验证浏览器行为
→ 捕获快照 → 确认 UI/功能正常工作
│
├─ 带有 annotate 关键词 → [3.3.1] VERIFY_UI (agentation 监视循环)
│ agentation_watch_annotations 阻塞 → 注释确认→修复→解决循环
│
▼
[4] CLEANUP
所有工作完成后 → bash scripts/worktree-cleanup.sh
git worktree prune
平台说明 :
/ralph斜杠命令仅在 Claude Code (omc) 中可用。对于 Codex/Gemini/OpenCode,请使用下面的"替代方法"。
Claude Code (omc):
/ralph "jeo-plan: <任务>" --completion-promise="PLAN_APPROVED" --max-iterations=5
Codex / Gemini / OpenCode (替代方法):
# 会话隔离的反馈目录 (防止并发运行冲突)
FEEDBACK_DIR=$(python3 -c "import hashlib,os; h=hashlib.md5(os.getcwd().encode()).hexdigest()[:8]; d=f'/tmp/jeo-{h}'; os.makedirs(d,exist_ok=True); print(d)" 2>/dev/null || echo '/tmp')
FEEDBACK_FILE="${FEEDBACK_DIR}/plannotator_feedback.txt"
# 1. 直接编写 plan.md,然后使用 plannotator 审查 (阻塞 — 无 &)
PLANNOTATOR_RUNTIME_HOME="${FEEDBACK_DIR}/.plannotator"
mkdir -p "$PLANNOTATOR_RUNTIME_HOME"
touch /tmp/jeo-plannotator-direct.lock && python3 -c "
import json
print(json.dumps({'tool_input': {'plan': open('plan.md').read(), 'permission_mode': 'acceptEdits'}}))
" | env HOME="$PLANNOTATOR_RUNTIME_HOME" PLANNOTATOR_HOME="$PLANNOTATOR_RUNTIME_HOME" plannotator > "$FEEDBACK_FILE" 2>&1
# ↑ 运行时不使用 &: 等待用户点击浏览器中的 Approve/Send Feedback
# 2. 检查结果并分支
if python3 -c "
import json, sys
try:
d = json.load(open('$FEEDBACK_FILE'))
sys.exit(0 if d.get('approved') is True else 1)
except Exception:
sys.exit(1)
" 2>/dev/null; then
echo "PLAN_APPROVED" # → 进入 EXECUTE 步骤
else
echo "PLAN_FEEDBACK" # → 读取 \"$FEEDBACK_FILE\",重新规划,重复上述步骤
fi
重要 : 不要使用
&(后台) 运行。必须运行阻塞式以接收用户反馈。
常见流程:
plan.md)"approved":true) → 进入 [2] EXECUTE 步骤/tmp/plannotator_feedback.txt 注释并重新规划 (循环)plan.md 内容jeo-state.json 的 plan_approved=true, plan_gate_status="manual_approved" 更新 → EXECUTEplan.md,重试循环,重复Claude Code 手动运行:
Shift+Tab×2 → 进入计划模式 → plannotator 在计划完成时自动运行
当团队可用时 (Claude Code + omc):
/omc:team 3:executor "jeo-exec: <基于已批准计划的任务>"
当 Claude Code 团队模式不可用时:
echo "❌ JEO 在 Claude Code 中需要 /omc:team。运行 bash scripts/setup-claude.sh,重启 Claude Code,然后重试。"
exit 1
当团队不可用时 (BMAD 后备 — Codex / Gemini / OpenCode):
/workflow-init # 初始化 BMAD 工作流
/workflow-status # 检查当前步骤
当存在基于浏览器的功能时,使用 agent-browser 验证行为。
# 从应用程序运行的 URL 捕获快照
agent-browser snapshot http://localhost:3000
# 检查特定元素 (无障碍树引用方法)
agent-browser snapshot http://localhost:3000 -i
# → 使用 @eN 引用号检查元素状态
# 保存截图
agent-browser screenshot http://localhost:3000 -o verify.png
默认行为 : 当浏览器相关工作完成时,自动运行 agent-browser 验证步骤。没有浏览器 UI 的后端/CLI 任务跳过此步骤。
在检测到 annotate 关键词时运行 agentation 监视循环。(agentui 关键词也支持以保持向后兼容性。) 这遵循与 plannotator 在 planui / ExitPlanMode 中运行相同的模式。
先决条件:
npx agentation-mcp server (HTTP :4747) 正在运行<Agentation endpoint="http://localhost:4747" /> 已挂载到应用程序中预检检查 (进入前必需 — 所有平台通用):
# 步骤 1: 检查服务器状态 (如果未运行则优雅跳过 — 不 exit 1)
if ! curl -sf --connect-timeout 2 http://localhost:4747/health >/dev/null 2>&1; then
echo "⚠️ agentation-mcp 服务器未运行 — 跳过 VERIFY_UI 并继续到 CLEANUP"
echo " (要使用 agentation: npx agentation-mcp server)"
python3 -c "
import json,os,subprocess,fcntl,time
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d['last_error']='agentation-mcp 未运行; VERIFY_UI 已跳过'
d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock
Keyword:
jeo·annotate·UI-review·agentui (deprecated)| Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCodeA unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup)
JEO uses one cross-platform abstraction for orchestration:
settings: platform/runtime configuration such as Claude hooks, Codex config.toml, Gemini settings.json, MCP registration, and prompt parametersrules: policy constraints that must hold on every platformhooks: event callbacks that enforce those rules on each platformThe key JEO rules are:
plan_gate_status to pendingThe authoritative state is .omc/state/jeo-state.json. Hooks may help advance the workflow, but they must obey the state file.
jeo keyword detection)The following are commands, not descriptions. Execute them in order. Each step only proceeds after the previous one completes.
mkdir -p .omc/state .omc/plans .omc/logs
If .omc/state/jeo-state.json does not exist, create it:
{
"phase": "plan",
"task": "<detected task>",
"plan_approved": false,
"plan_gate_status": "pending",
"plan_current_hash": null,
"last_reviewed_plan_hash": null,
"last_reviewed_plan_at": null,
"plan_review_method": null,
"team_available": null,
"retry_count": 0,
"last_error": null,
"checkpoint": null,
"created_at": "<ISO 8601>",
"updated_at": "<ISO 8601>",
"agentation": {
"active": false,
"session_id": null,
"keyword_used": null,
"submit_gate_status": "idle",
"submit_signal": null,
"submit_received_at": null,
"submitted_annotation_count": 0,
"started_at": null,
"timeout_seconds": 120,
"annotations": { "total": 0, "acknowledged": 0, "resolved": 0, "dismissed": 0, "pending": 0 },
"completed_at": null,
"exit_reason": null
}
}
Notify the user:
"JEO activated. Phase: PLAN. Add the
annotatekeyword if a UI feedback loop is needed."
Checkpoint recording — immediately after entering each STEP:
# Execute immediately at the start of each STEP (agent updates jeo-state.json directly)
python3 -c "
import json, datetime, os, subprocess, tempfile
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
import fcntl
with open(f, 'r+') as fh:
fcntl.flock(fh, fcntl.LOCK_EX)
try:
d = json.load(fh)
d['checkpoint']='<current_phase>' # 'plan'|'execute'|'verify'|'cleanup'
d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'
fh.seek(0)
json.dump(d, fh, ensure_ascii=False, indent=2)
fh.truncate()
finally:
fcntl.flock(fh, fcntl.LOCK_UN)
" 2>/dev/null || true
last_error recording — on pre-flight failure or exception:
python3 -c "
import json, datetime, os, subprocess, fcntl
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f, 'r+') as fh:
fcntl.flock(fh, fcntl.LOCK_EX)
try:
d = json.load(fh)
d['last_error']='<error message>'
d['retry_count']=d.get('retry_count',0)+1
d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'
fh.seek(0)
json.dump(d, fh, ensure_ascii=False, indent=2)
fh.truncate()
finally:
fcntl.flock(fh, fcntl.LOCK_UN)
" 2>/dev/null || true
Checkpoint-based resume on restart:
# If jeo-state.json already exists, resume from checkpoint
python3 -c "
import json, os, subprocess
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
d=json.load(open(f))
cp=d.get('checkpoint')
err=d.get('last_error')
if err: print(f'Previous error: {err}')
if cp: print(f'Resuming from: {cp}')
" 2>/dev/null || true
Rule : Before
exit 1in pre-flight, always updatelast_errorand incrementretry_count. Ifretry_count >= 3, ask the user whether to abort.
Pre-flight (required before entering):
# Record checkpoint
python3 -c "
import json,datetime,os,subprocess,fcntl,tempfile
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'plan','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
# NOTE: Claude Code — skip this entire bash block.
# plannotator is a hook-only binary; calling it directly always fails.
# For Claude Code: call EnterPlanMode → write plan → call ExitPlanMode.
# The ExitPlanMode PermissionRequest hook fires plannotator automatically.
# The following script is for Codex / Gemini / OpenCode only.
# GUARD: enforce no-repeat PLAN review by plan hash.
# same hash + terminal gate status => skip reopening the plan gate
# revised plan.md content => reset gate to pending and review again
PLAN_GATE_STATUS=$(python3 -c "
import json, os
try:
s = json.load(open('.omc/state/jeo-state.json'))
print(s.get('plan_gate_status', 'pending'))
except Exception:
print('pending')
" 2>/dev/null || echo "pending")
HASH_MATCH=$(python3 -c "
import hashlib, json, os
try:
s = json.load(open('.omc/state/jeo-state.json'))
if not os.path.exists('plan.md'):
print('no-match')
else:
current_hash = hashlib.sha256(open('plan.md', 'rb').read()).hexdigest()
print('match' if current_hash == (s.get('last_reviewed_plan_hash') or '') else 'no-match')
except Exception:
print('no-match')
" 2>/dev/null || echo "no-match")
if [[ "$HASH_MATCH" == "match" && "$PLAN_GATE_STATUS" =~ ^(approved|manual_approved|feedback_required|infrastructure_blocked)$ ]]; then
echo "✅ Current plan hash already has gate result: $PLAN_GATE_STATUS. Do not reopen plannotator."
exit 0
fi
# plannotator is mandatory for the PLAN step (Codex/Gemini/OpenCode).
# If missing, JEO auto-installs it before opening the PLAN gate.
# Resolve the JEO scripts directory (works from any CWD)
_JEO_SCRIPTS=""
for _candidate in \
"${JEO_SKILL_DIR:-}/scripts" \
"$HOME/.agent-skills/jeo/scripts" \
"$HOME/.codex/skills/jeo/scripts" \
"$(pwd)/.agent-skills/jeo/scripts" \
"scripts" \
; do
if [ -f "${_candidate}/plannotator-plan-loop.sh" ]; then
_JEO_SCRIPTS="$_candidate"
break
fi
done
if [ -z "$_JEO_SCRIPTS" ]; then
echo "❌ JEO scripts not found. Re-run: bash setup-codex.sh (or setup-gemini.sh)"
exit 1
fi
if ! bash "${_JEO_SCRIPTS}/ensure-plannotator.sh"; then
echo "❌ plannotator auto-install failed: cannot proceed with PLAN step."
echo " Retry: bash ${_JEO_SCRIPTS}/../scripts/install.sh --with-plannotator"
exit 1
fi
# Required PLAN gate (Codex / Gemini / OpenCode):
# - Must wait until approve/feedback is received
# - Auto-restart on session exit (up to 3 times)
# - After 3 exits, ask user whether to end PLAN
FEEDBACK_DIR=$(python3 -c "import hashlib,os; h=hashlib.md5(os.getcwd().encode()).hexdigest()[:8]; d=f'/tmp/jeo-{h}'; os.makedirs(d,exist_ok=True); print(d)" 2>/dev/null || echo '/tmp')
FEEDBACK_FILE="${FEEDBACK_DIR}/plannotator_feedback.txt"
bash "${_JEO_SCRIPTS}/plannotator-plan-loop.sh" plan.md "$FEEDBACK_FILE" 3
PLAN_RC=$?
if [ "$PLAN_RC" -eq 0 ]; then
echo "✅ Plan approved"
elif [ "$PLAN_RC" -eq 10 ]; then
echo "❌ Plan not approved — apply feedback, revise plan.md, and retry"
exit 1
elif [ "$PLAN_RC" -eq 32 ]; then
echo "⚠️ plannotator UI unavailable (sandbox/CI). Entering Conversation Approval Mode:"
echo " 1. Output plan.md content to user in conversation"
echo " 2. Ask user: 'approve' to proceed or provide feedback"
echo " 3. DO NOT proceed to EXECUTE until user explicitly approves"
exit 32
elif [ "$PLAN_RC" -eq 30 ] || [ "$PLAN_RC" -eq 31 ]; then
echo "⛔ PLAN exit decision (or awaiting confirmation). Confirm with user before retrying."
exit 1
else
echo "❌ plannotator PLAN gate failed (code=$PLAN_RC)"
exit 1
fi
mkdir -p .omc/plans .omc/logs
plan.md (include goal, steps, risks, and completion criteria)Claude Code (hook mode — only supported method) : plannotator is a hook-only binary. It cannot be called via MCP tool or CLI directly. Call EnterPlanMode, write the plan content in plan mode, then call ExitPlanMode. The ExitPlanMode PermissionRequest hook fires the JEO Claude plan-gate wrapper automatically. That wrapper must skip re-entry when the current plan hash already has a terminal review result. Wait for the hook to return before proceeding — approved or feedback will arrive via the hook result.
Codex / Gemini / OpenCode : run blocking CLI (never use &):
# _JEO_SCRIPTS must be resolved first via the dynamic path discovery block in the pre-flight above
bash "${_JEO_SCRIPTS}/plannotator-plan-loop.sh" plan.md /tmp/plannotator_feedback.txt 3
If plannotator is missing, JEO must auto-run bash "${_JEO_SCRIPTS}/ensure-plannotator.sh" first and continue only after the CLI is available.
3. Check result:
* approved: true (Claude Code: hook returns approved) → update jeo-state.json phase to "execute" and plan_approved to true → enter STEP 2
* Not approved (Claude Code: hook returns feedback; others: exit 10) → read feedback, revise plan.md → repeat step 2
* Infrastructure blocked () → localhost bind unavailable (e.g., sandbox/CI). Use manual gate in TTY; confirm with user and retry outside sandbox in non-TTY
* Session exited 3 times () → ask user whether to end PLAN and decide to abort or resume
NEVER: enter EXECUTE withoutapproved: true. NEVER: run with & background. NEVER: reopen the same unchanged plan afterapproved, manual_approved, feedback_required, or infrastructure_blocked.
Pre-flight (auto-detect team availability):
# Record checkpoint
python3 -c "
import json,datetime,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'execute','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
TEAM_AVAILABLE=false
if [[ "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-}" =~ ^(1|true|True|yes|YES)$ ]]; then
TEAM_AVAILABLE=true
elif python3 -c "
import json, os, sys
try:
s = json.load(open(os.path.expanduser('~/.claude/settings.json')))
val = s.get('env', {}).get('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS', '')
sys.exit(0 if str(val) in ('1', 'true', 'True', 'yes') else 1)
except Exception:
sys.exit(1)
" 2>/dev/null; then
TEAM_AVAILABLE=true
fi
export TEAM_AVAILABLE_BOOL="$TEAM_AVAILABLE"
python3 -c "
import json,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d['team_available']=os.environ.get('TEAM_AVAILABLE_BOOL','false').lower()=='true'
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
Update jeo-state.json phase to "execute"
Team available (Claude Code + omc) :
/omc:team 3:executor "<task>"
Claude Code but no team :
echo "❌ JEO requires Claude Code team mode. Re-run bash scripts/setup-claude.sh, restart Claude Code, and confirm CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1."
exit 1
Never fall back to single-agent execution in Claude Code. 4. No omc (BMAD fallback — Codex / Gemini / OpenCode only) :
/workflow-init # Initialize BMAD
/workflow-status # Check current step
Update jeo-state.json phase to "verify"
Basic verification with agent-browser (when browser UI is present):
agent-browser snapshot http://localhost:3000
annotate keyword detected → enter STEP 3.1
Otherwise → enter STEP 4
annotate keyword is detected)Pre-flight check (required before entering):
if ! curl -sf --connect-timeout 2 http://localhost:4747/health >/dev/null 2>&1; then
echo "⚠️ agentation-mcp server not running — skipping VERIFY_UI and proceeding to CLEANUP"
python3 -c "
import json,os,subprocess,fcntl,time try: root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip() except: root=os.getcwd() f=os.path.join(root,'.omc/state/jeo-state.json') if os.path.exists(f): with open(f,'r+') as fh: fcntl.flock(fh,fcntl.LOCK_EX) try: d=json.load(fh) d['last_error']='agentation-mcp not running; VERIFY_UI skipped' d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()) fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate() finally: fcntl.flock(fh,fcntl.LOCK_UN) " 2>/dev/null || true # Proceed to STEP 4 CLEANUP (no exit 1 — graceful skip) fi
2. Update `jeo-state.json`: `phase = "verify_ui"`, `agentation.active = true`, `agentation.submit_gate_status = "waiting_for_submit"`
3. Wait for explicit human submit:
- **Claude Code**: wait for `UserPromptSubmit` after the user presses **Send Annotations** / `onSubmit`
- **Codex / Gemini / OpenCode**: wait until the human confirms submission and the agent emits `ANNOTATE_READY` (or compatibility alias `AGENTUI_READY`)
4. Before that submit signal arrives, do not read `/pending`, do not acknowledge annotations, and do not start the fix loop
5. After submit arrives, switch `agentation.submit_gate_status = "submitted"` and record `submit_signal`, `submit_received_at`, and `submitted_annotation_count`
6. **Claude Code (MCP)**: blocking call to `agentation_watch_annotations` (`batchWindowSeconds:10`, `timeoutSeconds:120`)
7. **Codex / Gemini / OpenCode (HTTP)**: polling loop via `GET http://localhost:4747/pending`
8. Process each annotation: `acknowledge` → navigate code via `elementPath` → apply fix → `resolve`
9. `count=0` or timeout → reset the submit gate or finish the sub-phase → **enter STEP 4**
**NEVER: process draft annotations before submit/onSubmit.**
---
### STEP 4: CLEANUP
**Pre-flight (check before entering):**
```bash
# Record checkpoint
python3 -c "
import json,datetime,os,subprocess,fcntl
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d.update({'checkpoint':'cleanup','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "⚠️ Not a git repository — skipping worktree cleanup"
else
UNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
[[ "$UNCOMMITTED" -gt 0 ]] && echo "⚠️ ${UNCOMMITTED} uncommitted change(s) — recommend commit/stash before cleanup"
fi
Update jeo-state.json phase to "cleanup"
Worktree cleanup:
bash scripts/worktree-cleanup.sh || git worktree prune
Update jeo-state.json phase to "done"
Source of truth :
https://github.com/akillness/oh-my-skillsLocal paths like~/.claude/skills/jeo/are copies installed vianpx skills add. To update to the latest version, reinstall using the command below.
# Install JEO (npx skills add — recommended)
npx skills add https://github.com/akillness/oh-my-skills --skill jeo
# Full install (all AI tools + all components)
bash scripts/install.sh --all
# Check status
bash scripts/check-status.sh
# Individual AI tool setup
bash scripts/setup-claude.sh # Claude Code plugin + hooks
bash scripts/setup-codex.sh # Codex CLI developer_instructions
bash scripts/setup-gemini.sh # Gemini CLI hooks + GEMINI.md
bash scripts/setup-opencode.sh # OpenCode plugin registration
Tools that JEO installs and configures:
| Tool | Description | Install Command |
|---|---|---|
| omc (oh-my-claudecode) | Claude Code multi-agent orchestration | /plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode |
| omx | Multi-agent orchestration for OpenCode | bunx oh-my-opencode setup |
| ohmg | Multi-agent framework for Gemini CLI | bunx oh-my-ag |
| bmad | BMAD workflow orchestration | Included in skills |
| ralph | Self-referential completion loop |
jeo "<task>"
│
▼
[1] PLAN (ralph + plannotator)
Draft plan with ralph → visual review with plannotator → Approve/Feedback
│
▼
[2] EXECUTE
├─ team available? → /omc:team N:executor "<task>"
│ staged pipeline: plan→prd→exec→verify→fix
└─ no team? → /bmad /workflow-init → run BMAD steps
│
▼
[3] VERIFY (agent-browser — default behavior)
Verify browser behavior with agent-browser
→ capture snapshot → confirm UI/functionality is working
│
├─ with annotate keyword → [3.3.1] VERIFY_UI (agentation watch loop)
│ agentation_watch_annotations blocking → annotation ack→fix→resolve loop
│
▼
[4] CLEANUP
After all work is done → bash scripts/worktree-cleanup.sh
git worktree prune
Platform note : The
/ralphslash command is only available in Claude Code (omc). Use the "alternative method" below for Codex/Gemini/OpenCode.
Claude Code (omc):
/ralph "jeo-plan: <task>" --completion-promise="PLAN_APPROVED" --max-iterations=5
Codex / Gemini / OpenCode (alternative):
# Session-isolated feedback directory (prevents concurrent run conflicts)
FEEDBACK_DIR=$(python3 -c "import hashlib,os; h=hashlib.md5(os.getcwd().encode()).hexdigest()[:8]; d=f'/tmp/jeo-{h}'; os.makedirs(d,exist_ok=True); print(d)" 2>/dev/null || echo '/tmp')
FEEDBACK_FILE="${FEEDBACK_DIR}/plannotator_feedback.txt"
# 1. Write plan.md directly, then review with plannotator (blocking — no &)
PLANNOTATOR_RUNTIME_HOME="${FEEDBACK_DIR}/.plannotator"
mkdir -p "$PLANNOTATOR_RUNTIME_HOME"
touch /tmp/jeo-plannotator-direct.lock && python3 -c "
import json
print(json.dumps({'tool_input': {'plan': open('plan.md').read(), 'permission_mode': 'acceptEdits'}}))
" | env HOME="$PLANNOTATOR_RUNTIME_HOME" PLANNOTATOR_HOME="$PLANNOTATOR_RUNTIME_HOME" plannotator > "$FEEDBACK_FILE" 2>&1
# ↑ Run without &: waits until user clicks Approve/Send Feedback in browser
# 2. Check result and branch
if python3 -c "
import json, sys
try:
d = json.load(open('$FEEDBACK_FILE'))
sys.exit(0 if d.get('approved') is True else 1)
except Exception:
sys.exit(1)
" 2>/dev/null; then
echo "PLAN_APPROVED" # → enter EXECUTE step
else
echo "PLAN_FEEDBACK" # → read \"$FEEDBACK_FILE\", replan, repeat above
fi
Important : Do not run with
&(background). Must run blocking to receive user feedback.
Common flow:
plan.md)"approved":true) → enter [2] EXECUTE step/tmp/plannotator_feedback.txt annotations and replan (loop)plan.md content to userjeo-state.json plan_approved=true, plan_gate_status="manual_approved" → EXECUTEplan.md, retry loop, repeatClaude Code manual run:
Shift+Tab×2 → enter plan mode → plannotator runs automatically when plan is complete
When team is available (Claude Code + omc):
/omc:team 3:executor "jeo-exec: <task based on approved plan>"
When Claude Code team mode is unavailable:
echo "❌ JEO requires /omc:team in Claude Code. Run bash scripts/setup-claude.sh, restart Claude Code, then retry."
exit 1
When team is unavailable (BMAD fallback — Codex / Gemini / OpenCode):
/workflow-init # Initialize BMAD workflow
/workflow-status # Check current step
When browser-based functionality is present, verify behavior with agent-browser.
# Capture snapshot from the URL where the app is running
agent-browser snapshot http://localhost:3000
# Check specific elements (accessibility tree ref method)
agent-browser snapshot http://localhost:3000 -i
# → check element state using @eN ref numbers
# Save screenshot
agent-browser screenshot http://localhost:3000 -o verify.png
Default behavior : Automatically runs the agent-browser verification step when browser-related work is complete. Backend/CLI tasks without a browser UI skip this step.
Runs the agentation watch loop when the annotate keyword is detected. (The agentui keyword is also supported for backward compatibility.) This follows the same pattern as plannotator operating in planui / ExitPlanMode.
Prerequisites:
npx agentation-mcp server (HTTP :4747) is running<Agentation endpoint="http://localhost:4747" /> is mounted in the appPre-flight Check (required before entering — common to all platforms):
# Step 1: Check server status (graceful skip if not running — no exit 1)
if ! curl -sf --connect-timeout 2 http://localhost:4747/health >/dev/null 2>&1; then
echo "⚠️ agentation-mcp server not running — skipping VERIFY_UI and proceeding to CLEANUP"
echo " (to use agentation: npx agentation-mcp server)"
python3 -c "
import json,os,subprocess,fcntl,time
try:
root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()
except:
root=os.getcwd()
f=os.path.join(root,'.omc/state/jeo-state.json')
if os.path.exists(f):
with open(f,'r+') as fh:
fcntl.flock(fh,fcntl.LOCK_EX)
try:
d=json.load(fh)
d['last_error']='agentation-mcp not running; VERIFY_UI skipped'
d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())
fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()
finally:
fcntl.flock(fh,fcntl.LOCK_UN)
" 2>/dev/null || true
# Proceed to STEP 4 CLEANUP (no exit 1 — graceful skip)
else
# Step 2: Check session existence (<Agentation> component mount status)
SESSIONS=$(curl -sf http://localhost:4747/sessions 2>/dev/null)
S_COUNT=$(echo "$SESSIONS" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo 0)
[ "$S_COUNT" -eq 0 ] && echo "⚠️ No active sessions — <Agentation endpoint='http://localhost:4747' /> needs to be mounted"
echo "✅ agentation ready — server OK, ${S_COUNT} session(s)"
fi
After passing pre-flight (
elsebranch), update jeo-state.jsonphaseto"verify_ui", setagentation.activetotrue, and setagentation.submit_gate_statusto"waiting_for_submit". Do not call/pendingyet. Draft annotations are not actionable until the user explicitly submits them.
Claude Code (direct MCP tool call):
# annotate keyword detected (or agentui — backward compatible)
# 1. wait for UserPromptSubmit after the user clicks Send Annotations / onSubmit
# 2. the JEO submit-gate hook records submit_gate_status="submitted"
# 3. only then run the blocking agentation watch loop
#
# batchWindowSeconds:10 — receive annotations in 10-second batches
# timeoutSeconds:120 — auto-exit after 120 seconds with no annotations
#
# Per-annotation processing loop:
# 1. agentation_acknowledge_annotation({id}) — show 'processing' in UI
# 2. navigate code via annotation.elementPath (CSS selector) → apply fix
# 3. agentation_resolve_annotation({id, summary}) — mark 'done' + save summary
#
# Loop ends when annotation count=0 or timeout
Important :
agentation_watch_annotationsis a blocking call. Do not run with&background. Same as plannotator'sapproved:trueloop: annotation count=0 or timeout = completion signal.annotateis the primary keyword.agentuiis a backward-compatible alias and behaves identically.
Codex / Gemini / OpenCode (HTTP REST API fallback):
START_TIME=$(date +%s)
TIMEOUT_SECONDS=120
# Required gate: do not enter the loop until the human has clicked Send Annotations
# and the platform has opened agentation.submit_gate_status="submitted".
while true; do
# Timeout check
NOW=$(date +%s)
ELAPSED=$((NOW - START_TIME))
if [ $ELAPSED -ge $TIMEOUT_SECONDS ]; then
echo "[JEO] agentation polling timeout (${TIMEOUT_SECONDS}s) — some annotations may remain unresolved"
break
fi
SUBMIT_GATE=$(python3 -c "
import json
try:
print(json.load(open('.omc/state/jeo-state.json')).get('agentation', {}).get('submit_gate_status', 'idle'))
except Exception:
print('idle')
" 2>/dev/null || echo "idle")
if [ "$SUBMIT_GATE" != "submitted" ]; then
sleep 2
continue
fi
COUNT=$(curl -sf --connect-timeout 3 --max-time 5 http://localhost:4747/pending 2>/dev/null | python3 -c "import sys,json; data=sys.stdin.read(); d=json.loads(data) if data.strip() else {}; print(d.get('count', len(d.get('annotations', [])) if isinstance(d, dict) else 0))" 2>/dev/null || echo 0)
[ "$COUNT" -eq 0 ] && break
# Process each annotation:
# a) Acknowledge (show as in-progress)
curl -X PATCH http://localhost:4747/annotations/<id> \
-H 'Content-Type: application/json' \
-d '{"status": "acknowledged"}'
# b) Navigate code via elementPath (CSS selector) → apply fix
# c) Resolve (mark done + fix summary)
curl -X PATCH http://localhost:4747/annotations/<id> \
-H 'Content-Type: application/json' \
-d '{"status": "resolved", "resolution": "<fix summary>"}'
sleep 3
done
# Runs automatically after all work is complete
bash scripts/worktree-cleanup.sh
# Individual commands
git worktree list # List current worktrees
git worktree prune # Clean up worktrees for deleted branches
bash scripts/worktree-cleanup.sh --force # Force cleanup including dirty worktrees
Default run removes only clean extra worktrees; worktrees with changes are left with a warning. Use
--forceonly after review.
# Automatic setup
bash scripts/setup-claude.sh
# Or manually:
/plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode
/plugin install oh-my-claudecode
/omc:omc-setup
# Add plannotator hook
bash .agent-skills/plannotator/scripts/setup-hook.sh
Config file : ~/.claude/settings.json
{
"hooks": {
"PermissionRequest": [{
"matcher": "ExitPlanMode",
"hooks": [{
"type": "command",
"command": "python3 ~/.claude/skills/jeo/scripts/claude-plan-gate.py",
"timeout": 1800
}]
}]
}
}
agentation MCP config (~/.claude/settings.json or .claude/mcp.json):
{
"mcpServers": {
"agentation": {
"command": "npx",
"args": ["-y", "agentation-mcp", "server"]
}
},
"hooks": {
"UserPromptSubmit": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "python3 ~/.claude/skills/jeo/scripts/claude-agentation-submit-hook.py",
"timeout": 300
}]
}]
}
}
# Automatic setup
bash scripts/setup-codex.sh
# What gets configured:
# - developer_instructions: ~/.codex/config.toml
# - prompt file: ~/.codex/prompts/jeo.md
# - notify hook: ~/.codex/hooks/jeo-notify.py
# - [tui] notifications: agent-turn-complete
agentation MCP config (~/.codex/config.toml):
[mcp_servers.agentation]
command = "npx"
args = ["-y", "agentation-mcp", "server"]
notify hook (~/.codex/hooks/jeo-notify.py):
PLAN_READY signal in last-assistant-message when agent turn completesplan.md exists, compares the current hash against last_reviewed_plan_hash, and skips the gate when the plan was already reviewed/tmp/plannotator_feedback.txtANNOTATE_READY signal (or backward-compatible AGENTUI_READY) only in verify_uiagentation.submit_gate_status="submitted" first, then polls http://localhost:4747/pending~/.codex/config.toml config:
developer_instructions = """
# JEO Orchestration Workflow
# ...
"""
notify = ["python3", "~/.codex/hooks/jeo-notify.py"]
[tui]
notifications = ["agent-turn-complete"]
notification_method = "osc9"
developer_instructionsmust be a top-level string. Writing it as a[developer_instructions]table may cause Codex to fail on startup withinvalid type: map, expected a string.notifyand[tui].notificationsmust also be set correctly for the PLAN/ANNOTATE follow-up loop to actually work.
Using in Codex:
/prompts:jeo # Activate JEO workflow
# Agent writes plan.md and outputs "PLAN_READY" → notify hook runs automatically
# Automatic setup
bash scripts/setup-gemini.sh
# What gets configured:
# - AfterAgent backup hook: ~/.gemini/hooks/jeo-plannotator.sh
# - Instructions (MANDATORY loop): ~/.gemini/GEMINI.md
Key principle : The agent must call plannotator directly in blocking mode to receive feedback in the same turn. The AfterAgent hook serves only as a safety net (runs after turn ends → injected in next turn).
AfterAgent backup hook (~/.gemini/settings.json):
{
"hooks": {
"AfterAgent": [{
"matcher": "",
"hooks": [{
"name": "plannotator-review",
"type": "command",
"command": "bash ~/.gemini/hooks/jeo-plannotator.sh",
"description": "Run plannotator when plan.md is detected (AfterAgent backup)"
}]
}]
}
}
PLAN instructions added to GEMINI.md (mandatory loop) :
1. Write plan.md
2. Run plannotator blocking (no &) → /tmp/plannotator_feedback.txt
3. approved=true → EXECUTE / Not approved → revise and repeat step 2
NEVER proceed to EXECUTE without approved=true.
agentation MCP config (~/.gemini/settings.json):
{
"mcpServers": {
"agentation": {
"command": "npx",
"args": ["-y", "agentation-mcp", "server"]
}
}
}
Note : Gemini CLI hook events use
BeforeToolandAfterAgent.ExitPlanModeis a Claude Code-only hook.
# Automatic setup
bash scripts/setup-opencode.sh
# Added to opencode.json:
# "@plannotator/opencode@latest" plugin
# "@oh-my-opencode/opencode@latest" plugin (omx)
OpenCode slash commands:
/jeo-plan — plan with ralph + plannotator/jeo-exec — execute with team/bmad/jeo-annotate — start agentation watch loop (annotate; /jeo-agentui is a deprecated alias)/jeo-cleanup — worktree cleanupplannotator integration (MANDATORY blocking loop):
# Write plan.md then run PLAN gate (no &) — receive feedback in same turn
bash scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3
# - Must wait until approve/feedback is received
# - Auto-restart on session exit (up to 3 times)
# - After 3 exits, confirm with user whether to abort or resume
# - exit 32 if localhost bind unavailable (replace with manual gate in TTY)
# Branch based on result
# approved=true → enter EXECUTE
# not approved → apply feedback, revise plan.md → repeat above
agentation MCP config (opencode.json):
{
"mcp": {
"agentation": {
"type": "local",
"command": ["npx", "-y", "agentation-mcp", "server"]
}
}
}
JEO stores state at the following paths:
{worktree}/.omc/state/jeo-state.json # JEO execution state
{worktree}/.omc/plans/jeo-plan.md # Approved plan
{worktree}/.omc/logs/jeo-*.log # Execution logs
State file structure:
{
"mode": "jeo",
"phase": "plan|execute|verify|verify_ui|cleanup|done",
"session_id": "<uuid>",
"task": "current task description",
"plan_approved": true,
"plan_gate_status": "pending|approved|feedback_required|infrastructure_blocked|manual_approved",
"plan_current_hash": "<sha256 or null>",
"last_reviewed_plan_hash": "<sha256 or null>",
"last_reviewed_plan_at": "2026-02-24T00:00:00Z",
"plan_review_method": "plannotator|manual|null",
"team_available": true,
"retry_count": 0,
"last_error": null,
"checkpoint": "plan|execute|verify|verify_ui|cleanup",
"created_at": "2026-02-24T00:00:00Z",
"updated_at": "2026-02-24T00:00:00Z",
"agentation": {
"active": false,
"session_id": null,
"keyword_used": null,
"submit_gate_status": "idle|waiting_for_submit|submitted",
"submit_signal": "claude-user-prompt-submit|codex-notify|gemini-manual|null",
"submit_received_at": "2026-02-24T00:00:00Z",
"submitted_annotation_count": 0,
"started_at": null,
"timeout_seconds": 120,
"annotations": {
"total": 0, "acknowledged": 0, "resolved": 0, "dismissed": 0, "pending": 0
},
"completed_at": null,
"exit_reason": null
}
}
agentation fields :
active— whether the watch loop is running (used as hook guard),session_id— for resuming,submit_gate_status— prevents processing draft annotations before submit/onSubmit,submit_signal— which platform opened the gate,submit_received_at/submitted_annotation_count— audit trail for the submitted batch,exit_reason—"all_resolved"|"timeout"|"user_cancelled"|
Error recovery fields :
retry_count— number of retries after an error. Increments +1 on each pre-flight failure. Ask user to confirm if>= 3.last_error— most recent error message. Used to identify the cause on restart.checkpoint— last phase that was started. Resume from this phase on restart (plan|execute|verify|cleanup).
Checkpoint-based resume flow:
# Check checkpoint on restart
python3 -c "
import json, os, subprocess
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()
except:
root = os.getcwd()
f = os.path.join(root, '.omc/state/jeo-state.json')
if os.path.exists(f):
d=json.load(open(f))
cp=d.get('checkpoint')
err=d.get('last_error')
rc=d.get('retry_count',0)
print(f'Resume from: {cp or \"beginning\"}')
if err: print(f'Previous error ({rc} time(s)): {err}')
if rc >= 3: print('⚠️ Retry count exceeded 3 — user confirmation required')
"
Restore after restart:
# Check status and resume
bash scripts/check-status.sh --resume
# Step 1: Install (once)
bash scripts/install.sh --all
bash scripts/check-status.sh
# Step 2: Start work
jeo "<task description>" # Activate with keyword
# Or in Claude: Shift+Tab×2 → plan mode
# Step 3: Review plan with plannotator
# Approve or Send Feedback in browser UI
# Step 4: Automatic execution
# team or bmad handles the work
# Step 5: Cleanup after completion
bash scripts/worktree-cleanup.sh
worktree-cleanup.sh immediately after work completes (prevents branch pollution).omc/state/jeo-state.json to maintain state across sessionsannotate keyword to run the agentation watch loop for complex UI changes (precise code changes via CSS selector). agentui is a backward-compatible alias.| Issue | Solution |
|---|---|
| plannotator not running | JEO first auto-runs bash scripts/ensure-plannotator.sh; if it still fails, run bash .agent-skills/plannotator/scripts/check-status.sh |
| plannotator not opening in Claude Code | plannotator is hook-only. Do NOT call it via MCP or CLI. Use EnterPlanMode → write plan → ExitPlanMode; the hook fires automatically. Verify hook is set: `cat ~/.claude/settings.json |
| plannotator feedback not received | Remove & background execution → run blocking, then check /tmp/plannotator_feedback.txt (Codex/Gemini/OpenCode only) |
| Same plan is repeatedly re-reviewed in Codex |
annotate; agentui backward compatible)Weekly Installs
1
Repository
GitHub Stars
3
First Seen
1 day ago
Security Audits
Gen Agent Trust HubFailSocketWarnSnykWarn
Installed on
mcpjam1
claude-code1
junie1
windsurf1
zencoder1
crush1
Azure Data Explorer (Kusto) 查询技能:KQL数据分析、日志遥测与时间序列处理
119,800 周安装
exit 32exit 30/31| Included in omc or install separately |
| plannotator | Visual plan/diff review | Auto-installed during PLAN via bash scripts/ensure-plannotator.sh (or preinstall with bash scripts/install.sh --with-plannotator) |
| agentation | UI annotation → agent code fix integration (annotate keyword, agentui compatibility maintained) | bash scripts/install.sh --with-agentation |
| agent-browser | Headless browser for AI agents — primary tool for browser behavior verification | npm install -g agent-browser |
| playwriter | Playwright-based browser automation (optional) | npm install -g playwriter |
"error"dismissed annotations : When a user dismisses an annotation in the agentation UI (status becomes "dismissed"), the agent should skip code changes for that annotation, increment annotations.dismissed, and continue to the next pending annotation. Dismissed annotations are counted but not acted upon. The watch loop exits normally when pending == 0 (resolved + dismissed covers all).
plan_review_method : set to "plannotator" when approved via UI, "manual" when approved via TTY fallback gate.
cleanup_completed : set to true by worktree-cleanup.sh after successful worktree prune.
Compare last_reviewed_plan_hash in jeo-state.json with the current plan.md hash. If they match and plan_gate_status is terminal, do not re-run |
Codex startup failure (invalid type: map, expected a string) | Re-run bash scripts/setup-codex.sh and confirm developer_instructions in ~/.codex/config.toml is a top-level string |
| Gemini feedback loop missing | Add blocking direct call instruction to ~/.gemini/GEMINI.md |
| worktree conflict | git worktree prune && git worktree list |
| team mode not working | JEO requires team mode in Claude Code. Run bash scripts/setup-claude.sh, restart Claude Code, and verify CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 before retrying |
| omc install failed | Run /omc:omc-doctor |
| agent-browser error | Check agent-browser --version |
| annotate (agentation) not opening | Check curl http://localhost:4747/health and curl http://localhost:4747/sessions. JEO waits for explicit submit/onSubmit before polling /pending |
| annotation not reflected in code | Confirm summary field is present when calling agentation_resolve_annotation |
agentui keyword not activating | Use the annotate keyword (new). agentui is a deprecated alias but still works. |
| MCP tool not registered (Codex/Gemini) | Re-run bash scripts/setup-codex.sh / setup-gemini.sh |