Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<h1>LazyCodex</h1>

<p><strong>The one and only agent harness for complex codebases.</strong><br />
Project memory, planning, execution, and verified completion inside Codex.</p>
Project memory, planning, execution, and verified completion across Codex, Claude Code, and Gemini from one installer.</p>

<p>
<a href="https://github.com/code-yeongyu/lazycodex/stargazers">
Expand All @@ -28,7 +28,7 @@
> [!NOTE]
> **[OmO] 60K Stars: the terrifying token burner has arrived in LazyCodex.**
>
> Sisyphus Labs' OmO is the quality-obsessed agent harness whose public lore says it loved Anthropic models hard enough to get third-party clients blocked. Now that same OmO quality bar is available for Codex through LazyCodex.
> Sisyphus Labs' OmO is the quality-obsessed agent harness whose public lore says it loved Anthropic models hard enough to get third-party clients blocked. Now that same OmO quality bar is available through LazyCodex across Codex, Claude Code, and Gemini in one pass.
>
> If you wanted OmO but did not want the setup ceremony, start here:
>
Expand All @@ -46,8 +46,16 @@ One line. No global install, no `npm i -g`. Always use `npx`:
npx lazycodex-ai install
```

This is shorthand for `npx --yes --package oh-my-openagent omo install --platform=codex`. For a fully autonomous, no-TUI setup:
By default this runs OmO install for all supported targets: Codex, Claude Code,
and Gemini. To install only one surface, pass an explicit platform:

```bash
npx lazycodex-ai install --platform=codex
npx lazycodex-ai install --platform=claude-code
npx lazycodex-ai install --platform=gemini
```

For a fully autonomous, no-TUI setup:
```bash
npx lazycodex-ai install --no-tui --codex-autonomous
```
Expand Down Expand Up @@ -87,7 +95,7 @@ npx lazycodex-ai doctor
```

`doctor` prints the installation health report: plugin cache, hooks, MCP
servers, agents, and config state. Inside Codex, type `$` in the composer to
servers, agents, and config state. Inside the target agent, check the command menu or CLI shortcuts to
browse every installed skill — `init-deep`, `ulw-loop`, `ulw-plan`,
`start-work`, and the rest — and hooks announce themselves with
`LazyCodex(<version>): ...` status messages during a session.
Expand All @@ -98,13 +106,13 @@ browse every installed skill — `init-deep`, `ulw-loop`, `ulw-plan`,
npx lazycodex-ai uninstall
```

Removes the installed plugin cache, bin links, agent roles, and the managed
sections of `~/.codex/config.toml`.
Removes the installed plugin cache, bin links, agent roles, and managed
sections of the target agent config.

## ⚡ Commands

LazyCodex installs these as OmO commands for Codex. Invoke them with the
`$command` syntax shown by the installer.
LazyCodex installs these as OmO commands for the target agent. Invoke them with
the `$command` syntax shown by the installer.

| Command | Type this | What it does |
| --- | --- | --- |
Expand All @@ -117,15 +125,15 @@ Full documentation lives at [lazycodex.ai/docs](https://lazycodex.ai/docs).
## Use the built-in workflows

LazyCodex should be judged by the features it actually installs. It is the
Codex distribution for OmO's agent harness: project memory, planning,
light distribution for OmO's agent harness: project memory, planning,
execution, verified completion, skills, hooks, model routing, and diagnostics.

### 1. `$init-deep` creates project memory

`$init-deep` generates hierarchical `AGENTS.md` context. It scores complex
directories, writes local guidance near the code that needs it, and gives future
agents landmarks before they edit. Type `$init-deep` in the Codex composer
the `$` prefix is how every installed skill is invoked.
agents landmarks before they edit. Type `$init-deep` in the target agent
the `$` prefix is how every installed skill is invoked when the surface exposes command syntax.

Use it when the repository is too large to explain from memory. Run it again
when the shape of the codebase changes.
Expand Down Expand Up @@ -161,20 +169,19 @@ actual work:
| `rules` | Project instructions from AGENTS, rules, and instruction files |
| `comment-checker` | Feedback after edit-like operations |

### 4. Sub-agent roles ride Codex's native multi-agent tools
### 4. Sub-agent roles ride the target agent's multi-agent tools

LazyCodex installs selectable agent roles into `~/.codex/agents/`: `explorer`,
`librarian`, `plan`, `momus`, `metis`, and `codex-ultrawork-reviewer`. Pick one
by passing `agent_type` to Codex's `spawn_agent` tool — the child agent runs
with that role's model and instructions:
LazyCodex installs selectable agent roles for surfaces that expose sub-agent role
selection: `explorer`, `librarian`, `plan`, `momus`, `metis`, and
`lazycodex-gate-reviewer`. Pick one through the target agent's sub-agent tool
when that field is available:

```jsonc
spawn_agent({"message": "TASK: map the auth flow end to end.", "agent_type": "explorer"})
```

The installer exposes `agent_type` on `multi_agent_v2` sessions (Codex hides it
by default). If your Codex build's spawn tool has no `agent_type` parameter,
describe the role inside `message` instead — the skills are written to fall
If your target agent's spawn tool has no typed role parameter,
describe the role inside the task message instead — the skills are written to fall
back to that form automatically.

Start at [https://lazycodex.ai](https://lazycodex.ai).
Expand All @@ -183,15 +190,15 @@ Start at [https://lazycodex.ai](https://lazycodex.ai).

## 💤 What is this?

**LazyCodex** packages [OmO (oh-my-openagent)](https://github.com/code-yeongyu/oh-my-openagent) as the Codex agent harness for complex codebases.
**LazyCodex** packages [OmO (oh-my-openagent)](https://github.com/code-yeongyu/oh-my-openagent) as a light agent harness for complex codebases across Codex, Claude Code, and Gemini.

Think [LazyVim](https://github.com/LazyVim/LazyVim) for [lazy.nvim](https://github.com/folke/lazy.nvim), but for Codex.
Think [LazyVim](https://github.com/LazyVim/LazyVim) for [lazy.nvim](https://github.com/folke/lazy.nvim), but for coding-agent harness setup.

OmO is the agent harness: discipline agents, parallel orchestration, multi-model routing, skills, hooks, and verified completion. LazyCodex packages that harness for Codex.
OmO is the agent harness: discipline agents, parallel orchestration, multi-model routing, skills, hooks, and verified completion. LazyCodex packages the Hephaestus slice so the same setup can be installed into Codex, Claude Code, and Gemini without hand-wiring each surface.

> _"LazyVim made Neovim usable for the rest of us. LazyCodex does the same for Codex."_
> _"LazyVim made Neovim usable for the rest of us. LazyCodex does the same for agent harness setup."_

Credit: The LazyCodex name idea is inspired by [LazyVim](https://github.com/LazyVim/LazyVim). The Ultragoal and UltraQA ideas are inspired by [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex), reimplemented from concept for this Codex setup.
Credit: The LazyCodex name idea is inspired by [LazyVim](https://github.com/LazyVim/LazyVim). The Ultragoal and UltraQA ideas are inspired by [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex), reimplemented from concept for this setup.

## 🧩 What you get

Expand Down
100 changes: 81 additions & 19 deletions bin/lazycodex-ai.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,93 @@ import { spawnSync } from "node:child_process"
const args = process.argv.slice(2)
const dryRun = args[0] === "--dry-run"
const forwardedArgs = dryRun ? args.slice(1) : args
const commandArgs =
const DEFAULT_PLATFORMS = ["codex", "claude-code", "gemini"]
const ALL_PLATFORM_ALIASES = new Set(["all", "multi", "all-platforms", "*"])
const CODEX_ONLY_ARGS = new Set(["--codex-autonomous"])

function parseInstallArgs(values) {
const platforms = []
const passthrough = []
let allPlatforms = false

for (let i = 0; i < values.length; i += 1) {
const arg = values[i]
if (arg === "--all-platforms") {
allPlatforms = true
continue
}
if (arg === "--platform") {
const value = values[i + 1]
if (!value) {
passthrough.push(arg)
continue
}
i += 1
if (ALL_PLATFORM_ALIASES.has(value)) {
allPlatforms = true
} else {
platforms.push(value)
}
continue
}
if (arg.startsWith("--platform=")) {
const value = arg.slice("--platform=".length)
if (ALL_PLATFORM_ALIASES.has(value)) {
allPlatforms = true
} else {
platforms.push(value)
}
continue
}
passthrough.push(arg)
}

const targets = allPlatforms || platforms.length === 0 ? DEFAULT_PLATFORMS : [...new Set(platforms)]
return { targets, passthrough }
}

function argsForPlatform(platform, passthrough) {
const safeArgs = platform === "codex" ? passthrough : passthrough.filter((arg) => !CODEX_ONLY_ARGS.has(arg))
return [
"--yes",
"--package",
"oh-my-openagent",
"omo",
"install",
`--platform=${platform}`,
...safeArgs,
]
}

function buildInstallCommands(values) {
const { targets, passthrough } = parseInstallArgs(values)
return targets.map((platform) => argsForPlatform(platform, passthrough))
}

const installArgs = forwardedArgs.slice(1)
const commands =
forwardedArgs[0] === "install"
? [
"--yes",
"--package",
"oh-my-openagent",
"omo",
"install",
"--platform=codex",
...forwardedArgs.slice(1),
]
: ["--yes", "--package", "oh-my-openagent", "omo", ...forwardedArgs]
? buildInstallCommands(installArgs)
: [["--yes", "--package", "oh-my-openagent", "omo", ...forwardedArgs]]

if (dryRun) {
console.log(["npx", ...commandArgs].join(" "))
console.log(commands.map((commandArgs) => ["npx", ...commandArgs].join(" ")).join("\n"))
process.exit(0)
}

const result = spawnSync("npx", commandArgs, {
stdio: "inherit",
})
for (const commandArgs of commands) {
const result = spawnSync("npx", commandArgs, {
stdio: "inherit",
})

if (result.error) {
console.error(result.error.message)
process.exit(1)
}

if (result.error) {
console.error(result.error.message)
process.exit(1)
if ((result.status ?? 1) !== 0) {
process.exit(result.status ?? 1)
}
}

process.exit(result.status ?? 1)
process.exit(0)
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "lazycodex-ai",
"version": "0.2.2",
"description": "Codex install alias for oh-my-openagent. Run `npx lazycodex-ai install` to set up the Codex platform.",
"description": "OmO installer for Codex, Claude Code, and Gemini agent harness targets.",
"type": "module",
"bin": {
"lazycodex-ai": "bin/lazycodex-ai.js"
Expand All @@ -17,6 +17,8 @@
},
"keywords": [
"codex",
"claude-code",
"gemini",
"oh-my-openagent",
"ai-agents",
"orchestration"
Expand Down
63 changes: 61 additions & 2 deletions test/lazycodex-ai-bin.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("lazycodex-ai npm package", () => {
assert.match(publishWorkflow, new RegExp(`default: "${releaseVersion}"`))
})

it("dry-runs install through oh-my-openagent with the Codex platform default", () => {
it("dry-runs install through oh-my-openagent for Codex, Claude Code, and Gemini by default", () => {
// given
assert.equal(existsSync(binPath), true, "lazycodex-ai bin must exist")

Expand All @@ -55,12 +55,71 @@ describe("lazycodex-ai npm package", () => {
{ cwd: root, encoding: "utf8" },
)

// then
assert.equal(result.status, 0, result.stderr)
assert.deepEqual(result.stdout.trim().split("\n"), [
"npx --yes --package oh-my-openagent omo install --platform=codex --no-tui --codex-autonomous",
"npx --yes --package oh-my-openagent omo install --platform=claude-code --no-tui",
"npx --yes --package oh-my-openagent omo install --platform=gemini --no-tui",
])
})

it("preserves explicit install platform targets", () => {
// given
assert.equal(existsSync(binPath), true, "lazycodex-ai bin must exist")

// when
const result = spawnSync(
process.execPath,
[binPath, "--dry-run", "install", "--platform=claude-code", "--no-tui"],
{ cwd: root, encoding: "utf8" },
)

// then
assert.equal(result.status, 0, result.stderr)
assert.equal(
result.stdout.trim(),
"npx --yes --package oh-my-openagent omo install --platform=codex --no-tui --codex-autonomous",
"npx --yes --package oh-my-openagent omo install --platform=claude-code --no-tui",
)
})

it("preserves explicit install platform targets passed as a separate value", () => {
// given
assert.equal(existsSync(binPath), true, "lazycodex-ai bin must exist")

// when
const result = spawnSync(
process.execPath,
[binPath, "--dry-run", "install", "--platform", "gemini", "--no-tui"],
{ cwd: root, encoding: "utf8" },
)

// then
assert.equal(result.status, 0, result.stderr)
assert.equal(
result.stdout.trim(),
"npx --yes --package oh-my-openagent omo install --platform=gemini --no-tui",
)
})

it("expands --platform=all into every supported platform target", () => {
// given
assert.equal(existsSync(binPath), true, "lazycodex-ai bin must exist")

// when
const result = spawnSync(
process.execPath,
[binPath, "--dry-run", "install", "--platform=all", "--no-tui"],
{ cwd: root, encoding: "utf8" },
)

// then
assert.equal(result.status, 0, result.stderr)
assert.deepEqual(result.stdout.trim().split("\n"), [
"npx --yes --package oh-my-openagent omo install --platform=codex --no-tui",
"npx --yes --package oh-my-openagent omo install --platform=claude-code --no-tui",
"npx --yes --package oh-my-openagent omo install --platform=gemini --no-tui",
])
})

it("dry-runs non-install commands through oh-my-openagent", () => {
Expand Down