feat(omo-codex): install through sisyphuslabs marketplace
This commit is contained in:
@@ -254,6 +254,8 @@ jobs:
|
||||
done
|
||||
|
||||
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
|
||||
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/.codex-plugin/plugin.json > tmp.json && mv tmp.json packages/omo-codex/plugin/.codex-plugin/plugin.json
|
||||
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/package.json > tmp.json && mv tmp.json packages/omo-codex/plugin/package.json
|
||||
|
||||
- name: Build main package
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
@@ -419,6 +421,8 @@ jobs:
|
||||
done
|
||||
|
||||
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
|
||||
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/.codex-plugin/plugin.json > tmp.json && mv tmp.json packages/omo-codex/plugin/.codex-plugin/plugin.json
|
||||
jq --arg v "$VERSION" '.version = $v' packages/omo-codex/plugin/package.json > tmp.json && mv tmp.json packages/omo-codex/plugin/package.json
|
||||
|
||||
- name: Commit version bump
|
||||
env:
|
||||
@@ -426,7 +430,7 @@ jobs:
|
||||
run: |
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config user.name "github-actions[bot]"
|
||||
git add package.json packages/oh-my-opencode-*/package.json
|
||||
git add package.json packages/oh-my-opencode-*/package.json packages/omo-codex/plugin/.codex-plugin/plugin.json packages/omo-codex/plugin/package.json
|
||||
git diff --cached --quiet || git commit -m "release: v${VERSION}"
|
||||
|
||||
- name: Create release tag
|
||||
@@ -447,6 +451,32 @@ jobs:
|
||||
git push origin HEAD
|
||||
git push origin "v${VERSION}"
|
||||
|
||||
- name: Require LazyCodex sync token
|
||||
if: ${{ secrets.LAZYCODEX_SYNC_TOKEN == '' }}
|
||||
run: |
|
||||
echo "::error::LAZYCODEX_SYNC_TOKEN is required to push the Codex marketplace bundle to code-yeongyu/lazycodex."
|
||||
exit 1
|
||||
|
||||
- name: Checkout LazyCodex marketplace
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: code-yeongyu/lazycodex
|
||||
path: lazycodex-marketplace
|
||||
token: ${{ secrets.LAZYCODEX_SYNC_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sync LazyCodex Codex marketplace
|
||||
env:
|
||||
VERSION: ${{ needs.publish-main.outputs.version }}
|
||||
run: |
|
||||
bun run script/sync-lazycodex-marketplace.ts "$GITHUB_WORKSPACE" "$GITHUB_WORKSPACE/lazycodex-marketplace"
|
||||
cd "$GITHUB_WORKSPACE/lazycodex-marketplace"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config user.name "github-actions[bot]"
|
||||
git add .agents/plugins/marketplace.json plugins/omo
|
||||
git diff --cached --quiet || git commit -m "chore: sync Codex marketplace v${VERSION}"
|
||||
git push origin HEAD:main
|
||||
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
VERSION: ${{ needs.publish-main.outputs.version }}
|
||||
|
||||
+3
-2
@@ -22,8 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Per-agent `displayName` for i18n. Agents can present localized names in UI and logs. (PR #4081)
|
||||
- Grok family models registered with `reasoningEffort` support. (PR #4186)
|
||||
- CLI `setup` alias for `install`. Either command runs the interactive setup wizard. (PR #4174)
|
||||
- Codex harness adapter (`omo-codex`): one-command install via `bunx omo install --codex=yes` or the new `lazycodex` bin entry. Vendored Codex plugin namespace `omo` with rules, comment-checker, LSP, ultrawork, and ultragoal components. Plugin lands in `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/` and is enabled in `~/.codex/config.toml`. Idempotent installer (re-running is safe).
|
||||
- Three new bin entries: `omo` (short alias) and `lazycodex` (auto-defaults `--codex=yes`). Existing `oh-my-opencode` and `oh-my-openagent` continue to work unchanged.
|
||||
- Codex CLI Light edition (`omo-codex`): one-command install via `bunx omo install --platform=codex` or the new `lazycodex` bin entry. Vendored Codex plugin namespace `omo` with rules, comment-checker, LSP, ultrawork, and ultragoal components. Plugin lands in `~/.codex/plugins/cache/sisyphuslabs/omo/` and is enabled in `~/.codex/config.toml`. Idempotent installer (re-running is safe).
|
||||
- New `--platform <opencode|codex|both>` install flag (default `opencode`). Replaces the previous Codex-as-optional-addon model — `--platform=codex` installs only the Codex Light edition, `--platform=both` installs both editions in one run.
|
||||
- Three new bin entries: `omo` (short alias) and `lazycodex` (auto-defaults `--platform=codex`). Existing `oh-my-opencode` and `oh-my-openagent` continue to work unchanged.
|
||||
- New PostHog telemetry stream `omo_codex_daily_active` distinguishing omo-codex installations from omo-opencode. Independent opt-out via `OMO_CODEX_DISABLE_POSTHOG=1` or `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`; global `OMO_DISABLE_POSTHOG` and `OMO_SEND_ANONYMOUS_TELEMETRY` still suppress both products.
|
||||
- omo-codex now reports true daily-active usage (DAU/WAU/MAU). A new Codex plugin component `telemetry` (`packages/omo-codex/plugin/components/telemetry/`) fires a single `omo_codex_daily_active` event with `reason: "session_start"` from every Codex `SessionStart` hook, with the same UTC-day deduplication, hashed installation identifier, and opt-out env vars as the install-time event. Identity constants stay byte-equivalent across the CLI installer and the plugin runtime via `packages/omo-codex/src/telemetry/cross-package-equivalence.test.ts`.
|
||||
- Triple-publish to npm: `oh-my-opencode`, `oh-my-openagent`, and the new `lazycodex` package with the same compiled CLI and four bin commands. See `docs/reference/lazycodex-npm-reservation.md` for the first-publish playbook.
|
||||
|
||||
+26
-22
@@ -123,7 +123,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
|
||||
|
||||
匿名のテレメトリは、アクティブなインストール数(DAU/WAU/MAU)の集計のためにデフォルトで有効になっています。マシン1台につきUTC日あたり最大1回イベントが送信され、ハッシュ化されたインストール識別子を使用し、生のホスト名は使用せず、PostHog person profile も作成されません。無効化するには `OMO_SEND_ANONYMOUS_TELEMETRY=0` または `OMO_DISABLE_POSTHOG=1` を設定してください。[プライバシーポリシー](docs/legal/privacy-policy.md)と[利用規約](docs/legal/terms-of-service.md)をご覧ください。
|
||||
|
||||
OpenAI Codex CLI も使う場合は、オプションの Codex アダプターを `bunx omo install --codex=yes` かショートカットの `bunx lazycodex install` で追加できます。Codex 専用テレメトリは `OMO_CODEX_DISABLE_POSTHOG=1` または `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` で無効化できます。
|
||||
**Ultimate と Light:** oh-my-openagent は同じ製品の 2 つのエディションとして提供されます。**Ultimate エディション**(`bunx omo install` または `--platform=opencode`、デフォルト)は OpenCode 上のフル機能で、11 エージェント、54+ フック、Team Mode、すべての MCP、スラッシュコマンド、IntentGate モードを提供します。**Light エディション**(`bunx omo install --platform=codex`)は OpenAI Codex CLI のプラグインシステムへ綺麗に移植できる 5 コンポーネント(`rules`、`comment-checker`、`lsp`、`ultrawork`、`ultragoal`)のみを提供します。`bunx lazycodex install` は `--platform=codex` のショートカット別名です。両方を同時にインストールするには `--platform=both`。Codex 専用テレメトリは `OMO_CODEX_DISABLE_POSTHOG=1` または `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0` で無効化できます。
|
||||
|
||||
---
|
||||
|
||||
@@ -152,25 +152,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [GLM Coding プラン ($10)](https://z.ai/subscribe)
|
||||
- 従量課金 (pay-per-token) の対象であれば、Kimi や Gemini モデルを使っても費用はそれほどかかりません。
|
||||
|
||||
| | 機能 | 何をするのか |
|
||||
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphus が Hephaestus、Oracle、Librarian、Explore をオーケストレーションします。完全な AI 開発チームが並列で動きます。 |
|
||||
| 🧩 | **Codex Harness Adapter** | OpenAI Codex CLI でも同じ OMO 機能を利用できます。`bunx lazycodex install` または `bunx omo install --codex=yes` で導入できます。 |
|
||||
| 👥 | **Team Mode** (v4.0, オプトイン) | リードエージェント + 最大 8 メンバーの並列実行、リアルタイム tmux 可視化、専用 `team_*` ツール群。`hyperplan`(5 人の敵対的批評家)と `security-research`(3 人のハンター + 2 人の PoC エンジニア)を駆動します。[ドキュメント →](docs/guide/team-mode.md) |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 一言で OK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
|
||||
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-line エラー 0%。[oh-my-pi](https://github.com/can1357/oh-my-pi) にインスパイアされています。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、AST を考慮した書き換え。エージェントに IDE レベルの精度を提供します。 |
|
||||
| 🧠 | **バックグラウンドエージェント** | 5 人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
|
||||
| 📚 | **組み込み MCP** | Exa (Web 検索)、Context7 (公式ドキュメント)、Grep.app (GitHub 検索)。常にオンです。 |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100% 完了するまで絶対に止まりません。 |
|
||||
| ✅ | **Todo Enforcer** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
|
||||
| 💬 | **コメントチェッカー** | コメントから AI 臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
|
||||
| 🖥️ | **Tmux 統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUI アプリがすべてリアルタイムで動きます。 |
|
||||
| 🔌 | **Claude Code 互換性** | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。 |
|
||||
| 🎯 | **スキル内蔵 MCP** | スキルが独自の MCP サーバーを持ち歩きます。コンテキストが肥大化しません。 |
|
||||
| 📋 | **Prometheus プランナー** | インタビューモードで、実行前に戦略的な計画から立てます。 |
|
||||
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
|
||||
| | 機能 | Editions | 何をするのか |
|
||||
| :---: | :------------------------------------------------------- | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **規律あるエージェント (Discipline Agents)** | Ultimate | Sisyphus が Hephaestus、Oracle、Librarian、Explore をオーケストレーションします。完全な AI 開発チームが並列で動きます。 |
|
||||
| 🧩 | **Codex CLI Light Edition** | Light | OpenAI Codex CLI 上で動作する omo の 5 つの移植コンポーネント (rules, comment-checker, LSP, ultrawork, ultragoal)。インストール: `bunx omo install --platform=codex`。 |
|
||||
| 👥 | **Team Mode** (v4.0, オプトイン) | Ultimate | リードエージェント + 最大 8 メンバーの並列実行、リアルタイム tmux 可視化、専用 `team_*` ツール群。`hyperplan`(5 人の敵対的批評家)と `security-research`(3 人のハンター + 2 人の PoC エンジニア)を駆動します。[ドキュメント →](docs/guide/team-mode.md) |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | Both | 一言で OK。すべてのエージェント (Ultimate) または Codex `ultrawork` コンポーネント (Light) がアクティブになり、終わるまで止まりません。 |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Ultimate | ユーザーの真の意図を分析してから分類・行動します。`search` / `analyze` / `team` / `hyperplan` をトリガー。(Light は `ulw` / `ultrawork` のみフック。) |
|
||||
| 🔗 | **ハッシュベースの編集ツール** | Ultimate | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-line エラー 0%。[oh-my-pi](https://github.com/can1357/oh-my-pi) にインスパイア。[The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex はネイティブの `apply_patch` を使用。) |
|
||||
| 🛠️ | **LSP + AST-Grep** | Ultimate | ワークスペース単位のリネーム、ビルド前の診断、AST を考慮した書き換え。エージェントに IDE レベルの精度を提供。(LSP は Light でも `lsp` コンポーネントで動作; AST-Grep は Ultimate のみ。) |
|
||||
| 🧠 | **バックグラウンドエージェント** | Ultimate | 5 人以上の専門家を並列で投入。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
|
||||
| 📚 | **組み込み MCP** | Ultimate | Exa (Web 検索)、Context7 (公式ドキュメント)、Grep.app (GitHub 検索)。常にオン。(Light は LSP MCP のみ。) |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | 自己参照ループ。100% 完了するまで絶対に止まりません。 |
|
||||
| ✅ | **Todo Enforcer** (Boulder) | Ultimate | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
|
||||
| 💬 | **コメントチェッカー** | Both | コメントから AI 臭い無駄話を排除。両エディションで同じ `@code-yeongyu/comment-checker` バイナリが動作。 |
|
||||
| 📜 | **Rules Injection** | Both | `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**` の階層的コンテキスト注入。Ultimate はフック、Light は `rules` コンポーネント。 |
|
||||
| 🧬 | **Ultragoal** | Light | `.omo/ultragoal/` evidence audit ベースの永続的マルチゴール オーケストレーション。現在は Codex 専用; OpenCode 側への移植はロードマップ。 |
|
||||
| 🖥️ | **Tmux 統合** | Ultimate | 完全なインタラクティブターミナル。REPL、デバッガー、TUI アプリがすべてリアルタイムで動きます。 |
|
||||
| 🔌 | **Claude Code 互換性** | Ultimate | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。(Codex は独自のネイティブプラグインシステムを保有。) |
|
||||
| 🎯 | **スキル内蔵 MCP** | Ultimate | スキルが独自の MCP サーバーを持ち歩きます。コンテキストが肥大化しません。 |
|
||||
| 📋 | **Prometheus プランナー** | Ultimate | インタビューモードで、実行前に戦略的な計画から立てます。 |
|
||||
| 🔍 | **`/init-deep`** | Ultimate | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
|
||||
|
||||
> **Editions legend.** **Ultimate** = OpenCode 専用 (`bunx omo install`)。**Light** = Codex CLI 専用 (`bunx omo install --platform=codex`)。**Both** = 両エディションに提供、しばしば内部実装は若干異なる。
|
||||
|
||||
### 規律あるエージェント (Discipline Agents)
|
||||
|
||||
@@ -345,10 +349,10 @@ oh-my-openagent を削除するには:
|
||||
4. **omo-codex (Codex アダプター) を削除する**
|
||||
|
||||
```bash
|
||||
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
|
||||
rm -rf ~/.codex/plugins/cache/sisyphuslabs
|
||||
```
|
||||
|
||||
その後 `~/.codex/config.toml` を開き、`[plugins."omo@code-yeongyu-codex-plugins"]` ブロックと `[hooks.state.\"omo@...\"]` ブロックを削除してください。
|
||||
その後 `~/.codex/config.toml` を開き、`[plugins."omo@sisyphuslabs"]` ブロックと `[hooks.state.\"omo@...\"]` ブロックを削除してください。
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
+26
-22
@@ -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
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ Pick the edition(s) you want.
|
||||
| You want | Run | What lands on disk |
|
||||
| :--- | :--- | :--- |
|
||||
| **Ultimate** (OpenCode) | `bunx omo install` (TUI walks you through it) | Plugin registered in `opencode.json` + agent/model config + provider auth prompts |
|
||||
| **Light** (Codex CLI) | `bunx omo install --platform=codex` | `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/` + `~/.codex/config.toml` plugin block + `~/.local/bin/omo-*` |
|
||||
| **Light** (Codex CLI) | `bunx omo install --platform=codex` | `~/.codex/plugins/cache/sisyphuslabs/omo/` + `~/.codex/config.toml` plugin block + `~/.local/bin/omo-*` |
|
||||
| **Both** | `bunx omo install --platform=both` | Both of the above |
|
||||
|
||||
`--platform` defaults to `opencode` (Ultimate). The `bunx lazycodex install` alias is a one-letter-cheaper shortcut for `bunx omo install --platform=codex` — use whichever reads cleaner.
|
||||
@@ -395,10 +395,10 @@ To remove oh-my-openagent:
|
||||
4. **Remove omo-codex (Codex adapter)**
|
||||
|
||||
```bash
|
||||
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
|
||||
rm -rf ~/.codex/plugins/cache/sisyphuslabs
|
||||
```
|
||||
|
||||
Then open `~/.codex/config.toml` and remove the `[plugins."omo@code-yeongyu-codex-plugins"]` block and any `[hooks.state.\"omo@...\"]` blocks.
|
||||
Then open `~/.codex/config.toml` and remove the `[plugins."omo@sisyphuslabs"]` block and any `[hooks.state.\"omo@...\"]` blocks.
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
+26
-22
@@ -121,7 +121,7 @@ curl -s https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/head
|
||||
|
||||
Анонимная телеметрия включена по умолчанию для подсчёта активных установок (DAU/WAU/MAU). Не более одного события на машину за UTC-сутки, использует хешированный идентификатор установки, никогда не использует исходное имя хоста, и не создаёт PostHog person profile. Можно отключить через `OMO_SEND_ANONYMOUS_TELEMETRY=0` или `OMO_DISABLE_POSTHOG=1`. См. [Политику конфиденциальности](docs/legal/privacy-policy.md) и [Условия обслуживания](docs/legal/terms-of-service.md).
|
||||
|
||||
Если вы также используете OpenAI Codex CLI, установите дополнительный Codex-адаптер через `bunx omo install --codex=yes` или короткую команду `bunx lazycodex install`. Телеметрию только для Codex можно отключить через `OMO_CODEX_DISABLE_POSTHOG=1` или `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`.
|
||||
**Ultimate и Light:** oh-my-openagent поставляется в двух редакциях одного продукта. **Ultimate** (`bunx omo install` или `--platform=opencode`, по умолчанию) — полнофункциональная редакция поверх OpenCode: 11 агентов, 54+ хука, Team Mode, все MCP, все слэш-команды, режимы IntentGate. **Light** (`bunx omo install --platform=codex`) — только 5 компонентов omo, которые портируются в систему плагинов OpenAI Codex CLI: `rules`, `comment-checker`, `lsp`, `ultrawork`, `ultragoal`. `bunx lazycodex install` — это сокращённый псевдоним для `--platform=codex`. Чтобы установить обе редакции одной командой, используйте `--platform=both`. Телеметрию только для Codex можно отключить через `OMO_CODEX_DISABLE_POSTHOG=1` или `OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=0`.
|
||||
|
||||
------
|
||||
|
||||
@@ -151,25 +151,29 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [Тариф GLM Coding ($10)](https://z.ai/subscribe)
|
||||
- Если у вас есть доступ к оплате за токены, использование моделей Kimi и Gemini обойдётся недорого.
|
||||
|
||||
| | Функция | Что делает |
|
||||
| --- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
|
||||
| 🧩 | **Codex Harness Adapter** | Те же возможности OMO для OpenAI Codex CLI. Установка через `bunx lazycodex install` или `bunx omo install --codex=yes`. |
|
||||
| 👥 | **Team Mode** (v4.0, opt-in) | Лид-агент + до 8 параллельных участников, визуализация в tmux в реальном времени, выделенные инструменты `team_*`. Питает `hyperplan` (5 враждебных критиков) и `security-research` (3 охотника + 2 PoC-инженера). [Документация →](docs/guide/team-mode.md) |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
|
||||
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
|
||||
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
|
||||
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
|
||||
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
|
||||
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
|
||||
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
|
||||
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
|
||||
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
|
||||
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
|
||||
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
|
||||
| | Функция | Editions | Что делает |
|
||||
| --- | -------------------------------------------------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **Дисциплинированные агенты** | Ultimate | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
|
||||
| 🧩 | **Codex CLI Light Edition** | Light | 5 компонентов omo, портированных в OpenAI Codex CLI (rules, comment-checker, LSP, ultrawork, ultragoal). Установка: `bunx omo install --platform=codex`. |
|
||||
| 👥 | **Team Mode** (v4.0, opt-in) | Ultimate | Лид-агент + до 8 параллельных участников, визуализация в tmux в реальном времени, выделенные инструменты `team_*`. Питает `hyperplan` (5 враждебных критиков) и `security-research` (3 охотника + 2 PoC-инженера). [Документация →](docs/guide/team-mode.md) |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | Both | Одно слово. Все агенты (Ultimate) или Codex-компонент `ultrawork` (Light) активируются. Не останавливается, пока задача не выполнена. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Ultimate | Анализирует истинное намерение пользователя перед классификацией и действием. Триггеры `search` / `analyze` / `team` / `hyperplan`. (Light хукает только `ulw` / `ultrawork`.) |
|
||||
| 🔗 | **Инструмент правок на основе хэш-якорей** | Ultimate | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [The Harness Problem →](https://blog.can.ac/2026/02/12/the-harness-problem/) (Codex использует собственный `apply_patch`.) |
|
||||
| 🛠️ | **LSP + AST-Grep** | Ultimate | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. (LSP также работает в Light через компонент `lsp`; AST-Grep только Ultimate.) |
|
||||
| 🧠 | **Фоновые агенты** | Ultimate | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
|
||||
| 📚 | **Встроенные MCP** | Ultimate | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. (В Light только LSP MCP.) |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | Ultimate | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
|
||||
| ✅ | **Todo Enforcer** (Boulder) | Ultimate | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
|
||||
| 💬 | **Comment Checker** | Both | Никакого AI-мусора в комментариях. Тот же бинарник `@code-yeongyu/comment-checker` работает в обеих редакциях. |
|
||||
| 📜 | **Rules Injection** | Both | Иерархическое внедрение контекста из `AGENTS.md` / `CLAUDE.md` / `.omo/rules/**`. В Ultimate это хук, в Light — компонент `rules`. |
|
||||
| 🧬 | **Ultragoal** | Light | Долговечная оркестрация нескольких целей с аудитом доказательств в `.omo/ultragoal/`. Сейчас только в Codex; порт в сторону OpenCode в дорожной карте. |
|
||||
| 🖥️ | **Интеграция с Tmux** | Ultimate | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
|
||||
| 🔌 | **Совместимость с Claude Code** | Ultimate | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. (У Codex своя нативная плагин-система.) |
|
||||
| 🎯 | **MCP, встроенные в навыки** | Ultimate | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
|
||||
| 📋 | **Prometheus Planner** | Ultimate | Стратегическое планирование в режиме интервью перед любым выполнением. |
|
||||
| 🔍 | **`/init-deep`** | Ultimate | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
|
||||
|
||||
> **Editions, легенда.** **Ultimate** = только OpenCode (`bunx omo install`). **Light** = только Codex CLI (`bunx omo install --platform=codex`). **Both** = поставляется в обеих редакциях, часто с немного отличающейся реализацией.
|
||||
|
||||
### Дисциплинированные агенты
|
||||
|
||||
@@ -344,10 +348,10 @@ project/
|
||||
4. **Удалите omo-codex (Codex-адаптер)**
|
||||
|
||||
```bash
|
||||
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
|
||||
rm -rf ~/.codex/plugins/cache/sisyphuslabs
|
||||
```
|
||||
|
||||
Затем откройте `~/.codex/config.toml` и удалите блок `[plugins."omo@code-yeongyu-codex-plugins"]` и все блоки `[hooks.state.\"omo@...\"]`.
|
||||
Затем откройте `~/.codex/config.toml` и удалите блок `[plugins."omo@sisyphuslabs"]` и все блоки `[hooks.state.\"omo@...\"]`.
|
||||
|
||||
## Функции
|
||||
|
||||
|
||||
+26
-22
@@ -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
|
||||
|
||||
|
||||
+64
-54
@@ -1,17 +1,25 @@
|
||||
# Installation
|
||||
|
||||
oh-my-openagent installs as a plugin into one or both of these AI harnesses:
|
||||
oh-my-openagent ships in **two editions** of the same product:
|
||||
|
||||
- **[OpenCode](https://opencode.ai)** — terminal-based open-source AI coding agent.
|
||||
- **[OpenAI Codex CLI](https://github.com/openai/codex)** — OpenAI's official Codex CLI.
|
||||
- **Ultimate Edition (omo for [OpenCode](https://opencode.ai))** — the full omo experience. 11 discipline agents, 54+ lifecycle hooks, all built-in MCPs, every slash command, Team Mode, ultragoal, hashline edits, the works.
|
||||
- **Light Edition (omo for [OpenAI Codex CLI](https://github.com/openai/codex))** — the 5 components that port cleanly to Codex's plugin system: `rules`, `comment-checker`, `lsp`, `ultrawork`, `ultragoal`. No agent orchestration, no `team_*` tools, no built-in web/docs/code search MCPs — Codex CLI's native surface does that work.
|
||||
|
||||
Pick what you use. The installer handles both, separately or together.
|
||||
Most users want **Ultimate**. Pick **Light** if you are already invested in Codex CLI. Pick **both** if you want OMO available wherever you happen to be working that day.
|
||||
|
||||
| You want | Run | Lands on disk |
|
||||
| :--- | :--- | :--- |
|
||||
| Ultimate (OpenCode) | `bunx omo install` (TUI walks you through it) | Plugin registered in `opencode.json`, agent/model config, provider auth |
|
||||
| Light (Codex CLI) | `bunx omo install --platform=codex` (no questions) | `~/.codex/plugins/cache/...`, `~/.codex/config.toml` plugin block, `~/.local/bin/omo-*` |
|
||||
| Both | `bunx omo install --platform=both` | Both of the above |
|
||||
|
||||
`--platform` defaults to `opencode` (Ultimate). The `bunx lazycodex install` alias is a shortcut for `bunx omo install --platform=codex` — same compiled CLI, different default.
|
||||
|
||||
## For Humans
|
||||
|
||||
**Strongly recommended: let an LLM agent install this for you.** OpenCode setup involves subscription detection, model selection, provider authentication, and config migration — humans fat-finger these. An LLM agent reads the full guide and walks through every step correctly.
|
||||
**Strongly recommended: let an LLM agent install Ultimate for you.** Ultimate setup involves subscription detection, model selection across 11 agents, provider authentication, and config migration — humans fat-finger these. An LLM agent reads the full guide and walks every step correctly.
|
||||
|
||||
### OpenCode (or OpenCode + Codex)
|
||||
### Ultimate (OpenCode) — let an agent do it
|
||||
|
||||
Paste this prompt into Claude Code, AmpCode, Cursor, or any LLM agent session:
|
||||
|
||||
@@ -20,25 +28,27 @@ Install and configure oh-my-openagent by following the instructions here:
|
||||
https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/refs/heads/dev/docs/guide/installation.md
|
||||
```
|
||||
|
||||
### Codex CLI only
|
||||
### Light (Codex CLI) — one line, no agent needed
|
||||
|
||||
If you only want the Codex adapter, the installer asks no questions:
|
||||
The Light edition installer asks zero questions, so a human can run it directly:
|
||||
|
||||
```bash
|
||||
bunx omo install --platform=codex
|
||||
# equivalent:
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
This is the single command equivalent of `bunx omo install --platform=codex`. It writes only to `~/.codex/`. No OpenCode questions, no provider flags.
|
||||
It writes only to `~/.codex/`. No OpenCode interaction, no provider flags.
|
||||
|
||||
### A note on direct install
|
||||
|
||||
If you insist on running the installer yourself:
|
||||
If you insist on running the Ultimate installer yourself:
|
||||
|
||||
```bash
|
||||
bunx oh-my-openagent install
|
||||
```
|
||||
|
||||
The TUI walks you through it. **Do NOT use `npm install -g`, `bun add -g`, or `bun install -g`** — global installation is not officially supported, oh-my-openagent is an OpenCode plugin that needs to resolve from where OpenCode loads plugins, and the `prepare` script requires Bun. Always invoke via `bunx`.
|
||||
The TUI walks you through it. **Do NOT use `npm install -g`, `bun add -g`, or `bun install -g`** — global installation is not officially supported. oh-my-openagent is a plugin that must resolve from where OpenCode/Codex loads plugins, and the `prepare` script requires Bun. Always invoke via `bunx`.
|
||||
|
||||
## For LLM Agents
|
||||
|
||||
@@ -202,7 +212,7 @@ bunx oh-my-openagent install \
|
||||
| Platform | Writes |
|
||||
|----------|--------|
|
||||
| `opencode`, `both` | Registers `"oh-my-openagent"` in `opencode.json` `plugin` array. Generates agent → model mappings into `~/.config/opencode/oh-my-openagent.jsonc`. |
|
||||
| `codex`, `both` | Copies `packages/omo-codex/plugin/` into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`. Runs `npm install` + `npm run build` inside. Symlinks `~/.local/bin/omo-*` (or `$CODEX_LOCAL_BIN_DIR/omo-*`) for each of the 5 components. Computes SHA256 trusted-hashes for every hook and writes the `[plugins."omo@..."]` + `[hooks.state."omo@..."]` blocks into `~/.codex/config.toml`. |
|
||||
| `codex`, `both` | Copies `packages/omo-codex/plugin/` into `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`. Runs `npm install` + `npm run build` inside. Symlinks `~/.local/bin/omo-*` (or `$CODEX_LOCAL_BIN_DIR/omo-*`) for each of the 5 components. Computes SHA256 trusted-hashes for every hook and writes the `[plugins."omo@..."]` + `[hooks.state."omo@..."]` blocks into `~/.codex/config.toml`. |
|
||||
|
||||
Both halves are independent and idempotent — re-running is safe.
|
||||
|
||||
@@ -223,10 +233,10 @@ bunx oh-my-openagent doctor
|
||||
|
||||
```bash
|
||||
# Plugin cache present?
|
||||
ls ~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/
|
||||
ls ~/.codex/plugins/cache/sisyphuslabs/omo/
|
||||
|
||||
# Codex config has the plugin block?
|
||||
grep -A2 'omo@code-yeongyu-codex-plugins' ~/.codex/config.toml
|
||||
grep -A2 'omo@sisyphuslabs' ~/.codex/config.toml
|
||||
|
||||
# Component binaries linked?
|
||||
ls ~/.local/bin/ | grep -E 'omo-(rules|comment-checker|lsp|ultrawork|ultragoal)'
|
||||
@@ -458,35 +468,35 @@ After install, the user interacts with oh-my-openagent through five surfaces. Wa
|
||||
|
||||
Just type one of these words in your message and the system injects the corresponding mode prompt:
|
||||
|
||||
| Keyword | What it does |
|
||||
|---------|--------------|
|
||||
| `ultrawork` or `ulw` | Full orchestration mode — every agent activates, doesn't stop until done |
|
||||
| `search` | Web/doc search focus |
|
||||
| `analyze` | Deep analysis mode |
|
||||
| `team` | Forces `team_*` tools orchestration (requires `team_mode.enabled`) |
|
||||
| `hyperplan` | Adversarial planning via 5 hostile critics |
|
||||
| `hyperplan ultrawork` (combo) | Both at once |
|
||||
| Keyword | Editions | What it does |
|
||||
|---------|:--------:|--------------|
|
||||
| `ultrawork` or `ulw` | Both | Full orchestration mode — every agent (Ultimate) or the Codex `ultrawork` component (Light) activates, doesn't stop until done |
|
||||
| `search` | Ultimate | Web/doc search focus |
|
||||
| `analyze` | Ultimate | Deep analysis mode |
|
||||
| `team` | Ultimate | Forces `team_*` tools orchestration (requires `team_mode.enabled`) |
|
||||
| `hyperplan` | Ultimate | Adversarial planning via 5 hostile critics |
|
||||
| `hyperplan ultrawork` (combo) | Ultimate | Both at once |
|
||||
|
||||
#### Slash commands
|
||||
|
||||
Built-in:
|
||||
All built-in slash commands are **Ultimate-only** — Codex CLI does not have a slash-command surface, so the Light edition omits this entire layer.
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/init-deep` | Auto-generate hierarchical `AGENTS.md` files throughout the project |
|
||||
| `/start-work` | Spawn Prometheus to interview the user and build a plan, then execute |
|
||||
| `/ralph-loop` | Self-referential dev loop until 100% done |
|
||||
| `/ulw-loop` | Ultrawork-mode variant of the loop |
|
||||
| `/cancel-ralph` | Stop an active Ralph loop |
|
||||
| `/stop-continuation` | Stop ralph loop + todo continuation + boulder |
|
||||
| `/refactor` | LSP + AST-grep + TDD-verified intelligent refactor |
|
||||
| `/handoff` | Generate detailed context summary to continue in a new session |
|
||||
| `/remove-ai-slops` | Strip AI-generated code smells from recent changes |
|
||||
| `/hyperplan` | Direct invocation of hyperplan skill |
|
||||
| Command | Editions | Purpose |
|
||||
|---------|:--------:|---------|
|
||||
| `/init-deep` | Ultimate | Auto-generate hierarchical `AGENTS.md` files throughout the project |
|
||||
| `/start-work` | Ultimate | Spawn Prometheus to interview the user and build a plan, then execute |
|
||||
| `/ralph-loop` | Ultimate | Self-referential dev loop until 100% done |
|
||||
| `/ulw-loop` | Ultimate | Ultrawork-mode variant of the loop |
|
||||
| `/cancel-ralph` | Ultimate | Stop an active Ralph loop |
|
||||
| `/stop-continuation` | Ultimate | Stop ralph loop + todo continuation + boulder |
|
||||
| `/refactor` | Ultimate | LSP + AST-grep + TDD-verified intelligent refactor |
|
||||
| `/handoff` | Ultimate | Generate detailed context summary to continue in a new session |
|
||||
| `/remove-ai-slops` | Ultimate | Strip AI-generated code smells from recent changes |
|
||||
| `/hyperplan` | Ultimate | Direct invocation of hyperplan skill |
|
||||
|
||||
#### Agents (11)
|
||||
#### Agents (11) — Ultimate only
|
||||
|
||||
Sisyphus delegates to these — you don't usually call them directly, but knowing the cast helps:
|
||||
All 11 discipline agents are part of the Ultimate edition. The Light edition does not ship agent orchestration — Codex CLI's own model selection takes that role. Sisyphus delegates to these; you don't usually call them directly, but knowing the cast helps:
|
||||
|
||||
- **Sisyphus** — main orchestrator. Plans, delegates, drives to completion.
|
||||
- **Hephaestus** — "Codex on steroids." Deep autonomous worker, GPT-native.
|
||||
@@ -502,16 +512,16 @@ Sisyphus delegates to these — you don't usually call them directly, but knowin
|
||||
|
||||
#### Skills
|
||||
|
||||
Built-in skills load automatically when their description matches your task. The user does not need to invoke them by name.
|
||||
Built-in skills load automatically when their description matches your task. The user does not need to invoke them by name. The OpenCode skill system is **Ultimate-only**; the Light edition does not have a skill loader.
|
||||
|
||||
| Skill | When it triggers |
|
||||
|-------|-----------------|
|
||||
| `playwright` | Browser automation |
|
||||
| `git-master` | Atomic commits, rebases, history search |
|
||||
| `frontend-ui-ux` | UI/UX implementation work |
|
||||
| `review-work` | Post-implementation code review |
|
||||
| `ai-slop-remover` | Cleaning AI-generated code smells |
|
||||
| `team-mode` | Loaded only when `team_mode.enabled` |
|
||||
| Skill | Editions | When it triggers |
|
||||
|-------|:--------:|------------------|
|
||||
| `playwright` | Ultimate | Browser automation |
|
||||
| `git-master` | Ultimate | Atomic commits, rebases, history search |
|
||||
| `frontend-ui-ux` | Ultimate | UI/UX implementation work |
|
||||
| `review-work` | Ultimate | Post-implementation code review |
|
||||
| `ai-slop-remover` | Ultimate | Cleaning AI-generated code smells |
|
||||
| `team-mode` | Ultimate | Loaded only when `team_mode.enabled` |
|
||||
|
||||
Add custom skills under `.opencode/skills/<name>/SKILL.md` (project scope) or `~/.config/opencode/skills/<name>/SKILL.md` (user scope). Each `SKILL.md` declares a description that the agent matches against your message.
|
||||
|
||||
@@ -526,15 +536,15 @@ After verification, tell the user:
|
||||
|
||||
Then say **Congratulations! 🎉 You have successfully set up oh-my-openagent! Type `opencode` (or `codex`) in your terminal to start using it.**
|
||||
|
||||
### Step 7: Codex adapter deep dive
|
||||
### Step 7: Light Edition deep dive (Codex CLI)
|
||||
|
||||
Skip this section if `--platform=opencode`. Otherwise, the user installed the `omo-codex` adapter — here is what landed on disk and what each piece does.
|
||||
Skip this section if `--platform=opencode`. Otherwise, the user installed the **Light edition** (`omo-codex`) — here is what landed on disk and what each piece does.
|
||||
|
||||
#### What was installed
|
||||
|
||||
- **Plugin cache:** `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`
|
||||
- **Plugin cache:** `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`
|
||||
- **Component binaries:** `~/.local/bin/omo-rules`, `omo-comment-checker`, `omo-lsp`, `omo-ultrawork`, `omo-ultragoal` (or `$CODEX_LOCAL_BIN_DIR/omo-*` if set)
|
||||
- **Codex config edits:** `~/.codex/config.toml` gained `[features] plugins = true`, `[features] plugin_hooks = true`, a `[plugins."omo@code-yeongyu-codex-plugins"]` block, and SHA256-pinned `[hooks.state."omo@..."]` entries
|
||||
- **Codex config edits:** `~/.codex/config.toml` gained `[features] plugins = true`, `[features] plugin_hooks = true`, a `[plugins."omo@sisyphuslabs"]` block, and SHA256-pinned `[hooks.state."omo@..."]` entries
|
||||
|
||||
#### The 5 components
|
||||
|
||||
@@ -556,7 +566,7 @@ The Codex adapter is fully independent of the OpenCode plugin. You can install b
|
||||
|---------|-----|
|
||||
| `codex --help` does not list the omo plugin | Re-run `bunx omo install --platform=codex` (idempotent — hook hashes are recomputed) |
|
||||
| `command not found: omo-rules` | Add `~/.local/bin` to `PATH`, or set `$CODEX_LOCAL_BIN_DIR` to a directory already on `PATH` |
|
||||
| `npm install` fails mid-install | `rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins` and retry |
|
||||
| `npm install` fails mid-install | `rm -rf ~/.codex/plugins/cache/sisyphuslabs` and retry |
|
||||
| Plugin block is present but hooks do not fire | Verify `~/.codex/config.toml` contains `[features]\nplugins = true\nplugin_hooks = true` |
|
||||
| Hook trust hash mismatch warnings | Re-run the installer; hashes are regenerated each install |
|
||||
|
||||
@@ -746,11 +756,11 @@ opencode --version
|
||||
|
||||
```bash
|
||||
# 1. Remove the plugin cache
|
||||
rm -rf ~/.codex/plugins/cache/code-yeongyu-codex-plugins
|
||||
rm -rf ~/.codex/plugins/cache/sisyphuslabs
|
||||
|
||||
# 2. Edit ~/.codex/config.toml and remove these blocks:
|
||||
# [plugins."omo@code-yeongyu-codex-plugins"]
|
||||
# [hooks.state."omo@code-yeongyu-codex-plugins"]
|
||||
# [plugins."omo@sisyphuslabs"]
|
||||
# [hooks.state."omo@sisyphuslabs"]
|
||||
|
||||
# 3. Optional: remove the component symlinks
|
||||
rm -f ~/.local/bin/omo-rules ~/.local/bin/omo-comment-checker \
|
||||
|
||||
+14
-14
@@ -13,8 +13,8 @@ All published packages expose the same compiled CLI with these bin entries:
|
||||
|
||||
- `oh-my-opencode` (legacy name, still primary)
|
||||
- `oh-my-openagent` (renamed primary)
|
||||
- `omo` (short alias)
|
||||
- `lazycodex` (Codex-default alias; `lazycodex install` implies `--codex=yes` unless explicitly overridden)
|
||||
- `omo` (short alias, recommended in docs and prompts)
|
||||
- `lazycodex` (Light edition shortcut; `lazycodex install` is equivalent to `omo install --platform=codex` unless `--platform` is explicitly overridden)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -56,26 +56,26 @@ bunx oh-my-openagent install
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `--no-tui` | Run in non-interactive mode (requires all needed options) |
|
||||
| `--claude <value>` | Claude subscription: `no`, `yes`, `max20` |
|
||||
| `--openai <value>` | OpenAI/ChatGPT subscription: `no`, `yes` |
|
||||
| `--gemini <value>` | Gemini integration: `no`, `yes` |
|
||||
| `--copilot <value>` | GitHub Copilot subscription: `no`, `yes` |
|
||||
| `--opencode-zen <value>` | OpenCode Zen access: `no`, `yes` |
|
||||
| `--zai-coding-plan <value>` | Z.ai Coding Plan subscription: `no`, `yes` |
|
||||
| `--kimi-for-coding <value>` | Kimi For Coding subscription: `no`, `yes` |
|
||||
| `--opencode-go <value>` | OpenCode Go subscription: `no`, `yes` |
|
||||
| `--vercel-ai-gateway <value>` | Vercel AI Gateway: `no`, `yes` |
|
||||
| `--codex <value>` | Install Codex adapter (`omo-codex`): `no`, `yes` |
|
||||
| `--platform <value>` | Install target edition: `opencode` (Ultimate, default), `codex` (Light), or `both` |
|
||||
| `--claude <value>` | Claude subscription: `no`, `yes`, `max20` (Ultimate only) |
|
||||
| `--openai <value>` | OpenAI/ChatGPT subscription: `no`, `yes` (Ultimate only) |
|
||||
| `--gemini <value>` | Gemini integration: `no`, `yes` (Ultimate only) |
|
||||
| `--copilot <value>` | GitHub Copilot subscription: `no`, `yes` (Ultimate only) |
|
||||
| `--opencode-zen <value>` | OpenCode Zen access: `no`, `yes` (Ultimate only) |
|
||||
| `--zai-coding-plan <value>` | Z.ai Coding Plan subscription: `no`, `yes` (Ultimate only) |
|
||||
| `--kimi-for-coding <value>` | Kimi For Coding subscription: `no`, `yes` (Ultimate only) |
|
||||
| `--opencode-go <value>` | OpenCode Go subscription: `no`, `yes` (Ultimate only) |
|
||||
| `--vercel-ai-gateway <value>` | Vercel AI Gateway: `no`, `yes` (Ultimate only) |
|
||||
| `--skip-auth` | Skip authentication setup hints |
|
||||
|
||||
When using the `lazycodex` bin alias, `install` defaults to `--codex=yes`.
|
||||
When using the `lazycodex` bin alias, `install` defaults to `--platform=codex`. Subscription flags (`--claude`, `--openai`, etc.) only apply when `--platform` is `opencode` or `both` — they are rejected under `--platform=codex` because the Light edition does not write OpenCode model config.
|
||||
|
||||
### Telemetry and opt-out
|
||||
|
||||
Anonymous telemetry uses PostHog with a hashed installation identifier. Two streams exist:
|
||||
|
||||
- `omo_daily_active`: fired by the main plugin and `oh-my-openagent run`.
|
||||
- `omo_codex_daily_active`: fired by `omo install --codex=yes` (`reason: "install_completed"`) and by the Codex plugin's `SessionStart` hook on every Codex session (`reason: "session_start"`). Both sources share the same UTC-day deduplication, so daily/weekly/monthly active counts reflect real Codex usage, not just install events.
|
||||
- `omo_codex_daily_active`: fired by `omo install --platform=codex` or `--platform=both` (`reason: "install_completed"`) and by the Codex plugin's `SessionStart` hook on every Codex session (`reason: "session_start"`). Both sources share the same UTC-day deduplication, so daily/weekly/monthly active counts reflect real Codex usage, not just install events.
|
||||
|
||||
Opt-out env vars:
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@ Codex harness adapter for **oh-my-openagent**. Brings the OMO experience (rules
|
||||
|------|---------|
|
||||
| `plugin/` | Vendored Codex plugin namespace `omo` with 5 components. Shipped to the user via `~/.codex/plugins/cache/`. |
|
||||
| `marketplace.json` | Codex marketplace manifest. Identifies `omo` as the single installable plugin. |
|
||||
| `scripts/` | Node ESM build scripts (port of the original `codex-plugins/scripts/install-local.mjs`). |
|
||||
| `scripts/` | Node ESM build scripts for Codex cache installation and marketplace config updates. |
|
||||
| `src/` | TypeScript runtime: installer + telemetry consumed by the omodex CLI. |
|
||||
| `MARKETPLACE.md` | Vendored upstream marketplace README. |
|
||||
|
||||
@@ -22,16 +22,22 @@ Codex harness adapter for **oh-my-openagent**. Brings the OMO experience (rules
|
||||
|
||||
## Install
|
||||
|
||||
End users invoke through the omodex CLI:
|
||||
End users invoke through the omodex CLI. This package is the **Light edition** of omo — install it directly with:
|
||||
|
||||
```bash
|
||||
bunx omo install --codex=yes
|
||||
# or, equivalently:
|
||||
bunx oh-my-opencode install --codex=yes
|
||||
bunx oh-my-openagent install --codex=yes
|
||||
bunx omo install --platform=codex
|
||||
# or via the shortcut alias (same compiled CLI, defaults --platform=codex):
|
||||
bunx lazycodex install
|
||||
# or the longer package names:
|
||||
bunx oh-my-opencode install --platform=codex
|
||||
bunx oh-my-openagent install --platform=codex
|
||||
```
|
||||
|
||||
The installer copies the built plugin into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/<version>/`, enables it in `~/.codex/config.toml`, and links the per-component binaries into `~/.local/bin/` (or `CODEX_LOCAL_BIN_DIR`).
|
||||
To install **both** the Ultimate edition (OpenCode plugin) and the Light edition (this package) at once, use `--platform=both`.
|
||||
|
||||
The installer copies the built plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/<version>/`, enables `omo@sisyphuslabs` in `~/.codex/config.toml`, and registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository for native Codex marketplace upgrades.
|
||||
|
||||
To install both editions in one command, use `--platform=both`.
|
||||
|
||||
## Telemetry
|
||||
|
||||
@@ -39,7 +45,7 @@ Anonymous telemetry uses the same PostHog project as oh-my-openagent but emits t
|
||||
|
||||
| Source | Reason | Trigger |
|
||||
|--------|--------|---------|
|
||||
| `install` | `install_completed` | `bunx omo install --codex=yes` finishes (handled by `src/cli/install-codex/install-codex.ts`) |
|
||||
| `install` | `install_completed` | `bunx omo install --platform=codex` or `--platform=both` finishes (handled by `src/cli/install-codex/install-codex.ts`) |
|
||||
| `plugin` | `session_start` | Codex plugin `SessionStart` hook fires (handled by `plugin/components/telemetry/`) |
|
||||
|
||||
Both sources share the same SHA256-hashed installation identifier (`sha256("omo-codex:" + hostname)`), suppress PostHog person profiles, and write the daily dedup state to `~/.local/share/omo-codex/posthog-activity.json`.
|
||||
@@ -60,9 +66,9 @@ The identity constants and opt-out behavior are pinned across both sources by `s
|
||||
|
||||
See `/Users/yeongyu/local-workspaces/omodex/docs/legal/privacy-policy.md` for the full disclosure.
|
||||
|
||||
## Provenance
|
||||
## Component Sources
|
||||
|
||||
Vendored from [`code-yeongyu/codex-plugins`](https://github.com/code-yeongyu/codex-plugins) at the snapshot present in `/Users/yeongyu/local-workspaces/codex-plugins/` on 2026-05-25. Per-component upstream:
|
||||
The bundled component implementations come from the Sisyphus Labs Codex plugin family:
|
||||
|
||||
- [code-yeongyu/codex-rules](https://github.com/code-yeongyu/codex-rules)
|
||||
- [code-yeongyu/codex-comment-checker](https://github.com/code-yeongyu/codex-comment-checker)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-yeongyu-codex-plugins",
|
||||
"name": "sisyphuslabs",
|
||||
"interface": {
|
||||
"displayName": "Yeongyu Codex Plugins"
|
||||
"displayName": "Sisyphus Labs"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -51,21 +51,18 @@ node dist/cli.js hook post-tool-use < test/fixtures/post-tool-use.json
|
||||
|
||||
## Local Codex Installation
|
||||
|
||||
From the marketplace root containing this plugin:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /path/to/codex-plugins
|
||||
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0`, installs runtime dependencies there, and enables:
|
||||
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, installs runtime dependencies there, and enables:
|
||||
|
||||
```toml
|
||||
[features]
|
||||
plugins = true
|
||||
plugin_hooks = true
|
||||
|
||||
[plugins."omo@code-yeongyu-codex-plugins"]
|
||||
[plugins."omo@sisyphuslabs"]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
|
||||
@@ -118,17 +118,14 @@ printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/cli.j
|
||||
|
||||
## Local Codex Installation
|
||||
|
||||
From the marketplace root containing this plugin:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /path/to/codex-plugins
|
||||
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0` and enables:
|
||||
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, and enables:
|
||||
|
||||
```toml
|
||||
[plugins."omo@code-yeongyu-codex-plugins"]
|
||||
[plugins."omo@sisyphuslabs"]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
|
||||
@@ -42,17 +42,14 @@ Prefer strict TypeScript and keep runtime imports ESM-compatible.
|
||||
|
||||
## Install Locally
|
||||
|
||||
From the marketplace workspace:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /Users/yeongyu/local-workspaces/codex-plugins
|
||||
node /Users/yeongyu/local-workspaces/codex-plugins/scripts/install-local.mjs /Users/yeongyu/local-workspaces/codex-plugins
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
The local installer builds the plugin and copies a clean cache entry to:
|
||||
|
||||
```text
|
||||
~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0
|
||||
~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0
|
||||
```
|
||||
|
||||
It also enables:
|
||||
@@ -62,7 +59,7 @@ It also enables:
|
||||
plugins = true
|
||||
plugin_hooks = true
|
||||
|
||||
[plugins."omo@code-yeongyu-codex-plugins"]
|
||||
[plugins."omo@sisyphuslabs"]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
|
||||
@@ -46,21 +46,18 @@ npm pack --dry-run
|
||||
|
||||
## Local Codex Installation
|
||||
|
||||
From the marketplace root containing this plugin:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /path/to/codex-plugins
|
||||
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
If your local Codex build exposes plugin install commands, you can install from the UI or CLI instead. For older local builds, the marketplace installer builds and copies the plugin into `~/.codex/plugins/cache/<marketplace>/omo/0.1.0`, installs runtime dependencies there, and enables:
|
||||
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, installs runtime dependencies there, and enables:
|
||||
|
||||
```toml
|
||||
[features]
|
||||
plugins = true
|
||||
plugin_hooks = true
|
||||
|
||||
[plugins."omo@code-yeongyu-codex-plugins"]
|
||||
[plugins."omo@sisyphuslabs"]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
@@ -75,4 +72,4 @@ This plugin runs locally. The scaffold does not call a network service by itself
|
||||
## Related
|
||||
|
||||
- [oh-my-codex](https://github.com/code-yeongyu/oh-my-codex) - source project for the ultragoal port.
|
||||
- [codex-plugins](https://github.com/code-yeongyu/codex-plugins) - local Codex plugin marketplace.
|
||||
- [lazycodex](https://github.com/code-yeongyu/lazycodex) - Sisyphus Labs Codex marketplace repository.
|
||||
|
||||
@@ -20,11 +20,10 @@ The directive is currently 11,005 chars / 232 lines and follows the GPT-5.5 prom
|
||||
## Install (via this marketplace)
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /path/to/codex-plugins
|
||||
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
|
||||
bunx lazycodex install
|
||||
```
|
||||
|
||||
The installer copies the plugin into `~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0`, enables it in `~/.codex/config.toml`, registers the `UserPromptSubmit` hook, and installs the bundled agent TOMLs into `~/.codex/agents/` (symlinks on Unix, copies on Windows). A manifest at `<plugin-cache>/.installed-agents.json` records the installed paths for clean uninstall.
|
||||
The installer copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, enables `omo@sisyphuslabs` in `~/.codex/config.toml`, registers the `UserPromptSubmit` hook, and installs the bundled agent TOMLs into `~/.codex/agents/` (symlinks on Unix, copies on Windows). A manifest at `<plugin-cache>/.installed-agents.json` records the installed paths for clean uninstall.
|
||||
|
||||
## How it works
|
||||
|
||||
|
||||
@@ -1,60 +1,11 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdir, readFile, readlink, stat, writeFile } from "node:fs/promises";
|
||||
import { dirname } from "node:path";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { tmpdir } from "node:os";
|
||||
import { mkdtemp } from "node:fs/promises";
|
||||
|
||||
import { installMarketplaceLocally } from "./install-local.mjs";
|
||||
import { linkCachedPluginBins } from "./install/cache.mjs";
|
||||
|
||||
async function makeTempDir() {
|
||||
return mkdtemp(join(tmpdir(), "codex-plugins-install-"));
|
||||
}
|
||||
|
||||
async function writeJson(path, value) {
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
async function writePlugin(root, name, version) {
|
||||
const pluginRoot = join(root, "plugins", name);
|
||||
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "dist"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "hooks"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "skills", name), { recursive: true });
|
||||
await writeJson(join(pluginRoot, ".codex-plugin", "plugin.json"), {
|
||||
name,
|
||||
version,
|
||||
description: `${name} test plugin`,
|
||||
mcpServers: "./.mcp.json",
|
||||
hooks: "./hooks/hooks.json",
|
||||
skills: "./skills/",
|
||||
});
|
||||
await writeJson(join(pluginRoot, ".mcp.json"), {
|
||||
mcpServers: {
|
||||
[name]: {
|
||||
command: "node",
|
||||
args: ["./dist/cli.js", "mcp"],
|
||||
cwd: ".",
|
||||
},
|
||||
},
|
||||
});
|
||||
await writeJson(join(pluginRoot, "hooks", "hooks.json"), { hooks: {} });
|
||||
await writeFile(join(pluginRoot, "skills", name, "SKILL.md"), "---\nname: test\n---\n");
|
||||
await writeJson(join(pluginRoot, "package.json"), {
|
||||
name: `@example/${name}`,
|
||||
version,
|
||||
bin: {
|
||||
[name]: "./dist/cli.js",
|
||||
},
|
||||
scripts: {
|
||||
build: "node -e \"require('fs').writeFileSync('dist/cli.js', 'console.log(1)')\"",
|
||||
},
|
||||
dependencies: {},
|
||||
});
|
||||
}
|
||||
import { makeTempDir, writeJson, writePlugin } from "./install-test-fixtures.mjs";
|
||||
|
||||
test("#given local marketplace #when installing #then copies versioned plugins and enables config", async () => {
|
||||
const repoRoot = await makeTempDir();
|
||||
@@ -151,6 +102,35 @@ test("#given local marketplace #when installing #then copies versioned plugins a
|
||||
assert.doesNotMatch(config, /stale@debug-marketplace/);
|
||||
});
|
||||
|
||||
test("#given sisyphuslabs marketplace #when installing #then registers lazycodex git source", async () => {
|
||||
const repoRoot = await makeTempDir();
|
||||
const codexHome = await makeTempDir();
|
||||
|
||||
await mkdir(join(repoRoot, ".agents", "plugins"), { recursive: true });
|
||||
await writeJson(join(repoRoot, ".agents", "plugins", "marketplace.json"), {
|
||||
name: "sisyphuslabs",
|
||||
plugins: [{ name: "omo", source: "./plugins/omo" }],
|
||||
});
|
||||
await writePlugin(repoRoot, "omo", "0.1.0");
|
||||
|
||||
await installMarketplaceLocally({
|
||||
repoRoot,
|
||||
codexHome,
|
||||
runCommand: async () => {},
|
||||
log: () => {},
|
||||
});
|
||||
|
||||
const config = await readFile(join(codexHome, "config.toml"), "utf8");
|
||||
assert.match(config, /\[marketplaces\.sisyphuslabs\]/);
|
||||
assert.match(config, /source_type = "git"/);
|
||||
assert.match(config, /source = "https:\/\/github\.com\/code-yeongyu\/lazycodex\.git"/);
|
||||
assert.match(config, /ref = "main"/);
|
||||
assert.match(config, /\[plugins\."omo@sisyphuslabs"\]\nenabled = true/);
|
||||
assert.doesNotMatch(config, /\[marketplaces\.lazycodex\]/);
|
||||
assert.doesNotMatch(config, /code-yeongyu-codex-plugins/);
|
||||
assert.doesNotMatch(config, /source_type = "local"/);
|
||||
});
|
||||
|
||||
test("#given plugin hooks #when installing #then records trusted hook hashes", async () => {
|
||||
const repoRoot = await makeTempDir();
|
||||
const codexHome = await makeTempDir();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
export async function makeTempDir() {
|
||||
return mkdtemp(join(tmpdir(), "omo-codex-install-"));
|
||||
}
|
||||
|
||||
export async function writeJson(path, value) {
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
export async function writePlugin(root, name, version) {
|
||||
const pluginRoot = join(root, "plugins", name);
|
||||
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "dist"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "hooks"), { recursive: true });
|
||||
await mkdir(join(pluginRoot, "skills", name), { recursive: true });
|
||||
await writeJson(join(pluginRoot, ".codex-plugin", "plugin.json"), {
|
||||
name,
|
||||
version,
|
||||
description: `${name} test plugin`,
|
||||
mcpServers: "./.mcp.json",
|
||||
hooks: "./hooks/hooks.json",
|
||||
skills: "./skills/",
|
||||
});
|
||||
await writeJson(join(pluginRoot, ".mcp.json"), {
|
||||
mcpServers: {
|
||||
[name]: {
|
||||
command: "node",
|
||||
args: ["./dist/cli.js", "mcp"],
|
||||
cwd: ".",
|
||||
},
|
||||
},
|
||||
});
|
||||
await writeJson(join(pluginRoot, "hooks", "hooks.json"), { hooks: {} });
|
||||
await writeFile(join(pluginRoot, "skills", name, "SKILL.md"), "---\nname: test\n---\n");
|
||||
await writeJson(join(pluginRoot, "package.json"), {
|
||||
name: `@example/${name}`,
|
||||
version,
|
||||
bin: {
|
||||
[name]: "./dist/cli.js",
|
||||
},
|
||||
scripts: {
|
||||
build: "node -e \"require('fs').writeFileSync('dist/cli.js', 'console.log(1)')\"",
|
||||
},
|
||||
dependencies: {},
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,20 @@ import { dirname } from "node:path";
|
||||
|
||||
import { exists } from "./utils.mjs";
|
||||
|
||||
export async function updateCodexConfig({ configPath, repoRoot, marketplaceName, pluginNames, trustedHookStates = [] }) {
|
||||
const LAZYCODEX_MARKETPLACE_SOURCE = {
|
||||
sourceType: "git",
|
||||
source: "https://github.com/code-yeongyu/lazycodex.git",
|
||||
ref: "main",
|
||||
};
|
||||
|
||||
export async function updateCodexConfig({
|
||||
configPath,
|
||||
repoRoot,
|
||||
marketplaceName,
|
||||
marketplaceSource = defaultMarketplaceSource(marketplaceName, repoRoot),
|
||||
pluginNames,
|
||||
trustedHookStates = [],
|
||||
}) {
|
||||
await mkdir(dirname(configPath), { recursive: true });
|
||||
let config = "";
|
||||
if (await exists(configPath)) config = await readFile(configPath, "utf8");
|
||||
@@ -12,7 +25,7 @@ export async function updateCodexConfig({ configPath, repoRoot, marketplaceName,
|
||||
config = removeStaleMarketplaceHookStateBlocks(config, marketplaceName, new Set(pluginNames));
|
||||
config = ensureFeatureEnabled(config, "plugins");
|
||||
config = ensureFeatureEnabled(config, "plugin_hooks");
|
||||
config = ensureMarketplaceBlock(config, marketplaceName, repoRoot);
|
||||
config = ensureMarketplaceBlock(config, marketplaceName, marketplaceSource);
|
||||
for (const pluginName of pluginNames) {
|
||||
config = ensurePluginEnabled(config, `${pluginName}@${marketplaceName}`);
|
||||
}
|
||||
@@ -23,6 +36,14 @@ export async function updateCodexConfig({ configPath, repoRoot, marketplaceName,
|
||||
await writeFile(configPath, config.trimEnd() + "\n");
|
||||
}
|
||||
|
||||
function defaultMarketplaceSource(marketplaceName, repoRoot) {
|
||||
if (marketplaceName === "sisyphuslabs") return LAZYCODEX_MARKETPLACE_SOURCE;
|
||||
return {
|
||||
sourceType: "local",
|
||||
source: repoRoot,
|
||||
};
|
||||
}
|
||||
|
||||
function removeStaleMarketplacePluginBlocks(config, marketplaceName, keepPluginNames) {
|
||||
return removeTomlSections(config, (header) => {
|
||||
const pluginKey = parseQuotedPluginHeader(header);
|
||||
@@ -54,19 +75,19 @@ function ensureFeatureEnabled(config, featureName) {
|
||||
return replaceOrInsertSetting(config, section, featureName, "true");
|
||||
}
|
||||
|
||||
function ensureMarketplaceBlock(config, marketplaceName, repoRoot) {
|
||||
function ensureMarketplaceBlock(config, marketplaceName, source) {
|
||||
const header = `marketplaces.${marketplaceName}`;
|
||||
if (findTomlSection(config, header)) return config;
|
||||
return appendBlock(
|
||||
config,
|
||||
[
|
||||
`[${header}]`,
|
||||
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
|
||||
"source_type = \"local\"",
|
||||
`source = ${JSON.stringify(repoRoot)}`,
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
const block = [
|
||||
`[${header}]`,
|
||||
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
|
||||
`source_type = ${JSON.stringify(source.sourceType)}`,
|
||||
`source = ${JSON.stringify(source.source)}`,
|
||||
source.ref === undefined ? null : `ref = ${JSON.stringify(source.ref)}`,
|
||||
"",
|
||||
].filter((line) => line !== null).join("\n");
|
||||
const section = findTomlSection(config, header);
|
||||
if (section) return config.slice(0, section.start) + block + config.slice(section.end);
|
||||
return appendBlock(config, block);
|
||||
}
|
||||
|
||||
function ensurePluginEnabled(config, pluginKey) {
|
||||
|
||||
@@ -1,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 {}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { mkdir, mkdtemp, readFile, stat, writeFile } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { dirname, join } from "node:path"
|
||||
import { syncLazycodexMarketplace } from "./sync-lazycodex-marketplace"
|
||||
|
||||
async function writeJson(path: string, value: unknown): Promise<void> {
|
||||
await mkdir(dirname(path), { recursive: true })
|
||||
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`)
|
||||
}
|
||||
|
||||
async function writePluginFixture(sourceRoot: string): Promise<void> {
|
||||
await writeJson(join(sourceRoot, "packages", "omo-codex", "marketplace.json"), {
|
||||
name: "sisyphuslabs",
|
||||
plugins: [{ name: "omo", source: "./plugins/omo" }],
|
||||
})
|
||||
await writeJson(join(sourceRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"), {
|
||||
name: "omo",
|
||||
version: "1.2.3",
|
||||
})
|
||||
await writeFile(join(sourceRoot, "packages", "omo-codex", "plugin", "README.md"), "omo\n")
|
||||
await mkdir(join(sourceRoot, "packages", "omo-codex", "plugin", "node_modules", "ignored"), { recursive: true })
|
||||
await writeFile(join(sourceRoot, "packages", "omo-codex", "plugin", "node_modules", "ignored", "file.txt"), "ignored\n")
|
||||
}
|
||||
|
||||
describe("sync-lazycodex-marketplace", () => {
|
||||
test("copies the Codex marketplace manifest and clean plugin bundle", async () => {
|
||||
// given
|
||||
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-sync-source-"))
|
||||
const lazycodexRoot = await mkdtemp(join(tmpdir(), "omo-sync-lazycodex-"))
|
||||
await writePluginFixture(sourceRoot)
|
||||
|
||||
// when
|
||||
await syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })
|
||||
|
||||
// then
|
||||
const marketplace = JSON.parse(await readFile(join(lazycodexRoot, ".agents", "plugins", "marketplace.json"), "utf8"))
|
||||
expect(marketplace.name).toBe("sisyphuslabs")
|
||||
expect(marketplace.plugins[0].source).toBe("./plugins/omo")
|
||||
const manifest = JSON.parse(await readFile(join(lazycodexRoot, "plugins", "omo", ".codex-plugin", "plugin.json"), "utf8"))
|
||||
expect(manifest).toMatchObject({ name: "omo", version: "1.2.3" })
|
||||
await expect(stat(join(lazycodexRoot, "plugins", "omo", "node_modules"))).rejects.toThrow()
|
||||
})
|
||||
|
||||
test("rejects a source tree without a Codex plugin manifest", async () => {
|
||||
// given
|
||||
const sourceRoot = await mkdtemp(join(tmpdir(), "omo-sync-bad-source-"))
|
||||
const lazycodexRoot = await mkdtemp(join(tmpdir(), "omo-sync-bad-lazycodex-"))
|
||||
await writeJson(join(sourceRoot, "packages", "omo-codex", "marketplace.json"), {
|
||||
name: "sisyphuslabs",
|
||||
plugins: [{ name: "omo", source: "./plugins/omo" }],
|
||||
})
|
||||
|
||||
// when / then
|
||||
await expect(syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })).rejects.toThrow("missing Codex plugin manifest")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,101 @@
|
||||
import { cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises"
|
||||
import { dirname, join, resolve, sep } from "node:path"
|
||||
|
||||
const MARKETPLACE_SOURCE_PATH = join("packages", "omo-codex", "marketplace.json")
|
||||
const PLUGIN_SOURCE_PATH = join("packages", "omo-codex", "plugin")
|
||||
const MARKETPLACE_DESTINATION_PATH = join(".agents", "plugins", "marketplace.json")
|
||||
const PLUGIN_DESTINATION_PATH = join("plugins", "omo")
|
||||
|
||||
export interface SyncLazycodexMarketplaceInput {
|
||||
readonly sourceRoot: string
|
||||
readonly lazycodexRoot: string
|
||||
}
|
||||
|
||||
interface MarketplaceManifest {
|
||||
readonly name: string
|
||||
}
|
||||
|
||||
interface PluginManifest {
|
||||
readonly name: string
|
||||
readonly version?: string
|
||||
}
|
||||
|
||||
export async function syncLazycodexMarketplace(input: SyncLazycodexMarketplaceInput): Promise<void> {
|
||||
const sourceRoot = resolve(input.sourceRoot)
|
||||
const lazycodexRoot = resolve(input.lazycodexRoot)
|
||||
const marketplacePath = join(sourceRoot, MARKETPLACE_SOURCE_PATH)
|
||||
const pluginRoot = join(sourceRoot, PLUGIN_SOURCE_PATH)
|
||||
const pluginManifestPath = join(pluginRoot, ".codex-plugin", "plugin.json")
|
||||
|
||||
const marketplace = await readMarketplaceManifest(marketplacePath)
|
||||
if (marketplace.name !== "sisyphuslabs") {
|
||||
throw new Error(`Sisyphus Labs marketplace manifest must be named sisyphuslabs, got ${marketplace.name}`)
|
||||
}
|
||||
|
||||
const pluginManifest = await readPluginManifest(pluginManifestPath)
|
||||
if (pluginManifest.name !== "omo") {
|
||||
throw new Error(`Sisyphus Labs plugin manifest must be named omo, got ${pluginManifest.name}`)
|
||||
}
|
||||
|
||||
const destinationMarketplacePath = join(lazycodexRoot, MARKETPLACE_DESTINATION_PATH)
|
||||
await mkdir(dirname(destinationMarketplacePath), { recursive: true })
|
||||
await writeFile(destinationMarketplacePath, await readFile(marketplacePath, "utf8"))
|
||||
|
||||
const destinationPluginRoot = join(lazycodexRoot, PLUGIN_DESTINATION_PATH)
|
||||
await rm(destinationPluginRoot, { recursive: true, force: true })
|
||||
await mkdir(dirname(destinationPluginRoot), { recursive: true })
|
||||
await cp(pluginRoot, destinationPluginRoot, {
|
||||
recursive: true,
|
||||
filter: (path) => shouldCopyPluginPath(path, pluginRoot),
|
||||
})
|
||||
}
|
||||
|
||||
async function readMarketplaceManifest(path: string): Promise<MarketplaceManifest> {
|
||||
const parsed = JSON.parse(await readFile(path, "utf8"))
|
||||
if (isRecord(parsed) && typeof parsed.name === "string") {
|
||||
return { name: parsed.name }
|
||||
}
|
||||
throw new Error("invalid Sisyphus Labs marketplace manifest")
|
||||
}
|
||||
|
||||
async function readPluginManifest(path: string): Promise<PluginManifest> {
|
||||
if (!(await isFile(path))) {
|
||||
throw new Error(`missing Codex plugin manifest at ${path}`)
|
||||
}
|
||||
const parsed = JSON.parse(await readFile(path, "utf8"))
|
||||
if (isRecord(parsed) && typeof parsed.name === "string") {
|
||||
return {
|
||||
name: parsed.name,
|
||||
version: typeof parsed.version === "string" ? parsed.version : undefined,
|
||||
}
|
||||
}
|
||||
throw new Error("invalid Codex plugin manifest")
|
||||
}
|
||||
|
||||
async function isFile(path: string): Promise<boolean> {
|
||||
try {
|
||||
return (await stat(path)).isFile()
|
||||
} catch (error) {
|
||||
if (error instanceof Error) return false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function shouldCopyPluginPath(path: string, root: string): boolean {
|
||||
const relative = path === root ? "" : path.slice(root.length + sep.length)
|
||||
if (relative.length === 0) return true
|
||||
return !relative.split(sep).some((part) => part === ".git" || part === "node_modules")
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const sourceRoot = process.argv[2] ?? process.cwd()
|
||||
const lazycodexRoot = process.argv[3]
|
||||
if (lazycodexRoot === undefined) {
|
||||
throw new Error("Usage: bun run script/sync-lazycodex-marketplace.ts <source-root> <lazycodex-root>")
|
||||
}
|
||||
await syncLazycodexMarketplace({ sourceRoot, lazycodexRoot })
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -138,7 +138,7 @@ describe("runCliInstaller", () => {
|
||||
installed: [],
|
||||
configPath: "/tmp/codex-config.toml",
|
||||
codexHome: "/tmp/codex-home",
|
||||
marketplaceName: "code-yeongyu-codex-plugins",
|
||||
marketplaceName: "sisyphuslabs",
|
||||
})
|
||||
|
||||
const args: InstallArgs = {
|
||||
|
||||
@@ -14,16 +14,26 @@ describe("codex-config-toml", () => {
|
||||
await updateCodexConfig({
|
||||
configPath,
|
||||
repoRoot: "/repo/packages/omo-codex",
|
||||
marketplaceName: "code-yeongyu-codex-plugins",
|
||||
marketplaceName: "sisyphuslabs",
|
||||
marketplaceSource: {
|
||||
sourceType: "git",
|
||||
source: "https://github.com/code-yeongyu/lazycodex.git",
|
||||
ref: "main",
|
||||
},
|
||||
pluginNames: ["omo"],
|
||||
trustedHookStates: [{ key: "omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
|
||||
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
|
||||
})
|
||||
await updateCodexConfig({
|
||||
configPath,
|
||||
repoRoot: "/repo/packages/omo-codex",
|
||||
marketplaceName: "code-yeongyu-codex-plugins",
|
||||
marketplaceName: "sisyphuslabs",
|
||||
marketplaceSource: {
|
||||
sourceType: "git",
|
||||
source: "https://github.com/code-yeongyu/lazycodex.git",
|
||||
ref: "main",
|
||||
},
|
||||
pluginNames: ["omo"],
|
||||
trustedHookStates: [{ key: "omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
|
||||
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:abc" }],
|
||||
})
|
||||
|
||||
// then
|
||||
@@ -31,8 +41,14 @@ describe("codex-config-toml", () => {
|
||||
expect(content).toContain("[features]")
|
||||
expect(content).toContain("plugins = true")
|
||||
expect(content).toContain("plugin_hooks = true")
|
||||
expect(content).toContain("[marketplaces.code-yeongyu-codex-plugins]")
|
||||
expect(content).toContain("[plugins.\"omo@code-yeongyu-codex-plugins\"]")
|
||||
expect(content).toContain("[hooks.state.\"omo@code-yeongyu-codex-plugins:hooks/hooks.json:post_tool_use:0:0\"]")
|
||||
expect(content).toContain("[marketplaces.sisyphuslabs]")
|
||||
expect(content).toContain('source_type = "git"')
|
||||
expect(content).toContain('source = "https://github.com/code-yeongyu/lazycodex.git"')
|
||||
expect(content).toContain('ref = "main"')
|
||||
expect(content).toContain("[plugins.\"omo@sisyphuslabs\"]")
|
||||
expect(content).toContain("[hooks.state.\"omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0\"]")
|
||||
expect(content).not.toContain("[marketplaces.lazycodex]")
|
||||
expect(content).not.toContain("code-yeongyu-codex-plugins")
|
||||
expect(content).not.toContain('source_type = "local"')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises"
|
||||
import { dirname } from "node:path"
|
||||
import type { TrustedHookState } from "./types"
|
||||
import type { CodexMarketplaceSource, TrustedHookState } from "./types"
|
||||
|
||||
export async function updateCodexConfig(input: {
|
||||
readonly configPath: string
|
||||
readonly repoRoot: string
|
||||
readonly marketplaceName: string
|
||||
readonly marketplaceSource: CodexMarketplaceSource
|
||||
readonly pluginNames: readonly string[]
|
||||
readonly trustedHookStates?: readonly TrustedHookState[]
|
||||
}): Promise<void> {
|
||||
@@ -18,7 +19,7 @@ export async function updateCodexConfig(input: {
|
||||
config = removeStaleMarketplaceHookStateBlocks(config, input.marketplaceName, pluginSet)
|
||||
config = ensureFeatureEnabled(config, "plugins")
|
||||
config = ensureFeatureEnabled(config, "plugin_hooks")
|
||||
config = ensureMarketplaceBlock(config, input.marketplaceName, input.repoRoot)
|
||||
config = ensureMarketplaceBlock(config, input.marketplaceName, input.marketplaceSource)
|
||||
for (const pluginName of input.pluginNames) {
|
||||
config = ensurePluginEnabled(config, `${pluginName}@${input.marketplaceName}`)
|
||||
}
|
||||
@@ -60,18 +61,21 @@ function ensureFeatureEnabled(config: string, featureName: string): string {
|
||||
return replaceOrInsertSetting(config, section, featureName, "true")
|
||||
}
|
||||
|
||||
function ensureMarketplaceBlock(config: string, marketplaceName: string, repoRoot: string): string {
|
||||
function ensureMarketplaceBlock(config: string, marketplaceName: string, source: CodexMarketplaceSource): string {
|
||||
const header = `marketplaces.${marketplaceName}`
|
||||
if (findTomlSection(config, header)) return config
|
||||
const block = [
|
||||
`[${header}]`,
|
||||
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
|
||||
`source_type = ${JSON.stringify(source.sourceType)}`,
|
||||
`source = ${JSON.stringify(source.source)}`,
|
||||
`ref = ${JSON.stringify(source.ref)}`,
|
||||
"",
|
||||
].join("\n")
|
||||
const section = findTomlSection(config, header)
|
||||
if (section) return config.slice(0, section.start) + block + config.slice(section.end)
|
||||
return appendBlock(
|
||||
config,
|
||||
[
|
||||
`[${header}]`,
|
||||
`last_updated = "${new Date().toISOString().replace(/\.\d{3}Z$/, "Z")}"`,
|
||||
'source_type = "local"',
|
||||
`source = ${JSON.stringify(repoRoot)}`,
|
||||
"",
|
||||
].join("\n"),
|
||||
block,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("codex-hook-trust", () => {
|
||||
|
||||
// when
|
||||
const states = await trustedHookStatesForPlugin({
|
||||
marketplaceName: "code-yeongyu-codex-plugins",
|
||||
marketplaceName: "sisyphuslabs",
|
||||
pluginName: "omo",
|
||||
pluginRoot,
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("codex-marketplace", () => {
|
||||
const pkgRoot = join(root, "packages", "omo-codex")
|
||||
const pluginRoot = join(pkgRoot, "plugin", ".codex-plugin")
|
||||
await mkdir(pluginRoot, { recursive: true })
|
||||
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "code-yeongyu-codex-plugins", plugins: [{ name: "omo", source: "./plugins/omo" }] }))
|
||||
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "sisyphuslabs", plugins: [{ name: "omo", source: "./plugins/omo" }] }))
|
||||
await writeFile(join(pluginRoot, "plugin.json"), JSON.stringify({ name: "omo", version: "0.1.0" }))
|
||||
|
||||
// when
|
||||
@@ -20,7 +20,7 @@ describe("codex-marketplace", () => {
|
||||
const manifest = await readPluginManifest(sourcePath)
|
||||
|
||||
// then
|
||||
expect(marketplace.name).toBe("code-yeongyu-codex-plugins")
|
||||
expect(marketplace.name).toBe("sisyphuslabs")
|
||||
expect(sourcePath).toBe(join(pkgRoot, "plugin"))
|
||||
expect(manifest.name).toBe("omo")
|
||||
})
|
||||
@@ -30,7 +30,7 @@ describe("codex-marketplace", () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "omo-codex-marketplace-"))
|
||||
const pkgRoot = join(root, "packages", "omo-codex")
|
||||
await mkdir(pkgRoot, { recursive: true })
|
||||
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "code-yeongyu-codex-plugins", plugins: [{ name: "omo", source: "./../escape" }] }))
|
||||
await writeFile(join(pkgRoot, "marketplace.json"), JSON.stringify({ name: "sisyphuslabs", plugins: [{ name: "omo", source: "./../escape" }] }))
|
||||
|
||||
// when
|
||||
const action = readMarketplace(root)
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -9,6 +9,12 @@ import { readMarketplace, readPluginManifest, resolvePluginSource, validatePathS
|
||||
import { defaultRunCommand } from "./codex-process"
|
||||
import type { CodexInstallOptions, CodexInstallResult, InstalledPlugin } from "./types"
|
||||
|
||||
const LAZYCODEX_MARKETPLACE_SOURCE = {
|
||||
sourceType: "git",
|
||||
source: "https://github.com/code-yeongyu/lazycodex.git",
|
||||
ref: "main",
|
||||
} as const
|
||||
|
||||
export async function runCodexInstaller(options: CodexInstallOptions = {}): Promise<CodexInstallResult> {
|
||||
const repoRoot = resolve(options.repoRoot ?? findRepoRootFromImporter(import.meta.dir))
|
||||
const codexHome = resolve(options.codexHome ?? process.env.CODEX_HOME ?? join(homedir(), ".codex"))
|
||||
@@ -78,6 +84,7 @@ export async function runCodexInstaller(options: CodexInstallOptions = {}): Prom
|
||||
configPath,
|
||||
repoRoot: codexPackageRoot,
|
||||
marketplaceName: marketplace.name,
|
||||
marketplaceSource: LAZYCODEX_MARKETPLACE_SOURCE,
|
||||
pluginNames: marketplace.plugins.map((plugin) => plugin.name),
|
||||
trustedHookStates,
|
||||
})
|
||||
|
||||
@@ -30,6 +30,12 @@ export interface TrustedHookState {
|
||||
readonly trustedHash: string
|
||||
}
|
||||
|
||||
export interface CodexMarketplaceSource {
|
||||
readonly sourceType: "git"
|
||||
readonly source: string
|
||||
readonly ref: string
|
||||
}
|
||||
|
||||
export interface CommandRunOptions {
|
||||
readonly cwd: string
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { resolveInstallArgs } from "./cli-program"
|
||||
|
||||
describe("install platform resolution", () => {
|
||||
@@ -85,7 +83,7 @@ describe("install platform resolution", () => {
|
||||
|
||||
test("defines Commander choices so invalid --platform values are rejected", async () => {
|
||||
// given
|
||||
const cliProgramSource = await readFile(path.resolve(import.meta.dir, "cli-program.ts"), "utf-8")
|
||||
const cliProgramSource = await Bun.file(new URL("./cli-program.ts", import.meta.url)).text()
|
||||
|
||||
// when
|
||||
const installBlock = cliProgramSource.match(/program\s*\n\s*\.command\("install"\)([\s\S]*?)\.action\(/)
|
||||
|
||||
Reference in New Issue
Block a user