JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, text)\nif m1 and 'Keyword: jeo | Platforms: Codex, Claude, Gemini, OpenCode' in m1.group(1):\n raise SystemExit(0)\nm2 = re.search(r'(?m)^developer_instructions\\s*=\\s*\"(.*)\"\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, text)\nif m2 and 'Keyword: jeo | Platforms: Codex, Claude, Gemini, OpenCode' in bytes(m2.group(1), 'utf-8').decode('unicode_escape'):\n raise SystemExit(0)\nraise SystemExit(1)\nPYEOF\n then\n ok \"Codex CLI — JEO developer_instructions configured\"; ((PASS++)) || true\n else\n warn \"Codex CLI — JEO developer_instructions missing/invalid in config.toml\"; ((WARN++)) || true\n fi\n if [[ -f \"${HOME}/.codex/prompts/jeo.md\" ]]; then\n ok \"Codex CLI — /prompts:jeo available\"; ((PASS++)) || true\n else\n warn \"Codex CLI — /prompts:jeo not found\"; ((WARN++)) || true\n fi\n if grep -q \"jeo-notify.py\" \"${HOME}/.codex/config.toml\" 2>/dev/null; then\n ok \"Codex CLI — notify hook configured\"; ((PASS++)) || true\n else\n warn \"Codex CLI — notify hook missing\"; ((WARN++)) || true\n fi\n if grep -q 'agent-turn-complete' \"${HOME}/.codex/config.toml\" 2>/dev/null; then\n ok \"Codex CLI — tui notifications include agent-turn-complete\"; ((PASS++)) || true\n else\n warn \"Codex CLI — tui notifications missing agent-turn-complete\"; ((WARN++)) || true\n fi\nelse\n warn \"Codex CLI — ~/.codex/config.toml not found\"; ((WARN++)) || true\nfi\n\n# Gemini CLI\nif [[ -f \"${HOME}/.gemini/settings.json\" ]]; then\n if grep -q \"plannotator\" \"${HOME}/.gemini/settings.json\" 2>/dev/null; then\n ok \"Gemini CLI — plannotator hook configured\"; ((PASS++)) || true\n else\n warn \"Gemini CLI — plannotator hook not found\"; ((WARN++)) || true\n fi\nelse\n warn \"Gemini CLI — ~/.gemini/settings.json not found\"; ((WARN++)) || true\nfi\nif [[ -f \"${HOME}/.gemini/GEMINI.md\" ]]; then\n if grep -q \"JEO Orchestration\" \"${HOME}/.gemini/GEMINI.md\" 2>/dev/null; then\n ok \"Gemini CLI — JEO instructions in GEMINI.md\"; ((PASS++)) || true\n else\n warn \"Gemini CLI — JEO not in GEMINI.md\"; ((WARN++)) || true\n fi\nfi\n\n# OpenCode\nfor candidate in \"./opencode.json\" \"${HOME}/opencode.json\" \"${HOME}/.config/opencode/opencode.json\"; do\n if [[ -f \"$candidate\" ]]; then\n if grep -q \"plannotator\" \"$candidate\" 2>/dev/null; then\n ok \"OpenCode — plannotator plugin configured ($candidate)\"; ((PASS++)) || true\n else\n warn \"OpenCode — plannotator not in $candidate\"; ((WARN++)) || true\n fi\n break\n fi\ndone\necho \"\"\n\n# ── JEO State ─────────────────────────────────────────────────────────────────\ninfo \"JEO State\"\nGIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)\nSTATE_FILE=\"$GIT_ROOT/.omc/state/jeo-state.json\"\nPHASE=\"unknown\"\nif [[ -f \"$STATE_FILE\" ]]; then\n ok \"State file found: $STATE_FILE\"\n if command -v python3 >/dev/null 2>&1; then\n PHASE=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('phase','unknown'))\" 2>/dev/null || echo \"unknown\")\n TASK=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('task','(none)'))\" 2>/dev/null || echo \"(none)\")\n RETRY=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('retry_count',0))\" 2>/dev/null || echo \"0\")\n LAST_ERR=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); e=d.get('last_error'); print(e if e else '(none)')\" 2>/dev/null || echo \"(none)\")\n PLAN_GATE=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('plan_gate_status','(none)'))\" 2>/dev/null || echo \"(none)\")\n PLAN_HASH=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('last_reviewed_plan_hash') or '(none)')\" 2>/dev/null || echo \"(none)\")\n SUBMIT_GATE=$(python3 -c \"import json; d=json.load(open('$STATE_FILE')); print(d.get('agentation',{}).get('submit_gate_status','(none)'))\" 2>/dev/null || echo \"(none)\")\n echo \" Current phase: $PHASE\"\n echo \" Task: $TASK\"\n echo \" Plan gate: $PLAN_GATE\"\n [[ \"$PLAN_HASH\" != \"(none)\" ]] && echo \" Last reviewed hash: ${PLAN_HASH:0:12}...\"\n echo \" Agentation submit gate: $SUBMIT_GATE\"\n [[ \"$RETRY\" -gt 0 ]] && echo \" Retry count: $RETRY\"\n [[ \"$LAST_ERR\" != \"(none)\" ]] && echo -e \" ${RED}Last error: $LAST_ERR${NC}\"\n fi\n if $RESUME; then\n echo \"\"\n echo \" Resume instructions:\"\n echo \" The JEO workflow was in phase: $PHASE\"\n echo \" Check .omc/plans/jeo-plan.md for the approved plan\"\n fi\nelse\n warn \"No active JEO state (no workflow in progress)\"\n if $RESUME && command -v python3 >/dev/null 2>&1; then\n info \"Initializing fresh JEO state file for new session...\"\n mkdir -p \"$GIT_ROOT/.omc/state\" \"$GIT_ROOT/.omc/plans\"\n GIT_ROOT=\"$GIT_ROOT\" python3 - \u003c\u003c'PYEOF'\nimport json, datetime, os, uuid\nnow = datetime.datetime.utcnow().isoformat() + \"Z\"\ngit_root = os.environ[\"GIT_ROOT\"]\nstate = {\n \"mode\": \"jeo\",\n \"phase\": \"plan\",\n \"session_id\": str(uuid.uuid4()),\n \"task\": \"resumed session\",\n \"plan_approved\": False,\n \"plan_gate_status\": \"pending\",\n \"plan_current_hash\": None,\n \"last_reviewed_plan_hash\": None,\n \"last_reviewed_plan_at\": None,\n \"plan_review_method\": None,\n \"checkpoint\": None,\n \"last_error\": None,\n \"retry_count\": 0,\n \"team_available\": False,\n \"agentation\": {\n \"active\": False,\n \"session_id\": None,\n \"keyword_used\": None,\n \"submit_gate_status\": \"idle\",\n \"submit_signal\": None,\n \"submit_received_at\": None,\n \"submitted_annotation_count\": 0,\n \"started_at\": None,\n \"timeout_seconds\": 120,\n \"annotations\": {\"total\": 0, \"pending\": 0, \"acknowledged\": 0, \"resolved\": 0, \"dismissed\": 0},\n \"completed_at\": None,\n \"exit_reason\": None\n },\n \"created_at\": now,\n \"updated_at\": now\n}\nos.makedirs(os.path.join(git_root, \".omc\", \"state\"), exist_ok=True)\nos.makedirs(os.path.join(git_root, \".omc\", \"plans\"), exist_ok=True)\nstate_path = os.path.join(git_root, \".omc\", \"state\", \"jeo-state.json\")\nwith open(state_path, \"w\") as f:\n json.dump(state, f, indent=2)\nprint(f\"✓ Fresh JEO state initialized at {state_path} (phase: plan)\")\nPYEOF\n elif [[ -z \"${1:-}\" ]]; then\n echo \" Tip: Run with --resume to initialize state for a new session\"\n fi\nfi\n\n# Worktrees\nWORKTREE_COUNT=$(git worktree list 2>/dev/null | wc -l | tr -d ' ') || WORKTREE_COUNT=0\nif [[ \"$WORKTREE_COUNT\" -gt 1 ]]; then\n warn \"Active worktrees: $WORKTREE_COUNT (run worktree-cleanup.sh when done)\"\nelse\n ok \"No extra worktrees active\"\nfi\necho \"\"\n\n# ── Summary ────────────────────────────────────────────────────────────────────\necho \"══════════════════════════════════════════\"\necho -e \" ${GREEN}✓${NC} Passed: $PASS ${YELLOW}⚠${NC} Warnings: $WARN ${RED}✗${NC} Failed: $FAIL\"\necho \"══════════════════════════════════════════\"\necho \"\"\n\nif [[ $FAIL -gt 0 ]]; then\n echo \"Fix failures by running: bash scripts/install.sh --all\"\nelif [[ $WARN -gt 0 ]]; then\n echo \"Run platform setup scripts to resolve warnings:\"\n echo \" bash scripts/setup-claude.sh\"\n echo \" bash scripts/setup-codex.sh\"\n echo \" bash scripts/setup-gemini.sh\"\n echo \" bash scripts/setup-opencode.sh\"\nelse\n echo -e \"${GREEN}All checks passed! JEO is fully configured.${NC}\"\nfi\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":12275,"content_sha256":"0d3f409f0d2355ebec47aad7c92e6d6639a3e8966606c5dc06b7f5799fbec970"},{"filename":"scripts/claude-agentation-submit-hook.py","content":"#!/usr/bin/env python3\n\"\"\"JEO Claude UserPromptSubmit wrapper for agentation.\n\nOnly exposes pending annotations after an explicit submit-style prompt arrives\nduring the VERIFY_UI gate.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport os\nimport subprocess\nimport sys\nimport urllib.error\nimport urllib.request\nfrom pathlib import Path\nfrom typing import Any\n\n\nREADY_TOKENS = (\n \"annotate\",\n \"annotate_ready\",\n \"agentui\",\n \"ui검토\",\n \"send annotations\",\n \"submitted annotations\",\n \"elementpath\",\n)\n\n\ndef git_root() -> Path:\n try:\n return Path(\n subprocess.check_output(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n stderr=subprocess.DEVNULL,\n text=True,\n ).strip()\n )\n except Exception:\n return Path.cwd()\n\n\ndef state_path(root: Path) -> Path:\n return root / \".omc\" / \"state\" / \"jeo-state.json\"\n\n\ndef load_state(root: Path) -> dict[str, Any]:\n path = state_path(root)\n if not path.exists():\n return {}\n try:\n return json.loads(path.read_text(encoding=\"utf-8\"))\n except Exception:\n return {}\n\n\ndef save_state(root: Path, state: dict[str, Any]) -> None:\n path = state_path(root)\n if not path.parent.exists():\n path.parent.mkdir(parents=True, exist_ok=True)\n path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding=\"utf-8\")\n\n\ndef flatten_strings(node: Any) -> list[str]:\n parts: list[str] = []\n if isinstance(node, str):\n parts.append(node)\n elif isinstance(node, dict):\n for value in node.values():\n parts.extend(flatten_strings(value))\n elif isinstance(node, list):\n for value in node:\n parts.extend(flatten_strings(value))\n return parts\n\n\ndef submitted_prompt(payload: str) -> bool:\n try:\n data = json.loads(payload)\n except Exception:\n return False\n\n combined = \" \".join(flatten_strings(data)).lower()\n return any(token in combined for token in READY_TOKENS)\n\n\ndef fetch_pending() -> dict[str, Any] | None:\n try:\n with urllib.request.urlopen(\"http://localhost:4747/pending\", timeout=2) as response:\n return json.loads(response.read().decode(\"utf-8\"))\n except (urllib.error.URLError, TimeoutError, ValueError, json.JSONDecodeError):\n return None\n\n\ndef update_submit_gate(root: Path, state: dict[str, Any], count: int) -> None:\n agentation = state.setdefault(\"agentation\", {})\n agentation[\"submit_gate_status\"] = \"submitted\"\n agentation[\"submit_signal\"] = \"claude-user-prompt-submit\"\n agentation[\"submit_received_at\"] = subprocess.check_output(\n [\"python3\", \"-c\", \"import datetime;print(datetime.datetime.utcnow().isoformat()+\\\"Z\\\")\"],\n text=True,\n ).strip()\n agentation[\"submitted_annotation_count\"] = count\n save_state(root, state)\n\n\ndef main() -> int:\n payload = sys.stdin.read()\n root = git_root()\n state = load_state(root)\n\n if state.get(\"phase\") != \"verify_ui\":\n return 0\n if not submitted_prompt(payload):\n return 0\n\n pending = fetch_pending()\n if not pending:\n return 0\n\n count = int(pending.get(\"count\", 0) or 0)\n if count \u003c= 0:\n return 0\n\n update_submit_gate(root, state, count)\n\n print(f\"=== AGENTATION: {count} submitted UI annotations ===\")\n for index, annotation in enumerate(pending.get(\"annotations\", []), start=1):\n element = annotation.get(\"element\", \"?\")\n path = annotation.get(\"elementPath\", \"?\")\n comment = annotation.get(\"comment\", \"\")\n print(f\"[{index}] {element} ({path})\")\n if comment:\n print(f\" {comment}\")\n print(\"=== END ===\")\n return 0\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n","content_type":"text/x-python; charset=utf-8","language":"python","size":3784,"content_sha256":"58045c3cef395581b1d03d2cf9c3d289d50317a26cb0012b8074c800dc1338f2"},{"filename":"scripts/claude-plan-gate.py","content":"#!/usr/bin/env python3\n\"\"\"JEO Claude plan gate wrapper.\n\nWraps the Claude Code ExitPlanMode hook so JEO can skip redundant plannotator\nlaunches when the current plan content has already been reviewed.\n\nFixes applied:\n- Double execution: saves plannotator result to state so skip guard activates\n on subsequent ExitPlanMode hook invocations.\n- Page error: captures plannotator stdout so its approval JSON is NOT passed\n to Claude Code's hook system. Claude Code's PermissionRequest handler expects\n {\"decision\": \"allow\"} format; plannotator's {\"approved\": true, ...} format\n caused a hook result page/format error. emit_hook_decision() writes the\n correct format.\n- ralphmode transition: writes next_mode=\"ralphmode\" to state on approval so\n the JEO orchestration layer can invoke /omc:ralphmode after ExitPlanMode.\n- Concurrency: port probe + lockfile prevent duplicate plannotator launches\n when ExitPlanMode fires twice in quick succession.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport datetime\nimport hashlib\nimport json\nimport os\nimport socket\nimport subprocess\nimport sys\nimport tempfile\nfrom pathlib import Path\nfrom typing import Any, Optional\n\ntry:\n import fcntl\n _FCNTL_AVAILABLE = True\nexcept ImportError:\n _FCNTL_AVAILABLE = False\n\n\nSKIP_STATUSES = {\"approved\", \"manual_approved\", \"feedback_required\", \"infrastructure_blocked\"}\nPOST_PLAN_PHASES = {\"execute\", \"verify\", \"verify_ui\", \"cleanup\", \"done\", \"ralphmode\"}\nPLANNOTATOR_PORT = int(os.environ.get(\"PLANNOTATOR_PORT\", \"47291\"))\n_LOCK_FILE = Path(tempfile.gettempdir()) / \"jeo-plannotator-claude-gate.lock\"\n\n\ndef git_root() -> Path:\n try:\n return Path(\n subprocess.check_output(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n stderr=subprocess.DEVNULL,\n text=True,\n ).strip()\n )\n except Exception:\n return Path.cwd()\n\n\ndef state_path(root: Path) -> Path:\n return root / \".omc\" / \"state\" / \"jeo-state.json\"\n\n\ndef load_state(root: Path) -> dict[str, Any]:\n path = state_path(root)\n if not path.exists():\n return {}\n try:\n return json.loads(path.read_text(encoding=\"utf-8\"))\n except Exception:\n return {}\n\n\ndef save_state(root: Path, state: dict[str, Any]) -> None:\n path = state_path(root)\n if not path.parent.exists():\n path.parent.mkdir(parents=True, exist_ok=True)\n if _FCNTL_AVAILABLE:\n with open(path, \"w\", encoding=\"utf-8\") as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n json.dump(state, fh, ensure_ascii=False, indent=2)\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n else:\n path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding=\"utf-8\")\n\n\ndef find_plan_text(root: Path, payload: str) -> str:\n for candidate in (\n root / \".omc\" / \"plans\" / \"jeo-plan.md\",\n root / \"plan.md\",\n root / \"docs\" / \"plan.md\",\n ):\n if candidate.exists():\n try:\n return candidate.read_text(encoding=\"utf-8\")\n except Exception:\n continue\n\n try:\n data = json.loads(payload)\n except Exception:\n return \"\"\n\n tool_input = data.get(\"tool_input\", {})\n if isinstance(tool_input, dict):\n plan = tool_input.get(\"plan\")\n if isinstance(plan, str):\n return plan\n return \"\"\n\n\ndef plan_hash(plan_text: str) -> str:\n if not plan_text:\n return \"\"\n return hashlib.sha256(plan_text.encode(\"utf-8\")).hexdigest()\n\n\ndef should_skip(state: dict[str, Any], current_hash: str) -> bool:\n \"\"\"Return True if plannotator should NOT be launched.\n\n Skips when:\n - Phase has moved past plan (execute/verify/cleanup/done/ralphmode)\n - Same plan hash already has a terminal gate status (approved/feedback/blocked)\n \"\"\"\n phase = state.get(\"phase\")\n if phase in POST_PLAN_PHASES:\n return True\n\n gate_status = state.get(\"plan_gate_status\")\n last_hash = state.get(\"last_reviewed_plan_hash\")\n return bool(current_hash and gate_status in SKIP_STATUSES and last_hash == current_hash)\n\n\ndef reset_for_revised_plan(root: Path, state: dict[str, Any], current_hash: str) -> None:\n last_hash = state.get(\"last_reviewed_plan_hash\")\n if not current_hash or current_hash == last_hash:\n return\n\n if state.get(\"plan_gate_status\") in SKIP_STATUSES:\n state[\"plan_gate_status\"] = \"pending\"\n state[\"plan_approved\"] = False\n state[\"plan_current_hash\"] = current_hash\n # Reset stop-continuation guard so the new plan triggers continuation after approval\n state[\"_stop_continuation_triggered_hash\"] = \"\"\n state[\"updated_at\"] = datetime.datetime.utcnow().isoformat() + \"Z\"\n save_state(root, state)\n\n\ndef save_plannotator_feedback(root: Path, stdout: str, now: str) -> None:\n \"\"\"Persist plannotator feedback output so the Stop hook can pass it to Claude.\"\"\"\n if not stdout.strip():\n return\n feedback_path = root / \".omc\" / \"state\" / \"jeo-plannotator-feedback.json\"\n try:\n try:\n fb_data = json.loads(stdout)\n except Exception:\n fb_data = {\"raw\": stdout}\n feedback_text = fb_data.get(\"feedback\", fb_data.get(\"message\", \"\"))\n feedback_path.parent.mkdir(parents=True, exist_ok=True)\n feedback_path.write_text(\n json.dumps(\n {\"feedback\": feedback_text, \"raw\": fb_data, \"saved_at\": now},\n ensure_ascii=False,\n indent=2,\n ),\n encoding=\"utf-8\",\n )\n except Exception:\n pass\n\n\ndef update_state_after_plannotator(\n root: Path,\n state: dict[str, Any],\n rc: int,\n current_hash: str,\n stdout: str = \"\",\n) -> None:\n \"\"\"Persist plannotator result so the skip guard activates on next invocation.\n\n On approval (rc=0): writes next_mode=\"ralphmode\" so the JEO orchestration\n layer knows to invoke /omc:ralphmode after ExitPlanMode returns.\n The Stop hook (claude-stop-continuation.py) reads this state and injects\n a continuation instruction to Claude after ExitPlanMode completes.\n \"\"\"\n now = datetime.datetime.utcnow().isoformat() + \"Z\"\n\n if rc == 0:\n state[\"plan_gate_status\"] = \"approved\"\n state[\"plan_approved\"] = True\n state[\"phase\"] = \"execute\"\n state[\"next_mode\"] = \"ralphmode\"\n elif rc == 10:\n state[\"plan_gate_status\"] = \"feedback_required\"\n state[\"plan_approved\"] = False\n save_plannotator_feedback(root, stdout, now)\n\n state[\"last_reviewed_plan_hash\"] = current_hash\n state[\"plan_current_hash\"] = current_hash\n state[\"last_reviewed_plan_at\"] = now\n state[\"plan_review_method\"] = \"plannotator\"\n state[\"updated_at\"] = now\n save_state(root, state)\n\n\ndef is_plannotator_listening(port: int) -> bool:\n \"\"\"Return True if plannotator is already listening on the given port.\"\"\"\n try:\n s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n s.settimeout(0.5)\n s.connect((\"127.0.0.1\", port))\n s.close()\n return True\n except Exception:\n return False\n\n\ndef try_acquire_lock() -> Optional[Any]:\n \"\"\"Try to acquire an exclusive lock to prevent concurrent plannotator launches.\n\n Returns the file handle (truthy) on success, None if already locked.\n Caller must release via release_lock().\n \"\"\"\n if not _FCNTL_AVAILABLE:\n return object() # Non-Unix: skip locking, return truthy sentinel\n try:\n fh = open(_LOCK_FILE, \"w\")\n fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB)\n return fh\n except (IOError, OSError):\n return None\n\n\ndef release_lock(fh: Any) -> None:\n if not _FCNTL_AVAILABLE or fh is None:\n return\n try:\n fcntl.flock(fh, fcntl.LOCK_UN)\n fh.close()\n _LOCK_FILE.unlink(missing_ok=True)\n except Exception:\n pass\n\n\ndef run_plannotator(payload: str) -> tuple[int, str, str]:\n \"\"\"Run plannotator and return (exit_code, stdout, stderr).\n\n stdout is captured (not forwarded to this hook's stdout) so plannotator's\n approval JSON {\"approved\": true, ...} does not reach Claude Code's\n PermissionRequest handler. That handler expects {\"decision\": \"allow\"} format\n and would render a page/format error if it receives plannotator's raw output.\n emit_hook_decision() writes the correct PermissionRequest response instead.\n \"\"\"\n proc = subprocess.run(\n [\"plannotator\"],\n input=payload,\n text=True,\n capture_output=True,\n )\n return proc.returncode, proc.stdout, proc.stderr\n\n\ndef emit_hook_decision(decision: str, reason: str = \"\") -> None:\n \"\"\"Write a PermissionRequest decision JSON to stdout for Claude Code.\n\n Claude Code's hook system reads this from the hook's stdout to decide\n whether to allow or block ExitPlanMode. Must be the only stdout output.\n \"\"\"\n payload: dict[str, Any] = {\"decision\": decision}\n if reason:\n payload[\"reason\"] = reason\n print(json.dumps(payload))\n\n\ndef main() -> int:\n payload = sys.stdin.read()\n root = git_root()\n state = load_state(root)\n current_hash = plan_hash(find_plan_text(root, payload))\n\n if should_skip(state, current_hash):\n status = state.get(\"plan_gate_status\", \"unknown\")\n phase = state.get(\"phase\", \"unknown\")\n print(\n f\"[JEO][PLAN] Claude hook skipped: plan gate already recorded \"\n f\"(status={status}, phase={phase}).\",\n file=sys.stderr,\n )\n emit_hook_decision(\"allow\", f\"Skipped: plan already reviewed ({status})\")\n return 0\n\n # Guard 1: don't launch a second plannotator if one is already listening\n if is_plannotator_listening(PLANNOTATOR_PORT):\n print(\n f\"[JEO][PLAN] plannotator already listening on port {PLANNOTATOR_PORT} \"\n \"— deferring to existing instance.\",\n file=sys.stderr,\n )\n emit_hook_decision(\"allow\", \"Deferred: plannotator already running\")\n return 0\n\n # Guard 2: lockfile prevents two hook invocations racing to start plannotator\n lock_fh = try_acquire_lock()\n if lock_fh is None:\n print(\n \"[JEO][PLAN] Another plannotator launch is in progress — skipping duplicate.\",\n file=sys.stderr,\n )\n emit_hook_decision(\"allow\", \"Skipped: concurrent launch detected\")\n return 0\n\n try:\n reset_for_revised_plan(root, state, current_hash)\n rc, stdout, stderr = run_plannotator(payload)\n\n # Always persist result so the skip guard activates on the next ExitPlanMode.\n # Pass stdout so feedback is saved when rc=10 (feedback_required).\n update_state_after_plannotator(root, state, rc, current_hash, stdout=stdout)\n\n if rc == 0:\n emit_hook_decision(\n \"allow\",\n \"Plan approved by plannotator. \"\n \"JEO: next_mode=ralphmode written to state — invoke /omc:ralphmode to begin autonomous execution.\",\n )\n return 0\n elif rc == 10:\n emit_hook_decision(\n \"allow\",\n \"Plan review requested changes. \"\n \"Revise plan.md and re-enter plan mode to reopen plannotator.\",\n )\n return 0\n else:\n if stderr:\n print(f\"[JEO][PLAN] plannotator stderr: {stderr.strip()}\", file=sys.stderr)\n emit_hook_decision(\"allow\", f\"plannotator exited with code {rc}.\")\n return 0\n finally:\n release_lock(lock_fh)\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n","content_type":"text/x-python; charset=utf-8","language":"python","size":11666,"content_sha256":"551552a2c5cd48fb83e97cfc7ad020e0b41a40c82186fee77c4a08e2418848c4"},{"filename":"scripts/claude-stop-continuation.py","content":"#!/usr/bin/env python3\n\"\"\"JEO Claude Stop hook — auto-continues JEO workflow after plannotator review.\n\nFires on every Claude Code Stop event. Checks jeo-state.json for a pending\nplannotator result (approved or feedback_required) that hasn't been delivered\nto Claude yet, and blocks the stop to inject the continuation instruction.\n\nThis fixes the \"plannotator closes but JEO doesn't continue\" problem.\n\nHow it works:\n- ExitPlanMode fires → claude-plan-gate.py runs plannotator → stores result in jeo-state.json\n- claude-plan-gate.py emits {\"decision\":\"allow\"} — Claude exits plan mode\n- Claude generates a short \"plan mode exited\" response and is about to stop\n- THIS hook fires: sees plan_gate_status=approved, blocks stop with EXECUTE instruction\n- Claude reads the injected instruction and proceeds to STEP 2 (EXECUTE) immediately\n\nGuard: _stop_continuation_triggered_hash == last_reviewed_plan_hash prevents\nre-firing on subsequent stop events (once per plan review cycle).\n\"\"\"\n\nfrom __future__ import annotations\n\nimport datetime\nimport json\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom typing import Any\n\n\ndef git_root() -> Path:\n try:\n return Path(\n subprocess.check_output(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n stderr=subprocess.DEVNULL,\n text=True,\n ).strip()\n )\n except Exception:\n return Path.cwd()\n\n\ndef load_state(state_path: Path) -> dict[str, Any]:\n if not state_path.exists():\n return {}\n try:\n return json.loads(state_path.read_text(encoding=\"utf-8\"))\n except Exception:\n return {}\n\n\ndef save_state(state_path: Path, state: dict[str, Any]) -> None:\n try:\n state_path.parent.mkdir(parents=True, exist_ok=True)\n state_path.write_text(\n json.dumps(state, indent=2, ensure_ascii=False), encoding=\"utf-8\"\n )\n except Exception:\n pass\n\n\ndef load_feedback(root: Path) -> str:\n feedback_path = root / \".omc\" / \"state\" / \"jeo-plannotator-feedback.json\"\n if not feedback_path.exists():\n return \"\"\n try:\n fb = json.loads(feedback_path.read_text(encoding=\"utf-8\"))\n return fb.get(\"feedback\", fb.get(\"message\", \"\"))\n except Exception:\n return \"\"\n\n\ndef main() -> int:\n # Read stdin — Claude Code passes stop context including stop_hook_active flag\n try:\n stdin_raw = sys.stdin.read()\n stdin_data = json.loads(stdin_raw) if stdin_raw.strip() else {}\n except Exception:\n stdin_data = {}\n\n # Safety: prevent infinite stop-hook loops (Claude Code sets this when a\n # stop hook has already blocked once in this response cycle)\n if stdin_data.get(\"stop_hook_active\"):\n return 0\n\n root = git_root()\n state_path = root / \".omc\" / \"state\" / \"jeo-state.json\"\n state = load_state(state_path)\n\n if not state:\n return 0\n\n plan_gate_status = state.get(\"plan_gate_status\")\n phase = state.get(\"phase\")\n plan_hash = state.get(\"last_reviewed_plan_hash\") or \"\"\n triggered_hash = state.get(\"_stop_continuation_triggered_hash\") or \"\"\n\n # Guard: only fire once per plan review cycle (hash-based dedup)\n if plan_hash and triggered_hash == plan_hash:\n return 0\n\n now = datetime.datetime.utcnow().isoformat() + \"Z\"\n\n if plan_gate_status == \"approved\" and phase == \"execute\":\n # Mark as triggered so subsequent Stop events don't re-fire\n state[\"_stop_continuation_triggered_hash\"] = plan_hash\n state[\"updated_at\"] = now\n save_state(state_path, state)\n\n msg = (\n \"[JEO CONTINUATION] Plan approved by plannotator. \"\n \"jeo-state.json: plan_gate_status=approved, phase=execute, next_mode=ralphmode. \"\n \"PROCEED IMMEDIATELY to STEP 2 (EXECUTE) without waiting for user input. \"\n \"Check team availability (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS), \"\n \"then invoke /omc:team or ralphmode to begin implementation. \"\n \"Do NOT re-enter plan mode. Do NOT ask the user for confirmation.\"\n )\n print(json.dumps({\"decision\": \"block\", \"reason\": msg}))\n return 0\n\n if plan_gate_status == \"feedback_required\":\n # Mark as triggered\n state[\"_stop_continuation_triggered_hash\"] = plan_hash\n state[\"updated_at\"] = now\n save_state(state_path, state)\n\n feedback = load_feedback(root)\n feedback_part = f\" Feedback from reviewer: {feedback}.\" if feedback else \"\"\n\n msg = (\n \"[JEO CONTINUATION] plannotator requested plan changes. \"\n f\"jeo-state.json: plan_gate_status=feedback_required.{feedback_part} \"\n \"ACTION REQUIRED: Read .omc/state/jeo-plannotator-feedback.json for detailed feedback. \"\n \"Revise plan.md to address the feedback, then re-enter plan mode \"\n \"(EnterPlanMode → update plan → ExitPlanMode) to resubmit for review. \"\n \"Do NOT proceed to EXECUTE until plan is approved.\"\n )\n print(json.dumps({\"decision\": \"block\", \"reason\": msg}))\n return 0\n\n return 0\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n","content_type":"text/x-python; charset=utf-8","language":"python","size":5178,"content_sha256":"fb3bc6c86bcb819443318b745a518a8fab904d8565a6a32bba0cadac0d9ad1b7"},{"filename":"scripts/ensure-agentation.sh","content":"#!/usr/bin/env bash\n# JEO helper — ensures agentation npm package is installed and \u003cAgentation> is mounted\n# in the project's React entry point before VERIFY_UI runs.\n#\n# Usage: bash ensure-agentation.sh [--project-dir \u003cpath>] [--endpoint \u003curl>] [--quiet] [--dry-run]\n# Exit codes:\n# 0 — agentation ready (already installed, or just installed+injected)\n# 1 — could not install or inject (non-fatal: VERIFY_UI should graceful-skip)\n# 2 — package.json not found (not a Node.js project — skip silently)\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# ── defaults ──────────────────────────────────────────────────────────────────\nPROJECT_DIR=\"${PWD}\"\nENDPOINT=\"http://localhost:4747\"\nQUIET=false\nDRY_RUN=false\n\nfor arg in \"$@\"; do\n case \"$arg\" in\n --project-dir=*) PROJECT_DIR=\"${arg#*=}\" ;;\n --endpoint=*) ENDPOINT=\"${arg#*=}\" ;;\n --quiet) QUIET=true ;;\n --dry-run) DRY_RUN=true ;;\n -h|--help)\n echo \"Usage: bash ensure-agentation.sh [--project-dir \u003cpath>] [--endpoint \u003curl>] [--quiet] [--dry-run]\"\n echo \"Ensures agentation npm package is installed and \u003cAgentation> is mounted in the React entry point.\"\n exit 0\n ;;\n esac\ndone\n\nlog() { $QUIET || echo \"[JEO][ANNOTATE] $*\"; }\nwarn() { echo \"[JEO][ANNOTATE] ⚠️ $*\" >&2; }\n\n# ── locate package.json ───────────────────────────────────────────────────────\nfind_package_json() {\n local dir=\"$1\"\n # Check project dir and common sub-dirs\n for candidate in \"$dir\" \"$dir/src\" \"$dir/app\" \"$dir/frontend\" \"$dir/client\" \"$dir/web\"; do\n if [[ -f \"$candidate/package.json\" ]]; then\n echo \"$candidate\"\n return 0\n fi\n done\n return 1\n}\n\nPKG_DIR=\"\"\nif ! PKG_DIR=$(find_package_json \"$PROJECT_DIR\"); then\n log \"No package.json found — not a Node.js project, skipping agentation setup.\"\n exit 2\nfi\n\nlog \"Found package.json at: $PKG_DIR\"\n\n# ── check if agentation already in dependencies ───────────────────────────────\nis_package_installed() {\n python3 - \"$PKG_DIR/package.json\" \u003c\u003c'EOF'\nimport sys, json\ntry:\n d = json.load(open(sys.argv[1]))\n deps = {**d.get('dependencies', {}), **d.get('devDependencies', {})}\n sys.exit(0 if 'agentation' in deps else 1)\nexcept Exception:\n sys.exit(1)\nEOF\n}\n\n# ── detect package manager ────────────────────────────────────────────────────\ndetect_package_manager() {\n if [[ -f \"$PKG_DIR/bun.lockb\" ]] || [[ -f \"$PKG_DIR/bun.lock\" ]]; then\n echo \"bun\"\n elif [[ -f \"$PKG_DIR/pnpm-lock.yaml\" ]]; then\n echo \"pnpm\"\n elif [[ -f \"$PKG_DIR/yarn.lock\" ]]; then\n echo \"yarn\"\n else\n echo \"npm\"\n fi\n}\n\n# ── install agentation package ────────────────────────────────────────────────\ninstall_agentation() {\n local pm=\"$1\"\n log \"Installing agentation with $pm...\"\n if $DRY_RUN; then\n log \"[dry-run] would run: cd $PKG_DIR && $pm add agentation -D (or equivalent)\"\n return 0\n fi\n\n case \"$pm\" in\n bun) (cd \"$PKG_DIR\" && bun add agentation -D) ;;\n pnpm) (cd \"$PKG_DIR\" && pnpm add agentation -D) ;;\n yarn) (cd \"$PKG_DIR\" && yarn add agentation --dev) ;;\n npm) (cd \"$PKG_DIR\" && npm install agentation --save-dev --legacy-peer-deps) ;;\n esac\n}\n\n# ── find React entry point ────────────────────────────────────────────────────\nfind_entry_point() {\n local dir=\"$PROJECT_DIR\"\n local candidates=(\n # Vite / CRA\n \"$dir/src/main.tsx\"\n \"$dir/src/main.jsx\"\n \"$dir/main.tsx\"\n \"$dir/main.jsx\"\n # Next.js App Router\n \"$dir/app/layout.tsx\"\n \"$dir/app/layout.jsx\"\n \"$dir/src/app/layout.tsx\"\n \"$dir/src/app/layout.jsx\"\n # Next.js Pages Router\n \"$dir/pages/_app.tsx\"\n \"$dir/pages/_app.jsx\"\n \"$dir/src/pages/_app.tsx\"\n \"$dir/src/pages/_app.jsx\"\n )\n for f in \"${candidates[@]}\"; do\n if [[ -f \"$f\" ]]; then\n echo \"$f\"\n return 0\n fi\n done\n return 1\n}\n\n# ── inject \u003cAgentation> into entry point ─────────────────────────────────────\ninject_agentation() {\n local entry=\"$1\"\n local endpoint=\"$2\"\n\n # Already injected?\n if grep -q 'from.*agentation' \"$entry\" 2>/dev/null; then\n log \"\u003cAgentation> already imported in $entry\"\n return 0\n fi\n\n log \"Injecting \u003cAgentation endpoint=\\\"$endpoint\\\" /> into $entry\"\n\n if $DRY_RUN; then\n log \"[dry-run] would inject into $entry\"\n return 0\n fi\n\n # Detect framework pattern from filename\n local filename\n filename=\"$(basename \"$entry\")\"\n local framework=\"vite\"\n [[ \"$filename\" == \"layout.tsx\" || \"$filename\" == \"layout.jsx\" ]] && framework=\"next-app\"\n [[ \"$filename\" == \"_app.tsx\" || \"$filename\" == \"_app.jsx\" ]] && framework=\"next-pages\"\n\n python3 - \"$entry\" \"$endpoint\" \"$framework\" \u003c\u003c'PYEOF'\nimport sys, re\n\nentry_path = sys.argv[1]\nendpoint = sys.argv[2]\nframework = sys.argv[3]\n\nwith open(entry_path, 'r', encoding='utf-8') as f:\n content = f.read()\n\nIMPORT_LINE = \"import { Agentation } from 'agentation';\"\nCOMPONENT_DEV = f\"{{process.env.NODE_ENV === 'development' && \u003cAgentation endpoint=\\\"{endpoint}\\\" />}}\"\n\n# Already injected guard (double-check)\nif 'agentation' in content.lower():\n print(f\"[JEO][ANNOTATE] agentation already present in {entry_path}\", file=sys.stderr)\n sys.exit(0)\n\n# --- Add import line ---\n# Insert after the last existing import statement\nimport_match = list(re.finditer(r'^import\\s.+;?\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, content, re.MULTILINE))\nif import_match:\n last_import = import_match[-1]\n insert_pos = last_import.end()\n content = content[:insert_pos] + '\\n' + IMPORT_LINE + content[insert_pos:]\nelse:\n # No imports found; prepend\n content = IMPORT_LINE + '\\n' + content\n\n# --- Inject component ---\nif framework == 'next-app':\n # Next.js App Router layout.tsx: inject before closing \u003c/body> or \u003c/html>, or before return closing\n if '\u003c/body>' in content:\n content = content.replace('\u003c/body>', f' {COMPONENT_DEV}\\n \u003c/body>', 1)\n elif 'return (' in content:\n # Inject before last closing paren of return block\n last_paren = content.rfind('\\n)')\n if last_paren >= 0:\n content = content[:last_paren] + f'\\n {COMPONENT_DEV}' + content[last_paren:]\nelif framework == 'next-pages':\n # Next.js Pages _app: inject inside Component rendering\n if '\u003cComponent' in content:\n content = re.sub(\n r'(\u003cComponent\\s[^/]*/?>)',\n r'\\1\\n ' + COMPONENT_DEV,\n content,\n count=1\n )\n elif 'return (' in content:\n last_paren = content.rfind('\\n )')\n if last_paren >= 0:\n content = content[:last_paren] + f'\\n {COMPONENT_DEV}' + content[last_paren:]\nelse:\n # Vite / CRA: inject inside the root render call\n # Strategy 1: inject before ReactDOM.createRoot closing render call's last closing tag\n # Look for \u003c/StrictMode>, \u003c/React.StrictMode>, or the outermost JSX closing tag\n strict_match = re.search(r'(\u003c/(?:React\\.)?StrictMode>)', content)\n if strict_match:\n pos = strict_match.start()\n content = content[:pos] + f' {COMPONENT_DEV}\\n ' + content[pos:]\n else:\n # Strategy 2: find render(\u003c...>) and inject inside\n render_match = re.search(r'(\\.render\\s*\\(\\s*\u003c)([^)]+?)(\\s*>\\s*\\))', content, re.DOTALL)\n if render_match:\n inner = render_match.group(2)\n new_inner = inner + f'\\n {COMPONENT_DEV}'\n content = content[:render_match.start(2)] + new_inner + content[render_match.end(2):]\n\nwith open(entry_path, 'w', encoding='utf-8') as f:\n f.write(content)\n\nprint(f\"[JEO][ANNOTATE] ✅ Injected \u003cAgentation endpoint=\\\"{endpoint}\\\" /> into {entry_path}\")\nPYEOF\n}\n\n# ── main ──────────────────────────────────────────────────────────────────────\nmain() {\n local pm\n pm=$(detect_package_manager)\n log \"Detected package manager: $pm\"\n\n # 1. Install package if needed\n if is_package_installed; then\n log \"agentation package already in $PKG_DIR/package.json\"\n else\n log \"agentation package not found — installing...\"\n if ! install_agentation \"$pm\"; then\n warn \"Failed to install agentation package. Run manually: cd $PKG_DIR && $pm add agentation -D\"\n exit 1\n fi\n log \"agentation package installed successfully.\"\n fi\n\n # 2. Find entry point and inject component\n local entry=\"\"\n if ! entry=$(find_entry_point); then\n warn \"Could not find React entry point (main.jsx, app/layout.tsx, pages/_app.tsx). Mount \u003cAgentation endpoint=\\\"$ENDPOINT\\\" /> manually.\"\n exit 1\n fi\n\n inject_agentation \"$entry\" \"$ENDPOINT\"\n log \"Entry point: $entry — injection complete.\"\n exit 0\n}\n\nmain\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":9376,"content_sha256":"075236580a4cdb62fd116a6cf7596b284f6629c106e0cbd75e0dac8485dc7abe"},{"filename":"scripts/ensure-plannotator.sh","content":"#!/usr/bin/env bash\n# JEO helper — guarantees plannotator is available before PLAN.\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\nSKILLS_ROOT=\"$(dirname \"$SKILL_DIR\")\"\n\nQUIET=false\n\nfor arg in \"$@\"; do\n case \"$arg\" in\n --quiet) QUIET=true ;;\n -h|--help)\n echo \"Usage: bash ensure-plannotator.sh [--quiet]\"\n echo \"Ensures the plannotator CLI is installed before JEO PLAN runs.\"\n exit 0\n ;;\n esac\ndone\n\nlog() {\n if ! $QUIET; then\n echo \"$@\"\n fi\n}\n\nexport PATH=\"$HOME/.local/bin:$HOME/bin:$PATH\"\n\nif command -v plannotator >/dev/null 2>&1; then\n log \"[JEO][PLAN] plannotator already available: $(command -v plannotator)\"\n exit 0\nfi\n\nif [[ \"${JEO_SKIP_PLANNOTATOR_AUTO_INSTALL:-0}\" == \"1\" ]]; then\n echo \"[JEO][PLAN] plannotator not found and auto-install is disabled.\" >&2\n exit 127\nfi\n\nINSTALL_CANDIDATES=(\n \"$SKILLS_ROOT/plannotator/scripts/install.sh\"\n \"$HOME/.agent-skills/plannotator/scripts/install.sh\"\n \"$HOME/.codex/skills/plannotator/scripts/install.sh\"\n)\n\nfor candidate in \"${INSTALL_CANDIDATES[@]}\"; do\n if [[ ! -f \"$candidate\" ]]; then\n continue\n fi\n\n log \"[JEO][PLAN] plannotator missing. Installing via $candidate\"\n if bash \"$candidate\"; then\n export PATH=\"$HOME/.local/bin:$HOME/bin:$PATH\"\n if command -v plannotator >/dev/null 2>&1; then\n log \"[JEO][PLAN] plannotator installation completed: $(command -v plannotator)\"\n exit 0\n fi\n fi\n\n echo \"[JEO][PLAN] installer finished but plannotator is still unavailable: $candidate\" >&2\ndone\n\necho \"[JEO][PLAN] plannotator auto-install failed. Run: bash scripts/install.sh --with-plannotator\" >&2\nexit 127\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1700,"content_sha256":"b71ef6af502dcc97d5b199bf250d442d9018f5eefd6cc2df1f2c71d85d84f14f"},{"filename":"scripts/install.sh","content":"#!/usr/bin/env bash\n# JEO Skill — Master Installation Script\n# Installs and configures: ralph, omc, omx, ohmg, bmad, agent-browser, playwriter, plannotator, agentation\n# Usage: bash install.sh [--all] [--with-omc] [--with-plannotator] [--with-browser] [--with-agentation] [--dry-run]\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\nSKILLS_ROOT=\"$(dirname \"$SKILL_DIR\")\"\n\n# Colors\nRED='\\033[0;31m'; GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; BLUE='\\033[0;34m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\nerr() { echo -e \"${RED}✗${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false\nINSTALL_ALL=false\nINSTALL_OMC=false\nINSTALL_PLANNOTATOR=false\nINSTALL_AGENTATION=false\nINSTALL_BROWSER=false\nINSTALL_BMAD=false\nINSTALL_OMX=false\nINSTALL_OHMG=false\n\nfor arg in \"$@\"; do\n case $arg in\n --all) INSTALL_ALL=true ;;\n --with-omc) INSTALL_OMC=true ;;\n --with-plannotator) INSTALL_PLANNOTATOR=true ;;\n --with-browser) INSTALL_BROWSER=true ;;\n --with-bmad) INSTALL_BMAD=true ;;\n --with-omx) INSTALL_OMX=true ;;\n --with-ohmg) INSTALL_OHMG=true ;;\n --with-agentation) INSTALL_AGENTATION=true ;;\n --dry-run) DRY_RUN=true ;;\n -h|--help)\n echo \"JEO Master Installer\"\n echo \"Usage: bash install.sh [options]\"\n echo \"Options:\"\n echo \" --all Install all components\"\n echo \" --with-omc Install oh-my-claudecode (Claude Code)\"\n echo \" --with-plannotator Install plannotator CLI\"\n echo \" --with-browser Install agent-browser + playwriter\"\n echo \" --with-bmad Install BMAD orchestrator\"\n echo \" --with-omx Install omx (OpenCode multi-agent)\"\n echo \" --with-ohmg Install ohmg (Gemini multi-agent)\"\n echo \" --with-agentation Install agentation MCP (UI annotation \\u2194 agent)\"\n echo \" --dry-run Preview without executing\"\n exit 0\n ;;\n esac\ndone\n\nif $INSTALL_ALL; then\n INSTALL_OMC=true; INSTALL_PLANNOTATOR=true\n INSTALL_BROWSER=true; INSTALL_BMAD=true; INSTALL_OMX=true; INSTALL_OHMG=true; INSTALL_AGENTATION=true\nfi\n\necho \"\"\necho \"╔══════════════════════════════════════════╗\"\necho \"║ JEO Skill — Integrated Orchestration ║\"\necho \"║ Version 1.1.0 ║\"\necho \"╚══════════════════════════════════════════╝\"\necho \"\"\n\nrun() {\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} $*\"\n else\n eval \"$@\"\n fi\n}\n\n# ── Detect OS ─────────────────────────────────────────────────────────────────\nOS=\"unknown\"\nif [[ \"$OSTYPE\" == \"darwin\"* ]]; then OS=\"macos\"\nelif [[ \"$OSTYPE\" == \"linux\"* ]]; then OS=\"linux\"\nelif [[ \"$OSTYPE\" == \"msys\"* ]] || [[ \"$OSTYPE\" == \"cygwin\"* ]]; then OS=\"windows\"\nfi\ninfo \"Detected OS: $OS\"\n\n# ── Check prerequisites ────────────────────────────────────────────────────────\ninfo \"Checking prerequisites...\"\nMISSING_DEPS=()\nif command -v node >/dev/null 2>&1; then\n NODE_VER=$(node --version 2>/dev/null | grep -oE '[0-9]+' | head -1)\n if [[ -z \"$NODE_VER\" ]] || [[ \"$NODE_VER\" -lt 18 ]]; then\n MISSING_DEPS+=(\"node >=18 (현재: $(node --version 2>/dev/null || echo 'unknown'))\")\n fi\nelse\n MISSING_DEPS+=(\"node >=18\")\nfi\ncommand -v npm >/dev/null 2>&1 || MISSING_DEPS+=(\"npm\")\ncommand -v git >/dev/null 2>&1 || MISSING_DEPS+=(\"git\")\ncommand -v bash >/dev/null 2>&1 || MISSING_DEPS+=(\"bash\")\n\nif [[ ${#MISSING_DEPS[@]} -gt 0 ]]; then\n err \"Missing required dependencies: ${MISSING_DEPS[*]}\"\n echo \"Install them first, then re-run this script.\"\n exit 1\nfi\nok \"Prerequisites satisfied\"\n\n# ── 1. omc (oh-my-claudecode) ──────────────────────────────────────────────────\nif $INSTALL_OMC; then\n echo \"\"\n info \"Installing omc (oh-my-claudecode)...\"\n if command -v claude >/dev/null 2>&1; then\n echo \" Run these commands inside Claude Code:\"\n echo \" /plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode\"\n echo \" /plugin install oh-my-claudecode\"\n echo \" /omc:omc-setup\"\n ok \"omc install instructions shown (Claude Code required)\"\n else\n warn \"claude CLI not found — install Claude Code first, then run omc setup manually\"\n fi\nfi\n\n# ── 2. omx (oh-my-opencode) ───────────────────────────────────────────────────\nif $INSTALL_OMX; then\n echo \"\"\n info \"Installing omx (oh-my-opencode)...\"\n if command -v bun >/dev/null 2>&1; then\n run \"bunx oh-my-opencode setup 2>/dev/null || true\"\n ok \"omx (oh-my-opencode) configured\"\n elif command -v npx >/dev/null 2>&1; then\n run \"npx oh-my-opencode setup 2>/dev/null || true\"\n ok \"omx configured via npx\"\n else\n warn \"bun/npx not found — install bun first: curl -fsSL https://bun.sh/install | bash\"\n fi\nfi\n\n# ── 3. ohmg (oh-my-ag / Gemini) ───────────────────────────────────────────────\nif $INSTALL_OHMG; then\n echo \"\"\n info \"Installing ohmg (oh-my-ag for Gemini CLI)...\"\n if command -v bun >/dev/null 2>&1; then\n run \"bunx oh-my-ag 2>/dev/null || true\"\n ok \"ohmg configured\"\n else\n warn \"bun not found — install bun first: curl -fsSL https://bun.sh/install | bash\"\n fi\nfi\n\n# ── 4. plannotator ─────────────────────────────────────────────────────────────\nif $INSTALL_PLANNOTATOR; then\n echo \"\"\n info \"Installing plannotator...\"\n PLANNOTATOR_INSTALL=\"$SKILLS_ROOT/plannotator/scripts/install.sh\"\n PLANNOTATOR_INSTALLED=false\n if [[ -f \"$PLANNOTATOR_INSTALL\" ]]; then\n if run \"bash '$PLANNOTATOR_INSTALL' --all\"; then\n PLANNOTATOR_INSTALLED=true\n ok \"plannotator installed via skills script\"\n else\n err \"plannotator install script failed: $PLANNOTATOR_INSTALL\"\n fi\n else\n # Fallback: direct install\n if run \"curl -fsSL https://plannotator.ai/install.sh | bash\"; then\n PLANNOTATOR_INSTALLED=true\n ok \"plannotator installed\"\n else\n err \"plannotator install failed — check https://plannotator.ai\"\n fi\n fi\n\n export PATH=\"$HOME/.local/bin:$HOME/bin:$PATH\"\n if ! $PLANNOTATOR_INSTALLED || ! command -v plannotator >/dev/null 2>&1; then\n err \"plannotator is still unavailable after install attempt\"\n exit 1\n fi\nfi\n\n# ── 5. agent-browser ──────────────────────────────────────────────────────────\nif $INSTALL_BROWSER; then\n echo \"\"\n info \"Installing agent-browser...\"\n if command -v npm >/dev/null 2>&1; then\n run \"npm install -g agent-browser 2>/dev/null || npx agent-browser --version 2>/dev/null || true\"\n ok \"agent-browser installed\"\n else\n warn \"npm not found\"\n fi\n\n echo \"\"\n info \"Installing playwriter...\"\n if command -v npm >/dev/null 2>&1; then\n run \"npm install -g playwriter 2>/dev/null || true\"\n ok \"playwriter installed\"\n else\n warn \"npm not found\"\n fi\nfi\n\n# ── 7. bmad ───────────────────────────────────────────────────────────────────\nif $INSTALL_BMAD; then\n echo \"\"\n info \"Configuring BMAD orchestrator...\"\n BMAD_SKILL=\"$SKILLS_ROOT/bmad-orchestrator/SKILL.md\"\n if [[ -f \"$BMAD_SKILL\" ]]; then\n ok \"BMAD skill available at: $BMAD_SKILL\"\n else\n warn \"BMAD skill not found — ensure skills-template is properly installed\"\n fi\nfi\n\n# ── 8. agentation MCP ────────────────────────────────────────────────────────────────────────────\nif $INSTALL_AGENTATION; then\n echo \"\"\n info \"Installing agentation MCP...\"\n if command -v npx >/dev/null 2>&1; then\n run \"npx -y agentation-mcp doctor 2>/dev/null || npx -y agentation-mcp --version 2>/dev/null || true\"\n ok \"agentation-mcp (server) available via npx\"\n info \"Start server: npx agentation-mcp server\"\n info \"React component: auto-installed + injected by ensure-agentation.sh at VERIFY_UI time\"\n info \"Manual install: cd \u003cyour-app> && npm install agentation --save-dev\"\n info \"Manual inject: import { Agentation } from 'agentation'; \u003cAgentation endpoint=\\\"http://localhost:4747\\\" />\"\n else\n warn \"npx not found — install Node.js first\"\n fi\nfi\n\n# ── 9. Setup platform integrations ────────────────────────────────────────────────────────────────────────────\nif $INSTALL_ALL; then\n echo \"\"\n info \"Setting up platform integrations...\"\n run \"bash '$SCRIPT_DIR/setup-claude.sh' 2>/dev/null || true\"\n run \"bash '$SCRIPT_DIR/setup-codex.sh' 2>/dev/null || true\"\n run \"bash '$SCRIPT_DIR/setup-gemini.sh' 2>/dev/null || true\"\n run \"bash '$SCRIPT_DIR/setup-opencode.sh' 2>/dev/null || true\"\nfi\n\n# ── Done ──────────────────────────────────────────────────────────────────────\necho \"\"\necho \"╔══════════════════════════════════════════╗\"\necho \"║ JEO Installation Complete! ║\"\necho \"╚══════════════════════════════════════════╝\"\necho \"\"\nok \"JEO skill installed successfully\"\necho \"\"\necho \"Next steps:\"\necho \" 1. bash scripts/check-status.sh — Verify all integrations\"\necho \" 2. Restart your AI tools (Claude Code, Gemini CLI, OpenCode, Codex)\"\necho \" 3. Use keyword 'jeo' to activate the orchestration workflow\"\necho \" 4. Use keyword 'annotate' inside jeo to start agentation watch loop (agentui is a deprecated alias)\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":10814,"content_sha256":"a5c75e7d550d1952e05e0e9559393a9753ac7a7385193625daeec8bb85f38cb8"},{"filename":"scripts/plannotator-plan-loop.sh","content":"#!/usr/bin/env bash\n# JEO PLAN gate for plannotator.\n# Guarantees blocking review, retries dead sessions, and requires explicit stop decision.\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n# Cross-platform temp dir: respects TMPDIR (macOS/Linux) TMP/TEMP (Windows Git Bash)\n_TMPDIR=\"${TMPDIR:-${TMP:-${TEMP:-/tmp}}}\"\nPLAN_FILE=\"${1:-plan.md}\"\nFEEDBACK_FILE=\"${2:-}\"\nMAX_RESTARTS=\"${3:-3}\"\n# Dedicated port for plannotator (IANA unassigned — rarely conflicts with other services).\n# Override with: PLANNOTATOR_PORT=XXXXX bash plannotator-plan-loop.sh ...\nPLANNOTATOR_PORT=\"${PLANNOTATOR_PORT:-47291}\"\n# Seconds to wait for plannotator to bind its port after launch (startup detection).\nPLANNOTATOR_START_TIMEOUT=\"${PLANNOTATOR_START_TIMEOUT:-15}\"\nPORT_ERROR_REGEX='Failed to start server\\. Is port .* in use|EADDRINUSE|EPERM|operation not permitted|Failed to listen'\n\nif ! command -v plannotator >/dev/null 2>&1; then\n if ! bash \"$SCRIPT_DIR/ensure-plannotator.sh\" --quiet; then\n echo \"[JEO][PLAN] plannotator is required in PLAN phase.\" >&2\n exit 127\n fi\nfi\n\nexport PATH=\"$HOME/.local/bin:$HOME/bin:$PATH\"\n\nif [[ ! -f \"$PLAN_FILE\" ]]; then\n echo \"[JEO][PLAN] plan file not found: $PLAN_FILE\" >&2\n exit 2\nfi\n\nif ! [[ \"$MAX_RESTARTS\" =~ ^[0-9]+$ ]] || [[ \"$MAX_RESTARTS\" -lt 1 ]]; then\n echo \"[JEO][PLAN] invalid MAX_RESTARTS: $MAX_RESTARTS\" >&2\n exit 2\nfi\n\nPLAN_HASH=\"$(python3 - \"$PLAN_FILE\" \u003c\u003c'PYEOF'\nimport hashlib, pathlib, sys\npath = pathlib.Path(sys.argv[1])\ntry:\n data = path.read_text(encoding=\"utf-8\")\nexcept Exception:\n data = \"\"\nprint(hashlib.sha256(data.encode(\"utf-8\")).hexdigest() if data else \"\")\nPYEOF\n)\"\n\nSESSION_KEY=\"$(python3 -c \"import hashlib,os; print(hashlib.md5(os.getcwd().encode()).hexdigest()[:8])\" 2>/dev/null || echo \"default\")\"\nFEEDBACK_DIR=\"${_TMPDIR}/jeo-${SESSION_KEY}\"\nRUNTIME_HOME=\"${FEEDBACK_DIR}/.plannotator\"\nmkdir -p \"$FEEDBACK_DIR\" \"$RUNTIME_HOME\"\n\nif [[ -z \"$FEEDBACK_FILE\" ]]; then\n FEEDBACK_FILE=\"${FEEDBACK_DIR}/plannotator_feedback.txt\"\nelse\n mkdir -p \"$(dirname \"$FEEDBACK_FILE\")\"\nfi\n\nwrite_manual_feedback_json() {\n local approved=\"$1\"\n local note=\"${2:-}\"\n python3 - \"$FEEDBACK_FILE\" \"$approved\" \"$note\" \u003c\u003c'PYEOF'\nimport json, sys\npath, approved_raw, note = sys.argv[1], sys.argv[2], sys.argv[3]\napproved = approved_raw.lower() == \"true\"\npayload = {\n \"approved\": approved,\n \"source\": \"jeo-manual-fallback\",\n \"note\": note,\n}\nwith open(path, \"w\", encoding=\"utf-8\") as f:\n json.dump(payload, f, ensure_ascii=False, indent=2)\nPYEOF\n}\n\nwrite_state_gate_status() {\n local status=\"$1\"\n JEO_GATE_STATUS=\"$status\" python3 -c \"\nimport json, os, subprocess, datetime\ntry:\n root = subprocess.check_output(['git','rev-parse','--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()\nexcept Exception:\n root = os.getcwd()\nf = os.path.join(root, '.omc/state/jeo-state.json')\nif os.path.exists(f):\n try:\n import fcntl\n with open(f, 'r+') as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n d = json.load(fh)\n d['plan_gate_status'] = os.environ['JEO_GATE_STATUS']\n d['updated_at'] = datetime.datetime.utcnow().isoformat() + 'Z'\n fh.seek(0); json.dump(d, fh, indent=2); fh.truncate()\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n except Exception:\n pass\n\" 2>/dev/null || true\n}\n\npersist_plan_state() {\n local gate_status=\"$1\"\n local approved=\"$2\"\n local review_method=\"${3:-plannotator}\"\n local feedback_path=\"${4:-}\"\n JEO_GATE_STATUS=\"$gate_status\" \\\n JEO_APPROVED=\"$approved\" \\\n JEO_REVIEW_METHOD=\"$review_method\" \\\n JEO_PLAN_HASH=\"$PLAN_HASH\" \\\n JEO_FEEDBACK_FILE=\"$feedback_path\" \\\n python3 - \u003c\u003c'PYEOF'\nimport datetime\nimport json\nimport os\nimport subprocess\n\ntry:\n root = subprocess.check_output(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n stderr=subprocess.DEVNULL,\n text=True,\n ).strip()\nexcept Exception:\n root = os.getcwd()\n\nstate_path = os.path.join(root, \".omc\", \"state\", \"jeo-state.json\")\nif not os.path.exists(state_path):\n raise SystemExit(0)\n\ntry:\n import fcntl\nexcept Exception:\n fcntl = None\n\nfeedback_payload = None\nfeedback_file = os.environ.get(\"JEO_FEEDBACK_FILE\", \"\")\nif feedback_file and os.path.exists(feedback_file):\n try:\n feedback_payload = json.load(open(feedback_file))\n except Exception:\n feedback_payload = None\n\nwith open(state_path, \"r+\", encoding=\"utf-8\") as fh:\n if fcntl:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n state = json.load(fh)\n gate_status = os.environ.get(\"JEO_GATE_STATUS\", \"pending\")\n approved = os.environ.get(\"JEO_APPROVED\", \"false\").lower() == \"true\"\n review_method = os.environ.get(\"JEO_REVIEW_METHOD\", \"plannotator\")\n plan_hash = os.environ.get(\"JEO_PLAN_HASH\", \"\")\n state[\"plan_gate_status\"] = gate_status\n state[\"plan_approved\"] = approved\n state[\"plan_review_method\"] = review_method\n state[\"plan_current_hash\"] = plan_hash\n state[\"last_reviewed_plan_hash\"] = plan_hash\n state[\"last_reviewed_plan_at\"] = datetime.datetime.utcnow().isoformat() + \"Z\"\n if approved:\n state[\"phase\"] = \"execute\"\n elif gate_status == \"feedback_required\" and feedback_payload is not None:\n state[\"plannotator_feedback\"] = feedback_payload\n state[\"updated_at\"] = datetime.datetime.utcnow().isoformat() + \"Z\"\n fh.seek(0)\n json.dump(state, fh, ensure_ascii=False, indent=2)\n fh.truncate()\n finally:\n if fcntl:\n fcntl.flock(fh, fcntl.LOCK_UN)\nPYEOF\n}\n\nprepare_state_for_plan_hash() {\n JEO_PLAN_HASH=\"$PLAN_HASH\" python3 - \u003c\u003c'PYEOF'\nimport datetime\nimport json\nimport os\nimport subprocess\nimport sys\n\ntry:\n root = subprocess.check_output(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n stderr=subprocess.DEVNULL,\n text=True,\n ).strip()\nexcept Exception:\n root = os.getcwd()\n\nstate_path = os.path.join(root, \".omc\", \"state\", \"jeo-state.json\")\nif not os.path.exists(state_path):\n raise SystemExit(0)\n\nstate = json.load(open(state_path, encoding=\"utf-8\"))\ncurrent_hash = os.environ.get(\"JEO_PLAN_HASH\", \"\")\nlast_hash = state.get(\"last_reviewed_plan_hash\")\ngate_status = state.get(\"plan_gate_status\", \"pending\")\n\nif current_hash and last_hash == current_hash and gate_status in {\"approved\", \"manual_approved\"}:\n print(\"SKIP_APPROVED\")\n raise SystemExit(0)\nif current_hash and last_hash == current_hash and gate_status == \"feedback_required\":\n print(\"SKIP_FEEDBACK\")\n raise SystemExit(0)\nif current_hash and last_hash == current_hash and gate_status == \"infrastructure_blocked\":\n print(\"SKIP_BLOCKED\")\n raise SystemExit(0)\n\nstate[\"plan_current_hash\"] = current_hash\nif current_hash and last_hash and current_hash != last_hash and gate_status != \"pending\":\n state[\"plan_gate_status\"] = \"pending\"\n state[\"plan_approved\"] = False\n state[\"updated_at\"] = datetime.datetime.utcnow().isoformat() + \"Z\"\n with open(state_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(state, f, ensure_ascii=False, indent=2)\n print(\"RESET_FOR_NEW_HASH\")\nPYEOF\n}\n\nSTATE_PLAN_GUARD=\"$(prepare_state_for_plan_hash 2>/dev/null || true)\"\ncase \"$STATE_PLAN_GUARD\" in\n SKIP_APPROVED)\n echo \"[JEO][PLAN] plan gate already approved for current plan hash — skipping re-entry.\" >&2\n exit 0\n ;;\n SKIP_FEEDBACK)\n echo \"[JEO][PLAN] feedback already recorded for current plan hash — revise the plan before re-opening plannotator.\" >&2\n exit 10\n ;;\n SKIP_BLOCKED)\n echo \"[JEO][PLAN] infrastructure-blocked state already recorded for current plan hash — waiting for manual approval path.\" >&2\n exit 32\n ;;\n RESET_FOR_NEW_HASH)\n echo \"[JEO][PLAN] detected revised plan content — resetting gate status to pending.\" >&2\n ;;\nesac\n\nmanual_fallback_gate() {\n if [[ ! -t 0 || ! -t 1 ]]; then\n return 32\n fi\n\n echo \"[JEO][PLAN] plannotator UI를 열 수 없는 환경입니다. 수동 PLAN gate로 전환합니다.\" >&2\n echo \"[JEO][PLAN] 선택: [a]pprove / [f]eedback / [s]top\" >&2\n read -r -p \"선택하세요 [a/f/s]: \" choice\n\n case \"${choice,,}\" in\n a|approve)\n write_manual_feedback_json \"true\" \"manual-approve (fallback gate)\"\n persist_plan_state \"manual_approved\" \"true\" \"manual\" \"$FEEDBACK_FILE\"\n echo \"[JEO][PLAN] manual approved=true\" >&2\n return 0\n ;;\n f|feedback)\n read -r -p \"피드백 내용을 입력하세요: \" fb\n write_manual_feedback_json \"false\" \"${fb:-manual-feedback (fallback gate)}\"\n persist_plan_state \"feedback_required\" \"false\" \"manual\" \"$FEEDBACK_FILE\"\n echo \"[JEO][PLAN] manual approved=false (feedback)\" >&2\n return 10\n ;;\n s|stop|n|no)\n echo \"[JEO][PLAN] user requested PLAN stop.\" >&2\n return 30\n ;;\n *)\n echo \"[JEO][PLAN] invalid choice. stopping PLAN.\" >&2\n return 31\n ;;\n esac\n}\n\n# probe_plannotator_port PORT\n# Returns:\n# 0 — port is free and localhost bind is permitted (plannotator can start)\n# 1 — port is already in use (conflict — another instance may be running)\n# 2 — localhost bind is not permitted (sandbox/CI restriction)\nprobe_plannotator_port() {\n local port=\"${1:-$PLANNOTATOR_PORT}\"\n # Primary: Node.js probe on the exact plannotator port\n if command -v node >/dev/null 2>&1; then\n node -e \"\nconst net=require('net');\nconst s=net.createServer();\ns.on('error',(e)=>{\n process.exitCode = e.code==='EADDRINUSE' ? 1 : 2;\n process.exit();\n});\ns.listen({host:'127.0.0.1',port:${port}},()=>s.close(()=>process.exit(0)));\n\" >/dev/null 2>&1\n return $?\n fi\n # Fallback: Python3 socket probe on the exact port\n if command -v python3 >/dev/null 2>&1; then\n python3 -c \"\nimport socket, sys\nport = int(sys.argv[1])\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)\ntry:\n s.bind(('127.0.0.1', port))\n s.close()\n sys.exit(0)\nexcept OSError as e:\n import errno\n sys.exit(1 if e.errno in (errno.EADDRINUSE,) else 2)\n\" \"$port\" 2>/dev/null\n return $?\n fi\n # Neither available — assume free (conservative default)\n return 0\n}\n\n# wait_for_listen PORT PID [TIMEOUT_SECS]\n# Polls until plannotator binds PORT, the process dies, or timeout is reached.\n# Returns:\n# 0 — plannotator is listening (browser UI ready)\n# 1 — process exited before binding\n# 2 — timeout reached (process still alive but port not yet bound)\nwait_for_listen() {\n local port=\"$1\" pid=\"$2\" timeout=\"${3:-$PLANNOTATOR_START_TIMEOUT}\"\n local elapsed=0\n while [[ $elapsed -lt $timeout ]]; do\n kill -0 \"$pid\" 2>/dev/null || return 1\n # Primary: bash built-in /dev/tcp (no subprocess, available on Linux/macOS)\n if ( exec 3\u003c>/dev/tcp/127.0.0.1/\"$port\" ) 2>/dev/null; then\n return 0\n fi\n # Fallback: Python3 socket connect (Windows Git Bash, systems without /dev/tcp)\n if command -v python3 >/dev/null 2>&1; then\n python3 -c \"\nimport socket, sys\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.settimeout(0.5)\ntry:\n s.connect(('127.0.0.1', int(sys.argv[1])))\n s.close()\n sys.exit(0)\nexcept Exception:\n sys.exit(1)\n\" \"$port\" 2>/dev/null && return 0\n fi\n sleep 1\n (( elapsed++ )) || true\n done\n return 2\n}\n\n# Pre-launch probe: verify the dedicated port is available before starting plannotator.\nif [[ \"${JEO_SKIP_LISTEN_PROBE:-0}\" != \"1\" ]]; then\n set +e\n probe_plannotator_port \"$PLANNOTATOR_PORT\"\n probe_port_rc=$?\n set -e\n if [[ \"$probe_port_rc\" -eq 2 ]]; then\n echo \"[JEO][PLAN] localhost bind probe failed — listen not permitted (sandbox/CI).\" >&2\n set +e\n manual_fallback_gate\n probe_rc=$?\n set -e\n if [[ \"$probe_rc\" -eq 32 ]]; then\n write_state_gate_status \"infrastructure_blocked\"\n fi\n exit \"$probe_rc\"\n elif [[ \"$probe_port_rc\" -eq 1 ]]; then\n echo \"[JEO][PLAN] port ${PLANNOTATOR_PORT} already in use — another plannotator instance may be running.\" >&2\n echo \"[JEO][PLAN] override with: PLANNOTATOR_PORT=\u003cfree-port> or kill the existing process.\" >&2\n exit 32\n fi\n echo \"[JEO][PLAN] port ${PLANNOTATOR_PORT} is available — starting plannotator.\" >&2\nfi\n\nattempt=1\nwhile (( attempt \u003c= MAX_RESTARTS )); do\n : > \"$FEEDBACK_FILE\"\n\n # Write plan JSON to a temp file so plannotator can be backgrounded (enables PID tracking)\n PLAN_JSON_FILE=\"${FEEDBACK_DIR}/plan_input.json\"\n python3 -c \"\nimport json, sys\nplan = open(sys.argv[1]).read()\nsys.stdout.write(json.dumps({'tool_input': {'plan': plan, 'permission_mode': 'acceptEdits'}}))\n\" \"$PLAN_FILE\" > \"$PLAN_JSON_FILE\"\n\n # Launch plannotator with the dedicated port so we can monitor its binding state.\n PORT=\"$PLANNOTATOR_PORT\" env HOME=\"$RUNTIME_HOME\" PLANNOTATOR_HOME=\"$RUNTIME_HOME\" \\\n plannotator \u003c \"$PLAN_JSON_FILE\" > \"$FEEDBACK_FILE\" 2>&1 &\n PLANNOTATOR_PID=$!\n\n # Phase 1: STARTING — wait for plannotator to bind the port (browser UI ready).\n set +e\n wait_for_listen \"$PLANNOTATOR_PORT\" \"$PLANNOTATOR_PID\" \"$PLANNOTATOR_START_TIMEOUT\"\n listen_rc=$?\n set -e\n case \"$listen_rc\" in\n 0)\n echo \"[JEO][PLAN] plannotator listening on port ${PLANNOTATOR_PORT} — waiting for user input.\" >&2\n ;;\n 1)\n echo \"[JEO][PLAN] plannotator exited during startup (attempt ${attempt}/${MAX_RESTARTS}).\" >&2\n wait \"$PLANNOTATOR_PID\" 2>/dev/null || true\n ((attempt++)) || true\n continue\n ;;\n 2)\n echo \"[JEO][PLAN] plannotator startup timeout (${PLANNOTATOR_START_TIMEOUT}s) — port ${PLANNOTATOR_PORT} not bound yet; continuing to wait.\" >&2\n ;;\n esac\n\n # Phase 2: LISTENING / RUNNING — block while browser session is active.\n while kill -0 \"$PLANNOTATOR_PID\" 2>/dev/null; do\n sleep 1\n done\n wait \"$PLANNOTATOR_PID\" 2>/dev/null || true\n\n set +e\n python3 - \"$FEEDBACK_FILE\" \u003c\u003c'PYEOF'\nimport json, sys\npath = sys.argv[1]\ntry:\n payload = json.load(open(path))\nexcept Exception:\n sys.exit(20)\napproved = payload.get(\"approved\")\nif approved is True:\n sys.exit(0)\nif approved is False:\n sys.exit(10)\nsys.exit(20)\nPYEOF\n rc=$?\n set -e\n\n if [[ \"$rc\" -eq 0 ]]; then\n echo \"[JEO][PLAN] approved=true\"\n persist_plan_state \"approved\" \"true\" \"plannotator\" \"$FEEDBACK_FILE\"\n exit 0\n fi\n\n if [[ \"$rc\" -eq 10 ]]; then\n echo \"[JEO][PLAN] approved=false (feedback)\"\n persist_plan_state \"feedback_required\" \"false\" \"plannotator\" \"$FEEDBACK_FILE\"\n exit 10\n fi\n\n if grep -Eiq \"$PORT_ERROR_REGEX\" \"$FEEDBACK_FILE\"; then\n echo \"[JEO][PLAN] plannotator server bind failure detected (EADDRINUSE/EPERM).\" >&2\n set +e\n manual_fallback_gate\n fallback_rc=$?\n set -e\n if [[ \"$fallback_rc\" -eq 32 ]]; then\n write_state_gate_status \"infrastructure_blocked\"\n fi\n exit \"$fallback_rc\"\n fi\n\n # Classify crash type for clearer diagnostics before retrying\n if [[ -s \"$FEEDBACK_FILE\" ]]; then\n echo \"[JEO][PLAN] browser crash detected (non-JSON output, attempt ${attempt}/${MAX_RESTARTS}). restarting...\" >&2\n else\n echo \"[JEO][PLAN] plannotator exited without output (attempt ${attempt}/${MAX_RESTARTS}). restarting...\" >&2\n fi\n ((attempt++))\ndone\n\necho \"[JEO][PLAN] plannotator session ended ${MAX_RESTARTS} times.\" >&2\nset +e\nmanual_fallback_gate\nfallback_rc=$?\nset -e\nif [[ \"$fallback_rc\" -eq 32 ]]; then\n echo \"[JEO][PLAN] confirmation required. stop and ask user whether to continue PLAN.\" >&2\n write_state_gate_status \"infrastructure_blocked\"\n # Write a structured blocked-state file so agent frameworks can parse the situation\n python3 - \"$FEEDBACK_DIR/jeo-blocked.json\" \"$PLAN_FILE\" \u003c\u003c'PYEOF'\nimport json, sys\nout_path, plan_file = sys.argv[1], sys.argv[2]\ntry:\n with open(out_path, \"w\", encoding=\"utf-8\") as f:\n json.dump({\n \"status\": \"infrastructure_blocked\",\n \"reason\": \"plannotator_session_exhausted\",\n \"max_restarts_reached\": True,\n \"action_required\": \"manual_approval\",\n \"plan_file\": plan_file,\n \"instruction\": (\n \"Review the plan file and manually approve or reject: \"\n \"run the hook script again in a TTY, or set plan_gate_status \"\n \"in .omc/state/jeo-state.json to 'manual_approved'.\"\n ),\n }, f, ensure_ascii=False, indent=2)\nexcept Exception:\n pass\nPYEOF\nfi\nexit \"$fallback_rc\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":16413,"content_sha256":"024f18a67b3ef2d7969886527a6c8557173376db0442d4703ede1daa34788a84"},{"filename":"scripts/run-opencode-safe.sh","content":"#!/usr/bin/env bash\n# Launch OpenCode with writable runtime directories.\n# Useful when default HOME/XDG paths are readonly in sandboxed environments.\n\nset -euo pipefail\n\nif ! command -v opencode >/dev/null 2>&1; then\n echo \"opencode CLI not found. Install via: npm install -g opencode-ai\" >&2\n exit 1\nfi\n\nSESSION_KEY=\"$(python3 -c \"import hashlib,os; print(hashlib.md5(os.getcwd().encode()).hexdigest()[:8])\" 2>/dev/null || echo \"default\")\"\nRUNTIME_ROOT=\"/tmp/jeo-opencode-${SESSION_KEY}\"\n\nexport XDG_DATA_HOME=\"${RUNTIME_ROOT}/data\"\nexport XDG_STATE_HOME=\"${RUNTIME_ROOT}/state\"\nexport XDG_CACHE_HOME=\"${RUNTIME_ROOT}/cache\"\n\nmkdir -p \"${XDG_DATA_HOME}\" \"${XDG_STATE_HOME}\" \"${XDG_CACHE_HOME}\"\n\nexec opencode \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":717,"content_sha256":"ff1f6da0a2a8889f0bb9c1b2d48d045ce040a591d34b3fcbf60f7486a74ade17"},{"filename":"scripts/setup-claude.sh","content":"#!/usr/bin/env bash\n# JEO Skill — Claude Code Plugin & Hook Setup\n# Configures: omc plugin, plannotator hook, agentation MCP, jeo workflow in ~/.claude/settings.json\n# Usage: bash setup-claude.sh [--dry-run]\n\nset -euo pipefail\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; BLUE='\\033[0;34m'; RED='\\033[0;31m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\nerr() { echo -e \"${RED}✗${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false\n[[ \"${1:-}\" == \"--dry-run\" ]] && DRY_RUN=true\n\nJEO_SKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\nCLAUDE_SETTINGS=\"${HOME}/.claude/settings.json\"\n\necho \"\"\necho \"JEO — Claude Code Setup\"\necho \"========================\"\n\n# ── 1. Check Claude Code ──────────────────────────────────────────────────────\nif ! command -v claude >/dev/null 2>&1; then\n warn \"claude CLI not found. Install Claude Code first.\"\n echo \"\"\n echo \"Plugin installation (run inside Claude Code session):\"\n echo \" /plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode\"\n echo \" /plugin install oh-my-claudecode\"\n echo \" /omc:omc-setup\"\n echo \"\"\n echo \"plannotator plugin:\"\n echo \" /plugin marketplace add backnotprop/plannotator\"\n echo \" /plugin install plannotator@plannotator\"\nelse\n ok \"claude CLI found\"\nfi\n\n# ── 2. Configure ~/.claude/settings.json ─────────────────────────────────────\ninfo \"Configuring ~/.claude/settings.json...\"\n\nmkdir -p \"$(dirname \"$CLAUDE_SETTINGS\")\"\n\nif [[ -f \"$CLAUDE_SETTINGS\" ]]; then\n if ! $DRY_RUN; then\n cp \"$CLAUDE_SETTINGS\" \"${CLAUDE_SETTINGS}.jeo.bak\"\n ok \"Backup created: ${CLAUDE_SETTINGS}.jeo.bak\"\n fi\nfi\n\nif $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would sync plannotator hook, agent teams, agentation MCP, and UserPromptSubmit hook in $CLAUDE_SETTINGS\"\nelse\n JEO_SKILL_DIR=\"$JEO_SKILL_DIR\" python3 - \u003c\u003c'PYEOF'\nimport json\nimport os\n\nsettings_path = os.path.expanduser(\"~/.claude/settings.json\")\njeo_skill_dir = os.environ[\"JEO_SKILL_DIR\"]\nplan_gate_cmd = f'python3 \"{jeo_skill_dir}/scripts/claude-plan-gate.py\"'\nstop_continuation_cmd = f'python3 \"{jeo_skill_dir}/scripts/claude-stop-continuation.py\"'\nagentation_cmd = f'python3 \"{jeo_skill_dir}/scripts/claude-agentation-submit-hook.py\"'\n\ntry:\n with open(settings_path) as f:\n settings = json.load(f)\nexcept (FileNotFoundError, json.JSONDecodeError):\n settings = {}\n\nchanged = False\nmessages = []\n\nhooks = settings.setdefault(\"hooks\", {})\nperm_req = hooks.setdefault(\"PermissionRequest\", [])\nplannotator_entry = next((entry for entry in perm_req if entry.get(\"matcher\") == \"ExitPlanMode\"), None)\nif plannotator_entry is None:\n plannotator_entry = {\"matcher\": \"ExitPlanMode\", \"hooks\": []}\n perm_req.append(plannotator_entry)\n changed = True\n\nplan_hooks = plannotator_entry.setdefault(\"hooks\", [])\n# Remove ALL plannotator-related hooks so raw \"plannotator\" and claude-plan-gate.py\n# can never coexist — having both causes plannotator to open twice on ExitPlanMode.\nstale = [\n h for h in plan_hooks\n if h.get(\"command\", \"\").startswith(\"plannotator\")\n or \"claude-plan-gate.py\" in h.get(\"command\", \"\")\n]\nfor h in stale:\n plan_hooks.remove(h)\n# Insert the single correct entry\nplan_hooks.insert(0, {\"type\": \"command\", \"command\": plan_gate_cmd, \"timeout\": 1800})\nchanged = True\nif stale:\n messages.append(f\"✓ JEO plan gate wrapper synced ({len(stale)} stale entr{'y' if len(stale) == 1 else 'ies'} removed — dedup fix)\")\nelse:\n messages.append(\"✓ JEO plan gate wrapper added to ExitPlanMode\")\n\nenv = settings.setdefault(\"env\", {})\nif env.get(\"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS\") != \"1\":\n env[\"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS\"] = \"1\"\n changed = True\n messages.append(\"✓ Experimental agent teams enabled\")\nelse:\n messages.append(\"✓ Experimental agent teams already enabled\")\n\nmcp_servers = settings.setdefault(\"mcpServers\", {})\nif \"agentation\" not in mcp_servers:\n mcp_servers[\"agentation\"] = {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"agentation-mcp\", \"server\"],\n }\n changed = True\n messages.append(\"✓ agentation MCP server registered\")\nelse:\n messages.append(\"✓ agentation MCP already registered\")\n\nuser_prompt = hooks.setdefault(\"UserPromptSubmit\", [])\n\n# Migrate old-format entries (flat {\"type\":\"command\",...}) to new matcher format\nmigrated = False\nnew_user_prompt = []\nfor entry in user_prompt:\n if \"matcher\" in entry and \"hooks\" in entry:\n new_user_prompt.append(entry)\n elif entry.get(\"type\") == \"command\":\n # Old format → wrap in new matcher format\n new_user_prompt.append({\"matcher\": \"*\", \"hooks\": [entry]})\n migrated = True\n else:\n new_user_prompt.append(entry)\nif migrated:\n hooks[\"UserPromptSubmit\"] = new_user_prompt\n user_prompt = new_user_prompt\n changed = True\n messages.append(\"✓ UserPromptSubmit hooks migrated to new matcher format\")\n\nagentation_entry = next(\n (\n entry for entry in user_prompt\n if isinstance(entry, dict)\n and any(\n \"claude-agentation-submit-hook.py\" in h.get(\"command\", \"\")\n or h.get(\"command\", \"\").startswith(\"curl -sf --connect-timeout 1 http://localhost:4747\")\n for h in entry.get(\"hooks\", [])\n )\n ),\n None,\n)\nif agentation_entry is None:\n user_prompt.append({\n \"matcher\": \"*\",\n \"hooks\": [{\"type\": \"command\", \"command\": agentation_cmd, \"timeout\": 300}],\n })\n changed = True\n messages.append(\"✓ agentation submit-gate hook added\")\nelse:\n hook_list = agentation_entry.setdefault(\"hooks\", [])\n target_hook = next(\n (\n h for h in hook_list\n if \"claude-agentation-submit-hook.py\" in h.get(\"command\", \"\")\n or h.get(\"command\", \"\").startswith(\"curl -sf --connect-timeout 1 http://localhost:4747\")\n ),\n None,\n )\n if target_hook is None:\n hook_list.append({\"type\": \"command\", \"command\": agentation_cmd, \"timeout\": 300})\n changed = True\n else:\n if target_hook.get(\"command\") != agentation_cmd:\n target_hook[\"command\"] = agentation_cmd\n changed = True\n if target_hook.get(\"timeout\") != 300:\n target_hook[\"timeout\"] = 300\n changed = True\n messages.append(\"✓ agentation submit-gate hook synced\")\n\n# ── Stop hook: auto-continue after plannotator approval ──────────────────────\n# Registers claude-stop-continuation.py as a Stop hook so JEO automatically\n# proceeds to EXECUTE after plannotator approves the plan, without waiting\n# for user input.\nstop_hooks = hooks.setdefault(\"Stop\", [])\n# Find or create the default (no-matcher) Stop entry\nstop_entry = next(\n (e for e in stop_hooks if \"matcher\" not in e or e.get(\"matcher\") in (\"\", None, \"*\")),\n None,\n)\nif stop_entry is None:\n stop_entry = {\"hooks\": []}\n stop_hooks.append(stop_entry)\n changed = True\n\nstop_hook_list = stop_entry.setdefault(\"hooks\", [])\n# Remove stale JEO stop continuation hooks, then insert correct one\njeo_stop_stale = [\n h for h in stop_hook_list\n if \"claude-stop-continuation.py\" in h.get(\"command\", \"\")\n]\nfor h in jeo_stop_stale:\n stop_hook_list.remove(h)\nstop_hook_list.insert(0, {\"type\": \"command\", \"command\": stop_continuation_cmd, \"timeout\": 10})\nchanged = True\nif jeo_stop_stale:\n messages.append(f\"✓ JEO stop-continuation hook synced ({len(jeo_stop_stale)} stale removed)\")\nelse:\n messages.append(\"✓ JEO stop-continuation hook added (auto-continue after plannotator)\")\n\nif changed or not os.path.exists(settings_path):\n os.makedirs(os.path.dirname(settings_path), exist_ok=True)\n with open(settings_path, \"w\") as f:\n json.dump(settings, f, indent=2)\n\nfor message in messages:\n print(message)\nPYEOF\n ok \"Claude Code settings synced\"\nfi\n\n# ── 3. Instructions ───────────────────────────────────────────────────────────\necho \"\"\necho \"Manual plugin installation (run inside Claude Code):\"\necho \"\"\necho \" # Install oh-my-claudecode (omc)\"\necho \" /plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode\"\necho \" /plugin install oh-my-claudecode\"\necho \" /omc:omc-setup\"\necho \"\"\necho \" # Install plannotator\"\necho \" /plugin marketplace add backnotprop/plannotator\"\necho \" /plugin install plannotator@plannotator\"\necho \"\"\necho \" # Then restart Claude Code\"\necho \"\"\nok \"Claude Code setup complete\"\necho \" IMPORTANT: Restart Claude Code to activate all hooks and plugins\"\necho \" JEO requires /omc:team execution in Claude Code. Verify CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 after restart.\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8960,"content_sha256":"1baaa9b8845bee6bb4119e36b8e31453cce2284442a983bbcd48763e3ecc9d40"},{"filename":"scripts/setup-codex.sh","content":"#!/usr/bin/env bash\n# JEO Skill — Codex CLI Setup\n# Configures: developer_instructions + agentation MCP in ~/.codex/config.toml + /prompts:jeo\n# Usage: bash setup-codex.sh [--dry-run]\n\nset -euo pipefail\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; BLUE='\\033[0;34m'; RED='\\033[0;31m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false\n[[ \"${1:-}\" == \"--dry-run\" ]] && DRY_RUN=true\n\nJEO_SKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\nCODEX_CONFIG=\"${HOME}/.codex/config.toml\"\nCODEX_PROMPTS_DIR=\"${HOME}/.codex/prompts\"\nJEO_PROMPT_FILE=\"${CODEX_PROMPTS_DIR}/jeo.md\"\n\necho \"\"\necho \"JEO — Codex CLI Setup\"\necho \"======================\"\n\n# ── 1. Check Codex CLI ────────────────────────────────────────────────────────\nif ! command -v codex >/dev/null 2>&1; then\n warn \"codex CLI not found. Install via: npm install -g @openai/codex\"\nfi\n\n# ── 2. Configure ~/.codex/config.toml ────────────────────────────────────────\ninfo \"Configuring ~/.codex/config.toml...\"\n\nHOOK_DIR=\"${HOME}/.codex/hooks\"\nHOOK_FILE=\"${HOOK_DIR}/jeo-notify.py\"\n\nif $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would create/update $CODEX_CONFIG\"\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would create $JEO_PROMPT_FILE\"\nelse\n mkdir -p \"$(dirname \"$CODEX_CONFIG\")\" \"$CODEX_PROMPTS_DIR\"\n\n # Backup existing config\n [[ -f \"$CODEX_CONFIG\" ]] && cp \"$CODEX_CONFIG\" \"${CODEX_CONFIG}.jeo.bak\"\n\n JEO_INSTRUCTION=$(cat \u003c\u003cJEOEOF\n# JEO Orchestration Workflow\n# Keyword: jeo | Platforms: Codex, Claude, Gemini, OpenCode\n#\n# JEO provides integrated AI orchestration:\n# 1. PLAN: ralph+plannotator for visual plan review\n# 2. EXECUTE: team (if available) or bmad workflow\n# 3. VERIFY: agent-browser snapshot for UI verification\n# 4. CLEANUP: auto worktree cleanup after completion\n#\n# Trigger with: jeo \"\u003ctask description>\"\n# Use /prompts:jeo for full workflow activation\n#\n# PLAN phase protocol (Codex):\n# 1. Write plan to plan.md\n# 2. Run mandatory PLAN gate (auto-installs plannotator if missing, blocks for feedback/approve, retries dead sessions up to 3):\n# bash ${JEO_SKILL_DIR}/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n# Rule: if the current plan hash already has a terminal gate result\n# (approved/manual_approved/feedback_required/infrastructure_blocked), do not reopen plannotator.\n# Only a revised plan.md resets the gate to pending.\n# 3. Write signal file AND output PLAN_READY (two-path detection for notify hook reliability):\n# mkdir -p .omc/state && echo \"PLAN_READY\" > .omc/state/plan-signal.txt\n# echo \"PLAN_READY\"\n# 4. Check result:\n# - approved=true -> EXECUTE\n# - approved=false -> re-plan\n# - exit 32 -> localhost bind blocked (sandbox/CI). run PLAN gate in local TTY\n# 5. Never emit PLAN_READY in execute/verify phases.\n#\n# VERIFY_UI submit gate protocol:\n# 1. Enter verify_ui and set agentation.submit_gate_status=\"waiting_for_submit\"\n# 2. Wait until the user clicks Send Annotations / onSubmit in agentation\n# 3. Only then emit \"ANNOTATE_READY\" (or \"AGENTUI_READY\" for compatibility)\n# 4. notify hook may fetch /pending only after that explicit submit signal\n#\n# BMAD commands (fallback when team unavailable):\n# /workflow-init — initialize BMAD workflow\n# /workflow-status — check current BMAD phase\n#\n# Tools: agent-browser, playwriter, plannotator\nJEOEOF\n)\n\n python3 - \u003c\u003cPYEOF\nimport re, os\n\nconfig_path = os.path.expanduser(\"~/.codex/config.toml\")\njeo_instruction = \"\"\"${JEO_INSTRUCTION}\"\"\"\n\ntry:\n content = open(config_path).read() if os.path.exists(config_path) else \"\"\nexcept Exception:\n content = \"\"\n\ncontent = re.sub(r'(?ms)^\\[developer_instructions\\]\\s*\\n.*?(?=^\\[|\\Z)', '', content).strip() + \"\\n\"\n# Only strip legacy bare JEO block if it is NOT inside a developer_instructions value\nif not re.search(r'(?ms)^developer_instructions\\s*=\\s*\"\"\"', content):\n content = re.sub(r'(?ms)^# JEO Orchestration Workflow\\n.*?^\"\"\"\\s*\\n', '', content)\n\ndef parse_existing_instructions(text: str) -> str:\n m = re.search(r'(?ms)^developer_instructions\\s*=\\s*\"\"\"\\n?(.*?)\\n?\"\"\"\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, text)\n if m:\n return m.group(1)\n\n m = re.search(r'(?m)^developer_instructions\\s*=\\s*\"(.*)\"\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, text)\n if m:\n return bytes(m.group(1), \"utf-8\").decode(\"unicode_escape\")\n\n return \"\"\n\nexisting = parse_existing_instructions(content)\njeo_pattern = re.compile(\n r'(?ms)^# JEO Orchestration Workflow\\n.*?^# Tools: agent-browser, playwriter, plannotator\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

\n)\nif jeo_pattern.search(existing):\n merged = jeo_pattern.sub(jeo_instruction.strip(), existing).strip()\nelse:\n merged = (existing.rstrip() + \"\\n\\n\" if existing.strip() else \"\") + jeo_instruction.strip()\n\nnew_assignment = 'developer_instructions = \"\"\"\\n' + merged + '\\n\"\"\"\\n'\n\nif re.search(r'(?m)^developer_instructions\\s*=', content):\n content = re.sub(r'(?ms)^developer_instructions\\s*=\\s*(\"\"\".*?\"\"\"|\".*?\")\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, new_assignment, content, count=1)\nelse:\n first_table = re.search(r'(?m)^\\[', content)\n if first_table:\n content = content[:first_table.start()] + new_assignment + \"\\n\" + content[first_table.start():]\n else:\n content = new_assignment + \"\\n\" + content\n\nwith open(config_path, \"w\") as f:\n f.write(content)\n\n# Post-write: validate TOML and auto-fix stray standalone \" lines\ntry:\n import tomllib as _toml\n with open(config_path, \"rb\") as f:\n _toml.load(f)\nexcept Exception:\n with open(config_path) as f:\n raw = f.read()\n cleaned = re.sub(r'(?m)^\"$\\n', '', raw)\n with open(config_path, \"w\") as f:\n f.write(cleaned)\n\nprint(\"✓ JEO developer_instructions synced (top-level string)\")\nPYEOF\n ok \"JEO developer_instructions synced in ~/.codex/config.toml\"\n\n # ── 3. Create /prompts:jeo prompt file ──────────────────────────────────────\n cat > \"$JEO_PROMPT_FILE\" \u003c\u003c'PROMPTEOF'\n# JEO — Integrated Agent Orchestration Prompt\n\nYou are now operating in **JEO mode** — Integrated AI Agent Orchestration.\n\n## Your Workflow\n\n### Step 1: PLAN (plannotator — blocking loop)\nBefore writing any code, create and review a plan:\n1. Write a detailed implementation plan in \\`plan.md\\` (objectives, steps, risks, acceptance criteria)\n2. Run plannotator PLAN gate (blocking, mandatory; auto-installs plannotator if missing):\n \\`\\`\\`bash\n bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n # Write signal file (reliable fallback for notify hook) AND emit inline signal\n mkdir -p .omc/state && echo \"PLAN_READY\" > .omc/state/plan-signal.txt\n echo \"PLAN_READY\"\n \\`\\`\\`\n Same-hash guard:\n - if \\`plan_gate_status\\` is already terminal for the current plan hash, do not reopen plannotator\n - only a modified plan resets the gate to \\`pending\\`\n3. Read /tmp/plannotator_feedback.txt\n4. If \\`\"approved\":true\\` → proceed to EXECUTE\n5. If NOT approved → read annotations, revise plan.md, repeat from step 2\n6. **If PLAN gate exits with 32 → CONVERSATION APPROVAL MODE (MANDATORY)**:\n - Output the full contents of \\`plan.md\\` to the user in the conversation\n - Ask explicitly: \"⚠️ plannotator UI unavailable in this environment. Please review the plan above and reply 'approve' to proceed, or provide feedback.\"\n - **STOP and wait for user response. Do NOT proceed to EXECUTE until user approves.**\n - On 'approve'/'yes'/'ok': update \\`jeo-state.json\\` \\`plan_approved=true, plan_gate_status=\"manual_approved\"\\` → EXECUTE\n - On feedback text: revise \\`plan.md\\`, retry \\`plannotator-plan-loop.sh\\`, repeat\nNEVER skip plannotator. NEVER proceed to EXECUTE without approved=true or explicit user approval in conversation.\n\n### Step 2: EXECUTE (BMAD workflow for Codex)\nUse BMAD structured phases:\n- \\`/workflow-init\\` — Initialize BMAD for this project\n- Analysis phase: understand requirements fully\n- Planning phase: detailed technical plan\n- Solutioning phase: architecture decisions\n- Implementation phase: write code\n\n### Step 3: VERIFY (agent-browser)\nIf the task has browser UI:\n- Run: \\`agent-browser snapshot http://localhost:3000\\`\n- Check UI elements via accessibility tree (-i flag)\n- Save screenshot: \\`agent-browser screenshot \u003curl> -o verify.png\\`\n\n### Step 3.1: VERIFY_UI (agentation submit gate)\nIf \\`annotate\\` / \\`agentui\\` is requested:\n1. Update \\`.omc/state/jeo-state.json\\` to \\`phase=\"verify_ui\"\\`\n2. Set \\`agentation.submit_gate_status=\"waiting_for_submit\"\\`\n3. Wait until the human actually clicks **Send Annotations** / triggers \\`onSubmit\\`\n4. Only then emit \\`ANNOTATE_READY\\`\n5. Process agentation annotations after the notify hook reports submitted items\nNever read \\`/pending\\` before the submit gate opens.\n\n### Step 4: CLEANUP (worktree)\nAfter all tasks complete:\n- Run: git worktree prune\n- Run: bash ${JEO_SKILL_DIR}/scripts/worktree-cleanup.sh\n\n## Key Commands\n- Plan review — run plannotator BLOCKING (no &), then output PLAN_READY:\n Mandatory behavior:\n - If plannotator is missing, the PLAN gate auto-runs \\`ensure-plannotator.sh\\` first\n - Wait for approve/feedback every time\n - If session dies, restart up to 3 times\n - After 3 dead sessions, stop and ask whether PLAN should be terminated\n - If loop exits 32: localhost bind blocked. use local TTY/manual PLAN gate\n \\`\\`\\`bash\n bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n # Output PLAN_READY to trigger notify hook as backup signal\n echo \"PLAN_READY\"\n # Check result\n python3 -c \"\nimport json, sys\ntry:\n d = json.load(open('/tmp/plannotator_feedback.txt'))\n sys.exit(0 if d.get('approved') is True else 1)\nexcept Exception:\n sys.exit(1)\n\" && echo \"PLAN_APPROVED — proceed to EXECUTE\" || cat /tmp/plannotator_feedback.txt\n \\`\\`\\`\n- Browser verify: \\`agent-browser snapshot http://localhost:3000\\`\n- BMAD init: \\`/workflow-init\\`\n- Worktree cleanup: \\`bash ${JEO_SKILL_DIR}/scripts/worktree-cleanup.sh\\`\n\n## State File\nSave progress to: \\`.omc/state/jeo-state.json\\`\n\\`\\`\\`json\n{\n \"phase\": \"plan|execute|verify|verify_ui|cleanup|done\",\n \"task\": \"current task description\",\n \"plan_approved\": false,\n \"plan_gate_status\": \"pending\",\n \"plan_current_hash\": null,\n \"last_reviewed_plan_hash\": null,\n \"plan_review_method\": null,\n \"team_available\": false,\n \"retry_count\": 0,\n \"last_error\": null,\n \"checkpoint\": null,\n \"agentation\": {\n \"submit_gate_status\": \"idle\",\n \"submit_signal\": null,\n \"submit_received_at\": null,\n \"submitted_annotation_count\": 0\n }\n}\n\\`\\`\\`\n\nAlways check state file on resume to continue from last phase.\nPROMPTEOF\n\n # Fix absolute paths in prompt file (replace relative .agent-skills paths with absolute JEO_SKILL_DIR)\n sed -i.bak \\\n -e \"s|bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh|bash ${JEO_SKILL_DIR}/scripts/plannotator-plan-loop.sh|g\" \\\n -e \"s|\\\\\\${JEO_SKILL_DIR}|${JEO_SKILL_DIR}|g\" \\\n \"$JEO_PROMPT_FILE\"\n rm -f \"${JEO_PROMPT_FILE}.bak\"\n ok \"JEO prompt file created: $JEO_PROMPT_FILE\"\n\n # ── 4. Create plannotator notify hook ────────────────────────────────────────\n info \"Setting up plannotator notify hook...\"\n mkdir -p \"$HOOK_DIR\"\n\n cat > \"$HOOK_FILE\" \u003c\u003c 'HOOKEOF'\n#!/usr/bin/env python3\n\"\"\"JEO Codex notify hook — detects PLAN_READY / ANNOTATE_READY and triggers plannotator / agentation.\"\"\"\nimport hashlib, json, os, re, subprocess, sys, urllib.request, urllib.error, time\n\n# Exact signal strings (matched as standalone lines, allowing surrounding whitespace)\nPLAN_SIGNALS = [\"PLAN_READY\"]\nANNOTATE_SIGNALS = [\"ANNOTATE_READY\", \"AGENTUI_READY\"]\nPLAN_TERMINAL_STATUSES = {\"approved\", \"manual_approved\", \"feedback_required\", \"infrastructure_blocked\"}\n\ndef get_jeo_phase(cwd: str) -> str:\n \"\"\"Read current JEO phase from state file. Returns empty string if not available.\"\"\"\n state_path = os.path.join(cwd, \".omc\", \"state\", \"jeo-state.json\")\n try:\n with open(state_path) as f:\n return json.load(f).get(\"phase\", \"\")\n except (FileNotFoundError, json.JSONDecodeError, KeyError):\n return \"\"\n\n\ndef get_state_path(cwd: str) -> str:\n return os.path.join(cwd, \".omc\", \"state\", \"jeo-state.json\")\n\n\ndef load_state(cwd: str) -> dict:\n state_path = get_state_path(cwd)\n try:\n with open(state_path) as f:\n return json.load(f)\n except (FileNotFoundError, json.JSONDecodeError):\n return {}\n\n\ndef update_state(cwd: str, mutator) -> None:\n state_path = get_state_path(cwd)\n if not os.path.exists(state_path):\n return\n try:\n import fcntl, datetime\n\n with open(state_path, \"r+\") as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n try:\n data = json.load(fh)\n except Exception:\n data = {}\n mutator(data)\n data[\"updated_at\"] = datetime.datetime.utcnow().isoformat() + \"Z\"\n fh.seek(0)\n json.dump(data, fh, indent=2)\n fh.truncate()\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n except Exception:\n pass\n\ndef get_feedback_file(cwd: str) -> str:\n \"\"\"Return session-isolated feedback file path based on cwd MD5.\"\"\"\n _session_key = hashlib.md5(cwd.encode()).hexdigest()[:8]\n feedback_dir = f\"/tmp/jeo-{_session_key}\"\n os.makedirs(feedback_dir, exist_ok=True)\n return os.path.join(feedback_dir, \"plannotator_feedback.txt\")\n\n\ndef get_plannotator_env(cwd: str) -> dict:\n _session_key = hashlib.md5(cwd.encode()).hexdigest()[:8]\n runtime_home = f\"/tmp/jeo-{_session_key}/.plannotator\"\n os.makedirs(runtime_home, exist_ok=True)\n env = os.environ.copy()\n # Do NOT override HOME — plannotator-plan-loop.sh uses $HOME for PATH construction\n # (export PATH=\"$HOME/.local/bin:$HOME/bin:$PATH\"), so overriding HOME would prevent\n # discovery of plannotator when it is installed in ~/.local/bin.\n # PLANNOTATOR_HOME controls plannotator's state directory independently.\n env[\"PLANNOTATOR_HOME\"] = runtime_home\n return env\n\n\ndef get_plan_loop_script(cwd: str):\n candidates = [\n os.path.join(cwd, \".agent-skills\", \"jeo\", \"scripts\", \"plannotator-plan-loop.sh\"),\n os.path.expanduser(\"~/.codex/skills/jeo/scripts/plannotator-plan-loop.sh\"),\n os.path.expanduser(\"~/.agent-skills/jeo/scripts/plannotator-plan-loop.sh\"),\n ]\n for p in candidates:\n if os.path.exists(p):\n return p\n return None\n\n\ndef compute_plan_hash(plan_path: str) -> str:\n try:\n with open(plan_path, \"rb\") as fh:\n return hashlib.sha256(fh.read()).hexdigest()\n except Exception:\n return \"\"\n\n\ndef should_skip_plan_gate(cwd: str, plan_path: str) -> bool:\n state = load_state(cwd)\n current_hash = compute_plan_hash(plan_path)\n if not current_hash:\n return False\n return (\n state.get(\"phase\") == \"plan\"\n and state.get(\"plan_gate_status\") in PLAN_TERMINAL_STATUSES\n and state.get(\"last_reviewed_plan_hash\") == current_hash\n )\n\ndef write_plan_gate_result(cwd: str, rc: int, feedback_file: str) -> None:\n \"\"\"Write plannotator gate result to jeo-state.json.\"\"\"\n state_path = os.path.join(cwd, '.omc', 'state', 'jeo-state.json')\n if not os.path.exists(state_path):\n return\n try:\n import fcntl, datetime\n with open(state_path, 'r+') as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n d = json.load(fh)\n if rc == 0:\n d['plan_approved'] = True\n d['phase'] = 'execute'\n d['plan_gate_status'] = 'approved'\n elif rc == 10:\n d['plan_approved'] = False\n d['plan_gate_status'] = 'feedback_required'\n try:\n d['plannotator_feedback'] = json.load(open(feedback_file))\n except Exception:\n pass\n elif rc == 32:\n d['plan_gate_status'] = 'infrastructure_blocked'\n d['last_error'] = 'localhost bind unavailable (sandbox/CI)'\n d['updated_at'] = datetime.datetime.utcnow().isoformat() + 'Z'\n fh.seek(0); json.dump(d, fh, indent=2); fh.truncate()\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n except Exception:\n pass\n\n\ndef main() -> int:\n try:\n notification = json.loads(sys.argv[1])\n except (IndexError, json.JSONDecodeError):\n return 0\n\n if notification.get(\"type\") != \"agent-turn-complete\":\n return 0\n\n msg = notification.get(\"last-assistant-message\", \"\").strip()\n cwd = notification.get(\"cwd\", os.getcwd())\n phase = get_jeo_phase(cwd)\n\n # PLAN_READY: trigger plannotator (only during plan phase)\n if phase in (\"plan\",):\n if any(re.search(rf'(?m)^{re.escape(sig)}\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, msg or '') for sig in PLAN_SIGNALS):\n plan_candidates = [\"plan.md\", \".omc/plans/jeo-plan.md\", \"docs/plan.md\"]\n plan_path = None\n for candidate in plan_candidates:\n p = os.path.join(cwd, candidate)\n if os.path.exists(p):\n plan_path = p\n break\n if plan_path is None:\n print(\"[JEO] plan.md not found in known locations\")\n return 0\n if should_skip_plan_gate(cwd, plan_path):\n state = load_state(cwd)\n print(\n f\"[JEO] plan gate skipped: current hash already reviewed ({state.get('plan_gate_status', 'unknown')})\"\n )\n return 0\n feedback_file = get_feedback_file(cwd)\n loop_script = get_plan_loop_script(cwd)\n if loop_script:\n result = subprocess.run(\n [\"bash\", loop_script, plan_path, feedback_file, \"3\"],\n cwd=cwd,\n stdout=subprocess.PIPE,\n stderr=subprocess.STDOUT,\n text=True,\n env=get_plannotator_env(cwd),\n )\n if result.stdout:\n print(result.stdout.strip())\n write_plan_gate_result(cwd, result.returncode, feedback_file)\n if result.returncode == 32:\n print(\"[JEO] plannotator unavailable: localhost bind blocked (sandbox/CI).\")\n print(\"[JEO] CONVERSATION APPROVAL MODE: agent must output plan.md and request explicit user approval.\")\n print(f\"[JEO] plannotator loop result code={result.returncode} feedback={feedback_file}\")\n else:\n plan_content = open(plan_path).read()\n payload = json.dumps({\"tool_input\": {\"plan\": plan_content, \"permission_mode\": \"acceptEdits\"}})\n try:\n with open(feedback_file, \"w\") as f:\n subprocess.run([\"plannotator\"], input=payload, stdout=f, stderr=f, text=True, env=get_plannotator_env(cwd))\n print(f\"[JEO] plannotator feedback \\u2192 {feedback_file}\")\n except FileNotFoundError:\n print(\"[JEO] plannotator not found \\u2014 skipping\")\n return 0\n\n # ANNOTATE_READY: poll agentation HTTP API only after explicit submit signal in VERIFY_UI\n if phase == \"verify_ui\":\n if any(re.search(rf'(?m)^{re.escape(sig)}\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, msg or '') for sig in ANNOTATE_SIGNALS):\n state = load_state(cwd)\n agentation = state.get(\"agentation\", {})\n if agentation.get(\"submit_gate_status\") == \"submitted\":\n print(\"[JEO] agentation submit gate already opened for this turn\")\n else:\n def mark_submitted(data):\n agentation_state = data.setdefault(\"agentation\", {})\n agentation_state[\"submit_gate_status\"] = \"submitted\"\n agentation_state[\"submit_signal\"] = \"codex-notify\"\n agentation_state[\"submit_received_at\"] = time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime())\n\n update_state(cwd, mark_submitted)\n base_url = \"http://localhost:4747\"\n try:\n with urllib.request.urlopen(f\"{base_url}/pending\", timeout=2) as r:\n data = json.loads(r.read())\n count = data.get(\"count\", 0)\n annotations = data.get(\"annotations\", [])\n def record_count(state_data):\n agentation_state = state_data.setdefault(\"agentation\", {})\n agentation_state[\"submitted_annotation_count\"] = count\n if count == 0:\n agentation_state[\"submit_gate_status\"] = \"waiting_for_submit\"\n update_state(cwd, record_count)\n if count == 0:\n print(\"[JEO] agentation: no pending annotations\")\n else:\n print(f\"[JEO] agentation: {count} pending annotations\")\n for ann in annotations:\n sev = ann.get(\"severity\", \"suggestion\")\n print(f\" [{sev}] {ann.get('element','?')} | {ann.get('comment','')[:80]}\")\n print(f\" elementPath: {ann.get('elementPath','?')}\")\n except (urllib.error.URLError, Exception) as e:\n print(f\"[JEO] agentation server not reachable ({base_url}): {e}\")\n return 0\n\n return 0\nif __name__ == \"__main__\":\n sys.exit(main())\nHOOKEOF\n\n chmod +x \"$HOOK_FILE\"\n # Inject absolute JEO script path as first candidate in get_plan_loop_script()\n sed -i.bak \\\n \"s|os.path.join(cwd, \\\".agent-skills\\\", \\\"jeo\\\", \\\"scripts\\\", \\\"plannotator-plan-loop.sh\\\")|\\\"${JEO_SKILL_DIR}/scripts/plannotator-plan-loop.sh\\\",\\n os.path.join(cwd, \\\".agent-skills\\\", \\\"jeo\\\", \\\"scripts\\\", \\\"plannotator-plan-loop.sh\\\")|\" \\\n \"$HOOK_FILE\"\n rm -f \"${HOOK_FILE}.bak\"\n ok \"JEO notify hook created: $HOOK_FILE\"\n\n # Add notify + tui to config.toml\n python3 - \u003c\u003cPYEOF\nimport re, os\n\nconfig_path = os.path.expanduser(\"~/.codex/config.toml\")\nhook_path = os.path.expanduser(\"~/.codex/hooks/jeo-notify.py\")\n\ntry:\n content = open(config_path).read() if os.path.exists(config_path) else \"\"\nexcept Exception:\n content = \"\"\n\n# Use a dispatcher so multiple skills (JEO, OMU, …) can each register a notify hook\n# without the last setup overwriting the previous one's notify entry.\nimport json as _json\ndispatcher_path = os.path.expanduser(\"~/.codex/hooks/dispatcher.py\")\nregistry_path = os.path.expanduser(\"~/.codex/hooks/registry.json\")\n\n# Ensure dispatcher script exists\nif not os.path.exists(dispatcher_path):\n os.makedirs(os.path.dirname(dispatcher_path), exist_ok=True)\n with open(dispatcher_path, \"w\") as _f:\n _f.write(\n '#!/usr/bin/env python3\\n'\n '\"\"\"Codex notify dispatcher — fans out to all registered skill hooks.\\n'\n 'Register hooks in ~/.codex/hooks/registry.json: {\"hooks\": [\"/path/to/hook.py\"]}\\n\"\"\"\\n'\n 'import json, os, subprocess, sys\\n'\n 'REGISTRY = os.path.expanduser(\"~/.codex/hooks/registry.json\")\\n'\n 'def main():\\n'\n ' raw = sys.argv[1] if len(sys.argv) > 1 else \"\"\\n'\n ' try:\\n'\n ' hooks = json.load(open(REGISTRY)).get(\"hooks\", [])\\n'\n ' except Exception:\\n'\n ' return 0\\n'\n ' for h in hooks:\\n'\n ' if os.path.isfile(h):\\n'\n ' try:\\n'\n ' subprocess.run([\"python3\", h, raw], check=False)\\n'\n ' except Exception as e:\\n'\n ' print(f\"[dispatcher] {h}: {e}\", flush=True)\\n'\n ' return 0\\n'\n 'if __name__ == \"__main__\":\\n'\n ' raise SystemExit(main())\\n'\n )\n print(\"✓ dispatcher.py created\")\n\n# Register jeo-notify.py in dispatcher registry\ntry:\n registry = _json.load(open(registry_path)) if os.path.exists(registry_path) else {\"hooks\": []}\nexcept Exception:\n registry = {\"hooks\": []}\nif hook_path not in registry.get(\"hooks\", []):\n registry.setdefault(\"hooks\", []).append(hook_path)\n with open(registry_path, \"w\") as _f:\n _json.dump(registry, _f, indent=2)\n print(\"✓ jeo-notify.py registered in dispatcher registry\")\nelse:\n print(\"✓ jeo-notify.py already in dispatcher registry\")\n\n# Point config.toml notify to dispatcher (skip if already pointing there)\ndispatcher_line = f'notify = [\"python3\", \"{dispatcher_path}\"]'\nnotify_match = re.search(r'(?m)^notify\\s*=.*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, content)\nif notify_match:\n if \"dispatcher.py\" not in notify_match.group(0):\n content = re.sub(r'(?m)^notify\\s*=.*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, dispatcher_line, content, count=1)\n print(\"✓ notify updated to use dispatcher in config.toml\")\n else:\n print(\"✓ notify already uses dispatcher in config.toml\")\nelse:\n first_table = re.search(r'^\\[', content, re.MULTILINE)\n if first_table:\n content = content[:first_table.start()] + dispatcher_line + \"\\n\\n\" + content[first_table.start():]\n else:\n content = dispatcher_line + \"\\n\" + content\n print(\"✓ notify dispatcher registered in config.toml\")\n\n# Add agentation [[mcp_servers]] if missing (check both array-of-tables and table formats)\nif not re.search(r'(?ms)^\\[\\[mcp_servers\\]\\]\\s*\\nname\\s*=\\s*\"agentation\"\\s*\\n', content) and \\\n not re.search(r'(?m)^\\[mcp_servers\\.agentation\\]', content):\n agentation_block = '\\n[mcp_servers.agentation]\\ncommand = \"npx\"\\nargs = [\"-y\", \"agentation-mcp\", \"server\"]\\n'\n content = content.rstrip() + agentation_block\n print(\"\\u2713 agentation MCP server added to config.toml\")\nelse:\n print(\"\\u2713 agentation MCP already in config.toml\")\n\n# Add or sync [tui] section\ntui_match = re.search(r'(?ms)^\\[tui\\]\\s*\\n(.*?)(?=^\\[|\\Z)', content)\nif not tui_match:\n content = content.rstrip() + '\\n\\n[tui]\\nnotifications = [\"agent-turn-complete\"]\\nnotification_method = \"osc9\"\\n'\n print(\"✓ [tui] section added\")\nelse:\n tui_body = tui_match.group(1)\n notif_match = re.search(r'(?m)^notifications\\s*=\\s*\\[(.*?)\\]\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, tui_body)\n notifications = []\n if notif_match:\n notifications = re.findall(r'\"([^\"]+)\"', notif_match.group(1))\n if \"agent-turn-complete\" not in notifications:\n notifications.append(\"agent-turn-complete\")\n notifications_line = 'notifications = [' + ', '.join(f'\"{item}\"' for item in notifications) + ']'\n if notif_match:\n tui_body = re.sub(r'(?m)^notifications\\s*=\\s*\\[(.*?)\\]\\s*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, notifications_line, tui_body, count=1)\n else:\n tui_body = notifications_line + '\\n' + tui_body\n\n if re.search(r'(?m)^notification_method\\s*=', tui_body):\n tui_body = re.sub(r'(?m)^notification_method\\s*=.*

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…

, 'notification_method = \"osc9\"', tui_body, count=1)\n else:\n tui_body = tui_body.rstrip() + '\\nnotification_method = \"osc9\"\\n'\n\n content = content[:tui_match.start(1)] + tui_body + content[tui_match.end(1):]\n print(\"✓ [tui] notifications synced\")\n\nwith open(config_path, \"w\") as f:\n f.write(content)\nPYEOF\n\n ok \"Codex config.toml updated (notify hook + agentation MCP + tui)\"\nfi\n\necho \"\"\necho \"Codex CLI usage after setup:\"\necho \" /prompts:jeo ← Activate JEO orchestration workflow\"\necho \" notify hook: ~/.codex/hooks/jeo-notify.py\"\necho \" fires on: PLAN_READY / ANNOTATE_READY signals in agent output (AGENTUI_READY also accepted)\"\necho \" writes to: /tmp/plannotator_feedback.txt\"\necho \"\"\nok \"Codex CLI setup complete\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":28027,"content_sha256":"add86ef758c14834aedc5e0ff248107378fce8f29499771f1734f3a658f5393e"},{"filename":"scripts/setup-gemini.sh","content":"#!/usr/bin/env bash\n# JEO Skill — Gemini CLI Hook & GEMINI.md Setup\n# Configures: ExitPlanMode hook in ~/.gemini/settings.json + JEO instructions in GEMINI.md\n# Usage: bash setup-gemini.sh [--dry-run] [--hook-only] [--md-only]\n\nset -euo pipefail\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; BLUE='\\033[0;34m'; RED='\\033[0;31m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false; HOOK_ONLY=false; MD_ONLY=false\nfor arg in \"$@\"; do\n case $arg in --dry-run) DRY_RUN=true ;; --hook-only) HOOK_ONLY=true ;; --md-only) MD_ONLY=true ;; esac\ndone\n\nJEO_SKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\nGEMINI_SETTINGS=\"${HOME}/.gemini/settings.json\"\nGEMINI_MD=\"${HOME}/.gemini/GEMINI.md\"\n\necho \"\"\necho \"JEO — Gemini CLI Setup\"\necho \"======================\"\n\n# ── 1. Check Gemini CLI ───────────────────────────────────────────────────────\nif ! command -v gemini >/dev/null 2>&1; then\n warn \"gemini CLI not found. Install via: npm install -g @google/gemini-cli\"\nfi\n\n# NOTE: Gemini CLI uses AfterAgent hook (not ExitPlanMode, which is Claude Code-only).\n# The primary method is agent direct blocking call — do NOT use & (background).\n# Manual blocking call (same-turn feedback, auto-installs plannotator if missing):\n# bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n\n# ── 2. Configure ~/.gemini/settings.json ─────────────────────────────────────\nif ! $MD_ONLY; then\n info \"Configuring ~/.gemini/settings.json...\"\n\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would add AfterAgent hook to $GEMINI_SETTINGS\"\n else\n mkdir -p \"$(dirname \"$GEMINI_SETTINGS\")\"\n [[ -f \"$GEMINI_SETTINGS\" ]] && cp \"$GEMINI_SETTINGS\" \"${GEMINI_SETTINGS}.jeo.bak\"\n\n # Create hook helper script (avoids plannotator plan - hanging on empty stdin)\n GEMINI_HOOK_DIR=\"${HOME}/.gemini/hooks\"\n mkdir -p \"$GEMINI_HOOK_DIR\"\n cat > \"${GEMINI_HOOK_DIR}/jeo-plannotator.sh\" \u003c\u003c 'HOOKEOF'\n#!/usr/bin/env bash\n# JEO AfterAgent backup hook — runs plannotator if plan.md exists in cwd\n# Phase guard: only fire during PLAN phase to prevent conflict with agentation.\n# Repeat guard: same plan hash + terminal gate status must not reopen plannotator.\n\nJEO_STATE=\"${PWD}/.omc/state/jeo-state.json\"\nif [[ ! -f \"$JEO_STATE\" ]]; then\n exit 0 # JEO is not active — no state file\nfi\nPHASE=$(python3 -c \"\nimport json, sys\ntry:\n d = json.load(open('$JEO_STATE'))\n print(d.get('phase', 'unknown'))\nexcept Exception:\n print('unknown')\n\" 2>/dev/null || echo \"unknown\")\n\n# 화이트리스트: \"plan\"일 때만 실행, 그 외(unknown, done, execute 등) 모두 종료\nif [[ \"$PHASE\" != \"plan\" ]]; then\n exit 0\nfi\n\n# AfterAgent 이중 실행 방지: 에이전트가 직접 호출한 턴이면 건너뜀\nLOCK_FILE=\"/tmp/jeo-plannotator-direct.lock\"\nif [[ -f \"$LOCK_FILE\" ]]; then\n rm -f \"$LOCK_FILE\"\n exit 0\nfi\n\nPLAN_FILE=\"$(pwd)/plan.md\"\ntest -f \"$PLAN_FILE\" || exit 0\nLOOP_SCRIPT_CANDIDATES=(\n \"$(pwd)/.agent-skills/jeo/scripts/plannotator-plan-loop.sh\"\n \"$HOME/.codex/skills/jeo/scripts/plannotator-plan-loop.sh\"\n \"$HOME/.agent-skills/jeo/scripts/plannotator-plan-loop.sh\"\n)\n\nLOOP_SCRIPT=\"\"\nfor candidate in \"${LOOP_SCRIPT_CANDIDATES[@]}\"; do\n if [[ -f \"$candidate\" ]]; then\n LOOP_SCRIPT=\"$candidate\"\n break\n fi\ndone\n\nif [[ -n \"$LOOP_SCRIPT\" ]]; then\n set +e\n bash \"$LOOP_SCRIPT\" \"$PLAN_FILE\" /tmp/plannotator_feedback.txt 3\n LOOP_RC=$?\n set -e\n if [[ \"$LOOP_RC\" -eq 0 ]]; then\n echo \"[JEO] plannotator approved=true (written to jeo-state.json)\"\n elif [[ \"$LOOP_RC\" -eq 10 ]]; then\n echo \"[JEO] plannotator approved=false — feedback written to jeo-state.json\"\n elif [[ \"$LOOP_RC\" -eq 32 ]]; then\n echo \"[JEO] plannotator unavailable: localhost bind blocked (sandbox/CI).\" >&2\n echo \"[JEO] run PLAN gate in local TTY to use manual fallback approve/feedback.\" >&2\n fi\nelse\n PLANNOTATOR_RUNTIME_HOME=\"/tmp/jeo-$(python3 -c \"import hashlib,os; print(f'/tmp/jeo-{hashlib.md5(os.getcwd().encode()).hexdigest()[:8]}')\")/.plannotator\"\n mkdir -p \"$PLANNOTATOR_RUNTIME_HOME\"\n python3 -c \"\nimport json, sys\nplan = open(sys.argv[1]).read()\nsys.stdout.write(json.dumps({'tool_input': {'plan': plan, 'permission_mode': 'acceptEdits'}}))\n\" \"$PLAN_FILE\" | env HOME=\"$PLANNOTATOR_RUNTIME_HOME\" PLANNOTATOR_HOME=\"$PLANNOTATOR_RUNTIME_HOME\" plannotator > /tmp/plannotator_feedback.txt 2>&1 || true\nfi\nHOOKEOF\n chmod +x \"${GEMINI_HOOK_DIR}/jeo-plannotator.sh\"\n\n # Create agentation AfterAgent hook (phase-guarded + submit-gated)\n cat > \"${GEMINI_HOOK_DIR}/jeo-agentation.sh\" \u003c\u003c 'AGENTHOOKEOF'\n#!/usr/bin/env bash\n# JEO AfterAgent hook — check pending agentation annotations during VERIFY_UI phase\n# Submit gate: only process annotations after explicit Send Annotations / onSubmit confirmation.\n\n# Phase guard: only fire during verify_ui phase\nJEO_STATE=\"${PWD}/.omc/state/jeo-state.json\"\nif [[ -f \"$JEO_STATE\" ]]; then\n PHASE=$(python3 -c \"import json; print(json.load(open('$JEO_STATE')).get('phase',''))\" 2>/dev/null || echo \"\")\n SUBMIT_GATE=$(python3 -c \"import json; print(json.load(open('$JEO_STATE')).get('agentation',{}).get('submit_gate_status',''))\" 2>/dev/null || echo \"\")\n if [[ \"$PHASE\" != \"verify_ui\" ]]; then\n exit 0\n fi\n if [[ \"$SUBMIT_GATE\" != \"submitted\" ]]; then\n exit 0\n fi\nelse\n exit 0 # No state file means JEO is not active\nfi\n\n# Check agentation server and report pending annotations\nPENDING=$(curl -sf --connect-timeout 2 http://localhost:4747/pending 2>/dev/null) || exit 0\nCOUNT=$(echo \"$PENDING\" | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('count',0))\" 2>/dev/null || echo 0)\n\nif [ \"$COUNT\" -gt 0 ]; then\n echo \"=== AGENTATION: ${COUNT} annotations pending ===\"\n echo \"$PENDING\" | python3 -c \"\nimport sys, json\nd = json.load(sys.stdin)\nfor i, a in enumerate(d.get('annotations', [])):\n sev = a.get('severity', 'suggestion')\n print(f' [{i+1}] [{sev}] {a.get(\\\"element\\\",\\\"?\\\")} ({a.get(\\\"elementPath\\\",\\\"?\\\")[:60]})')\n print(f' {a.get(\\\"comment\\\",\\\"\\\")[:80]}')\n\" 2>/dev/null\n echo \"=== END ===\"\nfi\nAGENTHOOKEOF\n chmod +x \"${GEMINI_HOOK_DIR}/jeo-agentation.sh\"\n\n python3 - \u003c\u003cPYEOF\nimport json, os\n\nsettings_path = os.path.expanduser(\"~/.gemini/settings.json\")\nhook_path = os.path.expanduser(\"~/.gemini/hooks/jeo-plannotator.sh\")\ntry:\n with open(settings_path) as f:\n settings = json.load(f)\nexcept (FileNotFoundError, json.JSONDecodeError):\n settings = {}\n\nhooks = settings.setdefault(\"hooks\", {})\n# AfterAgent is the correct Gemini CLI hook (ExitPlanMode is Claude Code-only)\nafter_agent = hooks.setdefault(\"AfterAgent\", [])\n\n# Migrate old-format entries (flat {\"type\":\"command\",...}) to new matcher format\nmigrated = False\nnew_after_agent = []\nfor entry in after_agent:\n if \"matcher\" in entry and \"hooks\" in entry:\n # Ensure timeout on plannotator hooks (1800s for blocking UI wait)\n for h in entry.get(\"hooks\", []):\n if \"plannotator\" in h.get(\"command\", \"\") and \"timeout\" not in h:\n h[\"timeout\"] = 1800\n migrated = True\n new_after_agent.append(entry)\n elif entry.get(\"type\") == \"command\":\n # Old format -> wrap in new matcher format\n if \"timeout\" not in entry:\n entry[\"timeout\"] = 300\n new_after_agent.append({\"matcher\": \"\", \"hooks\": [entry]})\n migrated = True\n else:\n new_after_agent.append(entry)\nif migrated:\n hooks[\"AfterAgent\"] = new_after_agent\n after_agent = new_after_agent\n with open(settings_path, \"w\") as f:\n json.dump(settings, f, indent=2)\n print(\"\\\\u2713 AfterAgent hooks migrated to new matcher format with timeouts\")\n\n# Check if jeo plannotator hook already exists (old or new form)\nplanno_exists = any(\n any(\n h.get(\"command\", \"\").startswith(\"plannotator\") or \"jeo-plannotator\" in h.get(\"command\", \"\")\n for h in entry.get(\"hooks\", [])\n )\n for entry in after_agent\n)\n\nif not planno_exists:\n after_agent.append({\n \"matcher\": \"\",\n \"hooks\": [{\n \"name\": \"plannotator-review\",\n \"type\": \"command\",\n \"command\": f\"bash {hook_path}\",\n \"timeout\": 1800,\n \"description\": \"PLAN phase backup gate with no-repeat hash guard\"\n }]\n })\n with open(settings_path, \"w\") as f:\n json.dump(settings, f, indent=2)\n print(\"✓ plannotator AfterAgent hook added to ~/.gemini/settings.json\")\nelse:\n print(\"\\u2713 plannotator hook already present\")\n\n# Add agentation AfterAgent hook (phase-guarded + submit-gated)\nagentation_hook_path = os.path.expanduser(\"~/.gemini/hooks/jeo-agentation.sh\")\nagentation_exists = any(\n any(\n \"jeo-agentation\" in h.get(\"command\", \"\")\n for h in entry.get(\"hooks\", [])\n )\n for entry in after_agent\n)\nif not agentation_exists:\n after_agent.append({\n \"matcher\": \"\",\n \"hooks\": [{\n \"name\": \"agentation-check\",\n \"type\": \"command\",\n \"command\": f\"bash {agentation_hook_path}\",\n \"timeout\": 300,\n \"description\": \"VERIFY_UI submit gate opened: check pending agentation annotations\"\n }]\n })\n with open(settings_path, \"w\") as f:\n json.dump(settings, f, indent=2)\n print(\"\\u2713 agentation AfterAgent hook added to ~/.gemini/settings.json\")\nelse:\n print(\"\\u2713 agentation hook already present\")\n\n# Add agentation MCP server if missing\nmcp_servers = settings.setdefault(\"mcpServers\", {})\nif \"agentation\" not in mcp_servers:\n mcp_servers[\"agentation\"] = {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"agentation-mcp\", \"server\"]\n }\n with open(settings_path, \"w\") as f:\n json.dump(settings, f, indent=2)\n print(\"\\u2713 agentation MCP server added to ~/.gemini/settings.json\")\nelse:\n print(\"\\u2713 agentation MCP already present\")\nPYEOF\n ok \"Gemini CLI settings updated\"\n fi\nfi\n\n# ── 3. Update GEMINI.md ───────────────────────────────────────────────────────\nif ! $HOOK_ONLY; then\n info \"Updating ~/.gemini/GEMINI.md...\"\n\n JEO_SECTION='\n## JEO Orchestration Workflow\n\nKeyword: `jeo` | Tool: Gemini CLI\n\nJEO provides integrated AI agent orchestration across all AI tools.\n\n### Workflow Phases\n\n**PLAN** (plannotator — 직접 blocking 호출 필수):\n1. `plan.md` 작성 (목표, 단계, 리스크, 완료 기준 포함)\n2. PLAN gate 실행 (& 절대 금지, plannotator 없으면 자동 설치 후 계속 진행):\n bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n # 동작 보장:\n # - approve/feedback 입력까지 반드시 대기\n # - 같은 plan hash 에 이미 approved/feedback/infrastructure_blocked가 기록돼 있으면 재실행 금지\n # - plan.md 내용이 바뀔 때만 gate_status를 pending으로 리셋\n # - 세션 종료 시 자동 재시작 (최대 3회)\n # - 3회 종료 시 PLAN 종료 여부를 사용자에게 확인\n # - exit 32 시 localhost bind 차단(sandbox/CI): local TTY에서 수동 PLAN gate 실행\n3. /tmp/plannotator_feedback.txt 읽기\n4. \"approved\":true → EXECUTE 진입 / 미승인 → 피드백 반영 후 plan.md 수정 후 2번 반복\n5. PLAN gate exit 32면 인프라 차단이므로 local TTY에서 PLAN gate 재실행\nNEVER skip plannotator. NEVER proceed to EXECUTE without approved=true.\n\n**EXECUTE** (BMAD for Gemini):\n- BMAD is the primary orchestration fallback when omc team is unavailable\n- `/workflow-init` — Initialize BMAD structured workflow\n- `/workflow-status` — Check current phase\n- Phases: Analysis → Planning → Solutioning → Implementation\n\n**VERIFY** (agent-browser):\n- `agent-browser snapshot http://localhost:3000`\n- UI/기능 정상 여부 확인\n\n**CLEANUP** (worktree):\n- After all work: `bash '\"${JEO_SKILL_DIR}\"'/scripts/worktree-cleanup.sh`\n\n**ANNOTATE** (agentation watch loop — HTTP API 폴백):\nWhen user says \"annotate\" or \"agentui\" (deprecated alias) or asks to process UI annotations:\n1. Set `.omc/state/jeo-state.json` → `phase=\"verify_ui\"` and `agentation.submit_gate_status=\"waiting_for_submit\"`\n2. Wait for the human to click **Send Annotations** / trigger `onSubmit`\n3. Only after that explicit submit signal, reply `ANNOTATE_READY` and update `agentation.submit_gate_status=\"submitted\"`\n4. Then GET http://localhost:4747/pending — check count\n5. For each annotation: PATCH status:acknowledged, fix code via elementPath, PATCH status:resolved + resolution\n6. Repeat until count=0. Emit `AGENTUI_READY` when done.\nNEVER poll `/pending` before submit gate opens. NEVER treat draft annotations as actionable.\n\n### ohmg Integration\nFor Gemini multi-agent orchestration:\n```bash\nbunx oh-my-ag # Initialize ohmg\n/coordinate \"\u003ctask>\" # Coordinate multi-agent task\n```\n'\n\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would append JEO section to $GEMINI_MD\"\n else\n mkdir -p \"$(dirname \"$GEMINI_MD\")\"\n [[ -f \"$GEMINI_MD\" ]] && cp \"$GEMINI_MD\" \"${GEMINI_MD}.jeo.bak\"\n\n if [[ -f \"$GEMINI_MD\" ]] && grep -q \"^## JEO Orchestration Workflow\" \"$GEMINI_MD\"; then\n GEMINI_MD=\"$GEMINI_MD\" JEO_SECTION=\"$JEO_SECTION\" python3 - \u003c\u003c'PYEOF'\nimport os\nimport re\n\npath = os.environ[\"GEMINI_MD\"]\nsection = os.environ[\"JEO_SECTION\"].strip()\ntext = open(path, encoding=\"utf-8\").read()\npattern = re.compile(r\"\\n## JEO Orchestration Workflow\\n.*?\\Z\", re.S)\nupdated = pattern.sub(\"\\n\" + section + \"\\n\", text.rstrip() + \"\\n\")\nwith open(path, \"w\", encoding=\"utf-8\") as f:\n f.write(updated)\nPYEOF\n ok \"JEO section synced in ~/.gemini/GEMINI.md\"\n else\n echo \"$JEO_SECTION\" >> \"$GEMINI_MD\"\n ok \"JEO instructions added to ~/.gemini/GEMINI.md\"\n fi\n fi\nfi\n\necho \"\"\necho \"Gemini CLI usage after setup:\"\necho \" gemini --approval-mode plan ← Plan mode (plannotator fires on exit)\"\necho \" /workflow-init ← BMAD orchestration\"\necho \" bunx oh-my-ag ← ohmg multi-agent (Gemini)\"\necho \"\"\nok \"Gemini CLI setup complete\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":14348,"content_sha256":"c18458b8b102ceb035365b2976207ee92b4ab5ac7ab23bcb29e69e0045193949"},{"filename":"scripts/setup-opencode.sh","content":"#!/usr/bin/env bash\n# JEO Skill — OpenCode Plugin Registration\n# Configures: opencode.json plugin entry + agentation MCP + slash commands\n# Usage: bash setup-opencode.sh [--dry-run]\n\nset -euo pipefail\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; BLUE='\\033[0;34m'; RED='\\033[0;31m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false\n[[ \"${1:-}\" == \"--dry-run\" ]] && DRY_RUN=true\n\n# Resolve opencode.json priority:\n# 1) cwd (project-level config), then\n# 2) ~/.config/opencode/opencode.json, then\n# 3) legacy ~/.opencode.json\nOPENCODE_JSON=\"\"\nfor candidate in \"./opencode.json\" \"${HOME}/.config/opencode/opencode.json\" \"${HOME}/opencode.json\"; do\n [[ -f \"$candidate\" ]] && OPENCODE_JSON=\"$candidate\" && break\ndone\n\necho \"\"\necho \"JEO — OpenCode Plugin Setup\"\necho \"===========================\"\n\n# ── 1. Check OpenCode ────────────────────────────────────────────────────────\nif ! command -v opencode >/dev/null 2>&1; then\n warn \"opencode CLI not found. Install via: npm install -g opencode-ai\"\nfi\n\n# Optional runtime check: OpenCode writes SQLite/cache/state under XDG dirs.\n# If HOME-backed defaults are not writable, suggest tmp-based launcher.\nif command -v opencode >/dev/null 2>&1; then\n OPENCODE_DATA_DIR=\"${XDG_DATA_HOME:-$HOME/.local/share}/opencode\"\n if ! (mkdir -p \"$OPENCODE_DATA_DIR\" 2>/dev/null && [[ -w \"$OPENCODE_DATA_DIR\" ]]); then\n warn \"OpenCode data dir is not writable: $OPENCODE_DATA_DIR\"\n warn \"Use: bash $(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/run-opencode-safe.sh\"\n fi\nfi\n\n# ── 2. Configure opencode.json ────────────────────────────────────────────────\ninfo \"Configuring opencode.json...\"\n\nif [[ -z \"$OPENCODE_JSON\" ]]; then\n OPENCODE_JSON=\"${HOME}/.config/opencode/opencode.json\"\n warn \"No opencode.json found — will create at $OPENCODE_JSON\"\nfi\n\nif $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would configure $OPENCODE_JSON with JEO plugin\"\nelse\n # Backup\n mkdir -p \"$(dirname \"$OPENCODE_JSON\")\"\n [[ -f \"$OPENCODE_JSON\" ]] && cp \"$OPENCODE_JSON\" \"${OPENCODE_JSON}.jeo.bak\"\n\n OPENCODE_JSON_PATH=\"$OPENCODE_JSON\" python3 - \u003c\u003c'PYEOF'\nimport json, os\n\nconfig_path = os.environ[\"OPENCODE_JSON_PATH\"]\ntry:\n with open(config_path) as f:\n config = json.load(f)\nexcept (FileNotFoundError, json.JSONDecodeError):\n config = {}\n\n# Set schema (normalize old escaped key if present)\nlegacy_schema_key = \"\\\\$schema\"\nif legacy_schema_key in config:\n if \"$schema\" not in config:\n config[\"$schema\"] = config[legacy_schema_key]\n config.pop(legacy_schema_key)\nconfig.setdefault(\"$schema\", \"https://opencode.ai/config.json\")\n\n# Add plugins\nplugins = config.setdefault(\"plugin\", [])\n\n# Add plannotator if not present\nif \"@plannotator/opencode@latest\" not in plugins:\n plugins.append(\"@plannotator/opencode@latest\")\n print(\"✓ plannotator plugin added\")\n\n# Add omx if not present\nif \"@oh-my-opencode/opencode@latest\" not in plugins:\n plugins.append(\"@oh-my-opencode/opencode@latest\")\n print(\"\\u2713 omx (oh-my-opencode) plugin added\")\n\n# Add agentation MCP if not present\nmcp_config = config.setdefault(\"mcp\", {})\nif \"agentation\" not in mcp_config:\n mcp_config[\"agentation\"] = {\n \"type\": \"local\",\n \"command\": [\"npx\", \"-y\", \"agentation-mcp\", \"server\"]\n }\n print(\"\\u2713 agentation MCP added to opencode.json\")\nelse:\n print(\"\\u2713 agentation MCP already present\")\n\n# Migrate legacy instructions to valid type (OpenCode expects array)\nlegacy_instructions = config.get(\"instructions\")\nif isinstance(legacy_instructions, str):\n text = legacy_instructions.strip()\n config[\"instructions\"] = [text] if text else []\nelif legacy_instructions is not None and not isinstance(legacy_instructions, list):\n config[\"instructions\"] = [str(legacy_instructions)]\n\n# Register JEO slash commands in OpenCode's \"command\" table\ncommands = config.setdefault(\"command\", {})\njeo_commands = {\n \"jeo-plan\": {\n \"description\": \"JEO planning workflow (ralph + plannotator)\",\n \"template\": (\n \"Write plan.md, then run mandatory PLAN gate: \"\n \"bash .agent-skills/jeo/scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3. \"\n \"If plannotator is missing, the PLAN gate auto-installs it first. \"\n \"This waits for approve/feedback, restarts dead sessions up to 3 times, \"\n \"and asks whether to stop PLAN after repeated failures.\"\n ),\n },\n \"jeo-exec\": {\n \"description\": \"JEO execute workflow (team/bmad)\",\n \"template\": (\n \"Execute approved plan using team agents if available; otherwise use BMAD workflow phases.\"\n ),\n },\n \"jeo-annotate\": {\n \"description\": \"Process agentation annotations (VERIFY_UI loop)\",\n \"template\": (\n \"Run agentation watch loop: acknowledge, implement fix, resolve, and repeat until pending count is 0.\"\n ),\n },\n \"jeo-verify\": {\n \"description\": \"Verify browser behavior with agent-browser\",\n \"template\": \"Run agent-browser snapshot and verify the UI/flow for the current task.\",\n },\n \"jeo-cleanup\": {\n \"description\": \"Cleanup worktrees after JEO completion\",\n \"template\": \"Run: bash .agent-skills/jeo/scripts/worktree-cleanup.sh\",\n },\n}\n\nadded = 0\nfor name, spec in jeo_commands.items():\n if name not in commands:\n commands[name] = spec\n added += 1\n\nif added:\n print(f\"\\u2713 Added {added} JEO command(s) to opencode.json\")\nelse:\n print(\"\\u2713 JEO commands already present\")\n\nwith open(config_path, \"w\") as f:\n json.dump(config, f, indent=2)\nprint(f\"✓ opencode.json saved: {config_path}\")\nPYEOF\n\n ok \"OpenCode configuration updated\"\nfi\n\necho \"\"\necho \"OpenCode slash commands after setup:\"\necho \" /jeo-plan ← Start planning workflow\"\necho \" /jeo-exec \\u2190 Execute task\"\necho \" /jeo-annotate \\u2190 agentation watch loop (VERIFY_UI); /jeo-agentui is deprecated alias\"\necho \" /jeo-verify \\u2190 Verify UI with agent-browser\"\necho \" /jeo-cleanup ← Clean worktrees\"\necho \" /plannotator-review ← Code review UI\"\necho \"\"\necho \"If OpenCode shows 'readonly database', run:\"\necho \" bash $(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/run-opencode-safe.sh\"\necho \"\"\necho \" Restart OpenCode to activate plugins.\"\necho \"\"\nok \"OpenCode setup complete\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6637,"content_sha256":"46820148bc19133815e1324d09d588f1e0de580648f2375de8d1a2817f385f61"},{"filename":"scripts/worktree-cleanup.sh","content":"#!/usr/bin/env bash\n# JEO Skill — Worktree Cleanup Script\n# Removes stale git worktrees after task completion\n# Usage: bash worktree-cleanup.sh [--force] [--dry-run] [--list]\n\nset -euo pipefail\n\nGREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; RED='\\033[0;31m'; BLUE='\\033[0;34m'; NC='\\033[0m'\nok() { echo -e \"${GREEN}✓${NC} $*\"; }\nwarn() { echo -e \"${YELLOW}⚠${NC} $*\"; }\nerr() { echo -e \"${RED}✗${NC} $*\"; }\ninfo() { echo -e \"${BLUE}→${NC} $*\"; }\n\nDRY_RUN=false; FORCE=false; LIST_ONLY=false\nfor arg in \"$@\"; do\n case $arg in\n --dry-run) DRY_RUN=true ;;\n --force)\n FORCE=true\n warn \"WARNING: --force removes ALL non-main worktrees.\"\n warn \" Active feature branches in separate worktrees will also be removed.\"\n warn \" Press Ctrl+C within 5 seconds to cancel...\"\n sleep 5\n ;;\n --list) LIST_ONLY=true ;;\n esac\ndone\n\necho \"\"\necho \"JEO — Worktree Cleanup\"\necho \"=====================\"\n\n# ── Check git repo ─────────────────────────────────────────────────────────────\nif ! git rev-parse --git-dir >/dev/null 2>&1; then\n err \"Not a git repository. Run from project root.\"\n exit 1\nfi\n\nGIT_ROOT=$(git rev-parse --show-toplevel)\n\n# ── List all worktrees ─────────────────────────────────────────────────────────\ninfo \"Current worktrees:\"\ngit worktree list\necho \"\"\n\n# ── Identify stale worktrees ──────────────────────────────────────────────────\nMAIN_WORKTREE=$(git worktree list | head -1 | awk '{print $1}')\nWORKTREES_TO_REMOVE=()\nDIRTY_WORKTREES=()\n\nwhile IFS= read -r line; do\n WORKTREE_PATH=$(echo \"$line\" | awk '{print $1}')\n\n # Skip main worktree\n [[ \"$WORKTREE_PATH\" == \"$MAIN_WORKTREE\" ]] && continue\n\n if $FORCE; then\n WORKTREES_TO_REMOVE+=(\"$WORKTREE_PATH\")\n elif [[ -z \"$(git -C \"$WORKTREE_PATH\" status --porcelain 2>/dev/null)\" ]]; then\n WORKTREES_TO_REMOVE+=(\"$WORKTREE_PATH\")\n else\n DIRTY_WORKTREES+=(\"$WORKTREE_PATH\")\n fi\ndone \u003c \u003c(git worktree list | tail -n +2)\n\n# ── List mode ─────────────────────────────────────────────────────────────────\nif $LIST_ONLY; then\n if [[ ${#WORKTREES_TO_REMOVE[@]} -eq 0 ]] && [[ ${#DIRTY_WORKTREES[@]} -eq 0 ]]; then\n ok \"No extra worktrees found\"\n else\n if [[ ${#WORKTREES_TO_REMOVE[@]} -gt 0 ]]; then\n echo \"Worktrees ready to remove:\"\n for wt in \"${WORKTREES_TO_REMOVE[@]}\"; do\n echo \" - $wt\"\n done\n fi\n if [[ ${#DIRTY_WORKTREES[@]} -gt 0 ]]; then\n echo \"Dirty worktrees skipped by default:\"\n for wt in \"${DIRTY_WORKTREES[@]}\"; do\n echo \" - $wt\"\n done\n echo \"Use --force to remove dirty worktrees after reviewing them.\"\n fi\n fi\n exit 0\nfi\n\n# ── Remove identified worktrees ───────────────────────────────────────────────\nif [[ ${#WORKTREES_TO_REMOVE[@]} -eq 0 ]]; then\n ok \"No extra worktrees to remove\"\nelse\n info \"Removing ${#WORKTREES_TO_REMOVE[@]} worktree(s)...\"\n for WORKTREE_PATH in \"${WORKTREES_TO_REMOVE[@]}\"; do\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would remove: $WORKTREE_PATH\"\n else\n info \"Removing: $WORKTREE_PATH\"\n if $FORCE; then\n if git worktree remove \"$WORKTREE_PATH\" --force 2>/dev/null; then\n ok \"Removed: $WORKTREE_PATH\"\n else\n warn \"Could not remove $WORKTREE_PATH with --force\"\n fi\n elif git worktree remove \"$WORKTREE_PATH\" 2>/dev/null; then\n ok \"Removed: $WORKTREE_PATH\"\n else\n warn \"Could not remove $WORKTREE_PATH (dirty or already gone)\"\n fi\n fi\n done\nfi\n\nif [[ ${#DIRTY_WORKTREES[@]} -gt 0 ]] && ! $FORCE; then\n warn \"Skipped ${#DIRTY_WORKTREES[@]} dirty worktree(s). Re-run with --force after review.\"\nfi\n\n# ── Prune stale worktree entries ──────────────────────────────────────────────\ninfo \"Pruning stale worktree references...\"\nif $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would run: git worktree prune\"\nelse\n git worktree prune\n ok \"Stale worktree references pruned\"\nfi\n\n# ── Deactivate ralphmode (revert project permissionMode) ─────────────────────\n_PLAN_GATE_SCRIPT=\"\"\nfor _candidate in \\\n \"$(dirname \"${BASH_SOURCE[0]}\")/claude-plan-gate.py\" \\\n \"$HOME/.agent-skills/jeo/scripts/claude-plan-gate.py\" \\\n \"$HOME/.claude/skills/jeo/scripts/claude-plan-gate.py\"; do\n if [[ -f \"$_candidate\" ]]; then\n _PLAN_GATE_SCRIPT=\"$_candidate\"\n break\n fi\ndone\n\nif [[ -n \"$_PLAN_GATE_SCRIPT\" ]] && command -v python3 >/dev/null 2>&1; then\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would deactivate ralphmode (restore project permissionMode)\"\n else\n python3 \"$_PLAN_GATE_SCRIPT\" --deactivate 2>&1 || true\n fi\nfi\n\n# ── Update JEO state ──────────────────────────────────────────────────────────\nSTATE_FILE=\"$GIT_ROOT/.omc/state/jeo-state.json\"\nif [[ -f \"$STATE_FILE\" ]] && command -v python3 >/dev/null 2>&1; then\n if $DRY_RUN; then\n echo -e \"${YELLOW}[DRY-RUN]${NC} Would update JEO state: phase=cleanup, cleanup_completed=true\"\n else\n STATE_FILE=\"$STATE_FILE\" python3 - \u003c\u003c'PYEOF'\nimport json, os\nstate_path = os.environ[\"STATE_FILE\"]\ntry:\n with open(state_path) as f:\n state = json.load(f)\n state[\"phase\"] = \"done\"\n # state[\"worktrees\"] = [] # 미구현 필드 — SKILL.md 초기 스키마에서도 제거됨. 향후 멀티-worktree 추적 구현 시 복원.\n state[\"cleanup_completed\"] = True\n with open(state_path, \"w\") as f:\n json.dump(state, f, indent=2)\n print(\"✓ JEO state updated: cleanup complete\")\nexcept Exception as e:\n print(f\"⚠ Could not update state: {e}\")\nPYEOF\n fi\nfi\n\n# ── Final summary ─────────────────────────────────────────────────────────────\necho \"\"\necho \"Final worktree list:\"\ngit worktree list\necho \"\"\nok \"Worktree cleanup complete\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6664,"content_sha256":"068cf0397f04aa1d8f5a4da782287922917bbdd7c179c5e1030516edb0618c4f"},{"filename":"skill-report.json","content":"{\n \"schema_version\": \"2.0\",\n \"meta\": {\n \"generated_at\": \"2026-03-22T08:15:08.427Z\",\n \"slug\": \"supercent-io-jeo\",\n \"source_url\": \"https://github.com/supercent-io/skills-template/tree/main/.agent-skills/jeo/\",\n \"source_ref\": \"main\",\n \"model\": \"claude\",\n \"analysis_version\": \"3.0.0\",\n \"source_type\": \"community\",\n \"content_hash\": \"5bf136940012cfec17755f31385b4b1a349ae066185e216a28e4bbffd48d4109\",\n \"tree_hash\": \"97656511661be0b7c3942291d7d7a958e82702a898d1e9f5b3f47d613bda1619\"\n },\n \"skill\": {\n \"name\": \"jeo\",\n \"description\": \"JEO — Integrated AI agent orchestration skill. Plan with ralph+plannotator, execute with team/bmad, verify browser behavior with agent-browser, apply UI feedback with agentation(annotate), auto-cleanup worktrees after completion. Supports Claude, Codex, Gemini CLI, and OpenCode. Install: ralph, omc, omx, ohmg, bmad, plannotator, agent-browser, agentation.\",\n \"summary\": \"Unified AI agent orchestration for multi-agent workflows with visual plan approval and UI feedback loops\",\n \"icon\": \"📦\",\n \"version\": \"1.0.0\",\n \"author\": \"supercent-io\",\n \"license\": \"MIT\",\n \"category\": \"coding\",\n \"tags\": [\n \"orchestration\",\n \"multi-agent\",\n \"planning\",\n \"ui-verification\",\n \"workflow\"\n ],\n \"supported_tools\": [\n \"claude\",\n \"codex\",\n \"claude-code\"\n ],\n \"risk_factors\": [\n \"external_commands\",\n \"network\",\n \"filesystem\",\n \"env_access\"\n ]\n },\n \"security_audit\": {\n \"risk_level\": \"low\",\n \"is_blocked\": false,\n \"safe_to_publish\": true,\n \"summary\": \"All 1127 static findings evaluated as false positives. This is a legitimate AI agent orchestration skill that coordinates planning, execution, verification, and cleanup phases. Commands are hardcoded or use trusted tooling from official sources. Network access is limited to package installation and localhost agentation communication. Filesystem operations are confined to project directories and standard config paths. No malicious intent or data exfiltration patterns detected.\",\n \"critical_findings\": [],\n \"high_findings\": [],\n \"medium_findings\": [],\n \"low_findings\": [\n {\n \"title\": \"MD5 Used for Session Isolation Only\",\n \"description\": \"MD5 hashing is used exclusively for generating unique session/temp directory names in /tmp (e.g., hashlib.md5(os.getcwd().encode()).hexdigest()[:8]). This is not a cryptographic security concern as MD5 is not used for security-sensitive operations. SHA-256 is correctly used for plan hash comparison.\",\n \"locations\": [\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 275,\n \"line_end\": 275\n },\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 587,\n \"line_end\": 587\n },\n {\n \"file\": \"scripts/run-opencode-safe.sh\",\n \"line_start\": 12,\n \"line_end\": 12\n }\n ],\n \"confidence\": 0.92,\n \"confidence_reasoning\": \"Confirmed through code analysis: MD5 generates session keys for /tmp directory isolation, not for security. SHA-256 is used for plan integrity checks.\"\n },\n {\n \"title\": \"Shell Command Substitution in Setup Scripts\",\n \"description\": \"Command substitution syntax (e.g., $(), backticks) is used throughout setup scripts for variable expansion and script path resolution. All commands are hardcoded or use trusted tooling paths.\",\n \"locations\": [\n {\n \"file\": \"scripts/setup-opencode.sh\",\n \"line_start\": 40,\n \"line_end\": 40\n },\n {\n \"file\": \"scripts/setup-opencode.sh\",\n \"line_start\": 56,\n \"line_end\": 56\n }\n ],\n \"confidence\": 0.88,\n \"confidence_reasoning\": \"Command substitution is standard shell scripting pattern used for path resolution and environment variable access. No user input flows into these commands.\"\n }\n ],\n \"dangerous_patterns\": [\n {\n \"title\": \"Pipe to Shell for Package Installation\",\n \"description\": \"Static scanner flagged pipe-to-shell patterns (curl | bash) for installing bun and plannotator. EVALUATION: These are official installation methods from trusted sources (bun.sh, plannotator.ai). While not best practice, this is standard for these tools.\",\n \"locations\": [\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 134,\n \"line_end\": 134\n },\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 146,\n \"line_end\": 146\n },\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 165,\n \"line_end\": 165\n }\n ],\n \"confidence\": 0.85,\n \"confidence_reasoning\": \"These are official install scripts from trusted sources (bun.sh, plannotator.ai). Pattern is standard industry practice for these tools.\"\n },\n {\n \"title\": \"Home Directory File Access\",\n \"description\": \"Static scanner flagged access to hidden files in home directory (~/.bashrc, ~/.claude/settings.json, etc.). EVALUATION: These are standard setup scripts that configure the user's development environment by modifying config files.\",\n \"locations\": [\n {\n \"file\": \"scripts/setup-claude.sh\",\n \"line_start\": 3,\n \"line_end\": 3\n },\n {\n \"file\": \"scripts/setup-codex.sh\",\n \"line_start\": 3,\n \"line_end\": 3\n },\n {\n \"file\": \"scripts/setup-gemini.sh\",\n \"line_start\": 3,\n \"line_end\": 3\n }\n ],\n \"confidence\": 0.95,\n \"confidence_reasoning\": \"Setup scripts routinely access ~/.bashrc, ~/.config, and tool-specific config files. This is legitimate tooling behavior, not a security concern.\"\n },\n {\n \"title\": \"Reference Documentation Path Placeholder\",\n \"description\": \"Static scanner flagged 'rm -rf /path/to/worktree' as recursive delete on root/home. EVALUATION: This is documentation showing how to manually remove a worktree - the path is a placeholder, not an actual root/home path.\",\n \"locations\": [\n {\n \"file\": \"references/FLOW.md\",\n \"line_start\": 451,\n \"line_end\": 451\n }\n ],\n \"confidence\": 0.98,\n \"confidence_reasoning\": \"The path '/path/to/worktree' is clearly a documentation placeholder. The actual worktree-cleanup.sh script uses git worktree commands with proper safeguards.\"\n },\n {\n \"title\": \"subprocess.check_output for Git Operations\",\n \"description\": \"Python subprocess calls are used throughout for git operations, state file management, and running trusted tools. All arguments are hardcoded or derived from safe sources.\",\n \"locations\": [\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 101,\n \"line_end\": 101\n },\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 126,\n \"line_end\": 126\n },\n {\n \"file\": \"scripts/claude-agentation-submit-hook.py\",\n \"line_start\": 34,\n \"line_end\": 34\n }\n ],\n \"confidence\": 0.9,\n \"confidence_reasoning\": \"subprocess usage is for git operations, state file management, and running approved tools. Arguments are hardcoded or derived from safe project state files.\"\n }\n ],\n \"files_scanned\": 19,\n \"total_lines\": 5560,\n \"audit_model\": \"claude\",\n \"audited_at\": \"2026-03-22T08:15:08.427Z\",\n \"risk_factors\": [\n \"scripts\",\n \"network\",\n \"filesystem\",\n \"env_access\",\n \"external_commands\"\n ],\n \"risk_factor_evidence\": [\n {\n \"factor\": \"external_commands\",\n \"evidence\": [\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 1,\n \"line_end\": 1274\n },\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 1,\n \"line_end\": 253\n },\n {\n \"file\": \"scripts/setup-claude.sh\",\n \"line_start\": 1,\n \"line_end\": 240\n },\n {\n \"file\": \"scripts/setup-codex.sh\",\n \"line_start\": 1,\n \"line_end\": 663\n },\n {\n \"file\": \"scripts/setup-gemini.sh\",\n \"line_start\": 1,\n \"line_end\": 367\n }\n ]\n },\n {\n \"factor\": \"network\",\n \"evidence\": [\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 134,\n \"line_end\": 134\n },\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 146,\n \"line_end\": 146\n },\n {\n \"file\": \"scripts/install.sh\",\n \"line_start\": 165,\n \"line_end\": 165\n }\n ]\n },\n {\n \"factor\": \"filesystem\",\n \"evidence\": [\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 46,\n \"line_end\": 86\n },\n {\n \"file\": \"scripts/worktree-cleanup.sh\",\n \"line_start\": 1,\n \"line_end\": 177\n }\n ]\n },\n {\n \"factor\": \"env_access\",\n \"evidence\": [\n {\n \"file\": \"scripts/plannotator-plan-loop.sh\",\n \"line_start\": 94,\n \"line_end\": 94\n },\n {\n \"file\": \"scripts/setup-claude.sh\",\n \"line_start\": 60,\n \"line_end\": 60\n }\n ]\n }\n ]\n },\n \"content\": {\n \"user_title\": \"Automate full AI agent workflows with JEO orchestration\",\n \"value_statement\": \"Managing multi-agent development workflows across Claude, Codex, Gemini, and OpenCode requires coordinating planning, execution, verification, and cleanup phases. JEO provides unified orchestration with visual plan approval, browser verification, and automatic worktree cleanup.\",\n \"seo_keywords\": [\n \"JEO orchestration\",\n \"AI agent workflow\",\n \"Claude Code automation\",\n \"Codex CLI skill\",\n \"Gemini CLI integration\",\n \"multi-agent planning\",\n \"plan approval workflow\",\n \"UI verification automation\",\n \"worktree cleanup\",\n \"Claude Codex plannotator\"\n ],\n \"actual_capabilities\": [\n \"Plan tasks with ralph+plannotator for visual plan review and approval before coding\",\n \"Execute parallel agent workflows via team (Claude) or bmad orchestrator (other platforms)\",\n \"Verify browser-based behavior automatically using agent-browser snapshots\",\n \"Process UI feedback loops with agentation for annotation-based review cycles\",\n \"Clean up git worktrees automatically after task completion\",\n \"Coordinate the same workflow across Claude, Codex, Gemini CLI, and OpenCode\"\n ],\n \"limitations\": [\n \"Requires installation of dependencies: ralph, plannotator, agent-browser, agentation, and optional omc/omx/ohmg\",\n \"Browser verification requires a running dev server and localhost accessibility\",\n \"UI annotation feedback requires Agentation React component to be mounted in the project\"\n ],\n \"use_cases\": [\n {\n \"title\": \"Complex multi-agent task orchestration\",\n \"description\": \"DevOps teams managing complex features can use JEO to plan with visual approval, execute parallel agents, verify browser behavior, and automatically clean up worktrees. The plan hash prevents re-reviewing unchanged plans.\",\n \"target_user\": \"Development teams building complex features requiring multiple AI agents\"\n },\n {\n \"title\": \"Visual UI feedback loops\",\n \"description\": \"Frontend developers can trigger UI annotation review by saying 'annotate'. The agent waits for explicit submission, then processes annotations, fixes issues, and re-verifies browser rendering.\",\n \"target_user\": \"Frontend developers needing visual review and feedback on UI implementations\"\n },\n {\n \"title\": \"Cross-platform AI tool coordination\",\n \"description\": \"Users working with multiple AI coding tools can use the same JEO workflow across Claude Code, Codex, Gemini CLI, and OpenCode. Setup scripts configure each platform's specific integration.\",\n \"target_user\": \"Developers using multiple AI coding tools across different projects\"\n }\n ],\n \"prompt_templates\": [\n {\n \"title\": \"Start full JEO workflow\",\n \"prompt\": \"jeo 'implement user authentication with OAuth2'\",\n \"scenario\": \"Activate full JEO orchestration with plan, execute, verify, and cleanup phases\"\n },\n {\n \"title\": \"Plan-only with visual review\",\n \"prompt\": \"Plan this feature: 'add dark mode toggle to settings page'. Show me the plan for approval before coding.\",\n \"scenario\": \"Use ralph+plannotator for visual plan review without immediate execution\"\n },\n {\n \"title\": \"UI annotation review\",\n \"prompt\": \"annotate - review the current UI for accessibility issues and report any annotations.\",\n \"scenario\": \"Trigger UI feedback loop with agentation for annotation-based review\"\n },\n {\n \"title\": \"Resume from checkpoint\",\n \"prompt\": \"jeo - resume from previous checkpoint. Check the state file and continue where we left off.\",\n \"scenario\": \"Resume interrupted workflow using persisted state\"\n }\n ],\n \"output_examples\": [\n {\n \"input\": \"jeo 'add search functionality to dashboard'\",\n \"output\": \"JEO activated. Phase: PLAN.\\n\\nPlanning with ralph+plannotator...\\n- Plan hash: a1b2c3d4...\\n- Review status: awaiting approval\\n- Plan details saved to .omc/state/jeo-state.json\\n\\nAwaiting visual approval via plannotator...\"\n },\n {\n \"input\": \"After plan approval, agent executes the task\",\n \"output\": \"Plan approved. Transitioning to EXECUTE phase.\\n\\nTeam available: true\\nInvoking /omc:team 3:executor 'add search functionality to dashboard'\\n\\nParallel agents deployed: [analysis, planning, solutioning, implementation]\\nExecution complete.\"\n }\n ],\n \"best_practices\": [\n \"Review plans visually with ralph+plannotator before coding starts to catch direction errors early\",\n \"Use the annotate keyword when UI verification is needed - it properly gates on explicit submission\",\n \"Let the plan hash mechanism prevent unnecessary re-reviews when plan content has not changed\"\n ],\n \"anti_patterns\": [\n \"Do not skip the PLAN phase for complex features - plan approval prevents wasted execution\",\n \"Do not manually edit jeo-state.json during active workflows - use the documented checkpoint mechanism\",\n \"Do not run multiple JEO workflows in parallel on the same repository without worktree isolation\"\n ],\n \"faq\": [\n {\n \"question\": \"What AI tools does JEO support?\",\n \"answer\": \"JEO supports Claude Code, Codex CLI, Gemini CLI, and OpenCode. Each platform has specific setup scripts to configure the JEO hooks and integration.\"\n },\n {\n \"question\": \"How does the plan hash prevent unnecessary reviews?\",\n \"answer\": \"JEO computes SHA-256 hash of plan.md content. If the same hash already has terminal status (approved/feedback_required), the plan gate stays closed until plan content changes.\"\n },\n {\n \"question\": \"What is the annotate keyword for?\",\n \"answer\": \"The annotate keyword triggers UI feedback mode. It waits for explicit user submission via Send Annotations before processing annotations. This prevents premature interruption of execution.\"\n },\n {\n \"question\": \"How does worktree cleanup work?\",\n \"answer\": \"JEO runs worktree-cleanup.sh after task completion. It only removes git worktrees, not the main repository. Files created outside worktrees are preserved.\"\n },\n {\n \"question\": \"What dependencies need to be installed?\",\n \"answer\": \"Required: ralph, plannotator, agent-browser. Optional: omc, omx, ohmg, bmad, agentation. Run scripts/install.sh --all to install everything.\"\n },\n {\n \"question\": \"How does JEO coordinate across platforms?\",\n \"answer\": \"JEO uses a state file (.omc/state/jeo-state.json) that each platform reads and writes. This provides consistent state across Claude Code, Codex, Gemini, and OpenCode sessions.\"\n }\n ]\n },\n \"file_structure\": [\n {\n \"name\": \"references\",\n \"type\": \"dir\",\n \"path\": \"references\",\n \"children\": [\n {\n \"name\": \"FLOW.md\",\n \"type\": \"file\",\n \"path\": \"references/FLOW.md\",\n \"lines\": 467\n },\n {\n \"name\": \"PLANNOTATOR_RELIABILITY_CONSENSUS_2026-03-06.md\",\n \"type\": \"file\",\n \"path\": \"references/PLANNOTATOR_RELIABILITY_CONSENSUS_2026-03-06.md\",\n \"lines\": 117\n }\n ]\n },\n {\n \"name\": \"scripts\",\n \"type\": \"dir\",\n \"path\": \"scripts\",\n \"children\": [\n {\n \"name\": \"check-status.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/check-status.sh\",\n \"lines\": 283\n },\n {\n \"name\": \"claude-agentation-submit-hook.py\",\n \"type\": \"file\",\n \"path\": \"scripts/claude-agentation-submit-hook.py\",\n \"lines\": 142\n },\n {\n \"name\": \"claude-plan-gate.py\",\n \"type\": \"file\",\n \"path\": \"scripts/claude-plan-gate.py\",\n \"lines\": 348\n },\n {\n \"name\": \"claude-stop-continuation.py\",\n \"type\": \"file\",\n \"path\": \"scripts/claude-stop-continuation.py\",\n \"lines\": 147\n },\n {\n \"name\": \"ensure-agentation.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/ensure-agentation.sh\",\n \"lines\": 262\n },\n {\n \"name\": \"ensure-plannotator.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/ensure-plannotator.sh\",\n \"lines\": 66\n },\n {\n \"name\": \"install.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/install.sh\",\n \"lines\": 253\n },\n {\n \"name\": \"plannotator-plan-loop.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/plannotator-plan-loop.sh\",\n \"lines\": 500\n },\n {\n \"name\": \"run-opencode-safe.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/run-opencode-safe.sh\",\n \"lines\": 22\n },\n {\n \"name\": \"setup-claude.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/setup-claude.sh\",\n \"lines\": 240\n },\n {\n \"name\": \"setup-codex.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/setup-codex.sh\",\n \"lines\": 663\n },\n {\n \"name\": \"setup-gemini.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/setup-gemini.sh\",\n \"lines\": 367\n },\n {\n \"name\": \"setup-opencode.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/setup-opencode.sh\",\n \"lines\": 179\n },\n {\n \"name\": \"worktree-cleanup.sh\",\n \"type\": \"file\",\n \"path\": \"scripts/worktree-cleanup.sh\",\n \"lines\": 177\n }\n ]\n },\n {\n \"name\": \"opencode.json.jeo.bak\",\n \"type\": \"file\",\n \"path\": \"opencode.json.jeo.bak\",\n \"lines\": 19\n },\n {\n \"name\": \"SKILL.md\",\n \"type\": \"file\",\n \"path\": \"SKILL.md\",\n \"lines\": 1274\n },\n {\n \"name\": \"SKILL.toon\",\n \"type\": \"file\",\n \"path\": \"SKILL.toon\",\n \"lines\": 34\n }\n ]\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":19776,"content_sha256":"f1adcaffa665e428439e38178df6830942b90d2051fbaee7035d4b79c489cc94"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"JEO — Integrated Agent Orchestration","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Keyword: ","type":"text"},{"text":"jeo","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"UI-review","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"agentui (deprecated)","type":"text","marks":[{"type":"code_inline"}]},{"text":" | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode","type":"text"}]},{"type":"paragraph","content":[{"text":"A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup)","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Control Layers","type":"text"}]},{"type":"paragraph","content":[{"text":"JEO uses one cross-platform abstraction for orchestration:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"settings","type":"text","marks":[{"type":"code_inline"}]},{"text":": platform/runtime configuration such as Claude hooks, Codex ","type":"text"},{"text":"config.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":", Gemini ","type":"text"},{"text":"settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", MCP registration, and prompt parameters","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"rules","type":"text","marks":[{"type":"code_inline"}]},{"text":": policy constraints that must hold on every platform","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"hooks","type":"text","marks":[{"type":"code_inline"}]},{"text":": event callbacks that enforce those rules on each platform","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The key JEO rules are:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"do not reopen the PLAN gate when the current plan hash already has a terminal result","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"only a revised plan resets ","type":"text"},{"text":"plan_gate_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"pending","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"do not process agentation annotations before explicit submit/onSubmit opens the submit gate","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The authoritative state is ","type":"text"},{"text":".omc/state/jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":". Hooks may help advance the workflow, but they must obey the state file.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"0. Agent Execution Protocol (follow immediately upon ","type":"text"},{"text":"jeo","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword detection)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"The following are commands, not descriptions. Execute them in order. Each step only proceeds after the previous one completes.","type":"text"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 0: State File Bootstrap (required — always first)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"mkdir -p .omc/state .omc/plans .omc/logs","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":".omc/state/jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" does not exist, create it:","type":"text"}]},{"type":"paragraph","content":[{"text":"\u003c!-- NOTE: The ","type":"text"},{"text":"worktrees","type":"text","marks":[{"type":"code_inline"}]},{"text":" array was removed from the initial schema as it is not yet implemented. Add it back when multi-worktree parallel execution tracking is needed. worktree-cleanup.sh queries git worktree list directly, so it works without this field. -->","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"phase\": \"plan\",\n \"task\": \"\u003cdetected task>\",\n \"plan_approved\": false,\n \"plan_gate_status\": \"pending\",\n \"plan_current_hash\": null,\n \"last_reviewed_plan_hash\": null,\n \"last_reviewed_plan_at\": null,\n \"plan_review_method\": null,\n \"team_available\": null,\n \"retry_count\": 0,\n \"last_error\": null,\n \"checkpoint\": null,\n \"created_at\": \"\u003cISO 8601>\",\n \"updated_at\": \"\u003cISO 8601>\",\n \"agentation\": {\n \"active\": false,\n \"session_id\": null,\n \"keyword_used\": null,\n \"submit_gate_status\": \"idle\",\n \"submit_signal\": null,\n \"submit_received_at\": null,\n \"submitted_annotation_count\": 0,\n \"started_at\": null,\n \"timeout_seconds\": 120,\n \"annotations\": { \"total\": 0, \"acknowledged\": 0, \"resolved\": 0, \"dismissed\": 0, \"pending\": 0 },\n \"completed_at\": null,\n \"exit_reason\": null\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Notify the user:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"JEO activated. Phase: PLAN. Add the ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword if a UI feedback loop is needed.\"","type":"text"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 0.1: Error Recovery Protocol (applies to all STEPs)","type":"text"}]},{"type":"paragraph","content":[{"text":"Checkpoint recording — immediately after entering each STEP:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Execute immediately at the start of each STEP (agent updates jeo-state.json directly)\npython3 -c \"\nimport json, datetime, os, subprocess, tempfile\ntry:\n root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root = os.getcwd()\nf = os.path.join(root, '.omc/state/jeo-state.json')\nif os.path.exists(f):\n import fcntl\n with open(f, 'r+') as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n d = json.load(fh)\n d['checkpoint']='\u003ccurrent_phase>' # 'plan'|'execute'|'verify'|'cleanup'\n d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'\n fh.seek(0)\n json.dump(d, fh, ensure_ascii=False, indent=2)\n fh.truncate()\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n\" 2>/dev/null || true","type":"text"}]},{"type":"paragraph","content":[{"text":"last_error recording — on pre-flight failure or exception:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"python3 -c \"\nimport json, datetime, os, subprocess, fcntl\ntry:\n root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root = os.getcwd()\nf = os.path.join(root, '.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f, 'r+') as fh:\n fcntl.flock(fh, fcntl.LOCK_EX)\n try:\n d = json.load(fh)\n d['last_error']='\u003cerror message>'\n d['retry_count']=d.get('retry_count',0)+1\n d['updated_at']=datetime.datetime.utcnow().isoformat()+'Z'\n fh.seek(0)\n json.dump(d, fh, ensure_ascii=False, indent=2)\n fh.truncate()\n finally:\n fcntl.flock(fh, fcntl.LOCK_UN)\n\" 2>/dev/null || true","type":"text"}]},{"type":"paragraph","content":[{"text":"Checkpoint-based resume on restart:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# If jeo-state.json already exists, resume from checkpoint\npython3 -c \"\nimport json, os, subprocess\ntry:\n root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root = os.getcwd()\nf = os.path.join(root, '.omc/state/jeo-state.json')\nif os.path.exists(f):\n d=json.load(open(f))\n cp=d.get('checkpoint')\n err=d.get('last_error')\n if err: print(f'Previous error: {err}')\n if cp: print(f'Resuming from: {cp}')\n\" 2>/dev/null || true","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Rule","type":"text","marks":[{"type":"strong"}]},{"text":": Before ","type":"text"},{"text":"exit 1","type":"text","marks":[{"type":"code_inline"}]},{"text":" in pre-flight, always update ","type":"text"},{"text":"last_error","type":"text","marks":[{"type":"code_inline"}]},{"text":" and increment ","type":"text"},{"text":"retry_count","type":"text","marks":[{"type":"code_inline"}]},{"text":". If ","type":"text"},{"text":"retry_count >= 3","type":"text","marks":[{"type":"code_inline"}]},{"text":", ask the user whether to abort.","type":"text"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 1: PLAN (never skip)","type":"text"}]},{"type":"paragraph","content":[{"text":"Pre-flight (required before entering):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Record checkpoint\npython3 -c \"\nimport json,datetime,os,subprocess,fcntl,tempfile\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d.update({'checkpoint':'plan','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true\n\n# NOTE: Claude Code — skip this entire bash block.\n# plannotator is a hook-only binary; calling it directly always fails.\n# For Claude Code: call EnterPlanMode → write plan → call ExitPlanMode.\n# The ExitPlanMode PermissionRequest hook fires plannotator automatically.\n# The following script is for Codex / Gemini / OpenCode only.\n\n# GUARD: enforce no-repeat PLAN review by plan hash.\n# same hash + terminal gate status => skip reopening the plan gate\n# revised plan.md content => reset gate to pending and review again\nPLAN_GATE_STATUS=$(python3 -c \"\nimport json, os\ntry:\n s = json.load(open('.omc/state/jeo-state.json'))\n print(s.get('plan_gate_status', 'pending'))\nexcept Exception:\n print('pending')\n\" 2>/dev/null || echo \"pending\")\n\nHASH_MATCH=$(python3 -c \"\nimport hashlib, json, os\ntry:\n s = json.load(open('.omc/state/jeo-state.json'))\n if not os.path.exists('plan.md'):\n print('no-match')\n else:\n current_hash = hashlib.sha256(open('plan.md', 'rb').read()).hexdigest()\n print('match' if current_hash == (s.get('last_reviewed_plan_hash') or '') else 'no-match')\nexcept Exception:\n print('no-match')\n\" 2>/dev/null || echo \"no-match\")\n\nif [[ \"$HASH_MATCH\" == \"match\" && \"$PLAN_GATE_STATUS\" =~ ^(approved|manual_approved)$ ]]; then\n echo \"✅ Current plan hash already approved. Skipping re-review.\"\n exit 0\nfi\n\n# BUG FIX (v1.3.1): feedback_required + same hash must NOT exit 0 (approved).\n# The plan has outstanding feedback and must be revised before re-opening plannotator.\n# Exiting 0 here would cause JEO to proceed to EXECUTE with an unapproved plan.\nif [[ \"$HASH_MATCH\" == \"match\" && \"$PLAN_GATE_STATUS\" == \"feedback_required\" ]]; then\n echo \"❌ Feedback pending for this plan hash — revise plan.md content (new hash required) before re-opening plannotator.\"\n echo \" Check .omc/state/jeo-state.json → plannotator_feedback for the recorded feedback.\"\n exit 1\nfi\n\nif [[ \"$HASH_MATCH\" == \"match\" && \"$PLAN_GATE_STATUS\" == \"infrastructure_blocked\" ]]; then\n echo \"⚠️ Infrastructure blocked for current plan hash. Use manual TTY gate or set plan_gate_status='manual_approved' in jeo-state.json.\"\n exit 32\nfi\n\n# plannotator is mandatory for the PLAN step (Codex/Gemini/OpenCode).\n# If missing, JEO auto-installs it before opening the PLAN gate.\n# Resolve the JEO scripts directory (works from any CWD)\n_JEO_SCRIPTS=\"\"\nfor _candidate in \\\n \"${JEO_SKILL_DIR:-}/scripts\" \\\n \"$HOME/.agent-skills/jeo/scripts\" \\\n \"$HOME/.codex/skills/jeo/scripts\" \\\n \"$(pwd)/.agent-skills/jeo/scripts\" \\\n \"scripts\" \\\n ; do\n if [ -f \"${_candidate}/plannotator-plan-loop.sh\" ]; then\n _JEO_SCRIPTS=\"$_candidate\"\n break\n fi\ndone\n\nif [ -z \"$_JEO_SCRIPTS\" ]; then\n echo \"❌ JEO scripts not found. Re-run: bash setup-codex.sh (or setup-gemini.sh)\"\n exit 1\nfi\n\nif ! bash \"${_JEO_SCRIPTS}/ensure-plannotator.sh\"; then\n echo \"❌ plannotator auto-install failed: cannot proceed with PLAN step.\"\n echo \" Retry: bash ${_JEO_SCRIPTS}/../scripts/install.sh --with-plannotator\"\n exit 1\nfi\n\n# Required PLAN gate (Codex / Gemini / OpenCode):\n# - Must wait until approve/feedback is received\n# - Auto-restart on session exit (up to 3 times)\n# - After 3 exits, ask user whether to end PLAN\nFEEDBACK_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')\nFEEDBACK_FILE=\"${FEEDBACK_DIR}/plannotator_feedback.txt\"\nbash \"${_JEO_SCRIPTS}/plannotator-plan-loop.sh\" plan.md \"$FEEDBACK_FILE\" 3\nPLAN_RC=$?\n\nif [ \"$PLAN_RC\" -eq 0 ]; then\n echo \"✅ Plan approved\"\nelif [ \"$PLAN_RC\" -eq 10 ]; then\n echo \"❌ Plan not approved — apply feedback, revise plan.md, and retry\"\n exit 1\nelif [ \"$PLAN_RC\" -eq 32 ]; then\n echo \"⚠️ plannotator UI unavailable (sandbox/CI). Entering Conversation Approval Mode:\"\n echo \" 1. Output plan.md content to user in conversation\"\n echo \" 2. Ask user: 'approve' to proceed or provide feedback\"\n echo \" 3. DO NOT proceed to EXECUTE until user explicitly approves\"\n exit 32\nelif [ \"$PLAN_RC\" -eq 30 ] || [ \"$PLAN_RC\" -eq 31 ]; then\n echo \"⛔ PLAN exit decision (or awaiting confirmation). Confirm with user before retrying.\"\n exit 1\nelse\n echo \"❌ plannotator PLAN gate failed (code=$PLAN_RC)\"\n exit 1\nfi\nmkdir -p .omc/plans .omc/logs","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Write ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (include goal, steps, risks, and completion criteria)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Invoke plannotator","type":"text","marks":[{"type":"strong"}]},{"text":" (per platform):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Claude Code (hook mode — only supported method)","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"plannotator","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a hook-only binary. It cannot be called via MCP tool or CLI directly. Call ","type":"text"},{"text":"EnterPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":", write the plan content in plan mode, then call ","type":"text"},{"text":"ExitPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":". The ","type":"text"},{"text":"ExitPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" 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.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Codex / Gemini / OpenCode","type":"text","marks":[{"type":"strong"}]},{"text":": run blocking CLI (never use ","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# _JEO_SCRIPTS must be resolved first via the dynamic path discovery block in the pre-flight above\nbash \"${_JEO_SCRIPTS}/plannotator-plan-loop.sh\" plan.md /tmp/plannotator_feedback.txt 3","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"plannotator","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing, JEO must auto-run ","type":"text"},{"text":"bash \"${_JEO_SCRIPTS}/ensure-plannotator.sh\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" first and continue only after the CLI is available.","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check result:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"approved: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Claude Code: hook returns approved) → update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"execute\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"plan_approved","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"enter STEP 2","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Not approved (Claude Code: hook returns feedback; others: ","type":"text"},{"text":"exit 10","type":"text","marks":[{"type":"code_inline"}]},{"text":") → read feedback, revise ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" → repeat step 2","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Infrastructure blocked (","type":"text"},{"text":"exit 32","type":"text","marks":[{"type":"code_inline"}]},{"text":") → localhost bind unavailable (e.g., sandbox/CI). Use manual gate in TTY; confirm with user and retry outside sandbox in non-TTY","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Session exited 3 times (","type":"text"},{"text":"exit 30/31","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ask user whether to end PLAN and decide to abort or resume","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"NEVER: enter EXECUTE without ","type":"text","marks":[{"type":"strong"}]},{"text":"approved: true","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":". NEVER: run with ","type":"text","marks":[{"type":"strong"}]},{"text":"&","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" background.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"NEVER: reopen the same unchanged plan after ","type":"text","marks":[{"type":"strong"}]},{"text":"approved","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" or ","type":"text","marks":[{"type":"strong"}]},{"text":"manual_approved","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — it is already approved.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"NEVER: treat ","type":"text","marks":[{"type":"strong"}]},{"text":"feedback_required","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" as approved — it means the plan was REJECTED and must be revised.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"When ","type":"text","marks":[{"type":"strong"}]},{"text":"feedback_required","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" + same hash → exit 1 (not 0), revise plan content to get a new hash, then re-open plannotator.","type":"text","marks":[{"type":"strong"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 2: EXECUTE","type":"text"}]},{"type":"paragraph","content":[{"text":"Pre-flight (auto-detect team availability):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Record checkpoint\npython3 -c \"\nimport json,datetime,os,subprocess,fcntl\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d.update({'checkpoint':'execute','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true\n\nTEAM_AVAILABLE=false\nif [[ \"${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-}\" =~ ^(1|true|True|yes|YES)$ ]]; then\n TEAM_AVAILABLE=true\nelif python3 -c \"\nimport json, os, sys\ntry:\n s = json.load(open(os.path.expanduser('~/.claude/settings.json')))\n val = s.get('env', {}).get('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS', '')\n sys.exit(0 if str(val) in ('1', 'true', 'True', 'yes') else 1)\nexcept Exception:\n sys.exit(1)\n\" 2>/dev/null; then\n TEAM_AVAILABLE=true\nfi\nexport TEAM_AVAILABLE_BOOL=\"$TEAM_AVAILABLE\"\npython3 -c \"\nimport json,os,subprocess,fcntl\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d['team_available']=os.environ.get('TEAM_AVAILABLE_BOOL','false').lower()=='true'\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"execute\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Team available (Claude Code + omc)","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"/omc:team 3:executor \"\u003ctask>\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Claude Code but no team","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"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.\"\nexit 1","type":"text"}]},{"type":"paragraph","content":[{"text":"Never fall back to single-agent execution in Claude Code.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No omc (BMAD fallback — Codex / Gemini / OpenCode only)","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"/workflow-init # Initialize BMAD\n/workflow-status # Check current step","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 3: VERIFY","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"verify\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Basic verification with agent-browser","type":"text","marks":[{"type":"strong"}]},{"text":" (when browser UI is present):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"agent-browser snapshot http://localhost:3000","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword detected → ","type":"text"},{"text":"enter STEP 3.1","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Otherwise → ","type":"text"},{"text":"enter STEP 4","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"STEP 3.1: VERIFY_UI (only when ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword is detected)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-flight check (required before entering):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Auto-start server, auto-install package, auto-inject component (see §3.3.1 for full script)\nbash scripts/ensure-agentation.sh --project-dir \"${PROJECT_DIR:-$PWD}\" --endpoint \"http://localhost:4747\" || true\n# If agentation-mcp server is still not healthy after pre-flight → graceful skip to STEP 4\nif ! curl -sf --connect-timeout 2 http://localhost:4747/health >/dev/null 2>&1; then\n python3 -c \"","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"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","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"2. Update `jeo-state.json`: `phase = \"verify_ui\"`, `agentation.active = true`, `agentation.submit_gate_status = \"waiting_for_submit\"`\n3. Wait for explicit human submit:\n- **Claude Code**: wait for `UserPromptSubmit` after the user presses **Send Annotations** / `onSubmit`\n- **Codex / Gemini / OpenCode**: wait until the human confirms submission and the agent emits `ANNOTATE_READY` (or compatibility alias `AGENTUI_READY`)\n4. Before that submit signal arrives, do not read `/pending`, do not acknowledge annotations, and do not start the fix loop\n5. After submit arrives, switch `agentation.submit_gate_status = \"submitted\"` and record `submit_signal`, `submit_received_at`, and `submitted_annotation_count`\n6. **Claude Code (MCP)**: blocking call to `agentation_watch_annotations` (`batchWindowSeconds:10`, `timeoutSeconds:120`)\n7. **Codex / Gemini / OpenCode (HTTP)**: polling loop via `GET http://localhost:4747/pending`\n8. Process each annotation: `acknowledge` → navigate code via `elementPath` → apply fix → `resolve`\n9. `count=0` or timeout → reset the submit gate or finish the sub-phase → **enter STEP 4**\n\n**NEVER: process draft annotations before submit/onSubmit.**\n\n---\n\n### STEP 4: CLEANUP\n\n**Pre-flight (check before entering):**\n```bash\n# Record checkpoint\npython3 -c \"\nimport json,datetime,os,subprocess,fcntl\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d.update({'checkpoint':'cleanup','updated_at':datetime.datetime.utcnow().isoformat()+'Z'})\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true\n\nif ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then\necho \"⚠️ Not a git repository — skipping worktree cleanup\"\nelse\nUNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')\n[[ \"$UNCOMMITTED\" -gt 0 ]] && echo \"⚠️ ${UNCOMMITTED} uncommitted change(s) — recommend commit/stash before cleanup\"\nfi","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"cleanup\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Worktree cleanup:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/worktree-cleanup.sh || git worktree prune","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"done\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"1. Quick Start","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Source of truth","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"https://github.com/supercent-io/skills-template","type":"text","marks":[{"type":"code_inline"}]},{"text":" Local paths like ","type":"text"},{"text":"~/.claude/skills/jeo/","type":"text","marks":[{"type":"code_inline"}]},{"text":" are copies installed via ","type":"text"},{"text":"npx skills add","type":"text","marks":[{"type":"code_inline"}]},{"text":". To update to the latest version, reinstall using the command below.","type":"text"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Install JEO (npx skills add — recommended)\nnpx skills add https://github.com/supercent-io/skills-template --skill jeo\n\n# Full install (all AI tools + all components)\nbash scripts/install.sh --all\n\n# Check status\nbash scripts/check-status.sh\n\n# Individual AI tool setup\nbash scripts/setup-claude.sh # Claude Code plugin + hooks\nbash scripts/setup-codex.sh # Codex CLI developer_instructions\nbash scripts/setup-gemini.sh # Gemini CLI hooks + GEMINI.md\nbash scripts/setup-opencode.sh # OpenCode plugin registration","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"2. Installed Components","type":"text"}]},{"type":"paragraph","content":[{"text":"Tools that JEO installs and configures:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tool","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Install Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"omc","type":"text","marks":[{"type":"strong"}]},{"text":" (oh-my-claudecode)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Claude Code multi-agent orchestration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"omx","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-agent orchestration for OpenCode","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bunx oh-my-opencode setup","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ohmg","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-agent framework for Gemini CLI","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bunx oh-my-ag","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bmad","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"BMAD workflow orchestration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Included in skills","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ralph","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Self-referential completion loop","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Included in omc or install separately","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plannotator","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Visual plan/diff review","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto-installed during PLAN via ","type":"text"},{"text":"bash scripts/ensure-plannotator.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or preinstall with ","type":"text"},{"text":"bash scripts/install.sh --with-plannotator","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"agentation","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"UI annotation → agent code fix integration (","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword, ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" compatibility maintained)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bash scripts/install.sh --with-agentation","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"agent-browser","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Headless browser for AI agents — ","type":"text"},{"text":"primary tool for browser behavior verification","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npm install -g agent-browser","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"playwriter","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Playwright-based browser automation (optional)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npm install -g playwriter","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"3. JEO Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Full Flow","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"jeo \"\u003ctask>\"\n │\n ▼\n[1] PLAN (ralph + plannotator)\n Draft plan with ralph → visual review with plannotator → Approve/Feedback\n │\n ▼\n[2] EXECUTE\n ├─ team available? → /omc:team N:executor \"\u003ctask>\"\n │ staged pipeline: plan→prd→exec→verify→fix\n └─ no team? → /bmad /workflow-init → run BMAD steps\n │\n ▼\n[3] VERIFY (agent-browser — default behavior)\n Verify browser behavior with agent-browser\n → capture snapshot → confirm UI/functionality is working\n │\n ├─ with annotate keyword → [3.3.1] VERIFY_UI (agentation watch loop)\n │ agentation_watch_annotations blocking → annotation ack→fix→resolve loop\n │\n ▼\n[4] CLEANUP\n After all work is done → bash scripts/worktree-cleanup.sh\n git worktree prune","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3.1 PLAN Step (ralph + plannotator)","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Platform note","type":"text","marks":[{"type":"strong"}]},{"text":": The ","type":"text"},{"text":"/ralph","type":"text","marks":[{"type":"code_inline"}]},{"text":" slash command is only available in Claude Code (omc). Use the \"alternative method\" below for Codex/Gemini/OpenCode.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Claude Code (omc):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/ralph \"jeo-plan: \u003ctask>\" --completion-promise=\"PLAN_APPROVED\" --max-iterations=5","type":"text"}]},{"type":"paragraph","content":[{"text":"Codex / Gemini / OpenCode (alternative):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Session-isolated feedback directory (prevents concurrent run conflicts)\nFEEDBACK_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')\nFEEDBACK_FILE=\"${FEEDBACK_DIR}/plannotator_feedback.txt\"\n\n# 1. Write plan.md directly, then review with plannotator (blocking — no &)\nPLANNOTATOR_RUNTIME_HOME=\"${FEEDBACK_DIR}/.plannotator\"\nmkdir -p \"$PLANNOTATOR_RUNTIME_HOME\"\ntouch /tmp/jeo-plannotator-direct.lock && python3 -c \"\nimport json\nprint(json.dumps({'tool_input': {'plan': open('plan.md').read(), 'permission_mode': 'acceptEdits'}}))\n\" | env HOME=\"$PLANNOTATOR_RUNTIME_HOME\" PLANNOTATOR_HOME=\"$PLANNOTATOR_RUNTIME_HOME\" plannotator > \"$FEEDBACK_FILE\" 2>&1\n# ↑ Run without &: waits until user clicks Approve/Send Feedback in browser\n\n# 2. Check result and branch\nif python3 -c \"\nimport json, sys\ntry:\n d = json.load(open('$FEEDBACK_FILE'))\n sys.exit(0 if d.get('approved') is True else 1)\nexcept Exception:\n sys.exit(1)\n\" 2>/dev/null; then\n echo \"PLAN_APPROVED\" # → enter EXECUTE step\nelse\n echo \"PLAN_FEEDBACK\" # → read \\\"$FEEDBACK_FILE\\\", replan, repeat above\nfi","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Important","type":"text","marks":[{"type":"strong"}]},{"text":": Do not run with ","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":" (background). Must run blocking to receive user feedback.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Common flow:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate plan document (","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run plannotator blocking → browser UI opens automatically","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review plan in browser → Approve or Send Feedback","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Approve (","type":"text"},{"text":"\"approved\":true","type":"text","marks":[{"type":"code_inline"}]},{"text":") → enter [2] EXECUTE step","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feedback → read ","type":"text"},{"text":"/tmp/plannotator_feedback.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":" annotations and replan (loop)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"exit 32 (sandbox/CI — Conversation Approval Mode)","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Output full ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" content to user","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask: \"⚠️ plannotator UI unavailable. Reply 'approve' to proceed or provide feedback.\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WAIT for user response — do NOT proceed to EXECUTE","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On approval → update ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"plan_approved=true, plan_gate_status=\"manual_approved\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" → EXECUTE","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On feedback → revise ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", retry loop, repeat","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Claude Code manual run:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Shift+Tab×2 → enter plan mode → plannotator runs automatically when plan is complete","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3.2 EXECUTE Step","type":"text"}]},{"type":"paragraph","content":[{"text":"When team is available (Claude Code + omc):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/omc:team 3:executor \"jeo-exec: \u003ctask based on approved plan>\"","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"staged pipeline: team-plan → team-prd → team-exec → team-verify → team-fix","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Maximize speed with parallel agent execution","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When Claude Code team mode is unavailable:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"echo \"❌ JEO requires /omc:team in Claude Code. Run bash scripts/setup-claude.sh, restart Claude Code, then retry.\"\nexit 1","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not degrade to single-agent mode","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When team is unavailable (BMAD fallback — Codex / Gemini / OpenCode):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/workflow-init # Initialize BMAD workflow\n/workflow-status # Check current step","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Proceed in order: Analysis → Planning → Solutioning → Implementation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review documents with plannotator after each step completes","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3.3 VERIFY Step (agent-browser — default behavior)","type":"text"}]},{"type":"paragraph","content":[{"text":"When browser-based functionality is present, verify behavior with ","type":"text"},{"text":"agent-browser","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Capture snapshot from the URL where the app is running\nagent-browser snapshot http://localhost:3000\n\n# Check specific elements (accessibility tree ref method)\nagent-browser snapshot http://localhost:3000 -i\n# → check element state using @eN ref numbers\n\n# Save screenshot\nagent-browser screenshot http://localhost:3000 -o verify.png","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Default behavior","type":"text","marks":[{"type":"strong"}]},{"text":": Automatically runs the agent-browser verification step when browser-related work is complete. Backend/CLI tasks without a browser UI skip this step.","type":"text"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3.3.1 VERIFY_UI Step (annotate — agentation watch loop)","type":"text"}]},{"type":"paragraph","content":[{"text":"Runs the agentation watch loop when the ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword is detected. (The ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword is also supported for backward compatibility.) This follows the same pattern as plannotator operating in ","type":"text"},{"text":"planui","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"ExitPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Prerequisites (auto-resolved by pre-flight):","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"npx agentation-mcp server","type":"text","marks":[{"type":"code_inline"}]},{"text":" (HTTP :4747) is running — ","type":"text"},{"text":"auto-started if not running","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"agentation","type":"text","marks":[{"type":"code_inline"}]},{"text":" npm package installed in the project — ","type":"text"},{"text":"auto-installed if absent","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\u003cAgentation endpoint=\"http://localhost:4747\" />","type":"text","marks":[{"type":"code_inline"}]},{"text":" is mounted in the app — ","type":"text"},{"text":"auto-injected into entry point if absent","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"paragraph","content":[{"text":"Pre-flight Check (required before entering — common to all platforms):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# ── Step 1: Auto-start agentation-mcp server if not running ─────────────────\nJEO_AGENTATION_ENDPOINT=\"${JEO_AGENTATION_ENDPOINT:-http://localhost:4747}\"\nJEO_AGENTATION_PORT=\"${JEO_AGENTATION_PORT:-4747}\"\n\nif ! curl -sf --connect-timeout 2 \"${JEO_AGENTATION_ENDPOINT}/health\" >/dev/null 2>&1; then\n echo \"[JEO][ANNOTATE] agentation-mcp not running — attempting auto-start on port ${JEO_AGENTATION_PORT}...\"\n if command -v npx >/dev/null 2>&1; then\n npx -y agentation-mcp server --port \"${JEO_AGENTATION_PORT}\" >/tmp/agentation-mcp.log 2>&1 &\n AGENTATION_MCP_PID=$!\n echo \"[JEO][ANNOTATE] started agentation-mcp (PID ${AGENTATION_MCP_PID})\"\n # Wait up to 8 seconds for server to become healthy\n for i in $(seq 1 8); do\n sleep 1\n if curl -sf --connect-timeout 1 \"${JEO_AGENTATION_ENDPOINT}/health\" >/dev/null 2>&1; then\n echo \"[JEO][ANNOTATE] ✅ agentation-mcp server ready\"\n break\n fi\n done\n if ! curl -sf --connect-timeout 2 \"${JEO_AGENTATION_ENDPOINT}/health\" >/dev/null 2>&1; then\n echo \"[JEO][ANNOTATE] ⚠️ agentation-mcp failed to start — skipping VERIFY_UI\"\n python3 -c \"\nimport json,os,subprocess,fcntl,time\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d['last_error']='agentation-mcp failed to start; VERIFY_UI skipped'\n d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true\n # Proceed to STEP 4 CLEANUP (no exit 1 — graceful skip)\n fi\n else\n echo \"[JEO][ANNOTATE] ⚠️ npx not found — cannot auto-start agentation-mcp. Skipping VERIFY_UI.\"\n python3 -c \"\nimport json,os,subprocess,fcntl,time\ntry:\n root=subprocess.check_output(['git','rev-parse','--show-toplevel'],stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root=os.getcwd()\nf=os.path.join(root,'.omc/state/jeo-state.json')\nif os.path.exists(f):\n with open(f,'r+') as fh:\n fcntl.flock(fh,fcntl.LOCK_EX)\n try:\n d=json.load(fh)\n d['last_error']='npx not found; agentation-mcp auto-start skipped; VERIFY_UI skipped'\n d['updated_at']=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())\n fh.seek(0); json.dump(d,fh,ensure_ascii=False,indent=2); fh.truncate()\n finally:\n fcntl.flock(fh,fcntl.LOCK_UN)\n\" 2>/dev/null || true\n # Proceed to STEP 4 CLEANUP (no exit 1 — graceful skip)\n fi\nfi\n\n# ── Step 2: Auto-install agentation package + inject \u003cAgentation> if needed ──\n# Only runs when server is confirmed healthy\nif curl -sf --connect-timeout 2 \"${JEO_AGENTATION_ENDPOINT}/health\" >/dev/null 2>&1; then\n SESSIONS=$(curl -sf \"${JEO_AGENTATION_ENDPOINT}/sessions\" 2>/dev/null || echo \"[]\")\n S_COUNT=$(echo \"$SESSIONS\" | python3 -c \"import sys,json; print(len(json.load(sys.stdin)))\" 2>/dev/null || echo 0)\n\n if [ \"$S_COUNT\" -eq 0 ]; then\n echo \"[JEO][ANNOTATE] No active sessions — running ensure-agentation.sh to install package and inject component...\"\n\n # Locate ensure-agentation.sh (try common installation paths)\n ENSURE_SCRIPT=\"\"\n for candidate in \\\n \"$(dirname \"${BASH_SOURCE[0]:-$0}\")/ensure-agentation.sh\" \\\n \"$HOME/.claude/skills/jeo/scripts/ensure-agentation.sh\" \\\n \"$HOME/.agent-skills/jeo/scripts/ensure-agentation.sh\" \\\n \"$HOME/.codex/skills/jeo/scripts/ensure-agentation.sh\"; do\n if [[ -f \"$candidate\" ]]; then\n ENSURE_SCRIPT=\"$candidate\"\n break\n fi\n done\n\n if [[ -n \"$ENSURE_SCRIPT\" ]]; then\n ENSURE_EXIT=0\n bash \"$ENSURE_SCRIPT\" \\\n --project-dir \"${PROJECT_DIR:-$PWD}\" \\\n --endpoint \"${JEO_AGENTATION_ENDPOINT}\" || ENSURE_EXIT=$?\n\n if [ \"$ENSURE_EXIT\" -eq 0 ]; then\n echo \"[JEO][ANNOTATE] ensure-agentation completed — waiting up to 15s for browser to reconnect...\"\n # Wait for dev server hot-reload and browser reconnection\n for i in $(seq 1 15); do\n sleep 1\n NEW_S_COUNT=$(curl -sf \"${JEO_AGENTATION_ENDPOINT}/sessions\" 2>/dev/null | \\\n python3 -c \"import sys,json; print(len(json.load(sys.stdin)))\" 2>/dev/null || echo 0)\n if [ \"$NEW_S_COUNT\" -gt 0 ]; then\n echo \"[JEO][ANNOTATE] ✅ Browser session established (${NEW_S_COUNT} session(s))\"\n S_COUNT=$NEW_S_COUNT\n break\n fi\n done\n if [ \"$S_COUNT\" -eq 0 ]; then\n echo \"[JEO][ANNOTATE] ⚠️ Component injected but no browser session yet.\"\n echo \" → Refresh the browser at your app URL, then re-run annotate.\"\n fi\n elif [ \"$ENSURE_EXIT\" -eq 2 ]; then\n echo \"[JEO][ANNOTATE] ℹ️ Not a Node.js project — mount \u003cAgentation endpoint='${JEO_AGENTATION_ENDPOINT}' /> manually.\"\n else\n echo \"[JEO][ANNOTATE] ⚠️ ensure-agentation.sh failed (exit $ENSURE_EXIT) — mount \u003cAgentation endpoint='${JEO_AGENTATION_ENDPOINT}' /> manually.\"\n fi\n else\n echo \"[JEO][ANNOTATE] ⚠️ ensure-agentation.sh not found — mount \u003cAgentation endpoint='${JEO_AGENTATION_ENDPOINT}' /> manually.\"\n fi\n fi\n\n echo \"[JEO][ANNOTATE] ✅ agentation ready — server OK, ${S_COUNT} session(s)\"\nfi","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"After passing pre-flight (","type":"text"},{"text":"else","type":"text","marks":[{"type":"code_inline"}]},{"text":" branch), update jeo-state.json ","type":"text"},{"text":"phase","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"verify_ui\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", set ","type":"text"},{"text":"agentation.active","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", and set ","type":"text"},{"text":"agentation.submit_gate_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"\"waiting_for_submit\"","type":"text","marks":[{"type":"code_inline"}]},{"text":". Do not call ","type":"text"},{"text":"/pending","type":"text","marks":[{"type":"code_inline"}]},{"text":" yet. Draft annotations are not actionable until the user explicitly submits them.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Claude Code (direct MCP tool call):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"# annotate keyword detected (or agentui — backward compatible)\n# 1. wait for UserPromptSubmit after the user clicks Send Annotations / onSubmit\n# 2. the JEO submit-gate hook records submit_gate_status=\"submitted\"\n# 3. only then run the blocking agentation watch loop\n#\n# batchWindowSeconds:10 — receive annotations in 10-second batches\n# timeoutSeconds:120 — auto-exit after 120 seconds with no annotations\n#\n# Per-annotation processing loop:\n# 1. agentation_acknowledge_annotation({id}) — show 'processing' in UI\n# 2. navigate code via annotation.elementPath (CSS selector) → apply fix\n# 3. agentation_resolve_annotation({id, summary}) — mark 'done' + save summary\n#\n# Loop ends when annotation count=0 or timeout","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Important","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"agentation_watch_annotations","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a blocking call. Do not run with ","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":" background. Same as plannotator's ","type":"text"},{"text":"approved:true","type":"text","marks":[{"type":"code_inline"}]},{"text":" loop: annotation count=0 or timeout = completion signal. ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the primary keyword. ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a backward-compatible alias and behaves identically.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Codex / Gemini / OpenCode (HTTP REST API fallback):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"START_TIME=$(date +%s)\nTIMEOUT_SECONDS=120\n\n# Required gate: do not enter the loop until the human has clicked Send Annotations\n# and the platform has opened agentation.submit_gate_status=\"submitted\".\nwhile true; do\n # Timeout check\n NOW=$(date +%s)\n ELAPSED=$((NOW - START_TIME))\n if [ $ELAPSED -ge $TIMEOUT_SECONDS ]; then\n echo \"[JEO] agentation polling timeout (${TIMEOUT_SECONDS}s) — some annotations may remain unresolved\"\n break\n fi\n\n SUBMIT_GATE=$(python3 -c \"\nimport json\ntry:\n print(json.load(open('.omc/state/jeo-state.json')).get('agentation', {}).get('submit_gate_status', 'idle'))\nexcept Exception:\n print('idle')\n\" 2>/dev/null || echo \"idle\")\n if [ \"$SUBMIT_GATE\" != \"submitted\" ]; then\n sleep 2\n continue\n fi\n\n 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)\n [ \"$COUNT\" -eq 0 ] && break\n\n # Process each annotation:\n # a) Acknowledge (show as in-progress)\n curl -X PATCH http://localhost:4747/annotations/\u003cid> \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"acknowledged\"}'\n\n # b) Navigate code via elementPath (CSS selector) → apply fix\n\n # c) Resolve (mark done + fix summary)\n curl -X PATCH http://localhost:4747/annotations/\u003cid> \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"resolved\", \"resolution\": \"\u003cfix summary>\"}'\n\n sleep 3\ndone","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3.4 CLEANUP Step (automatic worktree cleanup)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Runs automatically after all work is complete\nbash scripts/worktree-cleanup.sh\n\n# Individual commands\ngit worktree list # List current worktrees\ngit worktree prune # Clean up worktrees for deleted branches\nbash scripts/worktree-cleanup.sh --force # Force cleanup including dirty worktrees","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Default run removes only clean extra worktrees; worktrees with changes are left with a warning. Use ","type":"text"},{"text":"--force","type":"text","marks":[{"type":"code_inline"}]},{"text":" only after review.","type":"text"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"4. Platform Plugin Configuration","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.1 Claude Code","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Automatic setup\nbash scripts/setup-claude.sh\n\n# Or manually:\n/plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode\n/plugin install oh-my-claudecode\n/omc:omc-setup\n\n# Add plannotator hook\nbash .agent-skills/plannotator/scripts/setup-hook.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Config file","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"hooks\": {\n \"PermissionRequest\": [{\n \"matcher\": \"ExitPlanMode\",\n \"hooks\": [{\n \"type\": \"command\",\n \"command\": \"python3 ~/.agent-skills/jeo/scripts/claude-plan-gate.py\",\n \"timeout\": 1800\n }]\n }]\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"agentation MCP config","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":".claude/mcp.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"mcpServers\": {\n \"agentation\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"agentation-mcp\", \"server\"]\n }\n },\n \"hooks\": {\n \"UserPromptSubmit\": [{\n \"matcher\": \"*\",\n \"hooks\": [{\n \"type\": \"command\",\n \"command\": \"python3 ~/.agent-skills/jeo/scripts/claude-agentation-submit-hook.py\",\n \"timeout\": 300\n }]\n }]\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"bash ~/.agent-skills/jeo/scripts/setup-claude.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" migrates stale ","type":"text"},{"text":"~/.agent-skills/omg/...","type":"text","marks":[{"type":"code_inline"}]},{"text":" hook paths to the active JEO install and installs compatibility shims for legacy Claude setups.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.2 Codex CLI","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Automatic setup\nbash scripts/setup-codex.sh\n\n# What gets configured:\n# - developer_instructions: ~/.codex/config.toml\n# - prompt file: ~/.codex/prompts/jeo.md\n# - notify hook: ~/.codex/hooks/jeo-notify.py\n# - [tui] notifications: agent-turn-complete","type":"text"}]},{"type":"paragraph","content":[{"text":"agentation MCP config","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"~/.codex/config.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"toml"},"content":[{"text":"[mcp_servers.agentation]\ncommand = \"npx\"\nargs = [\"-y\", \"agentation-mcp\", \"server\"]","type":"text"}]},{"type":"paragraph","content":[{"text":"notify hook","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"~/.codex/hooks/jeo-notify.py","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detects ","type":"text"},{"text":"PLAN_READY","type":"text","marks":[{"type":"code_inline"}]},{"text":" signal in ","type":"text"},{"text":"last-assistant-message","type":"text","marks":[{"type":"code_inline"}]},{"text":" when agent turn completes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirms ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" exists, compares the current hash against ","type":"text"},{"text":"last_reviewed_plan_hash","type":"text","marks":[{"type":"code_inline"}]},{"text":", and skips the gate when the plan was already reviewed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Saves result to ","type":"text"},{"text":"/tmp/plannotator_feedback.txt","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detects ","type":"text"},{"text":"ANNOTATE_READY","type":"text","marks":[{"type":"code_inline"}]},{"text":" signal (or backward-compatible ","type":"text"},{"text":"AGENTUI_READY","type":"text","marks":[{"type":"code_inline"}]},{"text":") only in ","type":"text"},{"text":"verify_ui","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Opens ","type":"text"},{"text":"agentation.submit_gate_status=\"submitted\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" first, then polls ","type":"text"},{"text":"http://localhost:4747/pending","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"~/.codex/config.toml","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" config:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"toml"},"content":[{"text":"developer_instructions = \"\"\"\n# JEO Orchestration Workflow\n# ...\n\"\"\"\n\nnotify = [\"python3\", \"~/.codex/hooks/jeo-notify.py\"]\n\n[tui]\nnotifications = [\"agent-turn-complete\"]\nnotification_method = \"osc9\"","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"developer_instructions","type":"text","marks":[{"type":"code_inline"}]},{"text":" must be a ","type":"text"},{"text":"top-level string","type":"text","marks":[{"type":"strong"}]},{"text":". Writing it as a ","type":"text"},{"text":"[developer_instructions]","type":"text","marks":[{"type":"code_inline"}]},{"text":" table may cause Codex to fail on startup with ","type":"text"},{"text":"invalid type: map, expected a string","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"notify","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"[tui].notifications","type":"text","marks":[{"type":"code_inline"}]},{"text":" must also be set correctly for the PLAN/ANNOTATE follow-up loop to actually work.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Using in Codex:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/prompts:jeo # Activate JEO workflow\n# Agent writes plan.md and outputs \"PLAN_READY\" → notify hook runs automatically","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.3 Gemini CLI","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Automatic setup\nbash scripts/setup-gemini.sh\n\n# What gets configured:\n# - AfterAgent backup hook: ~/.gemini/hooks/jeo-plannotator.sh\n# - Instructions (MANDATORY loop): ~/.gemini/GEMINI.md","type":"text"}]},{"type":"paragraph","content":[{"text":"Key principle","type":"text","marks":[{"type":"strong"}]},{"text":": The agent must call plannotator ","type":"text"},{"text":"directly in blocking mode","type":"text","marks":[{"type":"strong"}]},{"text":" to receive feedback in the same turn. The AfterAgent hook serves only as a safety net (runs after turn ends → injected in next turn).","type":"text"}]},{"type":"paragraph","content":[{"text":"AfterAgent backup hook","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"~/.gemini/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"hooks\": {\n \"AfterAgent\": [{\n \"matcher\": \"\",\n \"hooks\": [{\n \"name\": \"plannotator-review\",\n \"type\": \"command\",\n \"command\": \"bash ~/.gemini/hooks/jeo-plannotator.sh\",\n \"description\": \"Run plannotator when plan.md is detected (AfterAgent backup)\"\n }]\n }]\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"PLAN instructions added to GEMINI.md (mandatory loop)","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"1. Write plan.md\n2. Run plannotator blocking (no &) → /tmp/plannotator_feedback.txt\n3. approved=true → EXECUTE / Not approved → revise and repeat step 2\nNEVER proceed to EXECUTE without approved=true.","type":"text"}]},{"type":"paragraph","content":[{"text":"agentation MCP config","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"~/.gemini/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"mcpServers\": {\n \"agentation\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"agentation-mcp\", \"server\"]\n }\n }\n}","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Note","type":"text","marks":[{"type":"strong"}]},{"text":": Gemini CLI hook events use ","type":"text"},{"text":"BeforeTool","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"AfterAgent","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"ExitPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a Claude Code-only hook.","type":"text"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Hooks Official Guide","type":"text","marks":[{"type":"link","attrs":{"href":"https://developers.googleblog.com/tailor-gemini-cli-to-your-workflow-with-hooks/","title":null}}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4.4 OpenCode","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Automatic setup\nbash scripts/setup-opencode.sh\n\n# Added to opencode.json:\n# \"@plannotator/opencode@latest\" plugin\n# \"@oh-my-opencode/opencode@latest\" plugin (omx)","type":"text"}]},{"type":"paragraph","content":[{"text":"OpenCode slash commands:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/jeo-plan","type":"text","marks":[{"type":"code_inline"}]},{"text":" — plan with ralph + plannotator","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/jeo-exec","type":"text","marks":[{"type":"code_inline"}]},{"text":" — execute with team/bmad","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/jeo-annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" — start agentation watch loop (annotate; ","type":"text"},{"text":"/jeo-agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a deprecated alias)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/jeo-cleanup","type":"text","marks":[{"type":"code_inline"}]},{"text":" — worktree cleanup","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"plannotator integration","type":"text","marks":[{"type":"strong"}]},{"text":" (MANDATORY blocking loop):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Write plan.md then run PLAN gate (no &) — receive feedback in same turn\nbash scripts/plannotator-plan-loop.sh plan.md /tmp/plannotator_feedback.txt 3\n# - Must wait until approve/feedback is received\n# - Auto-restart on session exit (up to 3 times)\n# - After 3 exits, confirm with user whether to abort or resume\n# - exit 32 if localhost bind unavailable (replace with manual gate in TTY)\n\n# Branch based on result\n# approved=true → enter EXECUTE\n# not approved → apply feedback, revise plan.md → repeat above","type":"text"}]},{"type":"paragraph","content":[{"text":"agentation MCP config","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"opencode.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"mcp\": {\n \"agentation\": {\n \"type\": \"local\",\n \"command\": [\"npx\", \"-y\", \"agentation-mcp\", \"server\"]\n }\n }\n}","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"5. Memory & State","type":"text"}]},{"type":"paragraph","content":[{"text":"JEO stores state at the following paths:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"{worktree}/.omc/state/jeo-state.json # JEO execution state\n{worktree}/.omc/plans/jeo-plan.md # Approved plan\n{worktree}/.omc/logs/jeo-*.log # Execution logs","type":"text"}]},{"type":"paragraph","content":[{"text":"State file structure:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"mode\": \"jeo\",\n \"phase\": \"plan|execute|verify|verify_ui|cleanup|done\",\n \"session_id\": \"\u003cuuid>\",\n \"task\": \"current task description\",\n \"plan_approved\": true,\n \"plan_gate_status\": \"pending|approved|feedback_required|infrastructure_blocked|manual_approved\",\n \"plan_current_hash\": \"\u003csha256 or null>\",\n \"last_reviewed_plan_hash\": \"\u003csha256 or null>\",\n \"last_reviewed_plan_at\": \"2026-02-24T00:00:00Z\",\n \"plan_review_method\": \"plannotator|manual|null\",\n \"team_available\": true,\n \"retry_count\": 0,\n \"last_error\": null,\n \"checkpoint\": \"plan|execute|verify|verify_ui|cleanup\",\n \"created_at\": \"2026-02-24T00:00:00Z\",\n \"updated_at\": \"2026-02-24T00:00:00Z\",\n \"agentation\": {\n \"active\": false,\n \"session_id\": null,\n \"keyword_used\": null,\n \"submit_gate_status\": \"idle|waiting_for_submit|submitted\",\n \"submit_signal\": \"claude-user-prompt-submit|codex-notify|gemini-manual|null\",\n \"submit_received_at\": \"2026-02-24T00:00:00Z\",\n \"submitted_annotation_count\": 0,\n \"started_at\": null,\n \"timeout_seconds\": 120,\n \"annotations\": {\n \"total\": 0, \"acknowledged\": 0, \"resolved\": 0, \"dismissed\": 0, \"pending\": 0\n },\n \"completed_at\": null,\n \"exit_reason\": null\n }\n}","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"agentation fields","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"active","type":"text","marks":[{"type":"code_inline"}]},{"text":" — whether the watch loop is running (used as hook guard), ","type":"text"},{"text":"session_id","type":"text","marks":[{"type":"code_inline"}]},{"text":" — for resuming, ","type":"text"},{"text":"submit_gate_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" — prevents processing draft annotations before submit/onSubmit, ","type":"text"},{"text":"submit_signal","type":"text","marks":[{"type":"code_inline"}]},{"text":" — which platform opened the gate, ","type":"text"},{"text":"submit_received_at","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"submitted_annotation_count","type":"text","marks":[{"type":"code_inline"}]},{"text":" — audit trail for the submitted batch, ","type":"text"},{"text":"exit_reason","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"\"all_resolved\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" | ","type":"text"},{"text":"\"timeout\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" | ","type":"text"},{"text":"\"user_cancelled\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" | ","type":"text"},{"text":"\"error\"","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"dismissed annotations","type":"text","marks":[{"type":"strong"}]},{"text":": When a user dismisses an annotation in the agentation UI (status becomes ","type":"text"},{"text":"\"dismissed\"","type":"text","marks":[{"type":"code_inline"}]},{"text":"), the agent should skip code changes for that annotation, increment ","type":"text"},{"text":"annotations.dismissed","type":"text","marks":[{"type":"code_inline"}]},{"text":", and continue to the next pending annotation. Dismissed annotations are counted but not acted upon. The watch loop exits normally when ","type":"text"},{"text":"pending == 0","type":"text","marks":[{"type":"code_inline"}]},{"text":" (resolved + dismissed covers all).","type":"text"}]},{"type":"paragraph","content":[{"text":"plan_review_method","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": set to ","type":"text"},{"text":"\"plannotator\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" when approved via UI, ","type":"text"},{"text":"\"manual\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" when approved via TTY fallback gate.","type":"text"}]},{"type":"paragraph","content":[{"text":"cleanup_completed","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": set to ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":" by ","type":"text"},{"text":"worktree-cleanup.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" after successful worktree prune.","type":"text"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Error recovery fields","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"retry_count","type":"text","marks":[{"type":"code_inline"}]},{"text":" — number of retries after an error. Increments +1 on each pre-flight failure. Ask user to confirm if ","type":"text"},{"text":">= 3","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"last_error","type":"text","marks":[{"type":"code_inline"}]},{"text":" — most recent error message. Used to identify the cause on restart.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"checkpoint","type":"text","marks":[{"type":"code_inline"}]},{"text":" — last phase that was started. Resume from this phase on restart (","type":"text"},{"text":"plan|execute|verify|cleanup","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Checkpoint-based resume flow:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check checkpoint on restart\npython3 -c \"\nimport json, os, subprocess\ntry:\n root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], stderr=subprocess.DEVNULL).decode().strip()\nexcept:\n root = os.getcwd()\nf = os.path.join(root, '.omc/state/jeo-state.json')\nif os.path.exists(f):\n d=json.load(open(f))\n cp=d.get('checkpoint')\n err=d.get('last_error')\n rc=d.get('retry_count',0)\n print(f'Resume from: {cp or \\\"beginning\\\"}')\n if err: print(f'Previous error ({rc} time(s)): {err}')\n if rc >= 3: print('⚠️ Retry count exceeded 3 — user confirmation required')\n\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Restore after restart:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check status and resume\nbash scripts/check-status.sh --resume","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"6. Recommended Workflow","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"# Step 1: Install (once)\nbash scripts/install.sh --all\nbash scripts/check-status.sh\n\n# Step 2: Start work\njeo \"\u003ctask description>\" # Activate with keyword\n# Or in Claude: Shift+Tab×2 → plan mode\n\n# Step 3: Review plan with plannotator\n# Approve or Send Feedback in browser UI\n\n# Step 4: Automatic execution\n# team or bmad handles the work\n\n# Step 5: Cleanup after completion\nbash scripts/worktree-cleanup.sh","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"7. Best Practices","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Plan first","type":"text","marks":[{"type":"strong"}]},{"text":": always review the plan with ralph+plannotator before executing (catches wrong approaches early)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Team first","type":"text","marks":[{"type":"strong"}]},{"text":": omc team mode is most efficient in Claude Code","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bmad fallback","type":"text","marks":[{"type":"strong"}]},{"text":": use BMAD in environments without team (Codex, Gemini)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Worktree cleanup","type":"text","marks":[{"type":"strong"}]},{"text":": run ","type":"text"},{"text":"worktree-cleanup.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" immediately after work completes (prevents branch pollution)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"State persistence","type":"text","marks":[{"type":"strong"}]},{"text":": use ","type":"text"},{"text":".omc/state/jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" to maintain state across sessions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"annotate","type":"text","marks":[{"type":"strong"}]},{"text":": use the ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword to run the agentation watch loop for complex UI changes (precise code changes via CSS selector). ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a backward-compatible alias.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"8. Troubleshooting","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Issue","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plannotator not running","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JEO first auto-runs ","type":"text"},{"text":"bash scripts/ensure-plannotator.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":"; if it still fails, run ","type":"text"},{"text":"bash .agent-skills/plannotator/scripts/check-status.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plannotator not opening in Claude Code","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plannotator is hook-only. Do NOT call it via MCP or CLI. Use ","type":"text"},{"text":"EnterPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" → write plan → ","type":"text"},{"text":"ExitPlanMode","type":"text","marks":[{"type":"code_inline"}]},{"text":"; the hook fires automatically. Verify hook is set: ","type":"text"},{"text":"cat ~/.claude/settings.json | python3 -c \"import sys,json;h=json.load(sys.stdin).get('hooks',{});print(h.get('PermissionRequest','missing'))\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"plannotator feedback not received","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Remove ","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":" background execution → run blocking, then check ","type":"text"},{"text":"/tmp/plannotator_feedback.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Codex/Gemini/OpenCode only)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Same plan is repeatedly re-reviewed in Codex","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Compare ","type":"text"},{"text":"last_reviewed_plan_hash","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"jeo-state.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the current ","type":"text"},{"text":"plan.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" hash. If they match and ","type":"text"},{"text":"plan_gate_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" is terminal, do not re-run","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Codex startup failure (","type":"text"},{"text":"invalid type: map, expected a string","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-run ","type":"text"},{"text":"bash scripts/setup-codex.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" and confirm ","type":"text"},{"text":"developer_instructions","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"~/.codex/config.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a top-level string","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Gemini feedback loop missing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Add blocking direct call instruction to ","type":"text"},{"text":"~/.gemini/GEMINI.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"worktree conflict","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"git worktree prune && git worktree list","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"team mode not working","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JEO requires team mode in Claude Code. Run ","type":"text"},{"text":"bash scripts/setup-claude.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":", restart Claude Code, and verify ","type":"text"},{"text":"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" before retrying","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"omc install failed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"/omc:omc-doctor","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"agent-browser error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"agent-browser --version","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"annotate (agentation) not opening","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"curl http://localhost:4747/health","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"curl http://localhost:4747/sessions","type":"text","marks":[{"type":"code_inline"}]},{"text":". JEO waits for explicit submit/onSubmit before polling ","type":"text"},{"text":"/pending","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"annotation not reflected in code","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Confirm ","type":"text"},{"text":"summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" field is present when calling ","type":"text"},{"text":"agentation_resolve_annotation","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword not activating","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use the ","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword (new). ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a deprecated alias but still works.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MCP tool not registered (Codex/Gemini)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-run ","type":"text"},{"text":"bash scripts/setup-codex.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"setup-gemini.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"9. References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oh-my-claudecode","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/Yeachan-Heo/oh-my-claudecode","title":null}}]},{"text":" — Claude Code multi-agent","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"plannotator","type":"text","marks":[{"type":"link","attrs":{"href":"https://plannotator.ai","title":null}}]},{"text":" — visual plan/diff review","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"BMAD Method","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/bmad-dev/BMAD-METHOD","title":null}}]},{"text":" — structured AI development workflow","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Agent Skills Spec","type":"text","marks":[{"type":"link","attrs":{"href":"https://agentskills.io/specification","title":null}}]},{"text":" — skill format specification","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"agentation","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/benjitaylor/agentation","title":null}}]},{"text":" — UI annotation → agent code fix integration (","type":"text"},{"text":"annotate","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"agentui","type":"text","marks":[{"type":"code_inline"}]},{"text":" backward compatible)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"jeo","author":"@skillopedia","source":{"stars":336,"repo_name":"marketplace","origin_url":"https://github.com/aiskillstore/marketplace/blob/HEAD/skills/supercent-io/jeo/SKILL.md","repo_owner":"aiskillstore","body_sha256":"cd75917b80e1d4f9e427026ea498f29e8d2f62944c12c96b9dd55e87fee8a377","cluster_key":"15cde515cf4c9fefaf86069298078f92dee44a8b2e7c762ab2cf117a65a67de7","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aiskillstore/marketplace/skills/supercent-io/jeo/SKILL.md","attachments":[{"id":"913cdbc2-9e0c-5d19-a35f-9996de89ff14","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/913cdbc2-9e0c-5d19-a35f-9996de89ff14/attachment.toon","path":"SKILL.toon","size":3125,"sha256":"a3e0e3692d83981319b40c217bc1f19d7b25f9a852a2758348bb3d1309f9c345","contentType":"text/plain; charset=utf-8"},{"id":"17ff009b-4d9c-5fa7-8026-fb1bfc807383","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/17ff009b-4d9c-5fa7-8026-fb1bfc807383/attachment.bak","path":"opencode.json.jeo.bak","size":2929,"sha256":"664e97093e0ce50c2d01f9fc93ecd148da0a03cb521033fe273e7a7f7f3df369","contentType":"application/x-trash"},{"id":"06574e94-342f-5200-960c-43713e0da772","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/06574e94-342f-5200-960c-43713e0da772/attachment.md","path":"references/FLOW.md","size":18838,"sha256":"53fffbddfe0114c4c049c70a65d378d415440d87d510c5fd3d192ea305d57ecb","contentType":"text/markdown; charset=utf-8"},{"id":"a3d09dbc-7cce-5858-b51a-cd753e4bea2b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a3d09dbc-7cce-5858-b51a-cd753e4bea2b/attachment.md","path":"references/PLANNOTATOR_RELIABILITY_CONSENSUS_2026-03-06.md","size":4611,"sha256":"6321fdff1238b490b9f249391142f5cf9ed4d25c7cb147a63190d7d4bf48cf98","contentType":"text/markdown; charset=utf-8"},{"id":"16a492b0-b0ad-5d33-bae3-ed1337372c86","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/16a492b0-b0ad-5d33-bae3-ed1337372c86/attachment.sh","path":"scripts/check-status.sh","size":12275,"sha256":"0d3f409f0d2355ebec47aad7c92e6d6639a3e8966606c5dc06b7f5799fbec970","contentType":"application/x-sh; charset=utf-8"},{"id":"0f03d026-62c8-5a10-8398-299f7ad599e8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0f03d026-62c8-5a10-8398-299f7ad599e8/attachment.py","path":"scripts/claude-agentation-submit-hook.py","size":3784,"sha256":"58045c3cef395581b1d03d2cf9c3d289d50317a26cb0012b8074c800dc1338f2","contentType":"text/x-python; charset=utf-8"},{"id":"b576b12f-82aa-5bf7-b9f2-f4ed70866e89","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b576b12f-82aa-5bf7-b9f2-f4ed70866e89/attachment.py","path":"scripts/claude-plan-gate.py","size":11666,"sha256":"551552a2c5cd48fb83e97cfc7ad020e0b41a40c82186fee77c4a08e2418848c4","contentType":"text/x-python; charset=utf-8"},{"id":"1f5391e8-3781-52ec-8208-fb8c7db6a20d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1f5391e8-3781-52ec-8208-fb8c7db6a20d/attachment.py","path":"scripts/claude-stop-continuation.py","size":5178,"sha256":"fb3bc6c86bcb819443318b745a518a8fab904d8565a6a32bba0cadac0d9ad1b7","contentType":"text/x-python; charset=utf-8"},{"id":"9bea7ce2-d238-5c8d-a1c0-3b830aa061bc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9bea7ce2-d238-5c8d-a1c0-3b830aa061bc/attachment.sh","path":"scripts/ensure-agentation.sh","size":9376,"sha256":"075236580a4cdb62fd116a6cf7596b284f6629c106e0cbd75e0dac8485dc7abe","contentType":"application/x-sh; charset=utf-8"},{"id":"26d23f30-2f26-5fdf-a2bf-68d12c252385","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/26d23f30-2f26-5fdf-a2bf-68d12c252385/attachment.sh","path":"scripts/ensure-plannotator.sh","size":1700,"sha256":"b71ef6af502dcc97d5b199bf250d442d9018f5eefd6cc2df1f2c71d85d84f14f","contentType":"application/x-sh; charset=utf-8"},{"id":"2a160bdf-8eef-52f5-adb5-fc2027925f8b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2a160bdf-8eef-52f5-adb5-fc2027925f8b/attachment.sh","path":"scripts/install.sh","size":10814,"sha256":"a5c75e7d550d1952e05e0e9559393a9753ac7a7385193625daeec8bb85f38cb8","contentType":"application/x-sh; charset=utf-8"},{"id":"9420a894-34fc-5c33-9c15-5bd999ff79da","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9420a894-34fc-5c33-9c15-5bd999ff79da/attachment.sh","path":"scripts/plannotator-plan-loop.sh","size":16413,"sha256":"024f18a67b3ef2d7969886527a6c8557173376db0442d4703ede1daa34788a84","contentType":"application/x-sh; charset=utf-8"},{"id":"a5eda2a2-f6e8-5693-a5a4-6af3fe7e0250","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a5eda2a2-f6e8-5693-a5a4-6af3fe7e0250/attachment.sh","path":"scripts/run-opencode-safe.sh","size":717,"sha256":"ff1f6da0a2a8889f0bb9c1b2d48d045ce040a591d34b3fcbf60f7486a74ade17","contentType":"application/x-sh; charset=utf-8"},{"id":"5aad9e5c-db58-5b5a-b0ac-2f904752b876","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5aad9e5c-db58-5b5a-b0ac-2f904752b876/attachment.sh","path":"scripts/setup-claude.sh","size":8960,"sha256":"1baaa9b8845bee6bb4119e36b8e31453cce2284442a983bbcd48763e3ecc9d40","contentType":"application/x-sh; charset=utf-8"},{"id":"c2ce957e-e9c8-5623-83d7-50b975662436","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c2ce957e-e9c8-5623-83d7-50b975662436/attachment.sh","path":"scripts/setup-codex.sh","size":28027,"sha256":"add86ef758c14834aedc5e0ff248107378fce8f29499771f1734f3a658f5393e","contentType":"application/x-sh; charset=utf-8"},{"id":"42322f3e-5cff-5345-9d28-a52029c01a71","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/42322f3e-5cff-5345-9d28-a52029c01a71/attachment.sh","path":"scripts/setup-gemini.sh","size":14348,"sha256":"c18458b8b102ceb035365b2976207ee92b4ab5ac7ab23bcb29e69e0045193949","contentType":"application/x-sh; charset=utf-8"},{"id":"d4384935-29fc-57a3-b17c-38006315940b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d4384935-29fc-57a3-b17c-38006315940b/attachment.sh","path":"scripts/setup-opencode.sh","size":6637,"sha256":"46820148bc19133815e1324d09d588f1e0de580648f2375de8d1a2817f385f61","contentType":"application/x-sh; charset=utf-8"},{"id":"756c9b6c-defb-5321-a8f2-79437b09d32b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/756c9b6c-defb-5321-a8f2-79437b09d32b/attachment.sh","path":"scripts/worktree-cleanup.sh","size":6664,"sha256":"068cf0397f04aa1d8f5a4da782287922917bbdd7c179c5e1030516edb0618c4f","contentType":"application/x-sh; charset=utf-8"},{"id":"b3247065-2c5f-5a80-8550-cd3898ae15c8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b3247065-2c5f-5a80-8550-cd3898ae15c8/attachment.json","path":"skill-report.json","size":19776,"sha256":"f1adcaffa665e428439e38178df6830942b90d2051fbaee7035d4b79c489cc94","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"e315e7dd2371ea5020da2082caed67d26fee7ed80efb457a3feae0b88949e884","attachment_count":19,"text_attachments":17,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":2,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/supercent-io/jeo/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"browser-automation-scraping","category_label":"Browser"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"browser-automation-scraping","metadata":{"tags":"jeo, orchestration, ralph, plannotator, agentation, annotate, agentui, UI-review, team, bmad, omc, omx, ohmg, agent-browser, multi-agent, workflow, worktree-cleanup, browser-verification, ui-feedback","source":"supercent-io/skills-template","keyword":"jeo","version":"1.3.1","platforms":"Claude, Codex, Gemini, OpenCode"},"import_tag":"clean-skills-v1","description":"JEO — Integrated AI agent orchestration skill. Plan with ralph+plannotator, execute with team/bmad, verify browser behavior with agent-browser, apply UI feedback with agentation(annotate), auto-cleanup worktrees after completion. Supports Claude, Codex, Gemini CLI, and OpenCode. Install: ralph, omc, omx, ohmg, bmad, plannotator, agent-browser, agentation.","allowed-tools":"Read Write Bash Grep Glob Task","compatibility":"Requires git, node>=18, bash. Optional: bun, docker."}},"renderedAt":1782980963804}

JEO — Integrated Agent Orchestration Keyword: · · · | Platforms: Claude Code · Codex CLI · Gemini CLI · OpenCode A unified skill providing fully automated orchestration flow: Plan (ralph+plannotator) → Execute (team/bmad) → UI Feedback (agentation/annotate) → Cleanup (worktree cleanup) Control Layers JEO uses one cross-platform abstraction for orchestration: - : platform/runtime configuration such as Claude hooks, Codex , Gemini , MCP registration, and prompt parameters - : policy constraints that must hold on every platform - : event callbacks that enforce those rules on each platform The ke…