feat(omo-codex): install through sisyphuslabs marketplace

This commit is contained in:
YeonGyu-Kim
2026-05-27 18:48:33 +09:00
parent a3567056c0
commit 56f39d60fc
35 changed files with 620 additions and 305 deletions
+31 -1
View File
@@ -254,6 +254,8 @@ jobs:
done
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/.codex-plugin/plugin.json > tmp.json && mv tmp.json packages/omo-codex/plugin/.codex-plugin/plugin.json
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/package.json > tmp.json && mv tmp.json packages/omo-codex/plugin/package.json
- name: Build main package
if: steps.check.outputs.skip != 'true'
@@ -419,6 +421,8 @@ jobs:
done
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/.codex-plugin/plugin.json > tmp.json && mv tmp.json packages/omo-codex/plugin/.codex-plugin/plugin.json
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/package.json > tmp.json && mv tmp.json packages/omo-codex/plugin/package.json
- name: Commit version bump
env:
@@ -426,7 +430,7 @@ jobs:
run: |
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git add package.json packages/oh-my-opencode-*/package.json
git add package.json packages/oh-my-opencode-*/package.json packages/omo-codex/plugin/.codex-plugin/plugin.json packages/omo-codex/plugin/package.json
git diff --cached --quiet || git commit -m "release: v${VERSION}"
- name: Create release tag
@@ -447,6 +451,32 @@ jobs:
git push origin HEAD
git push origin "v${VERSION}"
- name: Require LazyCodex sync token
if: ${{ secrets.LAZYCODEX_SYNC_TOKEN == '' }}
run: |
echo "::error::LAZYCODEX_SYNC_TOKEN is required to push the Codex marketplace bundle to code-yeongyu/lazycodex."
exit 1
- name: Checkout LazyCodex marketplace
uses: actions/checkout@v4
with:
repository: code-yeongyu/lazycodex
path: lazycodex-marketplace
token: ${{ secrets.LAZYCODEX_SYNC_TOKEN }}
fetch-depth: 0
- name: Sync LazyCodex Codex marketplace
env:
VERSION: ${{ needs.publish-main.outputs.version }}
run: |
bun run script/sync-lazycodex-marketplace.ts "$GITHUB_WORKSPACE" "$GITHUB_WORKSPACE/lazycodex-marketplace"
cd "$GITHUB_WORKSPACE/lazycodex-marketplace"
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git add .agents/plugins/marketplace.json plugins/omo
git diff --cached --quiet || git commit -m "chore: sync Codex marketplace v${VERSION}"
git push origin HEAD:main
- name: Create GitHub release
env:
VERSION: ${{ needs.publish-main.outputs.version }}
+3 -2
View File
@@ -22,8 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Per-agent `displayName` for i18n. Agents can present localized names in UI and logs. (PR #4081)
- Grok family models registered with `reasoningEffort` support. (PR #4186)
- CLI `setup` alias for `install`. Either command runs the interactive setup wizard. (PR #4174)
- Codex harness adapter (`omo-codex`): one-command install via `bunx omo install --codex=yes` or the new `lazycodex` bin entry. Vendored Codex plugin namespace `omo` with rules, comment-checker, LSP, ultrawork, and ultragoal components. Plugin lands in `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/` and is enabled in `~/.codex/config.toml`. Idempotent installer (re-running is safe).
- Three new bin entries: `omo` (short alias) and `lazycodex` (auto-defaults `--codex=yes`). Existing `oh-my-opencode` and `oh-my-openagent` continue to work unchanged.
- Codex CLI Light edition (`omo-codex`): one-command install via `bunx omo install --platform=codex` or the new `lazycodex` bin entry. Vendored Codex plugin namespace `omo` with rules, comment-checker, LSP, ultrawork, and ultragoal components. Plugin lands in `~/.codex/plugins/cache/sisyphuslabs/omo/` and is enabled in `~/.codex/config.toml`. Idempotent installer (re-running is safe).
- New `--platform <opencode|codex|both>` install flag (default `opencode`). Replaces the previous Codex-as-optional-addon model — `--platform=codex` installs only the Codex Light edition, `--platform=both` installs both editions in one run.
- Three new bin entries: `omo` (short alias) and `lazycodex` (auto-defaults `--platform=codex`). Existing `oh-my-opencode` and `oh-my-openagent` continue to work unchanged.
- New PostHog telemetry stream `omo_codex_daily_active` distinguishing omo-codex installations from omo-opencode. Independent opt-out via `OMO_CODEX_DISABLE_POSTHOG=1` or `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`; global `OMO_DISABLE_POSTHOG` and `OMO_SEND_ANONYMOUS_TELEMETRY` still suppress both products.
- omo-codex now reports true daily-active usage (DAU/WAU/MAU). A new Codex plugin component `telemetry` (`packages/omo-codex/plugin/components/telemetry/`) fires a single `omo_codex_daily_active` event with `reason: "session_start"` from every Codex `SessionStart` hook, with the same UTC-day deduplication, hashed installation identifier, and opt-out env vars as the install-time event. Identity constants stay byte-equivalent across the CLI installer and the plugin runtime via `packages/omo-codex/src/telemetry/cross-package-equivalence.test.ts`.
- Triple-publish to npm: `oh-my-opencode`, `oh-my-openagent`, and the new `lazycodex` package with the same compiled CLI and four bin commands. See `docs/reference/lazycodex-npm-reservation.md` for the first-publish playbook.
+26 -22
View File
@@ -123,7 +123,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
匿名のテレメトリは、アクティブなインストール数(DAU/WAU/MAU)の集計のためにデフォルトで有効になっています。マシン1台につきUTC日あたり最大1回イベントが送信され、ハッシュ化されたインストール識別子を使用し、生のホスト名は使用せず、PostHog person profile も作成されません。無効化するには `OMO_SEND_ANONYMOUS_TELEMETRY=0` または `OMO_DISABLE_POSTHOG=1` を設定してください。[プライバシーポリシー](docs/legal/privacy-policy.md)と[利用規約](docs/legal/terms-of-service.md)をご覧ください。
OpenAI Codex CLI も使う場合は、オプションの Codex アダプターを `bunx omo install --codex=yes` かショートカットの `bunx lazycodex install` で追加できます。Codex 専用テレメトリは `OMO_CODEX_DISABLE_POSTHOG=1` または `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` で無効化できます。
**Ultimate と Light:** oh-my-openagent は同じ製品の 2 つのエディションとして提供されます。**Ultimate エディション**`bunx omo install` または `--platform=opencode`、デフォルト)は OpenCode 上のフル機能で、11 エージェント、54+ フック、Team Mode、すべての MCP、スラッシュコマンド、IntentGate モードを提供します。**Light エディション**(`bunx omo install --platform=codex`)は OpenAI Codex CLI のプラグインシステムへ綺麗に移植できる 5 コンポーネント(`rules``comment-checker``lsp``ultrawork``ultragoal`)のみを提供します。`bunx lazycodex install``--platform=codex` のショートカット別名です。両方を同時にインストールするには `--platform=both`。Codex 専用テレメトリは `OMO_CODEX_DISABLE_POSTHOG=1` または `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` で無効化できます。
---
@@ -152,25 +152,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding プラン ($10)](https://z.ai/subscribe)
- 従量課金 (pay-per-token) の対象であれば、Kimi や Gemini モデルを使っても費用はそれほどかかりません。
| | 機能 | 何をするのか |
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphus が Hephaestus、Oracle、Librarian、Explore をオーケストレーションします。完全な AI 開発チームが並列で動きます。 |
| 🧩 | **Codex Harness Adapter** | OpenAI Codex CLI でも同じ OMO 機能を利用できます。`bunx lazycodex install` または `bunx omo install --codex=yes` で導入できます。 |
| 👥 | **Team Mode** (v4.0, オプトイン) | リードエージェント + 最大 8 メンバーの並列実行、リアルタイム tmux 可視化、専用 `team_*` ツール群。`hyperplan`(5 人の敵対的批評家)と `security-research`(3 人のハンター + 2 人の PoC エンジニア)を駆動します。[ドキュメント →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | 一言で OK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-line エラー 0%。[oh-my-pi](https://github.com/can1357/oh-my-pi) にインスパイアされています。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、AST を考慮した書き換え。エージェントに IDE レベルの精度を提供します。 |
| 🧠 | **バックグラウンドエージェント** | 5 人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
| 📚 | **組み込み MCP** | Exa (Web 検索)、Context7 (公式ドキュメント)、Grep.app (GitHub 検索)。常にオンです。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100% 完了するまで絶対に止まりません。 |
| ✅ | **Todo Enforcer** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
| 💬 | **コメントチェッカー** | コメントから AI 臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
| 🖥️ | **Tmux 統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUI アプリがすべてリアルタイムで動きます。 |
| 🔌 | **Claude Code 互換性** | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。 |
| 🎯 | **スキル内蔵 MCP** | スキルが独自の MCP サーバーを持ち歩きます。コンテキストが肥大化しません。 |
| 📋 | **Prometheus プランナー** | インタビューモードで、実行前に戦略的な計画から立てます。 |
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
| | 機能 | Editions | 何をするのか |
| :---: | :------------------------------------------------------- | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **規律あるエージェント (Discipline Agents)** | Ultimate | Sisyphus が Hephaestus、Oracle、Librarian、Explore をオーケストレーションします。完全な AI 開発チームが並列で動きます。 |
| 🧩 | **Codex CLI Light Edition** | Light | OpenAI Codex CLI 上で動作する omo の 5 つの移植コンポーネント (rules, comment-checker, LSP, ultrawork, ultragoal)。インストール: `bunx omo install --platform=codex` |
| 👥 | **Team Mode** (v4.0, オプトイン) | Ultimate | リードエージェント + 最大 8 メンバーの並列実行、リアルタイム tmux 可視化、専用 `team_*` ツール群。`hyperplan`(5 人の敵対的批評家)と `security-research`(3 人のハンター + 2 人の PoC エンジニア)を駆動します。[ドキュメント →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | Both | 一言で OK。すべてのエージェント (Ultimate) または Codex `ultrawork` コンポーネント (Light) がアクティブになり、終わるまで止まりません。 |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Ultimate | ユーザーの真の意図を分析してから分類・行動します。`search` / `analyze` / `team` / `hyperplan` をトリガー。(Light は `ulw` / `ultrawork` のみフック。) |
| 🔗 | **ハッシュベースの編集ツール** | Ultimate | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-line エラー 0%。[oh-my-pi](https://github.com/can1357/oh-my-pi) にインスパイア。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex はネイティブの `apply_patch` を使用。) |
| 🛠️ | **LSP + AST-Grep** | Ultimate | ワークスペース単位のリネーム、ビルド前の診断、AST を考慮した書き換え。エージェントに IDE レベルの精度を提供。(LSP は Light でも `lsp` コンポーネントで動作; AST-Grep は Ultimate のみ。) |
| 🧠 | **バックグラウンドエージェント** | Ultimate | 5 人以上の専門家を並列で投入。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
| 📚 | **組み込み MCP** | Ultimate | Exa (Web 検索)、Context7 (公式ドキュメント)、Grep.app (GitHub 検索)。常にオン。(Light は LSP MCP のみ。) |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | 自己参照ループ。100% 完了するまで絶対に止まりません。 |
| ✅ | **Todo Enforcer** (Boulder) | Ultimate | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
| 💬 | **コメントチェッカー** | Both | コメントから AI 臭い無駄話を排除。両エディションで同じ `@code-yeongyu/comment-checker` バイナリが動作。 |
| 📜 | **Rules Injection** | Both | `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**` の階層的コンテキスト注入。Ultimate はフック、Light は `rules` コンポーネント。 |
| 🧬 | **Ultragoal** | Light | `.omo/ultragoal/` evidence audit ベースの永続的マルチゴール オーケストレーション。現在は Codex 専用; OpenCode 側への移植はロードマップ。 |
| 🖥️ | **Tmux 統合** | Ultimate | 完全なインタラクティブターミナル。REPL、デバッガー、TUI アプリがすべてリアルタイムで動きます。 |
| 🔌 | **Claude Code 互換性** | Ultimate | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。(Codex は独自のネイティブプラグインシステムを保有。) |
| 🎯 | **スキル内蔵 MCP** | Ultimate | スキルが独自の MCP サーバーを持ち歩きます。コンテキストが肥大化しません。 |
| 📋 | **Prometheus プランナー** | Ultimate | インタビューモードで、実行前に戦略的な計画から立てます。 |
| 🔍 | **`/init-deep`** | Ultimate | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
> **Editions legend.** **Ultimate** = OpenCode 専用 (`bunx omo install`)。**Light** = Codex CLI 専用 (`bunx omo install --platform=codex`)。**Both** = 両エディションに提供、しばしば内部実装は若干異なる。
### 規律あるエージェント (Discipline Agents)
@@ -345,10 +349,10 @@ oh-my-openagent を削除するには:
4. **omo-codex (Codex アダプター) を削除する**
```bash
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
```
その後 `~/.codex/config.toml` を開き、`[plugins."omo@code-yeongyu-codex-plugins"]` ブロックと `[hooks.state.\"omo@...\"]` ブロックを削除してください。
その後 `~/.codex/config.toml` を開き、`[plugins."omo@sisyphuslabs"]` ブロックと `[hooks.state.\"omo@...\"]` ブロックを削除してください。
## Features
+26 -22
View File
@@ -124,7 +124,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
익명 텔레메트리는 활성 설치 수(DAU/WAU/MAU) 집계를 위해 기본적으로 활성화되어 있습니다. 머신당 UTC 하루에 최대 1회만 이벤트가 전송되며, 해시된 설치 식별자를 사용하고 원시 호스트명은 절대 사용하지 않으며 PostHog person profile은 생성되지 않습니다. `OMO_SEND_ANONYMOUS_TELEMETRY=0` 또는 `OMO_DISABLE_POSTHOG=1`로 비활성화할 수 있습니다. [개인정보처리방침](docs/legal/privacy-policy.md)과 [서비스 이용약관](docs/legal/terms-of-service.md)을 참조하세요.
OpenAI Codex CLI도 사용한다면 선택 사항인 Codex 어댑터를 `bunx omo install --codex=yes` 또는 바로가기 `bunx lazycodex install`로 함께 설치하세요. Codex 전용 텔레메트리는 `OMO_CODEX_DISABLE_POSTHOG=1` 또는 `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`으로 비활성화할 수 있습니다.
**Ultimate vs Light:** oh-my-openagent는 같은 제품의 두 에디션으로 출시됩니다. **Ultimate 에디션**(`bunx omo install` 또는 `--platform=opencode`, 기본값)은 OpenCode 위에서 풀 기능 — 11 agent, 54+ hook, Team Mode, 모든 MCP, 슬래시 명령, IntentGate 모드 — 을 제공합니다. **Light 에디션**(`bunx omo install --platform=codex`)은 OpenAI Codex CLI의 플러그인 시스템에 깔끔히 포팅되는 5개 컴포넌트(`rules`, `comment-checker`, `lsp`, `ultrawork`, `ultragoal`)만 제공합니다. `bunx lazycodex install``--platform=codex`의 단축 별칭입니다. 둘 다 설치하려면 `--platform=both`. Codex 전용 텔레메트리는 `OMO_CODEX_DISABLE_POSTHOG=1` 또는 `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`으로 비활성화할 수 있습니다.
---
@@ -153,25 +153,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding 요금제 ($10)](https://z.ai/subscribe)
- 종량제(pay-per-token) 대상자라면 kimi와 gemini 모델을 써도 비용이 별로 안 나옵니다.
| | 기능 | 하는 일 |
| :---: | :------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **Discipline Agents** | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 지휘합니다. 병렬로 도는 풀스택 AI 개발팀. |
| 🧩 | **Codex Harness Adapter** | OpenAI Codex CLI에서일한 OMO 기능을 제공합니다. `bunx lazycodex install` 또는 `bunx omo install --codex=yes`로 설치하세요. |
| 👥 | **Team Mode** (v4.0, opt-in) | 리드 에이전트 + 최대 8명의 병렬 멤버, 실시간 tmux 시각화, 전용 `team_*` 도구. `hyperplan`(5명의 적대적 비평가)과 `security-research`(3명의 헌터 + 2명의 PoC 엔지니어)를 구동합니다. [문서 →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | 한 단어. 모든 에이전트가 켜집니다. 끝날 때까지 멈추지 않습니다. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | 분류하거나 행동하기 전에 사용자의 진짜 의도부터 분석합니다. 문자 그대로 오해하는 일은 끝. |
| 🔗 | **Hash-Anchored Edit Tool** | `LINE#ID` 콘텐츠 해시가 모든 변경을 검증합니다. 낡은 라인 에러 0건. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감. [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 워크스페이스 리네임, 빌드 전 진단, AST 기반 리라이트. 에이전트에게도 IDE 수준의 정밀도. |
| 🧠 | **Background Agents** | 전문가 5명 이상을 동시에 발사. 컨텍스트는 가볍게. 결과는 준비되면 도착. |
| 📚 | **Built-in MCPs** | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있음. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 자기참조 루프. 100% 끝날 때까지 멈추지 않습니다. |
| ✅ | **Todo Enforcer** | 에이전트가 놀고 있나요? 시스템이 다시 끌어옵니다. 당신의 작업은 반드시 끝납니다. |
| 💬 | **Comment Checker** | 주석에 AI 슬롭 금지. 시니어가 쓴 것처럼 읽히는 코드. |
| 🖥️ | **Tmux Integration** | 풀 인터랙티브 터미널. REPL, 디버거, TUI 전부 라이브. |
| 🔌 | **Claude Code Compatible** | 쓰시던 hook, command, skill, MCP, plugin 전부 그대로 동작합니다. |
| 🎯 | **Skill-Embedded MCPs** | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트 낭비 없음. |
| 📋 | **Prometheus Planner** | 실행 전 인터뷰 모드로 전략 플래닝. |
| 🔍 | **`/init-deep`** | 프로젝트 전반에 계층형 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율에도, 에이전트 성능에도 좋습니다. |
| | 기능 | Editions | 하는 일 |
| :---: | :------------------------------------------------------- | :------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **Discipline Agents** | Ultimate | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 지휘합니다. 병렬로 도는 풀스택 AI 개발팀. |
| 🧩 | **Codex CLI Light Edition** | Light | OpenAI Codex CLI에서 동작하는 omo의 5개 포팅 컴포넌트(rules, comment-checker, LSP, ultrawork, ultragoal). 설치: `bunx omo install --platform=codex`. |
| 👥 | **Team Mode** (v4.0, opt-in) | Ultimate | 리드 에이전트 + 최대 8명의 병렬 멤버, 실시간 tmux 시각화, 전용 `team_*` 도구. `hyperplan`(5명의 적대적 비평가)과 `security-research`(3명의 헌터 + 2명의 PoC 엔지니어)를 구동합니다. [문서 →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | Both | 한 단어. 모든 에이전트(Ultimate)나 Codex `ultrawork` 컴포넌트(Light)가 켜집니다. 끝날 때까지 멈추지 않습니다. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Ultimate | 분류하거나 행동하기 전에 사용자의 진짜 의도부터 분석합니다. `search` / `analyze` / `team` / `hyperplan` 트리거. (Light는 `ulw` / `ultrawork`만 hook.) |
| 🔗 | **Hash-Anchored Edit Tool** | Ultimate | `LINE#ID` 콘텐츠 해시가 모든 변경을 검증합니다. 낡은 라인 에러 0건. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감. [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex는 자체 `apply_patch` 사용.) |
| 🛠️ | **LSP + AST-Grep** | Ultimate | 워크스페이스 리네임, 빌드 전 진단, AST 기반 리라이트. 에이전트에게도 IDE 수준의 정밀도. (LSP는 Light에서도 `lsp` 컴포넌트로 동작; AST-Grep은 Ultimate 전용.) |
| 🧠 | **Background Agents** | Ultimate | 전문가 5명 이상을 동시에 발사. 컨텍스트는 가볍게. 결과는 준비되면 도착. |
| 📚 | **Built-in MCPs** | Ultimate | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있음. (Light는 LSP MCP만.) |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | 자기참조 루프. 100% 끝날 때까지 멈추지 않습니다. |
| ✅ | **Todo Enforcer** (Boulder) | Ultimate | 에이전트가 놀고 있나요? 시스템이 다시 끌어옵니다. 당신의 작업은 반드시 끝납니다. |
| 💬 | **Comment Checker** | Both | 주석에 AI 슬롭 금지. 동일한 `@code-yeongyu/comment-checker` 바이너리가 두 에디션 모두에서 동작. |
| 📜 | **Rules Injection** | Both | `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**` 계층형 컨텍스트 주입. Ultimate은 hook, Light는 `rules` 컴포넌트. |
| 🧬 | **Ultragoal** | Light | `.omo/ultragoal/` evidence audit 기반 영속 멀티 골 오케스트레이션. 현재 Codex 전용; OpenCode 사이드 포팅은 로드맵에 있음. |
| 🖥️ | **Tmux Integration** | Ultimate | 풀 인터랙티브 터미널. REPL, 디버거, TUI 전부 라이브. |
| 🔌 | **Claude Code Compatible** | Ultimate | 쓰시던 hook, command, skill, MCP, plugin 전부 그대로 동작합니다. (Codex는 자체 플러그인 시스템 보유.) |
| 🎯 | **Skill-Embedded MCPs** | Ultimate | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트 낭비 없음. |
| 📋 | **Prometheus Planner** | Ultimate | 실행 전 인터뷰 모드로 전략 플래닝. |
| 🔍 | **`/init-deep`** | Ultimate | 프로젝트 전반에 계층형 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율에도, 에이전트 성능에도 좋습니다. |
> **Editions legend.** **Ultimate** = OpenCode 전용 (`bunx omo install`). **Light** = Codex CLI 전용 (`bunx omo install --platform=codex`). **Both** = 두 에디션 모두 제공, 종종 내부 구현은 약간 다름.
### Discipline Agents
@@ -346,10 +350,10 @@ oh-my-openagent를 제거하려면:
4. **omo-codex (Codex 어댑터) 제거**
```bash
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
```
그런 다음 `~/.codex/config.toml`을 열어 `[plugins."omo@code-yeongyu-codex-plugins"]` 블록과 `[hooks.state.\"omo@...\"]` 블록들을 삭제하세요.
그런 다음 `~/.codex/config.toml`을 열어 `[plugins."omo@sisyphuslabs"]` 블록과 `[hooks.state.\"omo@...\"]` 블록들을 삭제하세요.
## Features
+3 -3
View File
@@ -113,7 +113,7 @@ Pick the edition(s) you want.
| You want | Run | What lands on disk |
| :--- | :--- | :--- |
| **Ultimate** (OpenCode) | `bunx omo install` (TUI walks you through it) | Plugin registered in `opencode.json` + agent/model config + provider auth prompts |
| **Light** (Codex CLI) | `bunx omo install --platform=codex` | `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/` + `~/.codex/config.toml` plugin block + `~/.local/bin/omo-*` |
| **Light** (Codex CLI) | `bunx omo install --platform=codex` | `~/.codex/plugins/cache/sisyphuslabs/omo/` + `~/.codex/config.toml` plugin block + `~/.local/bin/omo-*` |
| **Both** | `bunx omo install --platform=both` | Both of the above |
`--platform` defaults to `opencode` (Ultimate). The `bunx lazycodex install` alias is a one-letter-cheaper shortcut for `bunx omo install --platform=codex` — use whichever reads cleaner.
@@ -395,10 +395,10 @@ To remove oh-my-openagent:
4. **Remove omo-codex (Codex adapter)**
```bash
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
```
Then open `~/.codex/config.toml` and remove the `[plugins."omo@code-yeongyu-codex-plugins"]` block and any `[hooks.state.\"omo@...\"]` blocks.
Then open `~/.codex/config.toml` and remove the `[plugins."omo@sisyphuslabs"]` block and any `[hooks.state.\"omo@...\"]` blocks.
## Features
+26 -22
View File
@@ -121,7 +121,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
Анонимная телеметрия включена по умолчанию для подсчёта активных установок (DAU/WAU/MAU). Не более одного события на машину за UTC-сутки, использует хешированный идентификатор установки, никогда не использует исходное имя хоста, и не создаёт PostHog person profile. Можно отключить через `OMO_SEND_ANONYMOUS_TELEMETRY=0` или `OMO_DISABLE_POSTHOG=1`. См. [Политику конфиденциальности](docs/legal/privacy-policy.md) и [Условия обслуживания](docs/legal/terms-of-service.md).
Если вы также используете OpenAI Codex CLI, установите дополнительный Codex-адаптер через `bunx omo install --codex=yes` или короткую команду `bunx lazycodex install`. Телеметрию только для Codex можно отключить через `OMO_CODEX_DISABLE_POSTHOG=1` или `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`.
**Ultimate и Light:** oh-my-openagent поставляется в двух редакциях одного продукта. **Ultimate** (`bunx omo install` или `--platform=opencode`, по умолчанию) — полнофункциональная редакция поверх OpenCode: 11 агентов, 54+ хука, Team Mode, все MCP, все слэш-команды, режимы IntentGate. **Light** (`bunx omo install --platform=codex`) — только 5 компонентов omo, которые портируются в систему плагинов OpenAI Codex CLI: `rules`, `comment-checker`, `lsp`, `ultrawork`, `ultragoal`. `bunx lazycodex install` — это сокращённый псевдоним для `--platform=codex`. Чтобы установить обе редакции одной командой, используйте `--platform=both`. Телеметрию только для Codex можно отключить через `OMO_CODEX_DISABLE_POSTHOG=1` или `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`.
------
@@ -151,25 +151,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [Тариф GLM Coding ($10)](https://z.ai/subscribe)
- Если у вас есть доступ к оплате за токены, использование моделей Kimi и Gemini обойдётся недорого.
| | Функция | Что делает |
| --- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
| 🧩 | **Codex Harness Adapter** | Те же возможности OMO для OpenAI Codex CLI. Установка через `bunx lazycodex install` или `bunx omo install --codex=yes`. |
| 👥 | **Team Mode** (v4.0, opt-in) | Лид-агент + до 8 параллельных участников, визуализация в tmux в реальном времени, выделенные инструменты `team_*`. Питает `hyperplan` (5 враждебных критиков) и `security-research` (3 охотника + 2 PoC-инженера). [Документация →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
| | Функция | Editions | Что делает |
| --- | -------------------------------------------------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **Дисциплинированные агенты** | Ultimate | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
| 🧩 | **Codex CLI Light Edition** | Light | 5 компонентов omo, портированных в OpenAI Codex CLI (rules, comment-checker, LSP, ultrawork, ultragoal). Установка: `bunx omo install --platform=codex`. |
| 👥 | **Team Mode** (v4.0, opt-in) | Ultimate | Лид-агент + до 8 параллельных участников, визуализация в tmux в реальном времени, выделенные инструменты `team_*`. Питает `hyperplan` (5 враждебных критиков) и `security-research` (3 охотника + 2 PoC-инженера). [Документация →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | Both | Одно слово. Все агенты (Ultimate) или Codex-компонент `ultrawork` (Light) активируются. Не останавливается, пока задача не выполнена. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Ultimate | Анализирует истинное намерение пользователя перед классификацией и действием. Триггеры `search` / `analyze` / `team` / `hyperplan`. (Light хукает только `ulw` / `ultrawork`.) |
| 🔗 | **Инструмент правок на основе хэш-якорей** | Ultimate | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex использует собственный `apply_patch`.) |
| 🛠️ | **LSP + AST-Grep** | Ultimate | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. (LSP также работает в Light через компонент `lsp`; AST-Grep только Ultimate.) |
| 🧠 | **Фоновые агенты** | Ultimate | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
| 📚 | **Встроенные MCP** | Ultimate | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. (В Light только LSP MCP.) |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
| ✅ | **Todo Enforcer** (Boulder) | Ultimate | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
| 💬 | **Comment Checker** | Both | Никакого AI-мусора в комментариях. Тот же бинарник `@code-yeongyu/comment-checker` работает в обеих редакциях. |
| 📜 | **Rules Injection** | Both | Иерархическое внедрение контекста из `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**`. В Ultimate это хук, в Light — компонент `rules`. |
| 🧬 | **Ultragoal** | Light | Долговечная оркестрация нескольких целей с аудитом доказательств в `.omo/ultragoal/`. Сейчас только в Codex; порт в сторону OpenCode в дорожной карте. |
| 🖥️ | **Интеграция с Tmux** | Ultimate | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
| 🔌 | **Совместимость с Claude Code** | Ultimate | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. (У Codex своя нативная плагин-система.) |
| 🎯 | **MCP, встроенные в навыки** | Ultimate | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
| 📋 | **Prometheus Planner** | Ultimate | Стратегическое планирование в режиме интервью перед любым выполнением. |
| 🔍 | **`/init-deep`** | Ultimate | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
> **Editions, легенда.** **Ultimate** = только OpenCode (`bunx omo install`). **Light** = только Codex CLI (`bunx omo install --platform=codex`). **Both** = поставляется в обеих редакциях, часто с немного отличающейся реализацией.
### Дисциплинированные агенты
@@ -344,10 +348,10 @@ project/
4. **Удалите omo-codex (Codex-адаптер)**
```bash
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
```
Затем откройте `~/.codex/config.toml` и удалите блок `[plugins."omo@code-yeongyu-codex-plugins"]` и все блоки `[hooks.state.\"omo@...\"]`.
Затем откройте `~/.codex/config.toml` и удалите блок `[plugins."omo@sisyphuslabs"]` и все блоки `[hooks.state.\"omo@...\"]`.
## Функции
+26 -22
View File
@@ -123,7 +123,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
匿名遥测默认开启,用于统计活跃安装数(DAU/WAU/MAU)。每台机器每个 UTC 日最多发送一次事件,使用哈希化的安装标识符,绝不会使用原始主机名,且不会创建 PostHog person profile。可通过 `OMO_SEND_ANONYMOUS_TELEMETRY=0``OMO_DISABLE_POSTHOG=1` 禁用。详见 [隐私政策](docs/legal/privacy-policy.md) 和 [服务条款](docs/legal/terms-of-service.md)。
如果你也使用 OpenAI Codex CLI,可以额外安装可选的 Codex 适配器:`bunx omo install --codex=yes`,或使用快捷命令 `bunx lazycodex install`。Codex 专用遥测可通过 `OMO_CODEX_DISABLE_POSTHOG=1``OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` 禁用。
**Ultimate 与 Light:** oh-my-openagent 以同一产品的两个版本发布。**Ultimate 版本**`bunx omo install``--platform=opencode`,默认值)在 OpenCode 上提供完整功能 —— 11 个智能体、54+ 个生命周期钩子、Team Mode、所有 MCP、所有斜杠命令、IntentGate 模式。**Light 版本**`bunx omo install --platform=codex`)仅提供能够干净地移植到 OpenAI Codex CLI 插件系统的 5 个组件(`rules``comment-checker``lsp``ultrawork``ultragoal`)。`bunx lazycodex install``--platform=codex` 的快捷别名。要同时安装两个版本,使用 `--platform=both`。Codex 专用遥测可通过 `OMO_CODEX_DISABLE_POSTHOG=1``OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` 禁用。
---
@@ -152,25 +152,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding 套餐 ($10)](https://z.ai/subscribe)
- 如果你能使用按 token 计费的方式,用 Kimi 和 Gemini 模型花不了多少钱。
| | 特性 | 功能说明 |
| :---: | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🤖 | **自律军团 (Discipline Agents)** | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
| 🧩 | **Codex Harness Adapter** | 在 OpenAI Codex CLI 中也能使用同样的 OMO 功能。通过 `bunx lazycodex install``bunx omo install --codex=yes` 安装。 |
| 👥 | **Team Mode** (v4.0, 选择性启用) | 领导 Agent + 最多 8 个并行成员,实时 tmux 可视化,专用 `team_*` 工具家族。驱动 `hyperplan`(5 个敌对评论者) 和 `security-research`(3 个猎手 + 2 个 PoC 工程师)。[文档 →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | 一键触发,所有智能体出动。任务完成前绝不罢休。 |
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | 真正行动前,先分析用户的真实意图。彻底告别被字面意思误导的 AI 废话。 |
| 🔗 | **基于哈希的编辑工具** | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。 |
| 🧠 | **后台智能体** | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
| 📚 | **内置 MCP** | Exa(网络搜索)、Context7(官方文档)、Grep.app(GitHub 源码搜索)。默认开启。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自我引用闭环。达不到 100% 完成度绝不停止。 |
| ✅ | **Todo 强制执行** | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
| 💬 | **注释审查员** | 剔除带有浓烈 AI 味的冗余注释。写出的代码就像老练的高级工程师写的。 |
| 🖥️ | **Tmux 集成** | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
| 🔌 | **Claude Code 兼容** | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。 |
| 🎯 | **技能内嵌 MCP** | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
| 📋 | **Prometheus 规划师** | 动手写代码前,先通过访谈模式做好战略规划。 |
| 🔍 | **`/init-deep`** | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token,还能大幅提升 Agent 理解力。 |
| | 特性 | Editions | 功能说明 |
| :---: | :-------------------------------------------------------------- | :------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🤖 | **自律军团 (Discipline Agents)** | Ultimate | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
| 🧩 | **Codex CLI Light Edition** | Light | 在 OpenAI Codex CLI 中运行的 omo 的 5 个可移植组件 (rules, comment-checker, LSP, ultrawork, ultragoal)。安装: `bunx omo install --platform=codex` |
| 👥 | **Team Mode** (v4.0, 选择性启用) | Ultimate | 领导 Agent + 最多 8 个并行成员,实时 tmux 可视化,专用 `team_*` 工具家族。驱动 `hyperplan`(5 个敌对评论者) 和 `security-research`(3 个猎手 + 2 个 PoC 工程师)。[文档 →](docs/guide/team-mode.md) |
| ⚡ | **`ultrawork` / `ulw`** | Both | 一键触发,所有智能体(Ultimate)或 Codex `ultrawork` 组件(Light)出动。任务完成前绝不罢休。 |
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | Ultimate | 真正行动前,先分析用户的真实意图。触发 `search` / `analyze` / `team` / `hyperplan`。(Light 仅 hook `ulw` / `ultrawork`。) |
| 🔗 | **基于哈希的编辑工具** | Ultimate | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex 使用其原生 `apply_patch`。) |
| 🛠️ | **LSP + AST-Grep** | Ultimate | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。(LSP 在 Light 中也通过 `lsp` 组件提供; AST-Grep 仅 Ultimate。) |
| 🧠 | **后台智能体** | Ultimate | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
| 📚 | **内置 MCP** | Ultimate | Exa(网络搜索)、Context7(官方文档)、Grep.app(GitHub 源码搜索)。默认开启。(Light 仅 LSP MCP。) |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | 自我引用闭环。达不到 100% 完成度绝不停止。 |
| ✅ | **Todo 强制执行** (Boulder) | Ultimate | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
| 💬 | **注释审查员** | Both | 剔除带有浓烈 AI 味的冗余注释。同一个 `@code-yeongyu/comment-checker` 二进制在两个版本中运行。 |
| 📜 | **Rules Injection** | Both | `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**` 的分层上下文注入。Ultimate 中为 hookLight 中为 `rules` 组件。 |
| 🧬 | **Ultragoal** | Light | 基于 `.omo/ultragoal/` 证据审计的持久化多目标编排。目前仅 Codex 可用; OpenCode 侧的移植在路线图上。 |
| 🖥️ | **Tmux 集成** | Ultimate | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
| 🔌 | **Claude Code 兼容** | Ultimate | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。(Codex 拥有其自己的原生插件系统。) |
| 🎯 | **技能内嵌 MCP** | Ultimate | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
| 📋 | **Prometheus 规划师** | Ultimate | 动手写代码前,先通过访谈模式做好战略规划。 |
| 🔍 | **`/init-deep`** | Ultimate | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token,还能大幅提升 Agent 理解力。 |
> **Editions 图例。** **Ultimate** = 仅 OpenCode (`bunx omo install`)。**Light** = 仅 Codex CLI (`bunx omo install --platform=codex`)。**Both** = 两个版本均提供 (内部实现可能略有不同)。
### 自律军团 (Discipline Agents)
@@ -345,10 +349,10 @@ Agent 会自动顺藤摸瓜加载对应的 Context,免去了你所有的手动
4. **移除 omo-codexCodex 适配器)**
```bash
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
```
然后打开 `~/.codex/config.toml`,删除 `[plugins."omo@code-yeongyu-codex-plugins"]` 区块,以及所有 `[hooks.state.\"omo@...\"]` 区块。
然后打开 `~/.codex/config.toml`,删除 `[plugins."omo@sisyphuslabs"]` 区块,以及所有 `[hooks.state.\"omo@...\"]` 区块。
## Features
+64 -54
View File
@@ -1,17 +1,25 @@
# Installation
oh-my-openagent installs as a plugin into one or both of these AI harnesses:
oh-my-openagent ships in **two editions** of the same product:
- **[OpenCode](https://opencode.ai)** — terminal-based open-source AI coding agent.
- **[OpenAI Codex CLI](https://github.com/openai/codex)** — OpenAI's official Codex CLI.
- **Ultimate Edition (omo for [OpenCode](https://opencode.ai))** — the full omo experience. 11 discipline agents, 54+ lifecycle hooks, all built-in MCPs, every slash command, Team Mode, ultragoal, hashline edits, the works.
- **Light Edition (omo for [OpenAI Codex CLI](https://github.com/openai/codex))** — the 5 components that port cleanly to Codex's plugin system: `rules`, `comment-checker`, `lsp`, `ultrawork`, `ultragoal`. No agent orchestration, no `team_*` tools, no built-in web/docs/code search MCPs — Codex CLI's native surface does that work.
Pick what you use. The installer handles both, separately or together.
Most users want **Ultimate**. Pick **Light** if you are already invested in Codex CLI. Pick **both** if you want OMO available wherever you happen to be working that day.
| You want | Run | Lands on disk |
| :--- | :--- | :--- |
| Ultimate (OpenCode) | `bunx omo install` (TUI walks you through it) | Plugin registered in `opencode.json`, agent/model config, provider auth |
| Light (Codex CLI) | `bunx omo install --platform=codex` (no questions) | `~/.codex/plugins/cache/...`, `~/.codex/config.toml` plugin block, `~/.local/bin/omo-*` |
| Both | `bunx omo install --platform=both` | Both of the above |
`--platform` defaults to `opencode` (Ultimate). The `bunx lazycodex install` alias is a shortcut for `bunx omo install --platform=codex` — same compiled CLI, different default.
## For Humans
**Strongly recommended: let an LLM agent install this for you.** OpenCode setup involves subscription detection, model selection, provider authentication, and config migration — humans fat-finger these. An LLM agent reads the full guide and walks through every step correctly.
**Strongly recommended: let an LLM agent install Ultimate for you.** Ultimate setup involves subscription detection, model selection across 11 agents, provider authentication, and config migration — humans fat-finger these. An LLM agent reads the full guide and walks every step correctly.
### OpenCode (or OpenCode + Codex)
### Ultimate (OpenCode) — let an agent do it
Paste this prompt into Claude Code, AmpCode, Cursor, or any LLM agent session:
@@ -20,25 +28,27 @@ Install and configure oh-my-openagent by following the instructions here:
https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/docs/guide/installation.md
```
### Codex CLI only
### Light (Codex CLI) — one line, no agent needed
If you only want the Codex adapter, the installer asks no questions:
The Light edition installer asks zero questions, so a human can run it directly:
```bash
bunx omo install --platform=codex
# equivalent:
bunx lazycodex install
```
This is the single command equivalent of `bunx omo install --platform=codex`. It writes only to `~/.codex/`. No OpenCode questions, no provider flags.
It writes only to `~/.codex/`. No OpenCode interaction, no provider flags.
### A note on direct install
If you insist on running the installer yourself:
If you insist on running the Ultimate installer yourself:
```bash
bunx oh-my-openagent install
```
The TUI walks you through it. **Do NOT use `npm install -g`, `bun add -g`, or `bun install -g`** — global installation is not officially supported, oh-my-openagent is an OpenCode plugin that needs to resolve from where OpenCode loads plugins, and the `prepare` script requires Bun. Always invoke via `bunx`.
The TUI walks you through it. **Do NOT use `npm install -g`, `bun add -g`, or `bun install -g`** — global installation is not officially supported. oh-my-openagent is a plugin that must resolve from where OpenCode/Codex loads plugins, and the `prepare` script requires Bun. Always invoke via `bunx`.
## For LLM Agents
@@ -202,7 +212,7 @@ bunx oh-my-openagent install \
| Platform | Writes |
|----------|--------|
| `opencode`, `both` | Registers `"oh-my-openagent"` in `opencode.json` `plugin` array. Generates agent → model mappings into `~/.config/opencode/oh-my-openagent.jsonc`. |
| `codex`, `both` | Copies `packages/omo-codex/plugin/` into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`. Runs `npm install` + `npm run build` inside. Symlinks `~/.local/bin/omo-*` (or `$CODEX_LOCAL_BIN_DIR/omo-*`) for each of the 5 components. Computes SHA256 trusted-hashes for every hook and writes the `[plugins."omo@..."]` + `[hooks.state."omo@..."]` blocks into `~/.codex/config.toml`. |
| `codex`, `both` | Copies `packages/omo-codex/plugin/` into `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`. Runs `npm install` + `npm run build` inside. Symlinks `~/.local/bin/omo-*` (or `$CODEX_LOCAL_BIN_DIR/omo-*`) for each of the 5 components. Computes SHA256 trusted-hashes for every hook and writes the `[plugins."omo@..."]` + `[hooks.state."omo@..."]` blocks into `~/.codex/config.toml`. |
Both halves are independent and idempotent — re-running is safe.
@@ -223,10 +233,10 @@ bunx oh-my-openagent doctor
```bash
# Plugin cache present?
ls ~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/
ls ~/.codex/plugins/cache/sisyphuslabs/omo/
# Codex config has the plugin block?
grep -A2 'omo@code-yeongyu-codex-plugins' ~/.codex/config.toml
grep -A2 'omo@sisyphuslabs' ~/.codex/config.toml
# Component binaries linked?
ls ~/.local/bin/ | grep -E 'omo-(rules|comment-checker|lsp|ultrawork|ultragoal)'
@@ -458,35 +468,35 @@ After install, the user interacts with oh-my-openagent through five surfaces. Wa
Just type one of these words in your message and the system injects the corresponding mode prompt:
| Keyword | What it does |
|---------|--------------|
| `ultrawork` or `ulw` | Full orchestration mode — every agent activates, doesn't stop until done |
| `search` | Web/doc search focus |
| `analyze` | Deep analysis mode |
| `team` | Forces `team_*` tools orchestration (requires `team_mode.enabled`) |
| `hyperplan` | Adversarial planning via 5 hostile critics |
| `hyperplan ultrawork` (combo) | Both at once |
| Keyword | Editions | What it does |
|---------|:--------:|--------------|
| `ultrawork` or `ulw` | Both | Full orchestration mode — every agent (Ultimate) or the Codex `ultrawork` component (Light) activates, doesn't stop until done |
| `search` | Ultimate | Web/doc search focus |
| `analyze` | Ultimate | Deep analysis mode |
| `team` | Ultimate | Forces `team_*` tools orchestration (requires `team_mode.enabled`) |
| `hyperplan` | Ultimate | Adversarial planning via 5 hostile critics |
| `hyperplan ultrawork` (combo) | Ultimate | Both at once |
#### Slash commands
Built-in:
All built-in slash commands are **Ultimate-only** — Codex CLI does not have a slash-command surface, so the Light edition omits this entire layer.
| Command | Purpose |
|---------|---------|
| `/init-deep` | Auto-generate hierarchical `AGENTS.md` files throughout the project |
| `/start-work` | Spawn Prometheus to interview the user and build a plan, then execute |
| `/ralph-loop` | Self-referential dev loop until 100% done |
| `/ulw-loop` | Ultrawork-mode variant of the loop |
| `/cancel-ralph` | Stop an active Ralph loop |
| `/stop-continuation` | Stop ralph loop + todo continuation + boulder |
| `/refactor` | LSP + AST-grep + TDD-verified intelligent refactor |
| `/handoff` | Generate detailed context summary to continue in a new session |
| `/remove-ai-slops` | Strip AI-generated code smells from recent changes |
| `/hyperplan` | Direct invocation of hyperplan skill |
| Command | Editions | Purpose |
|---------|:--------:|---------|
| `/init-deep` | Ultimate | Auto-generate hierarchical `AGENTS.md` files throughout the project |
| `/start-work` | Ultimate | Spawn Prometheus to interview the user and build a plan, then execute |
| `/ralph-loop` | Ultimate | Self-referential dev loop until 100% done |
| `/ulw-loop` | Ultimate | Ultrawork-mode variant of the loop |
| `/cancel-ralph` | Ultimate | Stop an active Ralph loop |
| `/stop-continuation` | Ultimate | Stop ralph loop + todo continuation + boulder |
| `/refactor` | Ultimate | LSP + AST-grep + TDD-verified intelligent refactor |
| `/handoff` | Ultimate | Generate detailed context summary to continue in a new session |
| `/remove-ai-slops` | Ultimate | Strip AI-generated code smells from recent changes |
| `/hyperplan` | Ultimate | Direct invocation of hyperplan skill |
#### Agents (11)
#### Agents (11) — Ultimate only
Sisyphus delegates to these you don't usually call them directly, but knowing the cast helps:
All 11 discipline agents are part of the Ultimate edition. The Light edition does not ship agent orchestration — Codex CLI's own model selection takes that role. Sisyphus delegates to these; you don't usually call them directly, but knowing the cast helps:
- **Sisyphus** — main orchestrator. Plans, delegates, drives to completion.
- **Hephaestus** — "Codex on steroids." Deep autonomous worker, GPT-native.
@@ -502,16 +512,16 @@ Sisyphus delegates to these — you don't usually call them directly, but knowin
#### Skills
Built-in skills load automatically when their description matches your task. The user does not need to invoke them by name.
Built-in skills load automatically when their description matches your task. The user does not need to invoke them by name. The OpenCode skill system is **Ultimate-only**; the Light edition does not have a skill loader.
| Skill | When it triggers |
|-------|-----------------|
| `playwright` | Browser automation |
| `git-master` | Atomic commits, rebases, history search |
| `frontend-ui-ux` | UI/UX implementation work |
| `review-work` | Post-implementation code review |
| `ai-slop-remover` | Cleaning AI-generated code smells |
| `team-mode` | Loaded only when `team_mode.enabled` |
| Skill | Editions | When it triggers |
|-------|:--------:|------------------|
| `playwright` | Ultimate | Browser automation |
| `git-master` | Ultimate | Atomic commits, rebases, history search |
| `frontend-ui-ux` | Ultimate | UI/UX implementation work |
| `review-work` | Ultimate | Post-implementation code review |
| `ai-slop-remover` | Ultimate | Cleaning AI-generated code smells |
| `team-mode` | Ultimate | Loaded only when `team_mode.enabled` |
Add custom skills under `.opencode/skills/<name>/SKILL.md` (project scope) or `~/.config/opencode/skills/<name>/SKILL.md` (user scope). Each `SKILL.md` declares a description that the agent matches against your message.
@@ -526,15 +536,15 @@ After verification, tell the user:
Then say **Congratulations! 🎉 You have successfully set up oh-my-openagent! Type `opencode` (or `codex`) in your terminal to start using it.**
### Step 7: Codex adapter deep dive
### Step 7: Light Edition deep dive (Codex CLI)
Skip this section if `--platform=opencode`. Otherwise, the user installed the `omo-codex` adapter — here is what landed on disk and what each piece does.
Skip this section if `--platform=opencode`. Otherwise, the user installed the **Light edition** (`omo-codex`) — here is what landed on disk and what each piece does.
#### What was installed
- **Plugin cache:** `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`
- **Plugin cache:** `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`
- **Component binaries:** `~/.local/bin/omo-rules`, `omo-comment-checker`, `omo-lsp`, `omo-ultrawork`, `omo-ultragoal` (or `$CODEX_LOCAL_BIN_DIR/omo-*` if set)
- **Codex config edits:** `~/.codex/config.toml` gained `[features] plugins = true`, `[features] plugin_hooks = true`, a `[plugins."omo@code-yeongyu-codex-plugins"]` block, and SHA256-pinned `[hooks.state."omo@..."]` entries
- **Codex config edits:** `~/.codex/config.toml` gained `[features] plugins = true`, `[features] plugin_hooks = true`, a `[plugins."omo@sisyphuslabs"]` block, and SHA256-pinned `[hooks.state."omo@..."]` entries
#### The 5 components
@@ -556,7 +566,7 @@ The Codex adapter is fully independent of the OpenCode plugin. You can install b
|---------|-----|
| `codex --help` does not list the omo plugin | Re-run `bunx omo install --platform=codex` (idempotent — hook hashes are recomputed) |
| `command not found: omo-rules` | Add `~/.local/bin` to `PATH`, or set `$CODEX_LOCAL_BIN_DIR` to a directory already on `PATH` |
| `npm install` fails mid-install | `rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins` and retry |
| `npm install` fails mid-install | `rm -rf ~/.codex/plugins/cache/sisyphuslabs` and retry |
| Plugin block is present but hooks do not fire | Verify `~/.codex/config.toml` contains `[features]\nplugins = true\nplugin_hooks = true` |
| Hook trust hash mismatch warnings | Re-run the installer; hashes are regenerated each install |
@@ -746,11 +756,11 @@ opencode --version
```bash
# 1. Remove the plugin cache
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
rm -rf ~/.codex/plugins/cache/sisyphuslabs
# 2. Edit ~/.codex/config.toml and remove these blocks:
# [plugins."omo@code-yeongyu-codex-plugins"]
# [hooks.state."omo@code-yeongyu-codex-plugins"]
# [plugins."omo@sisyphuslabs"]
# [hooks.state."omo@sisyphuslabs"]
# 3. Optional: remove the component symlinks
rm -f ~/.local/bin/omo-rules ~/.local/bin/omo-comment-checker \
+14 -14
View File
@@ -13,8 +13,8 @@ All published packages expose the same compiled CLI with these bin entries:
- `oh-my-opencode` (legacy name, still primary)
- `oh-my-openagent` (renamed primary)
- `omo` (short alias)
- `lazycodex` (Codex-default alias; `lazycodex install` implies `--codex=yes` unless explicitly overridden)
- `omo` (short alias, recommended in docs and prompts)
- `lazycodex` (Light edition shortcut; `lazycodex install` is equivalent to `omo install --platform=codex` unless `--platform` is explicitly overridden)
## Basic Usage
@@ -56,26 +56,26 @@ bunx oh-my-openagent install
| Option | Description |
| --- | --- |
| `--no-tui` | Run in non-interactive mode (requires all needed options) |
| `--claude <value>` | Claude subscription: `no`, `yes`, `max20` |
| `--openai <value>` | OpenAI/ChatGPT subscription: `no`, `yes` |
| `--gemini <value>` | Gemini integration: `no`, `yes` |
| `--copilot <value>` | GitHub Copilot subscription: `no`, `yes` |
| `--opencode-zen <value>` | OpenCode Zen access: `no`, `yes` |
| `--zai-coding-plan <value>` | Z.ai Coding Plan subscription: `no`, `yes` |
| `--kimi-for-coding <value>` | Kimi For Coding subscription: `no`, `yes` |
| `--opencode-go <value>` | OpenCode Go subscription: `no`, `yes` |
| `--vercel-ai-gateway <value>` | Vercel AI Gateway: `no`, `yes` |
| `--codex <value>` | Install Codex adapter (`omo-codex`): `no`, `yes` |
| `--platform <value>` | Install target edition: `opencode` (Ultimate, default), `codex` (Light), or `both` |
| `--claude <value>` | Claude subscription: `no`, `yes`, `max20` (Ultimate only) |
| `--openai <value>` | OpenAI/ChatGPT subscription: `no`, `yes` (Ultimate only) |
| `--gemini <value>` | Gemini integration: `no`, `yes` (Ultimate only) |
| `--copilot <value>` | GitHub Copilot subscription: `no`, `yes` (Ultimate only) |
| `--opencode-zen <value>` | OpenCode Zen access: `no`, `yes` (Ultimate only) |
| `--zai-coding-plan <value>` | Z.ai Coding Plan subscription: `no`, `yes` (Ultimate only) |
| `--kimi-for-coding <value>` | Kimi For Coding subscription: `no`, `yes` (Ultimate only) |
| `--opencode-go <value>` | OpenCode Go subscription: `no`, `yes` (Ultimate only) |
| `--vercel-ai-gateway <value>` | Vercel AI Gateway: `no`, `yes` (Ultimate only) |
| `--skip-auth` | Skip authentication setup hints |
When using the `lazycodex` bin alias, `install` defaults to `--codex=yes`.
When using the `lazycodex` bin alias, `install` defaults to `--platform=codex`. Subscription flags (`--claude`, `--openai`, etc.) only apply when `--platform` is `opencode` or `both` — they are rejected under `--platform=codex` because the Light edition does not write OpenCode model config.
### Telemetry and opt-out
Anonymous telemetry uses PostHog with a hashed installation identifier. Two streams exist:
- `omo_daily_active`: fired by the main plugin and `oh-my-openagent run`.
- `omo_codex_daily_active`: fired by `omo install --codex=yes` (`reason: "install_completed"`) and by the Codex plugin's `SessionStart` hook on every Codex session (`reason: "session_start"`). Both sources share the same UTC-day deduplication, so daily/weekly/monthly active counts reflect real Codex usage, not just install events.
- `omo_codex_daily_active`: fired by `omo install --platform=codex` or `--platform=both` (`reason: "install_completed"`) and by the Codex plugin's `SessionStart` hook on every Codex session (`reason: "session_start"`). Both sources share the same UTC-day deduplication, so daily/weekly/monthly active counts reflect real Codex usage, not just install events.
Opt-out env vars:
+6 -7
View File
@@ -1,6 +1,6 @@
# codex-plugins
# Sisyphus Labs Codex Marketplace
Local marketplace for the `omo` Codex plugin components ported from `../pi-extensions`.
Native Codex marketplace for the `omo` plugin.
## Plugin
@@ -12,18 +12,17 @@ Local marketplace for the `omo` Codex plugin components ported from `../pi-exten
- `components/ultrawork`: injects the ultrawork orchestration directive when a user prompt contains `ultrawork` or `ulw`.
- `components/ultragoal`: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit (`.omo/ultragoal/`).
## Local Install
## Install
```bash
codex plugin marketplace add /Users/yeongyu/local-workspaces/codex-plugins
node /Users/yeongyu/local-workspaces/codex-plugins/scripts/install-local.mjs /Users/yeongyu/local-workspaces/codex-plugins
bunx lazycodex install
```
The installer builds `omo`, copies a clean versioned cache entry into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo`, installs runtime dependencies in the cache, prunes stale split-plugin cache/config entries, and enables `[plugins."omo@code-yeongyu-codex-plugins"]` in `~/.codex/config.toml`.
The installer builds `omo`, copies a clean versioned cache entry into `~/.codex/plugins/cache/sisyphuslabs/omo`, installs runtime dependencies in the cache, registers the `sisyphuslabs` marketplace from `https://github.com/code-yeongyu/lazycodex.git`, and enables `[plugins."omo@sisyphuslabs"]` in `~/.codex/config.toml`.
It also enables both `plugins = true` and `plugin_hooks = true` under `[features]` so bundled hook files run.
If your local Codex build exposes plugin install commands, you can use those instead. For older local builds, the installer replaces the manual copy fallback:
```text
~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0
~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0
```
+16 -10
View File
@@ -8,7 +8,7 @@ Codex harness adapter for **oh-my-openagent**. Brings the OMO experience (rules
|------|---------|
| `plugin/` | Vendored Codex plugin namespace `omo` with 5 components. Shipped to the user via `~/.codex/plugins/cache/`. |
| `marketplace.json` | Codex marketplace manifest. Identifies `omo` as the single installable plugin. |
| `scripts/` | Node ESM build scripts (port of the original `codex-plugins/scripts/install-local.mjs`). |
| `scripts/` | Node ESM build scripts for Codex cache installation and marketplace config updates. |
| `src/` | TypeScript runtime: installer + telemetry consumed by the omodex CLI. |
| `MARKETPLACE.md` | Vendored upstream marketplace README. |
@@ -22,16 +22,22 @@ Codex harness adapter for **oh-my-openagent**. Brings the OMO experience (rules
## Install
End users invoke through the omodex CLI:
End users invoke through the omodex CLI. This package is the **Light edition** of omo — install it directly with:
```bash
bunx omo install --codex=yes
# or, equivalently:
bunx oh-my-opencode install --codex=yes
bunx oh-my-openagent install --codex=yes
bunx omo install --platform=codex
# or via the shortcut alias (same compiled CLI, defaults --platform=codex):
bunx lazycodex install
# or the longer package names:
bunx oh-my-opencode install --platform=codex
bunx oh-my-openagent install --platform=codex
```
The installer copies the built plugin into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`, enables it in `~/.codex/config.toml`, and links the per-component binaries into `~/.local/bin/` (or `CODEX_LOCAL_BIN_DIR`).
To install **both** the Ultimate edition (OpenCode plugin) and the Light edition (this package) at once, use `--platform=both`.
The installer copies the built plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`, enables `omo@sisyphuslabs` in `~/.codex/config.toml`, and registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository for native Codex marketplace upgrades.
To install both editions in one command, use `--platform=both`.
## Telemetry
@@ -39,7 +45,7 @@ Anonymous telemetry uses the same PostHog project as oh-my-openagent but emits t
| Source | Reason | Trigger |
|--------|--------|---------|
| `install` | `install_completed` | `bunx omo install --codex=yes` finishes (handled by `src/cli/install-codex/install-codex.ts`) |
| `install` | `install_completed` | `bunx omo install --platform=codex` or `--platform=both` finishes (handled by `src/cli/install-codex/install-codex.ts`) |
| `plugin` | `session_start` | Codex plugin `SessionStart` hook fires (handled by `plugin/components/telemetry/`) |
Both sources share the same SHA256-hashed installation identifier (`sha256("omo-codex:" + hostname)`), suppress PostHog person profiles, and write the daily dedup state to `~/.local/share/omo-codex/posthog-activity.json`.
@@ -60,9 +66,9 @@ The identity constants and opt-out behavior are pinned across both sources by `s
See `/Users/yeongyu/local-workspaces/omodex/docs/legal/privacy-policy.md` for the full disclosure.
## Provenance
## Component Sources
Vendored from [`code-yeongyu/codex-plugins`](https://github.com/code-yeongyu/codex-plugins) at the snapshot present in `/Users/yeongyu/local-workspaces/codex-plugins/` on 2026-05-25. Per-component upstream:
The bundled component implementations come from the Sisyphus Labs Codex plugin family:
- [code-yeongyu/codex-rules](https://github.com/code-yeongyu/codex-rules)
- [code-yeongyu/codex-comment-checker](https://github.com/code-yeongyu/codex-comment-checker)
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "code-yeongyu-codex-plugins",
"name": "sisyphuslabs",
"interface": {
"displayName": "Yeongyu Codex Plugins"
"displayName": "Sisyphus Labs"
},
"plugins": [
{
@@ -51,21 +51,18 @@ node dist/cli.js hook post-tool-use < test/fixtures/post-tool-use.json
## Local Codex Installation
From the marketplace root containing this plugin:
```bash
codex plugin marketplace add /path/to/codex-plugins
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
bunx lazycodex install
```
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0`, installs runtime dependencies there, and enables:
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, installs runtime dependencies there, and enables:
```toml
[features]
plugins = true
plugin_hooks = true
[plugins."omo@code-yeongyu-codex-plugins"]
[plugins."omo@sisyphuslabs"]
enabled = true
```
@@ -118,17 +118,14 @@ printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/cli.j
## Local Codex Installation
From the marketplace root containing this plugin:
```bash
codex plugin marketplace add /path/to/codex-plugins
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
bunx lazycodex install
```
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0` and enables:
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, and enables:
```toml
[plugins."omo@code-yeongyu-codex-plugins"]
[plugins."omo@sisyphuslabs"]
enabled = true
```
@@ -42,17 +42,14 @@ Prefer strict TypeScript and keep runtime imports ESM-compatible.
## Install Locally
From the marketplace workspace:
```bash
codex plugin marketplace add /Users/yeongyu/local-workspaces/codex-plugins
node /Users/yeongyu/local-workspaces/codex-plugins/scripts/install-local.mjs /Users/yeongyu/local-workspaces/codex-plugins
bunx lazycodex install
```
The local installer builds the plugin and copies a clean cache entry to:
```text
~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0
~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0
```
It also enables:
@@ -62,7 +59,7 @@ It also enables:
plugins = true
plugin_hooks = true
[plugins."omo@code-yeongyu-codex-plugins"]
[plugins."omo@sisyphuslabs"]
enabled = true
```
@@ -46,21 +46,18 @@ npm pack --dry-run
## Local Codex Installation
From the marketplace root containing this plugin:
```bash
codex plugin marketplace add /path/to/codex-plugins
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
bunx lazycodex install
```
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0`, installs runtime dependencies there, and enables:
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, installs runtime dependencies there, and enables:
```toml
[features]
plugins = true
plugin_hooks = true
[plugins."omo@code-yeongyu-codex-plugins"]
[plugins."omo@sisyphuslabs"]
enabled = true
```
@@ -75,4 +72,4 @@ This plugin runs locally. The scaffold does not call a network service by itself
## Related
- [oh-my-codex](https://github.com/code-yeongyu/oh-my-codex) - source project for the ultragoal port.
- [codex-plugins](https://github.com/code-yeongyu/codex-plugins) - local Codex plugin marketplace.
- [lazycodex](https://github.com/code-yeongyu/lazycodex) - Sisyphus Labs Codex marketplace repository.
@@ -20,11 +20,10 @@ The directive is currently 11,005 chars / 232 lines and follows the GPT-5.5 prom
## Install (via this marketplace)
```bash
codex plugin marketplace add /path/to/codex-plugins
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
bunx lazycodex install
```
The installer copies the plugin into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0`, enables it in `~/.codex/config.toml`, registers the `UserPromptSubmit` hook, and installs the bundled agent TOMLs into `~/.codex/agents/` (symlinks on Unix, copies on Windows). A manifest at `<plugin-cache>/.installed-agents.json` records the installed paths for clean uninstall.
The installer copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, enables `omo@sisyphuslabs` in `~/.codex/config.toml`, registers the `UserPromptSubmit` hook, and installs the bundled agent TOMLs into `~/.codex/agents/` (symlinks on Unix, copies on Windows). A manifest at `<plugin-cache>/.installed-agents.json` records the installed paths for clean uninstall.
## How it works
@@ -1,60 +1,11 @@
import assert from "node:assert/strict";
import { mkdir, readFile, readlink, stat, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { join } from "node:path";
import test from "node:test";
import { tmpdir } from "node:os";
import { mkdtemp } from "node:fs/promises";
import { installMarketplaceLocally } from "./install-local.mjs";
import { linkCachedPluginBins } from "./install/cache.mjs";
async function makeTempDir() {
return mkdtemp(join(tmpdir(), "codex-plugins-install-"));
}
async function writeJson(path, value) {
await mkdir(dirname(path), { recursive: true });
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
async function writePlugin(root, name, version) {
const pluginRoot = join(root, "plugins", name);
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
await mkdir(join(pluginRoot, "dist"), { recursive: true });
await mkdir(join(pluginRoot, "hooks"), { recursive: true });
await mkdir(join(pluginRoot, "skills", name), { recursive: true });
await writeJson(join(pluginRoot, ".codex-plugin", "plugin.json"), {
name,
version,
description: `${name} test plugin`,
mcpServers: "./.mcp.json",
hooks: "./hooks/hooks.json",
skills: "./skills/",
});
await writeJson(join(pluginRoot, ".mcp.json"), {
mcpServers: {
[name]: {
command: "node",
args: ["./dist/cli.js", "mcp"],
cwd: ".",
},
},
});
await writeJson(join(pluginRoot, "hooks", "hooks.json"), { hooks: {} });
await writeFile(join(pluginRoot, "skills", name, "SKILL.md"), "---\nname: test\n---\n");
await writeJson(join(pluginRoot, "package.json"), {
name: `@example/${name}`,
version,
bin: {
[name]: "./dist/cli.js",
},
scripts: {
build: "node -e \"require('fs').writeFileSync('dist/cli.js', 'console.log(1)')\"",
},
dependencies: {},
});
}
import { makeTempDir, writeJson, writePlugin } from "./install-test-fixtures.mjs";
test("#given local marketplace #when installing #then copies versioned plugins and enables config", async () => {
const repoRoot = await makeTempDir();
@@ -151,6 +102,35 @@ test("#given local marketplace #when installing #then copies versioned plugins a
assert.doesNotMatch(config, /stale@debug-marketplace/);
});
test("#given sisyphuslabs marketplace #when installing #then registers lazycodex git source", async () => {
const repoRoot = await makeTempDir();
const codexHome = await makeTempDir();
await mkdir(join(repoRoot, ".agents", "plugins"), { recursive: true });
await writeJson(join(repoRoot, ".agents", "plugins", "marketplace.json"), {
name: "sisyphuslabs",
plugins: [{ name: "omo", source: "./plugins/omo" }],
});
await writePlugin(repoRoot, "omo", "0.1.0");
await installMarketplaceLocally({
repoRoot,
codexHome,
runCommand: async () => {},
log: () => {},
});
const config = await readFile(join(codexHome, "config.toml"), "utf8");
assert.match(config, /\[marketplaces\.sisyphuslabs\]/);
assert.match(config, /source_type = "git"/);
assert.match(config, /source = "https:\/\/github\.com\/code-yeongyu\/lazycodex\.git"/);
assert.match(config, /ref = "main"/);
assert.match(config, /\[plugins\."omo@sisyphuslabs"\]\nenabled = true/);
assert.doesNotMatch(config, /\[marketplaces\.lazycodex\]/);
assert.doesNotMatch(config, /code-yeongyu-codex-plugins/);
assert.doesNotMatch(config, /source_type = "local"/);
});
test("#given plugin hooks #when installing #then records trusted hook hashes", async () => {
const repoRoot = await makeTempDir();
const codexHome = await makeTempDir();
@@ -0,0 +1,50 @@
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
export async function makeTempDir() {
return mkdtemp(join(tmpdir(), "omo-codex-install-"));
}
export async function writeJson(path, value) {
await mkdir(dirname(path), { recursive: true });
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
export async function writePlugin(root, name, version) {
const pluginRoot = join(root, "plugins", name);
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
await mkdir(join(pluginRoot, "dist"), { recursive: true });
await mkdir(join(pluginRoot, "hooks"), { recursive: true });
await mkdir(join(pluginRoot, "skills", name), { recursive: true });
await writeJson(join(pluginRoot, ".codex-plugin", "plugin.json"), {
name,
version,
description: `${name} test plugin`,
mcpServers: "./.mcp.json",
hooks: "./hooks/hooks.json",
skills: "./skills/",
});
await writeJson(join(pluginRoot, ".mcp.json"), {
mcpServers: {
[name]: {
command: "node",
args: ["./dist/cli.js", "mcp"],
cwd: ".",
},
},
});
await writeJson(join(pluginRoot, "hooks", "hooks.json"), { hooks: {} });
await writeFile(join(pluginRoot, "skills", name, "SKILL.md"), "---\nname: test\n---\n");
await writeJson(join(pluginRoot, "package.json"), {
name: `@example/${name}`,
version,
bin: {
[name]: "./dist/cli.js",
},
scripts: {
build: "node -e \"require('fs').writeFileSync('dist/cli.js', 'console.log(1)')\"",
},
dependencies: {},
});
}
+35 -14
View File
@@ -3,7 +3,20 @@ import { dirname } from "node:path";
import { exists } from "./utils.mjs";
export async function updateCodexConfig({ configPath, repoRoot, marketplaceName, pluginNames, trustedHookStates = [] }) {
const LAZYCODEX_MARKETPLACE_SOURCE = {
sourceType: "git",
source: "https://github.com/code-yeongyu/lazycodex.git",
ref: "main",
};
export async function updateCodexConfig({
configPath,
repoRoot,
marketplaceName,
marketplaceSource = defaultMarketplaceSource(marketplaceName, repoRoot),
pluginNames,
trustedHookStates = [],
}) {
await mkdir(dirname(configPath), { recursive: true });
let config = "";
if (await exists(configPath)) config = await readFile(configPath, "utf8");
@@ -12,7 +25,7 @@ export async function updateCodexConfig({ configPath, repoRoot, marketplaceName,
config = removeStaleMarketplaceHookStateBlocks(config, marketplaceName, new Set(pluginNames));
config = ensureFeatureEnabled(config, "plugins");
config = ensureFeatureEnabled(config, "plugin_hooks");
config = ensureMarketplaceBlock(config, marketplaceName, repoRoot);
config = ensureMarketplaceBlock(config, marketplaceName, marketplaceSource);
for (const pluginName of pluginNames) {
config = ensurePluginEnabled(config, `${pluginName}@${marketplaceName}`);
}
@@ -23,6 +36,14 @@ export async function updateCodexConfig({ configPath, repoRoot, marketplaceName,
await writeFile(configPath, config.trimEnd() + "\n");
}
function defaultMarketplaceSource(marketplaceName, repoRoot) {
if (marketplaceName === "sisyphuslabs") return LAZYCODEX_MARKETPLACE_SOURCE;
return {
sourceType: "local",
source: repoRoot,
};
}
function removeStaleMarketplacePluginBlocks(config, marketplaceName, keepPluginNames) {
return removeTomlSections(config, (header) => {
const pluginKey = parseQuotedPluginHeader(header);
@@ -54,19 +75,19 @@ function ensureFeatureEnabled(config, featureName) {
return replaceOrInsertSetting(config, section, featureName, "true");
}
function ensureMarketplaceBlock(config, marketplaceName, repoRoot) {
function ensureMarketplaceBlock(config, marketplaceName, source) {
const header = `marketplaces.${marketplaceName}`;
if (findTomlSection(config, header)) return config;
return appendBlock(
config,
[
`[${header}]`,
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
"source_type = \"local\"",
`source = ${JSON.stringify(repoRoot)}`,
"",
].join("\n"),
);
const block = [
`[${header}]`,
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
`source_type = ${JSON.stringify(source.sourceType)}`,
`source = ${JSON.stringify(source.source)}`,
source.ref === undefined ? null : `ref = ${JSON.stringify(source.ref)}`,
"",
].filter((line) => line !== null).join("\n");
const section = findTomlSection(config, header);
if (section) return config.slice(0, section.start) + block + config.slice(section.end);
return appendBlock(config, block);
}
function ensurePluginEnabled(config, pluginKey) {
+1 -1
View File
@@ -1,4 +1,4 @@
// Codex install module entry point. Implemented in subsequent commits.
// Hosts the TypeScript port of scripts/install-local.mjs invoked from the
// omodex CLI when the user passes `--codex=yes` to `bunx omo install`.
// omodex CLI when the user passes `--platform=codex` to `bunx omo install`.
export {}
+18
View File
@@ -58,4 +58,22 @@ describe("test workflows", () => {
expect(hasCodexCommand, "Codex compatibility job must run the shared Codex test script").toBe(true)
expect(buildNeedsCodexMatrix, "Build must wait for Codex compatibility checks").toBe(true)
})
test("syncs the LazyCodex Codex marketplace bundle during release", () => {
// #given
const workflow = readFileSync(new URL("../.github/workflows/publish.yml", import.meta.url), "utf8")
// #when
const appliesCodexPluginVersion = workflow.includes("packages/omo-codex/plugin/.codex-plugin/plugin.json")
const syncsLazycodexMarketplace = workflow.includes("bun run script/sync-lazycodex-marketplace.ts")
const pushesLazycodexMarketplace = workflow.includes("code-yeongyu/lazycodex")
const requiresLazycodexSyncToken = workflow.includes("secrets.LAZYCODEX_SYNC_TOKEN == ''") &&
workflow.includes("token: ${{ secrets.LAZYCODEX_SYNC_TOKEN }}")
// #then
expect(appliesCodexPluginVersion, "release must version the Codex plugin manifest before marketplace sync").toBe(true)
expect(syncsLazycodexMarketplace, "release must sync the LazyCodex marketplace bundle").toBe(true)
expect(pushesLazycodexMarketplace, "release must target the LazyCodex repository").toBe(true)
expect(requiresLazycodexSyncToken, "release must require a cross-repo token for LazyCodex push").toBe(true)
})
})
+59
View File
@@ -0,0 +1,59 @@
/// <reference types="bun-types" />
import { describe, expect, test } from "bun:test"
import { mkdir, mkdtemp, readFile, stat, writeFile } from "node:fs/promises"
import { tmpdir } from "node:os"
import { dirname, join } from "node:path"
import { syncLazycodexMarketplace } from "./sync-lazycodex-marketplace"
async function writeJson(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true })
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`)
}
async function writePluginFixture(sourceRoot: string): Promise<void> {
await writeJson(join(sourceRoot, "packages", "omo-codex", "marketplace.json"), {
name: "sisyphuslabs",
plugins: [{ name: "omo", source: "./plugins/omo" }],
})
await writeJson(join(sourceRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"), {
name: "omo",
version: "1.2.3",
})
await writeFile(join(sourceRoot, "packages", "omo-codex", "plugin", "README.md"), "omo\n")
await mkdir(join(sourceRoot, "packages", "omo-codex", "plugin", "node_modules", "ignored"), { recursive: true })
await writeFile(join(sourceRoot, "packages", "omo-codex", "plugin", "node_modules", "ignored", "file.txt"), "ignored\n")
}
describe("sync-lazycodex-marketplace", () => {
test("copies the Codex marketplace manifest and clean plugin bundle", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-sync-source-"))
const lazycodexRoot = await mkdtemp(join(tmpdir(), "omo-sync-lazycodex-"))
await writePluginFixture(sourceRoot)
// when
await syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })
// then
const marketplace = JSON.parse(await readFile(join(lazycodexRoot, ".agents", "plugins", "marketplace.json"), "utf8"))
expect(marketplace.name).toBe("sisyphuslabs")
expect(marketplace.plugins[0].source).toBe("./plugins/omo")
const manifest = JSON.parse(await readFile(join(lazycodexRoot, "plugins", "omo", ".codex-plugin", "plugin.json"), "utf8"))
expect(manifest).toMatchObject({ name: "omo", version: "1.2.3" })
await expect(stat(join(lazycodexRoot, "plugins", "omo", "node_modules"))).rejects.toThrow()
})
test("rejects a source tree without a Codex plugin manifest", async () => {
// given
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-sync-bad-source-"))
const lazycodexRoot = await mkdtemp(join(tmpdir(), "omo-sync-bad-lazycodex-"))
await writeJson(join(sourceRoot, "packages", "omo-codex", "marketplace.json"), {
name: "sisyphuslabs",
plugins: [{ name: "omo", source: "./plugins/omo" }],
})
// when / then
await expect(syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })).rejects.toThrow("missing Codex plugin manifest")
})
})
+101
View File
@@ -0,0 +1,101 @@
import { cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises"
import { dirname, join, resolve, sep } from "node:path"
const MARKETPLACE_SOURCE_PATH = join("packages", "omo-codex", "marketplace.json")
const PLUGIN_SOURCE_PATH = join("packages", "omo-codex", "plugin")
const MARKETPLACE_DESTINATION_PATH = join(".agents", "plugins", "marketplace.json")
const PLUGIN_DESTINATION_PATH = join("plugins", "omo")
export interface SyncLazycodexMarketplaceInput {
readonly sourceRoot: string
readonly lazycodexRoot: string
}
interface MarketplaceManifest {
readonly name: string
}
interface PluginManifest {
readonly name: string
readonly version?: string
}
export async function syncLazycodexMarketplace(input: SyncLazycodexMarketplaceInput): Promise<void> {
const sourceRoot = resolve(input.sourceRoot)
const lazycodexRoot = resolve(input.lazycodexRoot)
const marketplacePath = join(sourceRoot, MARKETPLACE_SOURCE_PATH)
const pluginRoot = join(sourceRoot, PLUGIN_SOURCE_PATH)
const pluginManifestPath = join(pluginRoot, ".codex-plugin", "plugin.json")
const marketplace = await readMarketplaceManifest(marketplacePath)
if (marketplace.name !== "sisyphuslabs") {
throw new Error(`Sisyphus Labs marketplace manifest must be named sisyphuslabs, got ${marketplace.name}`)
}
const pluginManifest = await readPluginManifest(pluginManifestPath)
if (pluginManifest.name !== "omo") {
throw new Error(`Sisyphus Labs plugin manifest must be named omo, got ${pluginManifest.name}`)
}
const destinationMarketplacePath = join(lazycodexRoot, MARKETPLACE_DESTINATION_PATH)
await mkdir(dirname(destinationMarketplacePath), { recursive: true })
await writeFile(destinationMarketplacePath, await readFile(marketplacePath, "utf8"))
const destinationPluginRoot = join(lazycodexRoot, PLUGIN_DESTINATION_PATH)
await rm(destinationPluginRoot, { recursive: true, force: true })
await mkdir(dirname(destinationPluginRoot), { recursive: true })
await cp(pluginRoot, destinationPluginRoot, {
recursive: true,
filter: (path) => shouldCopyPluginPath(path, pluginRoot),
})
}
async function readMarketplaceManifest(path: string): Promise<MarketplaceManifest> {
const parsed = JSON.parse(await readFile(path, "utf8"))
if (isRecord(parsed) && typeof parsed.name === "string") {
return { name: parsed.name }
}
throw new Error("invalid Sisyphus Labs marketplace manifest")
}
async function readPluginManifest(path: string): Promise<PluginManifest> {
if (!(await isFile(path))) {
throw new Error(`missing Codex plugin manifest at ${path}`)
}
const parsed = JSON.parse(await readFile(path, "utf8"))
if (isRecord(parsed) && typeof parsed.name === "string") {
return {
name: parsed.name,
version: typeof parsed.version === "string" ? parsed.version : undefined,
}
}
throw new Error("invalid Codex plugin manifest")
}
async function isFile(path: string): Promise<boolean> {
try {
return (await stat(path)).isFile()
} catch (error) {
if (error instanceof Error) return false
return false
}
}
function shouldCopyPluginPath(path: string, root: string): boolean {
const relative = path === root ? "" : path.slice(root.length + sep.length)
if (relative.length === 0) return true
return !relative.split(sep).some((part) => part === ".git" || part === "node_modules")
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
if (import.meta.main) {
const sourceRoot = process.argv[2] ?? process.cwd()
const lazycodexRoot = process.argv[3]
if (lazycodexRoot === undefined) {
throw new Error("Usage: bun run script/sync-lazycodex-marketplace.ts <source-root> <lazycodex-root>")
}
await syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })
}
+1 -1
View File
@@ -11,5 +11,5 @@
"allowImportingTsExtensions": true,
"noEmit": true
},
"include": ["./publish-workflow.test.ts", "./package-layout.test.ts"]
"include": ["./publish-workflow.test.ts", "./package-layout.test.ts", "./sync-lazycodex-marketplace.ts", "./sync-lazycodex-marketplace.test.ts"]
}
+1 -1
View File
@@ -8,7 +8,7 @@ import type { CodexInstallResult } from "./install-codex"
import type { InstallArgs } from "./types"
const codexResult: CodexInstallResult = {
marketplaceName: "code-yeongyu-codex-plugins",
marketplaceName: "sisyphuslabs",
installed: [],
configPath: "/tmp/codex-config.toml",
codexHome: "/tmp/codex-home",
+1 -1
View File
@@ -138,7 +138,7 @@ describe("runCliInstaller", () => {
installed: [],
configPath: "/tmp/codex-config.toml",
codexHome: "/tmp/codex-home",
marketplaceName: "code-yeongyu-codex-plugins",
marketplaceName: "sisyphuslabs",
})
const args: InstallArgs = {
@@ -14,16 +14,26 @@ describe("codex-config-toml", () => {
await updateCodexConfig({
configPath,
repoRoot: "/repo/packages/omo-codex",
marketplaceName: "code-yeongyu-codex-plugins",
marketplaceName: "sisyphuslabs",
marketplaceSource: {
sourceType: "git",
source: "https://github.com/code-yeongyu/lazycodex.git",
ref: "main",
},
pluginNames: ["omo"],
trustedHookStates: [{ key: "omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
})
await updateCodexConfig({
configPath,
repoRoot: "/repo/packages/omo-codex",
marketplaceName: "code-yeongyu-codex-plugins",
marketplaceName: "sisyphuslabs",
marketplaceSource: {
sourceType: "git",
source: "https://github.com/code-yeongyu/lazycodex.git",
ref: "main",
},
pluginNames: ["omo"],
trustedHookStates: [{ key: "omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
})
// then
@@ -31,8 +41,14 @@ describe("codex-config-toml", () => {
expect(content).toContain("[features]")
expect(content).toContain("plugins = true")
expect(content).toContain("plugin_hooks = true")
expect(content).toContain("[marketplaces.code-yeongyu-codex-plugins]")
expect(content).toContain("[plugins.\"omo@code-yeongyu-codex-plugins\"]")
expect(content).toContain("[hooks.state.\"omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0\"]")
expect(content).toContain("[marketplaces.sisyphuslabs]")
expect(content).toContain('source_type = "git"')
expect(content).toContain('source = "https://github.com/code-yeongyu/lazycodex.git"')
expect(content).toContain('ref = "main"')
expect(content).toContain("[plugins.\"omo@sisyphuslabs\"]")
expect(content).toContain("[hooks.state.\"omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0\"]")
expect(content).not.toContain("[marketplaces.lazycodex]")
expect(content).not.toContain("code-yeongyu-codex-plugins")
expect(content).not.toContain('source_type = "local"')
})
})
+15 -11
View File
@@ -1,11 +1,12 @@
import { mkdir, readFile, writeFile } from "node:fs/promises"
import { dirname } from "node:path"
import type { TrustedHookState } from "./types"
import type { CodexMarketplaceSource, TrustedHookState } from "./types"
export async function updateCodexConfig(input: {
readonly configPath: string
readonly repoRoot: string
readonly marketplaceName: string
readonly marketplaceSource: CodexMarketplaceSource
readonly pluginNames: readonly string[]
readonly trustedHookStates?: readonly TrustedHookState[]
}): Promise<void> {
@@ -18,7 +19,7 @@ export async function updateCodexConfig(input: {
config = removeStaleMarketplaceHookStateBlocks(config, input.marketplaceName, pluginSet)
config = ensureFeatureEnabled(config, "plugins")
config = ensureFeatureEnabled(config, "plugin_hooks")
config = ensureMarketplaceBlock(config, input.marketplaceName, input.repoRoot)
config = ensureMarketplaceBlock(config, input.marketplaceName, input.marketplaceSource)
for (const pluginName of input.pluginNames) {
config = ensurePluginEnabled(config, `${pluginName}@${input.marketplaceName}`)
}
@@ -60,18 +61,21 @@ function ensureFeatureEnabled(config: string, featureName: string): string {
return replaceOrInsertSetting(config, section, featureName, "true")
}
function ensureMarketplaceBlock(config: string, marketplaceName: string, repoRoot: string): string {
function ensureMarketplaceBlock(config: string, marketplaceName: string, source: CodexMarketplaceSource): string {
const header = `marketplaces.${marketplaceName}`
if (findTomlSection(config, header)) return config
const block = [
`[${header}]`,
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
`source_type = ${JSON.stringify(source.sourceType)}`,
`source = ${JSON.stringify(source.source)}`,
`ref = ${JSON.stringify(source.ref)}`,
"",
].join("\n")
const section = findTomlSection(config, header)
if (section) return config.slice(0, section.start) + block + config.slice(section.end)
return appendBlock(
config,
[
`[${header}]`,
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
'source_type = "local"',
`source = ${JSON.stringify(repoRoot)}`,
"",
].join("\n"),
block,
)
}
@@ -14,7 +14,7 @@ describe("codex-hook-trust", () => {
// when
const states = await trustedHookStatesForPlugin({
marketplaceName: "code-yeongyu-codex-plugins",
marketplaceName: "sisyphuslabs",
pluginName: "omo",
pluginRoot,
})
@@ -11,7 +11,7 @@ describe("codex-marketplace", () => {
const pkgRoot = join(root, "packages", "omo-codex")
const pluginRoot = join(pkgRoot, "plugin", ".codex-plugin")
await mkdir(pluginRoot, { recursive: true })
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "code-yeongyu-codex-plugins", plugins: [{ name: "omo", source: "./plugins/omo" }] }))
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "sisyphuslabs", plugins: [{ name: "omo", source: "./plugins/omo" }] }))
await writeFile(join(pluginRoot, "plugin.json"), JSON.stringify({ name: "omo", version: "0.1.0" }))
// when
@@ -20,7 +20,7 @@ describe("codex-marketplace", () => {
const manifest = await readPluginManifest(sourcePath)
// then
expect(marketplace.name).toBe("code-yeongyu-codex-plugins")
expect(marketplace.name).toBe("sisyphuslabs")
expect(sourcePath).toBe(join(pkgRoot, "plugin"))
expect(manifest.name).toBe("omo")
})
@@ -30,7 +30,7 @@ describe("codex-marketplace", () => {
const root = await mkdtemp(join(tmpdir(), "omo-codex-marketplace-"))
const pkgRoot = join(root, "packages", "omo-codex")
await mkdir(pkgRoot, { recursive: true })
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "code-yeongyu-codex-plugins", plugins: [{ name: "omo", source: "./../escape" }] }))
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "sisyphuslabs", plugins: [{ name: "omo", source: "./../escape" }] }))
// when
const action = readMarketplace(root)
+9 -3
View File
@@ -19,16 +19,22 @@ describe("install-codex", () => {
const second = await runCodexInstaller({ codexHome, binDir, repoRoot, runCommand: async () => undefined })
// then
expect(first.marketplaceName).toBe("code-yeongyu-codex-plugins")
expect(first.marketplaceName).toBe("sisyphuslabs")
expect(second.installed.length).toBe(1)
const configContent = await readFile(join(codexHome, "config.toml"), "utf8")
expect(configContent).toContain("[features]")
expect(configContent).toContain("[marketplaces.code-yeongyu-codex-plugins]")
expect(configContent).toContain("[plugins.\"omo@code-yeongyu-codex-plugins\"]")
expect(configContent).toContain("[marketplaces.sisyphuslabs]")
expect(configContent).toContain('source_type = "git"')
expect(configContent).toContain('source = "https://github.com/code-yeongyu/lazycodex.git"')
expect(configContent).toContain('ref = "main"')
expect(configContent).toContain("[plugins.\"omo@sisyphuslabs\"]")
expect(configContent).toContain("[hooks.state.")
expect(configContent).not.toContain("code-yeongyu-codex-plugins")
expect(configContent).not.toContain("[marketplaces.lazycodex]")
const pluginPath = first.installed[0]?.path
expect(pluginPath).toBeDefined()
expect(pluginPath).toContain(join("plugins", "cache", "sisyphuslabs", "omo"))
const stats = await stat(pluginPath ?? "")
expect(stats.isDirectory()).toBe(true)
})
+7
View File
@@ -9,6 +9,12 @@ import { readMarketplace, readPluginManifest, resolvePluginSource, validatePathS
import { defaultRunCommand } from "./codex-process"
import type { CodexInstallOptions, CodexInstallResult, InstalledPlugin } from "./types"
const LAZYCODEX_MARKETPLACE_SOURCE = {
sourceType: "git",
source: "https://github.com/code-yeongyu/lazycodex.git",
ref: "main",
} as const
export async function runCodexInstaller(options: CodexInstallOptions = {}): Promise<CodexInstallResult> {
const repoRoot = resolve(options.repoRoot ?? findRepoRootFromImporter(import.meta.dir))
const codexHome = resolve(options.codexHome ?? process.env.CODEX_HOME ?? join(homedir(), ".codex"))
@@ -78,6 +84,7 @@ export async function runCodexInstaller(options: CodexInstallOptions = {}): Prom
configPath,
repoRoot: codexPackageRoot,
marketplaceName: marketplace.name,
marketplaceSource: LAZYCODEX_MARKETPLACE_SOURCE,
pluginNames: marketplace.plugins.map((plugin) => plugin.name),
trustedHookStates,
})
+6
View File
@@ -30,6 +30,12 @@ export interface TrustedHookState {
readonly trustedHash: string
}
export interface CodexMarketplaceSource {
readonly sourceType: "git"
readonly source: string
readonly ref: string
}
export interface CommandRunOptions {
readonly cwd: string
}
+1 -3
View File
@@ -1,8 +1,6 @@
/// <reference types="bun-types" />
import { describe, expect, test } from "bun:test"
import { readFile } from "node:fs/promises"
import path from "node:path"
import { resolveInstallArgs } from "./cli-program"
describe("install platform resolution", () => {
@@ -85,7 +83,7 @@ describe("install platform resolution", () => {
test("defines Commander choices so invalid --platform values are rejected", async () => {
// given
const cliProgramSource = await readFile(path.resolve(import.meta.dir, "cli-program.ts"), "utf-8")
const cliProgramSource = await Bun.file(new URL("./cli-program.ts", import.meta.url)).text()
// when
const installBlock = cliProgramSource.match(/program\s*\n\s*\.command\("install"\)([\s\S]*?)\.action\(/)