From 56f39d60fc9b9158d3c882abe9580bbaad73b79d Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 27 May 2026 18:48:33 +0900 Subject: [PATCH] feat(omo-codex): install through sisyphuslabs marketplace --- .github/workflows/publish.yml | 32 ++++- CHANGELOG.md | 5 +- README.ja.md | 48 +++---- README.ko.md | 48 +++---- README.md | 6 +- README.ru.md | 48 +++---- README.zh-cn.md | 48 +++---- docs/guide/installation.md | 118 ++++++++++-------- docs/reference/cli.md | 28 ++--- packages/omo-codex/MARKETPLACE.md | 13 +- packages/omo-codex/README.md | 26 ++-- packages/omo-codex/marketplace.json | 4 +- .../components/comment-checker/README.md | 9 +- .../omo-codex/plugin/components/lsp/README.md | 9 +- .../plugin/components/rules/README.md | 9 +- .../plugin/components/ultragoal/README.md | 11 +- .../plugin/components/ultrawork/README.md | 5 +- .../omo-codex/scripts/install-local.test.mjs | 80 +++++------- .../scripts/install-test-fixtures.mjs | 50 ++++++++ packages/omo-codex/scripts/install/config.mjs | 49 +++++--- packages/omo-codex/src/install/index.ts | 2 +- script/publish-workflow.test.ts | 18 +++ script/sync-lazycodex-marketplace.test.ts | 59 +++++++++ script/sync-lazycodex-marketplace.ts | 101 +++++++++++++++ script/tsconfig.json | 2 +- src/cli/cli-installer.platform.test.ts | 2 +- src/cli/cli-installer.test.ts | 2 +- .../install-codex/codex-config-toml.test.ts | 30 +++-- src/cli/install-codex/codex-config-toml.ts | 26 ++-- .../install-codex/codex-hook-trust.test.ts | 2 +- .../install-codex/codex-marketplace.test.ts | 6 +- src/cli/install-codex/install-codex.test.ts | 12 +- src/cli/install-codex/install-codex.ts | 7 ++ src/cli/install-codex/types.ts | 6 + src/cli/install-platform-resolution.test.ts | 4 +- 35 files changed, 620 insertions(+), 305 deletions(-) create mode 100644 packages/omo-codex/scripts/install-test-fixtures.mjs create mode 100644 script/sync-lazycodex-marketplace.test.ts create mode 100644 script/sync-lazycodex-marketplace.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 56559a8f7..e7ec8aec2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -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 }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e9aa2410..a32e22430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 ` 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. diff --git a/README.ja.md b/README.ja.md index 9e31cafa6..754aea88e 100644 --- a/README.ja.md +++ b/README.ja.md @@ -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 diff --git a/README.ko.md b/README.ko.md index e07502672..0d15677f0 100644 --- a/README.ko.md +++ b/README.ko.md @@ -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 diff --git a/README.md b/README.md index ebf67e35d..19e38c306 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/README.ru.md b/README.ru.md index 66ad8bb69..2073efad9 100644 --- a/README.ru.md +++ b/README.ru.md @@ -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@...\"]`. ## Функции diff --git a/README.zh-cn.md b/README.zh-cn.md index 11ab2727a..f451260c8 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -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 中为 hook,Light 中为 `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-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 diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 910255853..d2b087893 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -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//`. 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//`. 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//SKILL.md` (project scope) or `~/.config/opencode/skills//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//` +- **Plugin cache:** `~/.codex/plugins/cache/sisyphuslabs/omo//` - **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 \ diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 50493d541..36eddd6f6 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -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 ` | Claude subscription: `no`, `yes`, `max20` | -| `--openai ` | OpenAI/ChatGPT subscription: `no`, `yes` | -| `--gemini ` | Gemini integration: `no`, `yes` | -| `--copilot ` | GitHub Copilot subscription: `no`, `yes` | -| `--opencode-zen ` | OpenCode Zen access: `no`, `yes` | -| `--zai-coding-plan ` | Z.ai Coding Plan subscription: `no`, `yes` | -| `--kimi-for-coding ` | Kimi For Coding subscription: `no`, `yes` | -| `--opencode-go ` | OpenCode Go subscription: `no`, `yes` | -| `--vercel-ai-gateway ` | Vercel AI Gateway: `no`, `yes` | -| `--codex ` | Install Codex adapter (`omo-codex`): `no`, `yes` | +| `--platform ` | Install target edition: `opencode` (Ultimate, default), `codex` (Light), or `both` | +| `--claude ` | Claude subscription: `no`, `yes`, `max20` (Ultimate only) | +| `--openai ` | OpenAI/ChatGPT subscription: `no`, `yes` (Ultimate only) | +| `--gemini ` | Gemini integration: `no`, `yes` (Ultimate only) | +| `--copilot ` | GitHub Copilot subscription: `no`, `yes` (Ultimate only) | +| `--opencode-zen ` | OpenCode Zen access: `no`, `yes` (Ultimate only) | +| `--zai-coding-plan ` | Z.ai Coding Plan subscription: `no`, `yes` (Ultimate only) | +| `--kimi-for-coding ` | Kimi For Coding subscription: `no`, `yes` (Ultimate only) | +| `--opencode-go ` | OpenCode Go subscription: `no`, `yes` (Ultimate only) | +| `--vercel-ai-gateway ` | 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: diff --git a/packages/omo-codex/MARKETPLACE.md b/packages/omo-codex/MARKETPLACE.md index 813929fa6..126fae6c1 100644 --- a/packages/omo-codex/MARKETPLACE.md +++ b/packages/omo-codex/MARKETPLACE.md @@ -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 ``` diff --git a/packages/omo-codex/README.md b/packages/omo-codex/README.md index 6a2fddd7f..8be0bc4e0 100644 --- a/packages/omo-codex/README.md +++ b/packages/omo-codex/README.md @@ -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//`, 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//`, 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) diff --git a/packages/omo-codex/marketplace.json b/packages/omo-codex/marketplace.json index 4ceaf3d17..f7e029253 100644 --- a/packages/omo-codex/marketplace.json +++ b/packages/omo-codex/marketplace.json @@ -1,7 +1,7 @@ { - "name": "code-yeongyu-codex-plugins", + "name": "sisyphuslabs", "interface": { - "displayName": "Yeongyu Codex Plugins" + "displayName": "Sisyphus Labs" }, "plugins": [ { diff --git a/packages/omo-codex/plugin/components/comment-checker/README.md b/packages/omo-codex/plugin/components/comment-checker/README.md index f75d813a2..d48d38f80 100644 --- a/packages/omo-codex/plugin/components/comment-checker/README.md +++ b/packages/omo-codex/plugin/components/comment-checker/README.md @@ -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//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 ``` diff --git a/packages/omo-codex/plugin/components/lsp/README.md b/packages/omo-codex/plugin/components/lsp/README.md index 661358b33..a449ef993 100644 --- a/packages/omo-codex/plugin/components/lsp/README.md +++ b/packages/omo-codex/plugin/components/lsp/README.md @@ -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//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 ``` diff --git a/packages/omo-codex/plugin/components/rules/README.md b/packages/omo-codex/plugin/components/rules/README.md index eb85634d8..c7a40dab5 100644 --- a/packages/omo-codex/plugin/components/rules/README.md +++ b/packages/omo-codex/plugin/components/rules/README.md @@ -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 ``` diff --git a/packages/omo-codex/plugin/components/ultragoal/README.md b/packages/omo-codex/plugin/components/ultragoal/README.md index ff7ce78ad..d783ab301 100644 --- a/packages/omo-codex/plugin/components/ultragoal/README.md +++ b/packages/omo-codex/plugin/components/ultragoal/README.md @@ -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//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. diff --git a/packages/omo-codex/plugin/components/ultrawork/README.md b/packages/omo-codex/plugin/components/ultrawork/README.md index b44b599ba..8a4a022b7 100644 --- a/packages/omo-codex/plugin/components/ultrawork/README.md +++ b/packages/omo-codex/plugin/components/ultrawork/README.md @@ -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 `/.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 `/.installed-agents.json` records the installed paths for clean uninstall. ## How it works diff --git a/packages/omo-codex/scripts/install-local.test.mjs b/packages/omo-codex/scripts/install-local.test.mjs index 88f55d0bb..da0e5c3f9 100644 --- a/packages/omo-codex/scripts/install-local.test.mjs +++ b/packages/omo-codex/scripts/install-local.test.mjs @@ -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(); diff --git a/packages/omo-codex/scripts/install-test-fixtures.mjs b/packages/omo-codex/scripts/install-test-fixtures.mjs new file mode 100644 index 000000000..01fd378d4 --- /dev/null +++ b/packages/omo-codex/scripts/install-test-fixtures.mjs @@ -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: {}, + }); +} diff --git a/packages/omo-codex/scripts/install/config.mjs b/packages/omo-codex/scripts/install/config.mjs index 48d22827b..e01510f89 100644 --- a/packages/omo-codex/scripts/install/config.mjs +++ b/packages/omo-codex/scripts/install/config.mjs @@ -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) { diff --git a/packages/omo-codex/src/install/index.ts b/packages/omo-codex/src/install/index.ts index 190e7fe7c..e14a092e9 100644 --- a/packages/omo-codex/src/install/index.ts +++ b/packages/omo-codex/src/install/index.ts @@ -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 {} diff --git a/script/publish-workflow.test.ts b/script/publish-workflow.test.ts index 2059dbce1..ad691d1ff 100644 --- a/script/publish-workflow.test.ts +++ b/script/publish-workflow.test.ts @@ -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) + }) }) diff --git a/script/sync-lazycodex-marketplace.test.ts b/script/sync-lazycodex-marketplace.test.ts new file mode 100644 index 000000000..141e8030a --- /dev/null +++ b/script/sync-lazycodex-marketplace.test.ts @@ -0,0 +1,59 @@ +/// + +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 { + await mkdir(dirname(path), { recursive: true }) + await writeFile(path, `${JSON.stringify(value, null, 2)}\n`) +} + +async function writePluginFixture(sourceRoot: string): Promise { + 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") + }) +}) diff --git a/script/sync-lazycodex-marketplace.ts b/script/sync-lazycodex-marketplace.ts new file mode 100644 index 000000000..960740060 --- /dev/null +++ b/script/sync-lazycodex-marketplace.ts @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 ") + } + await syncLazycodexMarketplace({ sourceRoot, lazycodexRoot }) +} diff --git a/script/tsconfig.json b/script/tsconfig.json index b4a10ba05..4c78bbbfb 100644 --- a/script/tsconfig.json +++ b/script/tsconfig.json @@ -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"] } diff --git a/src/cli/cli-installer.platform.test.ts b/src/cli/cli-installer.platform.test.ts index 5302fbca8..5228b80ac 100644 --- a/src/cli/cli-installer.platform.test.ts +++ b/src/cli/cli-installer.platform.test.ts @@ -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", diff --git a/src/cli/cli-installer.test.ts b/src/cli/cli-installer.test.ts index a61c50068..d2458fc21 100644 --- a/src/cli/cli-installer.test.ts +++ b/src/cli/cli-installer.test.ts @@ -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 = { diff --git a/src/cli/install-codex/codex-config-toml.test.ts b/src/cli/install-codex/codex-config-toml.test.ts index cdffb487a..87185b4b1 100644 --- a/src/cli/install-codex/codex-config-toml.test.ts +++ b/src/cli/install-codex/codex-config-toml.test.ts @@ -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"') }) }) diff --git a/src/cli/install-codex/codex-config-toml.ts b/src/cli/install-codex/codex-config-toml.ts index 7ddecd287..ce148604c 100644 --- a/src/cli/install-codex/codex-config-toml.ts +++ b/src/cli/install-codex/codex-config-toml.ts @@ -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 { @@ -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, ) } diff --git a/src/cli/install-codex/codex-hook-trust.test.ts b/src/cli/install-codex/codex-hook-trust.test.ts index c5501bb6b..3e9a11f09 100644 --- a/src/cli/install-codex/codex-hook-trust.test.ts +++ b/src/cli/install-codex/codex-hook-trust.test.ts @@ -14,7 +14,7 @@ describe("codex-hook-trust", () => { // when const states = await trustedHookStatesForPlugin({ - marketplaceName: "code-yeongyu-codex-plugins", + marketplaceName: "sisyphuslabs", pluginName: "omo", pluginRoot, }) diff --git a/src/cli/install-codex/codex-marketplace.test.ts b/src/cli/install-codex/codex-marketplace.test.ts index 2252281c2..99effc610 100644 --- a/src/cli/install-codex/codex-marketplace.test.ts +++ b/src/cli/install-codex/codex-marketplace.test.ts @@ -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) diff --git a/src/cli/install-codex/install-codex.test.ts b/src/cli/install-codex/install-codex.test.ts index 075c359bc..2bc5133b3 100644 --- a/src/cli/install-codex/install-codex.test.ts +++ b/src/cli/install-codex/install-codex.test.ts @@ -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) }) diff --git a/src/cli/install-codex/install-codex.ts b/src/cli/install-codex/install-codex.ts index bddab476e..58998027f 100644 --- a/src/cli/install-codex/install-codex.ts +++ b/src/cli/install-codex/install-codex.ts @@ -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 { 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, }) diff --git a/src/cli/install-codex/types.ts b/src/cli/install-codex/types.ts index c1269a64c..f4e4e5510 100644 --- a/src/cli/install-codex/types.ts +++ b/src/cli/install-codex/types.ts @@ -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 } diff --git a/src/cli/install-platform-resolution.test.ts b/src/cli/install-platform-resolution.test.ts index f48e57170..b740f3796 100644 --- a/src/cli/install-platform-resolution.test.ts +++ b/src/cli/install-platform-resolution.test.ts @@ -1,8 +1,6 @@ /// 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\(/)