| name | codex-plusplus-tweak-system |
| description | Expert knowledge for using codex-plusplus to inject tweaks, patch Codex desktop app, and write custom ESM tweak modules with lifecycle APIs. |
| triggers | ["install codex plusplus","write a tweak for codex","patch codex app with tweaks","add custom features to codex desktop","codex plusplus tweak development","how to create a codex tweak","codex plusplus install and setup","inject custom code into codex app"] |
codex-plusplus Tweak System
Skill by ara.so — Daily 2026 Skills collection.
codex-plusplus is a tweak injection system for the Codex desktop app (Electron-based). It patches app.asar, injects a loader stub, and runs a hot-reloadable runtime from the user directory. Tweaks are small ESM modules with a manifest and start/stop lifecycle — no app rebuild required.
Installation
Homebrew (macOS, recommended)
brew install b-nnett/codex-plusplus/codexplusplus
codexplusplus install
Bun (global)
bun install -g github:b-nnett/codex-plusplus
codexplusplus install
Source bootstrap (macOS / Linux)
curl -fsSL https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.sh | bash
Windows PowerShell
irm https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.ps1 | iex
The installer:
- Locates
Codex.app (or Windows equivalent)
- Backs up the original to
~/.codex-plusplus/backup/
- Patches
app.asar to require the loader
- Recomputes asar header SHA-256, writes it into
Info.plist
- Flips
EnableEmbeddedAsarIntegrityValidation in the Electron Framework binary
- Re-signs ad-hoc on macOS (
codesign --force --deep --sign -)
- Installs a launch agent / login item for auto-repair on Codex updates
- Installs default tweaks unless
--no-default-tweaks is passed
Key CLI Commands
codexplusplus install
codexplusplus install --no-default-tweaks
codexplusplus status
codexplusplus doctor
codexplusplus repair
codexplusplus repair --quiet
codexplusplus update
codexplusplus update-codex
codexplusplus uninstall
codexplusplus tweaks list
codexplusplus tweaks open
File Locations
| Artifact | Path |
|---|
| Loader stub | Codex.app/Contents/Resources/app.asar |
| Runtime | <user-data-dir>/runtime/ |
| Tweaks | <user-data-dir>/tweaks/ |
| Config | <user-data-dir>/config.json |
| Backup | <user-data-dir>/backup/ |
<user-data-dir> per OS:
- macOS:
~/Library/Application Support/codex-plusplus/
- Linux:
$XDG_DATA_HOME/codex-plusplus/ (default ~/.local/share/codex-plusplus/)
- Windows:
%APPDATA%/codex-plusplus/
Writing a Tweak
Folder Structure
<user-data-dir>/tweaks/my-tweak/
├── manifest.json
└── index.ts # or .js / .mjs
manifest.json
{
"id": "com.yourname.my-tweak",
"name": "My Tweak",
"version": "0.1.0",
"githubRepo": "yourname/my-tweak",
"author": "yourname",
"description": "Short description of what this tweak does.",
"minRuntime": "0.1.0"
}
Required fields: id (reverse-domain), name, version, githubRepo (for update checks), author, description, minRuntime.
index.ts — Minimal Tweak
import type { Tweak } from "@codex-plusplus/sdk";
export default {
start(api) {
api.log.info("My tweak started");
},
stop() {
},
} satisfies Tweak;
index.ts — Settings Panel
import type { Tweak } from "@codex-plusplus/sdk";
export default {
start(api) {
api.settings.register({
id: "my-tweak",
title: "My Tweak",
render(root) {
root.innerHTML = `
<div style="padding: 16px;">
<h2>My Tweak Settings</h2>
<label>
<input type="checkbox" id="my-tweak-toggle" />
Enable feature
</label>
</div>
`;
const toggle = root.querySelector<HTMLInputElement>("#my-tweak-toggle")!;
toggle.checked = api.storage.get("enabled") ?? false;
toggle.addEventListener("change", () => {
api.storage.set("enabled", toggle.checked);
});
},
});
},
stop() {},
} satisfies Tweak;
index.ts — DOM Injection with Cleanup
import type { Tweak } from "@codex-plusplus/sdk";
let cleanup: (() => void) | null = null;
export default {
start(api) {
const unobserve = api.dom.waitFor(".codex-toolbar", (toolbar) => {
const btn = document.createElement("button");
btn.textContent = "My Action";
btn.className = "codex-plusplus-btn";
btn.addEventListener("click", () => {
api.log.info("Button clicked");
});
toolbar.appendChild(btn);
cleanup = () => btn.remove();
});
api.onStop(() => {
unobserve();
cleanup?.();
});
},
stop() {},
} satisfies Tweak;
index.ts — Keyboard Shortcut
import type { Tweak } from "@codex-plusplus/sdk";
export default {
start(api) {
const handler = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "k") {
e.preventDefault();
api.log.info("Shortcut triggered: Cmd/Ctrl+Shift+K");
}
};
document.addEventListener("keydown", handler);
api.onStop(() => document.removeEventListener("keydown", handler));
},
stop() {},
} satisfies Tweak;
Tweak API Reference (api object in start)
| API | Description |
|---|
api.log.info(msg) | Log to Codex++ console |
api.log.warn(msg) | Warning log |
api.log.error(msg) | Error log |
api.settings.register({ id, title, render }) | Add a panel under Settings → Tweaks |
api.storage.get(key) | Read persisted value for this tweak |
api.storage.set(key, value) | Persist value (JSON-serializable) |
api.dom.waitFor(selector, cb) | Watch for a DOM element; returns unobserve fn |
api.onStop(fn) | Register a cleanup callback called on stop() |
Config (config.json)
{
"autoRepair": true,
"autoUpdateRuntime": true,
"tweaks": {
"com.yourname.my-tweak": {
"enabled": true
}
}
}
autoRepair — watcher re-patches after Codex updates (default true)
autoUpdateRuntime — daily runtime refresh from CLI (default true); disable in Settings → Codex Plus Plus → Config
Tweak Update Checks
Every tweak with githubRepo set is checked against GitHub Releases once per day. Codex++ compares the latest release tag (semver) to the local manifest.json version.
- No auto-update — users see "Update Available" in Settings → Tweaks with a link to the release.
- To publish a tweak update: create a GitHub Release with a semver tag (e.g.
v0.2.0) and attach the tweak folder as a zip.
Default Tweaks
Installed on first run (skip with --no-default-tweaks):
| ID | Repo |
|---|
co.bennett.custom-keyboard-shortcuts | b-nnett/codex-plusplus-keyboard-shortcuts |
co.bennett.ui-improvements | b-nnett/codex-plusplus-bennett-ui |
Updating Codex (macOS — Sparkle conflict)
Codex++ ad-hoc signs the app, breaking Sparkle's integrity check. Use:
codexplusplus update-codex
This restores an official Developer ID–signed build for the updater. After Codex updates and restarts, the launch agent watcher re-applies Codex++ automatically.
Uninstall
codexplusplus uninstall
Restores the backed-up original app.asar, removes the launch agent, and removes <user-data-dir>. Tweaks you wrote remain on disk unless you delete them manually.
Troubleshooting
"App is damaged and can't be opened" (macOS)
The ad-hoc signature isn't trusted on first launch. Run:
xattr -cr /Applications/Codex.app
Then open the app again.
Patch not persisting after Codex update
The launch agent should re-run repair automatically. Check status:
codexplusplus status
codexplusplus doctor
If the launch agent isn't running, reinstall:
codexplusplus uninstall && codexplusplus install
Tweak not loading
- Check
manifest.json has all required fields (id, name, version, githubRepo, author, description, minRuntime).
- Ensure
index.js / index.ts exports a default object with start and stop.
- Check logs:
api.log.error output appears in Codex DevTools console.
- Run
codexplusplus doctor for runtime integrity checks.
Runtime version mismatch
codexplusplus repair
Tweaks directory location
codexplusplus tweaks open
Security Model
- Tweaks run in the renderer process with the same privileges as Codex's own renderer code.
- Codex++ does not auto-update tweak code — users must manually review and install updates.
githubRepo is only used for version checks via the GitHub Releases API, not for auto-downloading code.
- See
SECURITY.md for the full policy and vulnerability reporting process.
Community