feat: add Codex CLI as a first-class fourth platform (#35)

Rebuilt from scratch following Codex CLI's actual architecture (as
outlined in the review on PR #26). Closes the integration gap with a
proper build adapter, correct install paths, TOML agent files, and
all the architectural differences documented and tested.

What changed vs the previous attempt (PR #26):

- Agents: build adapter generates .toml files (name/description/
  developer_instructions) into dist/codex-cli/.codex/agents/ instead
  of copying .md files with sed transforms
- Skills: installed to .agents/skills/ (correct Codex discovery path)
  instead of .codex/skills/
- Dispatcher: AGENTS.md uses a root-context orchestration header that
  works within agents.max_depth=1 constraints; named-agent routing
  replaced with embedded-instructions workaround for the known
  spawn_agents limitation (openai/codex#15250)
- Tool compat: AskUserQuestion and request_user_input removed; all
  prompts adapted to Codex's actual tool set and approval/confirmation
  flow
- Installer/updater: launchme.sh --platform codex-cli and updateme.sh
  with Codex auto-detection, creating the correct split layout
  (AGENTS.md + .codex/agents/ + .codex/config.toml + .agents/skills/)
- Tests: new per-adapter test suite (tests/adapters/codex-cli/),
  install/update smoke (tests/scripts/codex-cli-install.test.sh), and
  a four-platform parity gate that proves Codex changes do not regress
  Claude Code, Gemini CLI, or OpenCode
- Docs: new codex-cli.md guide, codex-migration.md for users switching
  from other platforms, and README/getting-started/examples updated for
  four-platform positioning
- Bash harness: .gitattributes added to enforce LF on .sh files;
  harness LF-normalized so tests/run.sh works on Windows checkouts
- .gitignore: .planning/ added (internal GSD workflow artifacts)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Arpit Behera
2026-04-12 22:26:56 +03:00
committed by GitHub
parent 49839486b8
commit 18fc58c398
18 changed files with 3136 additions and 30 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.sh text eol=lf
*.yaml text eol=lf
*.yml text eol=lf

4
.gitignore vendored
View File

@@ -8,4 +8,6 @@ Test-SecondBrain/
*.zip
# Dev config
.claude/settings.local.json
.mcp.json
.mcp.json
# GSD planning artifacts (internal workflow — not for upstream)
.planning/

View File

@@ -13,10 +13,11 @@
<img src="https://img.shields.io/badge/Claude_Code-555555?style=for-the-badge" alt="Claude Code" />
<img src="https://img.shields.io/badge/Gemini_CLI-555555?style=for-the-badge" alt="Gemini CLI" />
<img src="https://img.shields.io/badge/OpenCode-555555?style=for-the-badge" alt="OpenCode" />
<img src="https://img.shields.io/badge/Codex_CLI-555555?style=for-the-badge" alt="Codex CLI" />
</p>
<p align="center">
<em>One codebase. Three platforms. Same crew.</em>
<em>One codebase. Four platforms. Same crew.</em>
</p>
<p align="center">
@@ -253,9 +254,12 @@ The Crew works on multiple agent platforms. The installer builds from a single s
| **Claude Code** (CLI & Desktop) | `bash scripts/launchme.sh --platform claude-code` | `.claude/` | `CLAUDE.md` |
| **Gemini CLI** | `bash scripts/launchme.sh --platform gemini-cli` | `.gemini/` | `GEMINI.md` |
| **OpenCode** | `bash scripts/launchme.sh --platform opencode` | `.opencode/` | `AGENTS.md` |
| **Codex CLI** | `bash scripts/launchme.sh --platform codex-cli` | `.codex/ + .agents/` | `AGENTS.md` |
If you omit `--platform`, the installer asks you to choose. Each platform gets agents, skills, references, hooks, and MCP servers translated to its native format. `launchme.sh` installs everything automatically.
> **Codex CLI on Windows:** Codex CLI's Windows support is experimental. Running inside WSL (Windows Subsystem for Linux) is strongly recommended. See [docs/codex-cli.md](docs/codex-cli.md) for details.
Your vault follows a hybrid **PARA + Zettelkasten** structure:
```
@@ -380,7 +384,7 @@ The `/contact-sync` skill syncs contacts to Apple Contacts on macOS. It requires
- **[@griches/apple-contacts-mcp](https://www.npmjs.com/package/@griches/apple-contacts-mcp)** — an MCP server that provides full CRUD access to Apple Contacts (search, create, update, delete contacts and groups).
Add it to your `.mcp.json` inside the `mcpServers` object:
**Claude Code / Gemini CLI / OpenCode** — add it to your `.mcp.json` inside the `mcpServers` object:
```json
{
@@ -393,6 +397,8 @@ Add it to your `.mcp.json` inside the `mcpServers` object:
}
```
**Codex CLI** — add it to `.codex/config.toml` instead (Codex uses TOML, not `.mcp.json`). Follow the instructions in [docs/codex-cli.md](docs/codex-cli.md) for the correct `[mcp_servers.apple-contacts]` table format.
Once connected, the `/contact-sync` skill auto-syncs contacts when you reply to emails or on demand ("sync contact", "add to contacts").
All other agents and skills work with just your local Obsidian vault. No integrations needed.
@@ -493,6 +499,23 @@ your-vault/
└── ... your Obsidian notes
```
Codex CLI uses a split layout instead of a single platform directory:
```
your-vault/
├── .codex/
│ ├── agents/ ← 8 core agents (.toml format)
│ ├── references/ ← shared docs
│ └── config.toml ← MCP servers + profiles + sandbox policy
├── .agents/
│ └── skills/ ← 14 specialized skills
├── Meta/
│ └── scripts/ ← orchestra scripts
├── AGENTS.md ← dispatcher (Codex reads this)
├── My-Brain-Is-Full-Crew/ ← the repo (for updates)
└── ... your Obsidian notes
```
---
## Contributing (seriously, please help)
@@ -548,5 +571,5 @@ MIT: use it, modify it, share it. Just keep the attribution.
<p align="center">
<i>Built by someone who got tired of forgetting things.</i>
<br><br>
<a href="docs/getting-started.md"><strong>Get Started</strong></a> · <a href="docs/examples.md"><strong>Examples</strong></a> · <a href="docs/agents/architect.md"><strong>Meet the Agents</strong></a> · <a href="CONTRIBUTING.md"><strong>Contribute</strong></a>
<a href="docs/getting-started.md"><strong>Get Started</strong></a> · <a href="docs/examples.md"><strong>Examples</strong></a> · <a href="docs/codex-cli.md"><strong>Codex CLI Guide</strong></a> · <a href="docs/codex-migration.md"><strong>Migrate to Codex</strong></a> · <a href="docs/agents/architect.md"><strong>Meet the Agents</strong></a> · <a href="CONTRIBUTING.md"><strong>Contribute</strong></a>
</p>

View File

@@ -0,0 +1,676 @@
#!/usr/bin/env bash
# =============================================================================
# adapters/codex-cli/adapter.sh — Codex CLI framework adapter
# =============================================================================
# Sourced by scripts/build.sh AFTER adapters/lib.sh.
# Translates source files into a dist/codex-cli/ tree that mirrors what
# Codex CLI expects in the user's vault.
#
# Codex CLI specifics:
# - Dispatcher file: AGENTS.md (same as OpenCode)
# - Platform config dir: .codex/
# - MCP config: .codex/config.toml (TOML format with [mcp_servers.*] tables)
# - Skills dir: .agents/skills/<name>/SKILL.md
# =============================================================================
CC_PLATFORM="codex-cli"
CC_FW_DIR="codex"
CC_DISPATCHER="AGENTS.md"
# rewrite_codex_paths <file>
# Applies Codex-specific targeted path rewrites in-place.
# This is narrower than the shared rewrite_platform_paths helper because Codex
# uses mixed destination roots depending on the source path.
rewrite_codex_paths() {
local file="$1"
[[ -f "$file" ]] || return 0
perl -i -pe '
s|\.platform/agents/|.codex/agents/|g;
s|\.platform/references/|.codex/references/|g;
s|\.platform/skills/|.agents/skills/|g;
s|\.platform/|.codex/|g;
s|DISPATCHER\.md|AGENTS.md|g;
' "$file"
}
# rewrite_tool_compat <file>
# Rewrites unsupported or platform-specific tool references in a file to
# Codex-compatible neutral language. Applied AFTER rewrite_platform_paths so
# path references are already resolved.
#
# Rewrites applied (T-01-06):
# - `AskUserQuestion` / AskUserQuestion → "ask the user" (preserves one-at-a-time constraint phrasing elsewhere)
# - `request_user_input` / request_user_input → "ask the user"
# - "Skill tool" → "invoke the skill"
# - "Agent tool" → "invoke the agent"
# - "Read tool" → "read files"
# - "Glob tool" → "search files"
# - "Grep tool" → "search files"
# - "Bash tool" → "shell"
rewrite_tool_compat() {
local file="$1"
[[ -f "$file" ]] || return 0
# Use perl for reliable in-place multi-substitution across all platforms.
# Each substitution is a plain string replacement (no regex heavy-lifting).
perl -i -pe '
s/`AskUserQuestion`/ask the user/g;
s/\bAskUserQuestion\b/ask the user/g;
s/`request_user_input`/ask the user/g;
s/\brequest_user_input\b/ask the user/g;
s/\bSkill tool\b/invoke the skill/g;
s/\bAgent tool\b/invoke the agent/g;
s/\bRead tool\b/read files/g;
s/\bGlob tool\b/search files/g;
s/\bGrep tool\b/search files/g;
s/\bBash tool\b/shell/g;
' "$file"
}
# normalize_codex_routing_contract <file>
# Rewrites copied dispatcher/reference content so the generated Codex output
# describes one coherent contract: root-context orchestration, bounded child
# agents, and direct-chat confirmations under agents.max_depth = 1.
normalize_codex_routing_contract() {
local file="$1"
[[ -f "$file" ]] || return 0
rewrite_tool_compat "$file"
perl -0pi -e '
s/handle complex, multi-step, or conversational flows\. Invoke them via the \*\*invoke the skill\*\*\. They run in the main conversation context \(multi-turn state is preserved\)\./handle complex, multi-step, or conversational flows. In Codex, the root context follows the relevant skill instructions directly so multi-turn state stays in one chat./g;
s/handle reactive, single-shot operations\. Invoke them via the \*\*invoke the agent\*\*\. They run as subprocesses\./handle reactive, bounded tasks. In Codex, the root context may spawn a bounded child agent when delegation is worth it, but orchestration decisions stay in the root context because `agents.max_depth = 1`./g;
s/If a user message matches a skill trigger, the skill is invoked via the \*\*invoke the skill\*\* \(not the invoke the agent\)\. The dispatcher does NOT also invoke the source agent\./If a user message matches a skill trigger, the root context follows that skill directly in the main conversation. Do not also spawn a child agent for the same trigger./g;
s/Skills run in the \*\*main conversation context\*\*, preserving multi-turn state\. This is different from agents, which run as subprocesses\./Skills stay in the main conversation context so multi-turn state remains in one chat. Child agents are only for bounded side tasks./g;
s/check registry, check call chain, max depth 3/check the registry and decide in the root context whether another bounded child task is still necessary under `agents.max_depth = 1`/g;
s/Skills count as step 1 in the call chain when they produce agent suggestions\./Skills do not consume extra child depth because the root context executes them directly./g;
s/The dispatcher should confirm with the user first:/The root context should confirm with the user directly in chat first:/g;
s/asks the user if they want the Architect to create a custom agent/asks the user directly in chat whether they want a custom agent/g;
s/"Call chain so far: \[scribe, architect\]\. You are step 3 of max 3\."/"Root context history: [scribe, architect]. If another bounded child task is still useful, the root context decides whether to spawn it within `agents.max_depth = 1`."/g;
s/\*\*Max depth: 3\*\*: no more than 3 agents per user request/\*\*Child depth: `agents.max_depth = 1`\*\*: a spawned child may finish one bounded task, then the root context decides what happens next/g;
s/If the dispatcher would need a 4th agent, it:/If deeper recursion would be required, the root context returns the current results to the user and decides the next step in chat:/g;
s/\bmax depth 3\b/`agents.max_depth = 1`/g;
s/\bstep 3 of max 3\b/root-context follow-up after a bounded child task/g;
s/\bmax depth reached\b/root-context delegation limit reached/g;
s/No duplicates: never invoke the same agent twice in one chain/No duplicates: do not spawn the same child agent twice for the same request/g;
s/No circular patterns: if Agent A suggests Agent B and B is already in the chain, skip/No circular patterns: if a suggested child is already in the root decision history, skip it/g;
s/Do NOT call other agents/Do NOT spawn or imply deeper child recursion/g;
s/only the dispatcher invokes agents/only the root context decides when to spawn a bounded child agent/g;
' "$file"
perl -0pi -e '
s/\binvoke the skill\b/follow the skill instructions directly in the root context/g;
s/\binvoke the agent\b/spawn a bounded child agent from the root context/g;
s/\bask the user\b/ask the user directly in chat and wait for the reply/g;
' "$file"
}
# _cc_prepend_codex_header <file>
# Prepends the Codex-specific routing workaround header to a file (T-01-08).
# Uses a unique marker (<!-- CODEX-ROUTING-HEADER -->) so tests can detect it
# and so repeated builds are idempotent.
_cc_prepend_codex_header() {
local file="$1"
[[ -f "$file" ]] || return 0
# Idempotency: skip if header is already present
grep -qF '<!-- CODEX-ROUTING-HEADER -->' "$file" && return 0
local header
header="$(cat <<'HEADER'
<!-- CODEX-ROUTING-HEADER -->
<!-- Generated by adapters/codex-cli/adapter.sh — do not edit manually -->
## Codex CLI — Routing Notes
> **Context:** Codex CLI loads this file as the root dispatcher. Due to
> multi-agent depth limits (`agents.max_depth = 1` by default), all
> orchestration decisions stay in this root context. Spawned agents emit
> a `### Suggested next agent` signal; the root context then decides whether
> to continue with another agent.
**Custom agents** live in `.codex/agents/*.toml` and are discovered automatically.
**Skills** live in `.agents/skills/` inside the project (or `$HOME/.agents/skills/`).
**Compatibility guide:** see `.codex/references/codex-cli-compat.md` for source-to-Codex mappings, approval wording, and troubleshooting.
**Tool language:** Use neutral, conceptual wording — "run shell commands",
"edit files", "spawn sub-agents", "wait for sub-agents" — rather than
hard-coded tool identifiers that may not exist across all Codex versions.
---
HEADER
)"
# Prepend header before existing content
local tmp; tmp="$(mktemp)"
printf '%s\n' "$header" > "$tmp"
cat "$file" >> "$tmp"
mv "$tmp" "$file"
}
# adapter_translate_dispatcher <source_dispatcher_md> <dest_dir>
# Copies the source DISPATCHER.md to dest_dir/AGENTS.md (Codex CLI's vault-root
# dispatcher filename). Rewrites .platform/ and DISPATCHER.md to codex paths,
# normalizes Codex routing semantics, and prepends the Codex routing header.
adapter_translate_dispatcher() {
local src="$1" dst="$2"
[[ -f "$src" ]] || return 0
mkdir -p "$dst"
cp "$src" "$dst/AGENTS.md"
rewrite_codex_paths "$dst/AGENTS.md"
normalize_codex_routing_contract "$dst/AGENTS.md"
_cc_prepend_codex_header "$dst/AGENTS.md"
}
# adapter_translate_references <source_refs_dir> <dest_root>
# Copies *.md into dest_root/.codex/references/, rewriting framework paths
# and normalizing Codex routing semantics.
adapter_translate_references() {
local src="$1" dst="$2"
[[ -d "$src" ]] || return 0
local out="$dst/.codex/references"
mkdir -p "$out"
for f in "$src"/*.md; do
[[ -f "$f" ]] || continue
should_include "$f" "$CC_PLATFORM" || continue
local out_file="$out/$(basename "$f")"
cp "$f" "$out/"
rewrite_codex_paths "$out_file"
if [[ "$(basename "$f")" == "codex-cli-compat.md" ]]; then
continue
fi
normalize_codex_routing_contract "$out_file"
done
}
# adapter_translate_skills <source_skills_dir> <dest_root>
# Copies each skill directory into dest_root/.agents/skills/<name>/, preserving
# optional subdirectories and rewriting text content for Codex-native output.
_cc_rewrite_skill_markdown() {
local skill_name="$1" file="$2"
[[ -f "$file" ]] || return 0
rewrite_codex_paths "$file"
rewrite_tool_compat "$file"
case "$skill_name" in
onboarding)
perl -0pi -e '
s/You MUST use the ask the user tool for EVERY question in every phase\. This is not optional\. This is how the onboarding works:/Use direct chat for every question in every phase. Ask one direct plain-text question, wait for the user'\''s reply before continuing, and resume from the saved state file if the flow is already active. This is not optional. This is how the onboarding works:/g;
s/1\. Ask ONE question using ask the user/1. Ask one direct plain-text question/g;
s/2\. Read the user'\''s answer/2. Wait for the user'\''s reply before continuing/g;
s/4\. Ask the NEXT question using ask the user/4. Ask the next direct plain-text question/g;
s/\*\*ONE question per ask the user call\.\*\* Never bundle 2\+ questions in one message\./**One direct plain-text question at a time.** Never bundle 2+ questions in one message./g;
s/\.codex\/agents\/\{name\}\.md/.codex\/agents\/{name}.toml/g;
s/\.mcp\.json/.codex\/config.toml/g;
s/\$HOME\/\.platform\/agents/\$HOME\/.codex\/agents/g;
' "$file"
;;
create-agent)
perl -0pi -e '
s/You MUST use the ask the user tool for EVERY question in every phase\. This is not optional\. This is how the conversation works:/Use direct chat for every question in every phase. Ask one direct plain-text question, wait for the user'\''s reply before continuing, and resume from the saved state file if the flow is already active. This is not optional. This is how the conversation works:/g;
s/1\. Ask ONE question using ask the user/1. Ask one direct plain-text question/g;
s/2\. Read the user'\''s answer/2. Wait for the user'\''s reply before continuing/g;
s/4\. Ask the NEXT question using ask the user/4. Ask the next direct plain-text question/g;
s/\*\*ONE question per ask the user call\.\*\* Never bundle 2\+ questions\./**One direct plain-text question at a time.** Never bundle 2+ questions./g;
s/\.codex\/agents\/\{name\}\.md/.codex\/agents\/{name}.toml/g;
' "$file"
;;
manage-agent)
perl -0pi -e '
s/using `ask the user`/by asking the user directly/g;
s/Use `ask the user` to/Ask one direct plain-text question to/g;
s/If the user specifies a name, read `\.codex\/agents\/\{name\}\.md`/If the user specifies a name, read `\.codex\/agents\/{name}.toml`/g;
s/Modify the agent file at `\.codex\/agents\/\{name\}\.md`/Modify the agent file at `\.codex\/agents\/{name}.toml`/g;
s/locate `\.codex\/agents\/\{name\}\.md`/locate `\.codex\/agents\/{name}.toml`/g;
s/Delete the agent file from `\.codex\/agents\/\{name\}\.md`/Delete the agent file from `\.codex\/agents\/{name}.toml`/g;
' "$file"
if ! grep -qi 'resume from the saved state file if the flow is already active' "$file"; then
cat <<'EOF' >> "$file"
## Codex Conversation Flow
- Ask one direct plain-text question when clarification is required.
- Wait for the user's reply before continuing.
- Resume from the saved state file if the flow is already active.
EOF
fi
;;
transcribe)
perl -0pi -e '
s/Use ask the user to collect:/Use direct chat to collect this intake context: ask one direct plain-text question, wait for the user'\''s reply before continuing, and resume from the saved state file if the flow is already active. Collect:/g;
' "$file"
;;
esac
}
_cc_rewrite_skill_text_tree() {
local skill_name="$1" root="$2"
[[ -d "$root" ]] || return 0
if [[ -f "$root/SKILL.md" ]]; then
_cc_rewrite_skill_markdown "$skill_name" "$root/SKILL.md"
fi
if [[ -d "$root/references" ]]; then
while IFS= read -r ref; do
rewrite_codex_paths "$ref"
rewrite_tool_compat "$ref"
done < <(find "$root/references" -type f -name '*.md')
fi
if [[ -d "$root/assets" ]]; then
while IFS= read -r asset; do
rewrite_codex_paths "$asset"
rewrite_tool_compat "$asset"
done < <(find "$root/assets" -type f -name '*.md')
fi
if [[ -f "$root/agents/openai.yaml" ]]; then
rewrite_codex_paths "$root/agents/openai.yaml"
rewrite_tool_compat "$root/agents/openai.yaml"
fi
}
adapter_translate_skills() {
local src="$1" dst="$2"
[[ -d "$src" ]] || return 0
for skill_dir in "$src"/*/; do
[[ -f "${skill_dir}SKILL.md" ]] || continue
should_include "${skill_dir}SKILL.md" "$CC_PLATFORM" || continue
local name; name="$(basename "$skill_dir")"
local out="$dst/.agents/skills/$name"
mkdir -p "$out"
cp "${skill_dir}SKILL.md" "$out/SKILL.md"
[[ -d "${skill_dir}scripts" ]] && mkdir -p "$out/scripts" && cp -R "${skill_dir}scripts/." "$out/scripts/"
[[ -d "${skill_dir}references" ]] && mkdir -p "$out/references" && cp -R "${skill_dir}references/." "$out/references/"
[[ -d "${skill_dir}assets" ]] && mkdir -p "$out/assets" && cp -R "${skill_dir}assets/." "$out/assets/"
if [[ -f "${skill_dir}agents/openai.yaml" ]]; then
mkdir -p "$out/agents"
cp "${skill_dir}agents/openai.yaml" "$out/agents/openai.yaml"
fi
_cc_rewrite_skill_text_tree "$name" "$out"
done
}
# _cc_toml_quote_key <name>
# Emits the TOML table key for [mcp_servers.<name>].
# Codex CLI requires MCP server names to match ^[a-zA-Z0-9_-]+$, so any
# character outside that set is replaced with a hyphen before writing.
_cc_toml_quote_key() {
local name="$1"
# Sanitize: replace any character not in [a-zA-Z0-9_-] with a hyphen
local safe_name="${name//[^a-zA-Z0-9_-]/-}"
printf '[mcp_servers.%s]' "$safe_name"
}
# _cc_toml_escape_string <value>
# Escapes a value for use in a TOML double-quoted string.
# Escapes backslashes and double-quotes; other characters pass through.
_cc_toml_escape_string() {
local val="$1"
# Escape backslash first, then double-quote
val="${val//\\/\\\\}"
val="${val//\"/\\\"}"
printf '%s' "$val"
}
# adapter_translate_config <source_mcp_dir> <dest_root>
# Reads mcp/servers.yaml and writes dest_root/.codex/config.toml.
#
# Config structure:
# - Safe baseline Codex settings at the top
# - [mcp_servers.<name>] tables for each server
# - HTTP servers: url = "..." (plus env inline table)
# - Local servers: command = ["...", "..."] (plus env inline table)
#
# Security (T-01-01): TOML table names with spaces/special chars are quoted.
# Security (T-01-02): Safe approval_policy and sandbox_mode are always emitted.
adapter_translate_config() {
local src="$1" dst="$2"
local yaml="$src/servers.yaml"
[[ -f "$yaml" ]] || return 0
local out_dir="$dst/.codex"
mkdir -p "$out_dir"
local out="$out_dir/config.toml"
{
# ── Safe baseline Codex settings (T-01-02) ──────────────────────────────
echo '# Generated by adapters/codex-cli/adapter.sh — do not edit manually'
echo 'approval_policy = "on-request"'
echo 'sandbox_mode = "workspace-write"'
echo 'sandbox_workspace_write.network_access = false'
echo ''
echo '# Inherit uses the top-level defaults; named profiles override them explicitly.'
echo '[profiles.quality]'
echo 'model = "gpt-5.4"'
echo 'model_reasoning_effort = "high"'
echo ''
echo '[profiles.balanced]'
echo 'model = "gpt-5.4-mini"'
echo 'model_reasoning_effort = "medium"'
echo ''
echo '[profiles.budget]'
echo 'model = "gpt-5.3-codex-spark"'
echo 'model_reasoning_effort = "medium"'
echo ''
echo '[agents]'
echo 'max_depth = 1'
echo ''
# ── Parse servers.yaml line-by-line and emit TOML tables ────────────────
local current_name="" current_type="" current_url="" current_cmd="" current_env=""
local in_servers=0
_cc_flush_server() {
[[ -z "$current_name" ]] && return 0
local key; key="$(_cc_toml_quote_key "$current_name")"
echo "$key"
if [[ "$current_type" == "local" || ( -z "$current_type" && -n "$current_cmd" ) ]]; then
# Local server: command = ["arg1", "arg2", ...]
# current_cmd is the raw YAML array content e.g. npx, -y, "@anthropic-ai/gmail-mcp"
local cmd_toml
cmd_toml="$(_cc_build_command_array "$current_cmd")"
echo "command = $cmd_toml"
else
# HTTP server: url = "..."
local url_escaped; url_escaped="$(_cc_toml_escape_string "$current_url")"
echo "url = \"$url_escaped\""
fi
# env is only valid for command-based (local) servers; omit for HTTP servers
if [[ "$current_type" == "local" || ( -z "$current_type" && -n "$current_cmd" ) ]]; then
if [[ -n "$current_env" ]]; then
echo "env = $(_cc_build_env_table "$current_env")"
fi
fi
echo ''
}
_cc_build_command_array() {
# Convert a comma-separated YAML sequence payload (without brackets) into
# a TOML array of quoted strings: ["npx", "-y", "@anthropic-ai/gmail-mcp"]
local raw="$1"
local items=()
IFS=',' read -ra parts <<< "$raw"
for part in "${parts[@]}"; do
# Trim leading/trailing whitespace and surrounding quotes
part="${part#"${part%%[![:space:]]*}"}"
part="${part%"${part##*[![:space:]]}"}"
part="${part#\"}" ; part="${part%\"}"
part="${part#\'}" ; part="${part%\'}"
items+=("$part")
done
# Build TOML array
local result='['
local first=1
for item in "${items[@]}"; do
[[ -z "$item" ]] && continue
local escaped; escaped="$(_cc_toml_escape_string "$item")"
[[ $first -eq 1 ]] && first=0 || result+=', '
result+="\"$escaped\""
done
result+=']'
echo "$result"
}
_cc_build_env_table() {
local raw="$1"
local result='{'
local first=1
IFS=',' read -ra pairs <<< "$raw"
for pair in "${pairs[@]}"; do
pair="${pair#"${pair%%[![:space:]]*}"}"
pair="${pair%"${pair##*[![:space:]]}"}"
[[ -z "$pair" ]] && continue
local key="${pair%%:*}"
local value="${pair#*:}"
key="${key#"${key%%[![:space:]]*}"}"
key="${key%"${key##*[![:space:]]}"}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
key="${key#\"}" ; key="${key%\"}"
value="${value#\"}" ; value="${value%\"}"
local escaped_key escaped_value
escaped_key="$(_cc_toml_escape_string "$key")"
escaped_value="$(_cc_toml_escape_string "$value")"
[[ $first -eq 1 ]] && first=0 || result+=', '
result+="\"$escaped_key\" = \"$escaped_value\""
done
result+='}'
echo "$result"
}
while IFS= read -r line; do
line="${line%$'\r'}"
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line//[[:space:]]/}" ]] && continue
if [[ "$line" =~ ^[[:space:]]*servers:[[:space:]]*$ ]]; then
in_servers=1
elif [[ "$line" =~ ^[[:space:]]*-[[:space:]]name:[[:space:]]*(.+)$ ]]; then
[[ $in_servers -eq 1 ]] && _cc_flush_server
current_name="$(printf '%s' "${BASH_REMATCH[1]}" | tr -d '"')"
current_type=""
current_url=""
current_cmd=""
current_env=""
elif [[ "$line" =~ ^[[:space:]]{4}type:[[:space:]]*(.+)$ ]]; then
current_type="$(printf '%s' "${BASH_REMATCH[1]}" | tr -d '"')"
elif [[ "$line" =~ ^[[:space:]]{4}url:[[:space:]]*(.+)$ ]]; then
current_url="$(printf '%s' "${BASH_REMATCH[1]}" | tr -d '"')"
elif [[ "$line" =~ ^[[:space:]]{4}command:[[:space:]]*\[(.*)\][[:space:]]*$ ]]; then
# Extract the array payload between [ and ]
current_cmd="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^[[:space:]]{4}env:[[:space:]]*\{(.*)\}[[:space:]]*$ ]]; then
current_env="${BASH_REMATCH[1]}"
fi
done < "$yaml"
_cc_flush_server
} > "$out"
}
# _cc_extract_description <agent_file>
# Extracts the description value from YAML frontmatter, handling both:
# - Scalar: description: Some text
# - Folded block (description: >): captures all indented continuation lines,
# joins them with spaces, and trims the result.
# Emits the description as a single-line string (no leading/trailing whitespace).
_cc_extract_description() {
local file="$1"
awk '
/^---$/ { fm++; next }
fm == 1 && /^description:[[:space:]]*>/ {
# Folded-block style: value starts on next indented lines
in_desc=1
next
}
fm == 1 && /^description:/ {
# Scalar style: description: value on same line
sub(/^description:[[:space:]]*/, "")
desc=$0
in_desc=0
next
}
fm == 1 && in_desc && /^[[:space:]]/ {
# Continuation line (indented)
sub(/^[[:space:]]+/, "")
if (desc == "") desc=$0
else desc=desc " " $0
next
}
fm == 1 && in_desc && !/^[[:space:]]/ {
# End of folded block
in_desc=0
}
fm >= 2 { exit }
END {
# Trim trailing whitespace/period added by folded-block joining
gsub(/[[:space:]]+$/, "", desc)
print desc
}
' "$file"
}
# _cc_toml_escape_dquote_string <value>
# Escapes a string for embedding in a TOML double-quoted basic string.
# Handles backslash and double-quote characters.
_cc_toml_escape_dquote_string() {
local val="$1"
val="${val//\\/\\\\}"
val="${val//\"/\\\"}"
printf '%s' "$val"
}
# _cc_model_to_codex <model>
# Maps the source model tier into a Codex model id.
_cc_model_to_codex() {
local model="$1"
case "$model" in
*/*|gpt-*) echo "$model" ;;
low) echo "gpt-5.3-codex-spark" ;;
mid) echo "gpt-5.4-mini" ;;
high) echo "gpt-5.4" ;;
*) echo "$model" ;;
esac
}
# _cc_model_reasoning_effort <model>
# Maps the source model tier into a Codex reasoning effort.
_cc_model_reasoning_effort() {
local model="$1"
case "$model" in
high) echo "high" ;;
*) echo "medium" ;;
esac
}
# _cc_capabilities_to_sandbox <space-separated capabilities>
# Preserves the source intent by only granting workspace writes to agents that
# explicitly declare write/edit/bash capabilities.
_cc_capabilities_to_sandbox() {
local caps="$1"
for cap in $caps; do
case "$cap" in
write|edit|bash)
echo "workspace-write"
return 0
;;
esac
done
echo "read-only"
}
# adapter_translate_agent_toml <agent_file> <dest_root>
# Translates a single agent .md file to a Codex CLI custom agent TOML file at:
# <dest_root>/.codex/agents/<basename-without-md>.toml
#
# The TOML file contains:
# name = "<name>"
# description = "<single-line description>"
# developer_instructions = '''
# <body with platform paths rewritten>
# '''
#
# Security (T-01-04): Uses TOML multiline literal strings (''') to avoid
# backslash-escape pitfalls. Falls back to multiline basic strings (""") if
# the body contains the ''' delimiter sequence, escaping as needed.
adapter_translate_agent_toml() {
local agent_file="$1" dst_root="$2"
[[ -f "$agent_file" ]] || return 0
local name; name="$(parse_frontmatter "$agent_file" name)"
[[ -z "$name" ]] && name="$(basename "$agent_file" .md)"
local description; description="$(_cc_extract_description "$agent_file")"
local desc_escaped; desc_escaped="$(_cc_toml_escape_dquote_string "$description")"
local model_raw; model_raw="$(parse_frontmatter "$agent_file" model)"
local model; model="$(_cc_model_to_codex "$model_raw")"
local model_reasoning_effort; model_reasoning_effort="$(_cc_model_reasoning_effort "$model_raw")"
local capabilities; capabilities="$(parse_capabilities "$agent_file")"
local sandbox_mode; sandbox_mode="$(_cc_capabilities_to_sandbox "$capabilities")"
# Get the agent body, apply Codex path rewrites, and then neutralize tool naming.
local body; body="$(agent_body "$agent_file")"
local _body_tmp; _body_tmp="$(mktemp)"
printf '%s\n' "$body" > "$_body_tmp"
rewrite_codex_paths "$_body_tmp"
rewrite_tool_compat "$_body_tmp"
body="$(cat "$_body_tmp")"
rm -f "$_body_tmp"
local out_dir="$dst_root/.codex/agents"
mkdir -p "$out_dir"
local out_file="$out_dir/${name}.toml"
# Choose multiline literal (''') vs basic (""") based on body content (T-01-04)
if [[ "$body" == *"'''"* ]]; then
# Fallback: use multiline basic string, escape backslashes and double-quotes
local escaped_body; escaped_body="${body//\\/\\\\}"
escaped_body="${escaped_body//\"/\\\"}"
{
printf 'name = "%s"\n' "$(_cc_toml_escape_dquote_string "$name")"
printf 'description = "%s"\n' "$desc_escaped"
printf 'model = "%s"\n' "$(_cc_toml_escape_dquote_string "$model")"
printf 'model_reasoning_effort = "%s"\n' "$model_reasoning_effort"
printf 'sandbox_mode = "%s"\n' "$sandbox_mode"
printf 'developer_instructions = """\n'
printf '%s\n' "$escaped_body"
printf '"""\n'
} > "$out_file"
else
# Preferred: multiline literal string — no escaping needed
{
printf 'name = "%s"\n' "$(_cc_toml_escape_dquote_string "$name")"
printf 'description = "%s"\n' "$desc_escaped"
printf 'model = "%s"\n' "$(_cc_toml_escape_dquote_string "$model")"
printf 'model_reasoning_effort = "%s"\n' "$model_reasoning_effort"
printf 'sandbox_mode = "%s"\n' "$sandbox_mode"
printf "developer_instructions = '''\n"
printf '%s\n' "$body"
printf "'''\n"
} > "$out_file"
fi
}
# adapter_translate_agents_toml <source_agents_dir> <dest_root>
# Iterates over all *.md agent files and calls adapter_translate_agent_toml for each.
adapter_translate_agents_toml() {
local src="$1" dst="$2"
[[ -d "$src" ]] || return 0
while IFS= read -r agent; do
[[ -f "$agent" ]] || continue
should_include "$agent" "$CC_PLATFORM" || continue
adapter_translate_agent_toml "$agent" "$dst"
done < <(enumerate_agents "$src")
}
# adapter_build <source_dir> <dest_dir>
# The single entry point invoked by scripts/build.sh.
# Writes into $DIST_DIR/codex-cli/ (dest_dir is already platform-scoped).
adapter_build() {
local src="$1" dst="$2"
local OUT_DIR="$dst"
# Delete and recreate the platform output directory (scoped; T-01-03 accepted)
rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"
adapter_translate_dispatcher "$src/DISPATCHER.md" "$OUT_DIR"
adapter_translate_references "$src/references" "$OUT_DIR"
adapter_translate_skills "$src/skills" "$OUT_DIR"
adapter_translate_config "$src/mcp" "$OUT_DIR"
adapter_translate_agents_toml "$src/agents" "$OUT_DIR"
}

219
docs/codex-cli.md Normal file
View File

@@ -0,0 +1,219 @@
# Codex CLI Guide
This guide covers everything you need to install, update, and run My Brain Is Full — Crew on [Codex CLI](https://openai.com/codex) (`@openai/codex`).
> **Windows note:** Codex CLI's Windows support is experimental. If you are on Windows, running inside WSL (Windows Subsystem for Linux) is strongly recommended.
---
## Install and update commands
### First-time install
```bash
# Install Codex CLI globally
npm i -g @openai/codex@latest
# Clone the repo inside your vault and install the Crew
cd /path/to/your-vault
git clone https://github.com/gnekt/My-Brain-Is-Full-Crew.git
cd My-Brain-Is-Full-Crew
bash scripts/launchme.sh --platform codex-cli
```
The installer accepts an optional `--target` flag if you want to point it at a vault in a non-standard location:
```bash
bash scripts/launchme.sh --platform codex-cli --target /path/to/your-vault
```
### Update after a git pull
```bash
cd /path/to/your-vault/My-Brain-Is-Full-Crew
git pull
bash scripts/updateme.sh --platform codex-cli
```
The updater auto-detects Codex CLI by checking for `.codex/agents` in your vault. If multiple platforms are installed, pass `--platform codex-cli` explicitly.
---
## What installs where
After running `launchme.sh --platform codex-cli`, your vault will contain:
```
your-vault/
├── .codex/
│ ├── agents/ ← 8 core crew agents (.toml format)
│ ├── references/ ← shared docs the agents read
│ └── config.toml ← MCP server definitions + profiles + sandbox policy
├── .agents/
│ └── skills/ ← 14 specialized skills (plain text instructions)
├── Meta/
│ └── scripts/ ← orchestra scripts (permission-free agent commands)
└── AGENTS.md ← dispatcher (project instructions for Codex)
```
Key differences from other platforms:
| Path | Purpose |
|------|---------|
| `.codex/agents/*.toml` | Custom agent definitions (Codex native format) |
| `.agents/skills/` | Repo-scoped skill instructions (shared discovery path) |
| `.codex/config.toml` | MCP servers, approval policy, sandbox mode, model profiles |
| `AGENTS.md` | Dispatcher — Codex reads this as its primary project instruction file |
---
## Architecture differences from Claude Code, Gemini CLI, and OpenCode
### Dispatcher
All platforms use a dispatcher file, but the name and format differ:
| Platform | Dispatcher file |
|----------|----------------|
| Claude Code | `CLAUDE.md` |
| Gemini CLI | `GEMINI.md` |
| OpenCode | `AGENTS.md` |
| Codex CLI | `AGENTS.md` (with root-context routing header) |
Codex CLI shares the `AGENTS.md` name with OpenCode but prepends a routing header that handles orchestration within the `agents.max_depth = 1` constraint (see below).
### Agent format
Claude Code, Gemini CLI, and OpenCode all use Markdown (`.md`) agent files. Codex CLI uses TOML:
```
.claude/agents/architect.md ← Claude Code
.gemini/agents/architect.md ← Gemini CLI
.opencode/agents/architect.md ← OpenCode
.codex/agents/architect.toml ← Codex CLI
```
### Skills location
Skills install to `.agents/skills/` for Codex (not `.codex/skills/`). Codex CLI discovers skills from this shared path.
### Agent chaining (max_depth constraint)
Codex CLI enforces `agents.max_depth = 1`. This means child agents can only go one level deep. My Brain Is Full — Crew handles this through root-context orchestration:
- The dispatcher embeds orchestration instructions in the root context (not in a child)
- Child agents (`spawn_agent`) finish one bounded task and return to root
- Any next step is decided from the root context, not by a nested child
### Tool name differences
Codex CLI does not have the `AskUserQuestion` or `request_user_input` tools. The equivalent patterns are:
| Source concept | Codex CLI equivalent |
|---|---|
| `AskUserQuestion` | Ask a direct question in the chat thread and wait for the reply |
| `request_user_input` | Same — use the root conversation for follow-up questions |
| `Skill tool` | Follow the skill instructions directly in the root context |
| `Agent tool` | Use `spawn_agent` for a bounded child task; orchestration returns to root |
| `max chain depth 3` | `agents.max_depth = 1` with root-only orchestration |
| `.mcp.json` | `.codex/config.toml` |
### MCP configuration
Claude Code uses `.mcp.json`. Codex CLI uses `.codex/config.toml`. The MCP server, approval policy, sandbox mode, and model profile settings all live in the TOML config. The CLI and Codex IDE extension share this same config file.
---
## Runtime smoke matrix
Use this table to verify the Crew works correctly in a real Codex vault after install or update. Run each row and compare the result against the expected outcome.
| Surface | Name | Prompt or command | Expected result |
|---------|------|-------------------|----------------|
| Agent | Architect | `@Architect Set up my vault structure` | Architect starts onboarding conversation or confirms vault is already set up |
| Agent | Scribe | `@Scribe Save this note: quick test` | Scribe creates a note in 00-Inbox with proper frontmatter |
| Agent | Sorter | `@Sorter Triage my inbox` | Sorter reviews inbox notes and files them, or reports inbox is empty |
| Agent | Seeker | `@Seeker What do I know about this project?` | Seeker searches the vault and returns results with source citations |
| Agent | Connector | `@Connector Find connections in my recent notes` | Connector analyzes the vault graph and suggests wikilinks |
| Agent | Librarian | `@Librarian Run a vault health check` | Librarian scans for broken links, duplicates, and orphan notes |
| Agent | Transcriber | `@Transcriber Process this transcript: [paste text]` | Transcriber generates structured meeting notes |
| Agent | Postman | `@Postman Check my email` | Postman scans Gmail (or Hey) and saves actionable emails, or reports missing integration |
| Skill | onboarding | `/onboarding` | Architect starts the full onboarding conversation |
| Skill | create-agent | `/create-agent` | Architect walks through designing a new custom agent |
| Skill | manage-agent | `/manage-agent` | Architect lists, edits, or removes custom agents |
| Skill | defrag | `/defrag` | Architect runs the 5-phase vault defragmentation |
| Skill | email-triage | `/email-triage` | Postman scans and prioritizes unread emails |
| Skill | meeting-prep | `/meeting-prep` | Postman generates a comprehensive meeting brief |
| Skill | weekly-agenda | `/weekly-agenda` | Postman produces a day-by-day week overview |
| Skill | deadline-radar | `/deadline-radar` | Postman produces a unified deadline timeline |
| Skill | transcribe | `/transcribe` | Transcriber processes a recording or transcript into structured notes |
| Skill | vault-audit | `/vault-audit` | Librarian runs the full 7-phase vault audit |
| Skill | deep-clean | `/deep-clean` | Librarian runs the extended vault cleanup |
| Skill | tag-garden | `/tag-garden` | Librarian analyzes and cleans up tags |
| Skill | inbox-triage | `/inbox-triage` | Sorter processes and routes all inbox notes |
| Skill | contact-sync | `/contact-sync` | Postman syncs contacts to Apple Contacts |
| Chaining | bounded child-agent chain | `@Sorter Triage my inbox` (with notes present that mention a new project) | Sorter files notes, then dispatcher signals Architect to create the new project folder; child returns to root before Architect runs |
| MCP | MCP visibility | `codex -C <vault> mcp list` | Lists the MCP servers configured in `.codex/config.toml`, or shows the auth/setup state for each server |
### Running the non-interactive discovery smoke
```bash
codex exec -C <vault> "List the project custom agents under .codex/agents, the repo skills under .agents/skills, and the dispatcher file used in this workspace."
```
Expected output references:
- `AGENTS.md` (the dispatcher)
- `.codex/agents` path (custom agents)
- `.agents/skills` path (repo skills)
### Running the MCP visibility smoke
```bash
codex -C <vault> mcp list
```
Expected: lists MCP servers from `.codex/config.toml` (e.g., `Gmail`, `Calendar`) or shows their auth/setup state.
---
## Troubleshooting
### Agents are not discovered
- Verify `.codex/agents/` exists in your vault root and contains `.toml` files.
- Open Codex CLI from your vault directory: `codex -C /path/to/your-vault`
- Check that `AGENTS.md` exists at the vault root (not inside the repo subdirectory).
### Skills are not available
- Verify `.agents/skills/` exists in your vault root and contains subdirectories.
- Skills must be at the vault root level: `<vault>/.agents/skills/<skill-name>/`
### Child agent chain does not return to root
- This is a Codex `agents.max_depth = 1` constraint. Child agents can only go one level deep.
- The dispatcher uses root-context orchestration to work within this constraint.
- If a task seems to require deeper nesting, flatten it: complete the first bounded step in a child, then handle the next step in the root context.
### MCP server not connecting
- MCP configuration lives in `.codex/config.toml` (not `.mcp.json`).
- Check `codex -C <vault> mcp list` to see the current server status.
- For Gmail/Calendar setup, see `docs/gws-setup-guide.md`.
- For Apple Contacts, verify the `apple-contacts` server entry in `.codex/config.toml`.
### Codex errors about approvals
- Child agent approvals surface in the child thread. Approve or deny there, then continue orchestration from the root context after the child returns.
- If a task requires deeper recursion, stop spawning children and flatten the next step into the root context or split the work into separate bounded child tasks.
### Windows users
Codex CLI's Windows support is experimental. Use WSL (Windows Subsystem for Linux) for the most reliable experience. From WSL, follow the standard Linux install path above.
### Reinstall vs update
- **Reinstall** (`launchme.sh`): Use when setting up a new vault or recovering from a broken state.
- **Update** (`updateme.sh`): Use after `git pull` to push new agents, skills, and references to an existing vault. Custom agents are never overwritten.
For a migration from another platform, see [docs/codex-migration.md](codex-migration.md).

172
docs/codex-migration.md Normal file
View File

@@ -0,0 +1,172 @@
# Migrating to Codex CLI
This guide covers how to move an existing My Brain Is Full — Crew installation from Claude Code, Gemini CLI, or OpenCode to Codex CLI. It also explains what transfers automatically and what needs manual attention.
> **If you are doing a fresh install** (not migrating), follow [docs/codex-cli.md](codex-cli.md) instead.
---
## When to reinstall vs update
| Scenario | Recommended action |
|----------|-------------------|
| You have an existing Claude Code / Gemini CLI / OpenCode vault and want to add Codex CLI alongside it | Run `bash scripts/launchme.sh --platform codex-cli` in the same vault — multiple platforms can coexist |
| You want to switch exclusively to Codex CLI | Run `launchme.sh --platform codex-cli`; the other platform files remain but are inactive |
| Your Codex layout is broken or missing files | Run `launchme.sh --platform codex-cli` again — it is idempotent and safe to re-run |
| You pulled new repo changes and want to update Codex | Run `bash scripts/updateme.sh --platform codex-cli` |
You do not need to remove other platform directories. Codex CLI only reads `.codex/` and `.agents/skills/`; it ignores `.claude/`, `.gemini/`, and `.opencode/`.
---
## Path mapping by platform
When you switch to Codex CLI, the project files move to new paths. Use this table to locate your existing files and understand where the equivalent lives in Codex.
| Source platform | Dispatcher | Agents | Skills | MCP or config | Codex target |
|----------------|-----------|--------|--------|--------------|-------------|
| Claude Code | `CLAUDE.md` | `.claude/agents/*.md` | `.claude/skills/` | `.mcp.json` | `AGENTS.md` / `.codex/agents/*.toml` / `.agents/skills/` / `.codex/config.toml` |
| Gemini CLI | `GEMINI.md` | `.gemini/agents/*.md` | `.gemini/skills/` | (none) | `AGENTS.md` / `.codex/agents/*.toml` / `.agents/skills/` / `.codex/config.toml` |
| OpenCode | `AGENTS.md` | `.opencode/agents/*.md` | `.opencode/skills/` | `opencode.json` | `AGENTS.md` / `.codex/agents/*.toml` / `.agents/skills/` / `.codex/config.toml` |
After running `launchme.sh --platform codex-cli`, the Codex files are installed automatically. You do not need to copy the old platform files manually.
---
## Moving from Claude Code
1. Pull the latest repo changes:
```bash
cd /path/to/your-vault/My-Brain-Is-Full-Crew
git pull
```
2. Run the Codex installer:
```bash
bash scripts/launchme.sh --platform codex-cli
```
3. The installer creates:
- `.codex/agents/` — all 8 core agents in TOML format
- `.agents/skills/` — all 14 skills as plain text instructions
- `.codex/config.toml` — MCP servers (translated from `mcp/servers.yaml`)
- `AGENTS.md` — dispatcher with Codex routing header
4. Your existing `.claude/` directory and `CLAUDE.md` are left untouched.
5. MCP configuration: Claude Code uses `.mcp.json`. Codex CLI uses `.codex/config.toml`. If you added custom MCP servers to `.mcp.json` manually, you will need to add them to `.codex/config.toml` as well. See the `[mcp_servers.*]` TOML table format.
6. Custom agents: Claude Code custom agents live in `.claude/agents/`. Codex CLI custom agents must be in `.toml` format in `.codex/agents/`. Custom agents created via the `/create-agent` skill are not automatically migrated — see [Custom agents and what does not migrate automatically](#custom-agents-and-what-does-not-migrate-automatically).
---
## Moving from Gemini CLI
1. Pull the latest repo changes:
```bash
cd /path/to/your-vault/My-Brain-Is-Full-Crew
git pull
```
2. Run the Codex installer:
```bash
bash scripts/launchme.sh --platform codex-cli
```
3. The installer creates the full Codex layout (same as above).
4. Your existing `.gemini/` directory and `GEMINI.md` are left untouched.
5. MCP configuration: Gemini CLI does not use `.mcp.json`. If you have MCP servers configured elsewhere, add them to `.codex/config.toml` manually.
6. Custom agents: Gemini CLI custom agents live in `.gemini/agents/`. These are Markdown files. For Codex CLI, custom agents must be TOML files in `.codex/agents/`. See [Custom agents and what does not migrate automatically](#custom-agents-and-what-does-not-migrate-automatically).
---
## Moving from OpenCode
1. Pull the latest repo changes:
```bash
cd /path/to/your-vault/My-Brain-Is-Full-Crew
git pull
```
2. Run the Codex installer:
```bash
bash scripts/launchme.sh --platform codex-cli
```
3. The installer creates the full Codex layout. Note that both OpenCode and Codex CLI use `AGENTS.md` as the dispatcher. The installer will overwrite `AGENTS.md` with the Codex-specific version (which includes the root-context routing header). If you are running both platforms from the same vault, be aware that the two platforms share `AGENTS.md`.
4. Your existing `.opencode/` directory is left untouched.
5. MCP configuration: OpenCode uses `opencode.json`. Codex CLI uses `.codex/config.toml`. If you added custom MCP servers to `opencode.json`, add them to `.codex/config.toml` manually.
6. Custom agents: OpenCode custom agents live in `.opencode/agents/` as Markdown files. Codex CLI custom agents must be TOML files in `.codex/agents/`. See [Custom agents and what does not migrate automatically](#custom-agents-and-what-does-not-migrate-automatically).
---
## Custom agents and what does not migrate automatically
When you run the installer, the 8 core crew agents are automatically translated to Codex TOML format. However, **custom agents you created with `/create-agent`** are not automatically migrated because:
- They live in your platform's agents directory (`.claude/agents/`, `.gemini/agents/`, etc.)
- They are Markdown files; Codex requires TOML
- The installer never overwrites or deletes files in the agents directory that it did not create
### To migrate a custom agent manually
1. Locate your custom agent file (e.g., `.claude/agents/budget-tracker.md`)
2. Open Codex CLI in your vault and run `/create-agent`
3. Describe the agent's purpose — the Architect will guide you through creating a new `.toml` file in `.codex/agents/`
4. Alternatively, create the TOML file manually using one of the generated core agents as a template (e.g., `.codex/agents/scribe.toml`)
### What the TOML format looks like
```toml
[agent]
name = "budget-tracker"
description = "Monitors spending notes and flags when you are close to the monthly limit"
model = "o4-mini"
[agent.prompt]
content = """
You are the Budget Tracker agent for the My Brain Is Full — Crew system.
... (your agent instructions here)
"""
```
---
## Verification after migration
After running the installer, verify the Codex layout with these commands:
### Check that files installed correctly
```bash
ls <vault>/.codex/agents/ # Should list *.toml files for all 8 agents
ls <vault>/.agents/skills/ # Should list subdirectories for all 14 skills
ls <vault>/.codex/config.toml # Should exist with [mcp_servers.*] tables
ls <vault>/AGENTS.md # Should exist with Codex routing header
```
### Run the non-interactive discovery smoke
```bash
codex exec -C <vault> "List the project custom agents under .codex/agents, the repo skills under .agents/skills, and the dispatcher file used in this workspace."
```
Expected: response references `AGENTS.md`, `.codex/agents`, and `.agents/skills`.
### Check MCP visibility
```bash
codex -C <vault> mcp list
```
Expected: lists MCP servers from `.codex/config.toml`.
### Run the full runtime smoke matrix
See [docs/codex-cli.md — Runtime smoke matrix](codex-cli.md#runtime-smoke-matrix) for the complete list of agents, skills, chaining, and MCP checks.

View File

@@ -155,6 +155,57 @@ Practical scenarios showing how the Crew works in daily life. Each example shows
---
## Codex CLI session examples
These examples show how to start and use the Crew with Codex CLI specifically.
### Install and launch
```bash
# Install Codex CLI
npm i -g @openai/codex@latest
# Install the Crew for Codex CLI
bash scripts/launchme.sh --platform codex-cli
# Launch Codex in your vault
codex -C /path/to/your-vault
```
### Verify the layout before your first session
```bash
# Non-interactive discovery smoke — confirms agents, skills, and dispatcher are visible
codex exec -C /path/to/your-vault "List the project custom agents under .codex/agents, the repo skills under .agents/skills, and the dispatcher file used in this workspace."
# Check MCP server visibility
codex -C /path/to/your-vault mcp list
```
### Using agents and skills inside Codex
Once inside the interactive `codex` session, the Crew works the same way as on other platforms — just talk naturally:
```
"Initialize my vault" → /onboarding skill starts
"Save this note: quick idea" → Scribe agent captures it
"Triage my inbox" → /inbox-triage skill runs
"Check my email" → /email-triage skill scans Gmail
"Weekly review" → /vault-audit skill audits vault
```
### Update after a git pull
```bash
cd /path/to/your-vault/My-Brain-Is-Full-Crew
git pull
bash scripts/updateme.sh --platform codex-cli
```
For the full runtime smoke matrix covering all 8 agents, all 14 skills, bounded child-agent chaining, and MCP visibility, see [docs/codex-cli.md](codex-cli.md).
---
## Daily Workflow Cheat Sheet
| Time | What to say | Skill/Agent |

View File

@@ -8,7 +8,9 @@ A step-by-step guide for setting up your AI-powered vault. No technical backgrou
### Required
- **Obsidian**: A free note-taking app. Download it at [obsidian.md](https://obsidian.md)
- **An agent platform**: one of [Claude Code](https://claude.ai/code) (Pro/Max/Team), [Gemini CLI](https://github.com/google-gemini/gemini-cli), or [OpenCode](https://opencode.ai).
- **An agent platform**: one of [Claude Code](https://claude.ai/code) (Pro/Max/Team), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [OpenCode](https://opencode.ai), or [Codex CLI](https://openai.com/codex) (`npm i -g @openai/codex`).
> **Windows + Codex CLI:** Codex CLI's Windows support is experimental. If you plan to use Codex CLI on Windows, run it inside WSL (Windows Subsystem for Linux) for the best experience.
- **An Obsidian vault**: This is just a folder on your computer where Obsidian stores your notes. If you don't have one yet, Obsidian will create one for you when you first open it.
- **Git**: A tool to download the project. On Mac, the terminal will prompt you to install it automatically the first time you use it. On Windows, download it from [git-scm.com](https://git-scm.com).
@@ -66,8 +68,9 @@ Install one of the following:
| **Claude Code** | [claude.ai/code](https://claude.ai/code) | Claude Pro, Max, or Team |
| **Gemini CLI** | [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) | Google account |
| **OpenCode** | [opencode.ai](https://opencode.ai) | Varies by provider |
| **Codex CLI** | `npm i -g @openai/codex` | OpenAI account |
Claude Code works as both CLI and Desktop app (Cowork). The Crew works on all supported platforms.
Claude Code works as both CLI and Desktop app (Cowork). The Crew works on all four supported platforms.
---
@@ -95,19 +98,34 @@ bash scripts/launchme.sh
```
The script will ask a couple of questions:
1. **Which platform?** Select your agent platform (Claude Code, Gemini CLI, or OpenCode)
1. **Which platform?** Select your agent platform (Claude Code, Gemini CLI, OpenCode, or Codex CLI)
2. **Is this your vault folder?** Confirm or enter the correct path
When it's done, your vault will look like this (paths vary by platform):
```
your-vault/
├── .<platform>/ ← .claude/, .gemini/, .opencode/, or any platform dir that will be supported in the future
├── .<platform>/ ← .claude/, .gemini/, .opencode/
│ ├── agents/ ← 8 lightweight crew agents
│ ├── skills/ ← 14 specialized skills for complex flows
│ ├── hooks/ ← file protection and validation
│ └── references/ ← shared docs the agents read
├── CLAUDE.md / GEMINI.md / AGENTS.md / ... ← dispatcher (varies by platform)
├── CLAUDE.md / GEMINI.md / AGENTS.md ← dispatcher (varies by platform)
├── My-Brain-Is-Full-Crew/ ← the repo (for future updates)
└── ... your Obsidian notes
```
**Codex CLI** uses a split layout instead of a single platform directory:
```
your-vault/
├── .codex/
│ ├── agents/ ← 8 core agents (.toml format)
│ ├── references/ ← shared docs
│ └── config.toml ← MCP servers + profiles + sandbox policy
├── .agents/
│ └── skills/ ← 14 specialized skills
├── AGENTS.md ← dispatcher
├── My-Brain-Is-Full-Crew/ ← the repo (for future updates)
└── ... your Obsidian notes
```
@@ -118,13 +136,18 @@ your-vault/
## Step 4: Connect your vault
1. Open your agent platform (Claude Code, Gemini CLI, or OpenCode)
1. Open your agent platform (Claude Code, Gemini CLI, OpenCode, or Codex CLI)
2. Open it **inside your Obsidian vault folder**. This is important: the platform needs to be in your vault to read and write your notes.
If you're using a CLI tool:
```bash
cd /path/to/your-vault
claude # or: gemini, opencode
claude # or: gemini, opencode, codex
```
For Codex CLI, you can also use the `-C` flag to point directly at your vault:
```bash
codex -C /path/to/your-vault
```
If you're using Claude Code Desktop (Cowork), open the vault folder as your working directory.
@@ -220,7 +243,11 @@ The Crew works best with simple daily routines:
Make sure your agent platform is open inside your vault folder (not a different directory). Verify agent files exist in the platform's agents directory (e.g., `.claude/agents/`). Try saying the trigger phrase differently. Agents and skills understand natural language in multiple languages.
### "Email/Calendar isn't working"
The Postman needs at least one email backend: GWS CLI (`gws`), Hey CLI (`hey`), or MCP connectors. For GWS, see `docs/gws-setup-guide.md`. For Hey, install from [github.com/basecamp/hey-cli](https://github.com/basecamp/hey-cli) and run `hey auth login`. For MCP, run the installer again (`bash scripts/launchme.sh`) and answer **yes** to the Gmail/Calendar question, or manually copy `.mcp.json` from the repo to your vault root.
The Postman needs at least one email backend: GWS CLI (`gws`), Hey CLI (`hey`), or MCP connectors. For GWS, see `docs/gws-setup-guide.md`. For Hey, install from [github.com/basecamp/hey-cli](https://github.com/basecamp/hey-cli) and run `hey auth login`.
For MCP connectors:
- **Claude Code / OpenCode**: run the installer again (`bash scripts/launchme.sh`) and answer **yes** to the Gmail/Calendar question, or manually add the servers to your `.mcp.json` at the vault root.
- **Codex CLI**: MCP servers are configured in `.codex/config.toml` (not `.mcp.json`). Run `bash scripts/launchme.sh --platform codex-cli` and the installer writes them automatically. See [docs/codex-cli.md](codex-cli.md) for the full MCP setup details.
### "My vault structure looks different from the docs"
The Architect customizes the structure based on your onboarding answers.
@@ -233,6 +260,11 @@ git pull
bash scripts/updateme.sh
```
For Codex CLI specifically:
```bash
bash scripts/updateme.sh --platform codex-cli
```
Only changed files are updated. Your vault notes are never touched.
### "An agent did something weird"
@@ -249,6 +281,8 @@ Open an issue on GitHub with:
## Next steps
- **[Examples](examples.md)**: See real-world usage scenarios
- **[Codex CLI Guide](codex-cli.md)**: Install/update guide, architecture differences, runtime smoke matrix, and troubleshooting for Codex CLI
- **[Migrate to Codex CLI](codex-migration.md)**: Step-by-step migration from Claude Code, Gemini CLI, or OpenCode
- **[Mobile Access](mobile-access.md)**: Use the Crew from your phone
- **[Meet the Agents](agents/)**: Deep-dive into each agent's capabilities
- **[Contributing](../CONTRIBUTING.md)**: Help make the Crew better

View File

@@ -4,7 +4,7 @@ servers:
url: "https://gmail.mcp.claude.com/mcp"
env: {}
exclude: []
- name: Google Calendar
- name: Google-Calendar
type: http
url: "https://gcal.mcp.claude.com/mcp"
env: {}

View File

@@ -0,0 +1,38 @@
---
exclude: [claude-code, gemini-cli, opencode]
---
# Codex CLI Compatibility Guide
Use this reference when source workflows mention platform-specific tools or recursion rules that do not map 1:1 to Codex CLI.
| Source concept | Codex CLI mapping | Notes |
|---|---|---|
| `AskUserQuestion` | Ask a direct plain-text question in chat and wait for the reply | Codex uses the root conversation for confirmations and follow-up questions. |
| `request_user_input` | Ask a direct plain-text question in chat and wait for the reply | Use the same root-thread confirmation flow as any other user interaction. |
| `Skill tool` | Follow the relevant skill instructions directly in the root context | Skills stay in the main chat; do not invent a separate Skill API. |
| `Agent tool` | Use `spawn_agent` only for bounded child tasks | The root context keeps orchestration and integration decisions. |
| `max chain depth 3` | `agents.max_depth = 1` with root-only orchestration | A child can finish one bounded task; any next step is decided back in the root context. |
| `.mcp.json` | `.codex/config.toml` | Codex MCP and profile settings live in the TOML config. |
## Flattened Workflow Example
Source workflow wording:
1. Call `AskUserQuestion` for confirmation.
2. Use the `Skill tool` for the setup flow.
3. Use the `Agent tool` for a follow-up task.
Codex CLI wording:
1. Ask the user directly in chat and wait for the reply.
2. Keep the setup flow in the root context by following the skill instructions directly.
3. If a bounded side task remains, use `spawn_agent`, then return to the root context to decide what happens next.
## Troubleshooting
- Child approvals surface in the child thread. Approve or deny there, then continue orchestration from the root context after the child returns.
- If a task would require deeper recursion, stop spawning children and flatten the next step into the root context or split the work into separate bounded child tasks.
- Codex custom agents live in `.codex/agents/*.toml`.
- Repo-scoped Codex skills live in `.agents/skills/`.
- MCP servers, approval policy, sandbox mode, and profiles live in `.codex/config.toml`.

View File

@@ -28,7 +28,12 @@ if [[ ! -d "$REPO_ROOT/adapters/$PLATFORM" ]]; then
fi
# ── Check dependencies ─────────────────────────────────────────────────────
command -v jq >/dev/null 2>&1 || die "jq is required for the build (install via brew, apt, etc.)"
case "$PLATFORM" in
codex-cli) ;;
*)
command -v jq >/dev/null 2>&1 || die "jq is required for the build (install via brew, apt, etc.)"
;;
esac
# ── Source adapters ────────────────────────────────────────────────────────
# shellcheck source=adapters/lib.sh

View File

@@ -102,6 +102,7 @@ EXISTING=0
[[ -d "$VAULT_DIR/.claude" ]] && EXISTING=1
[[ -d "$VAULT_DIR/.opencode" ]] && EXISTING=1
[[ -d "$VAULT_DIR/.gemini" ]] && EXISTING=1
[[ -d "$VAULT_DIR/.codex" ]] && EXISTING=1
[[ -f "$VAULT_DIR/CLAUDE.md" ]] && EXISTING=1
[[ -f "$VAULT_DIR/AGENTS.md" ]] && EXISTING=1
[[ -f "$VAULT_DIR/GEMINI.md" ]] && EXISTING=1
@@ -111,6 +112,7 @@ if [[ $EXISTING -eq 1 ]]; then
[[ -d "$VAULT_DIR/.claude" ]] && warn " .claude/ directory exists"
[[ -d "$VAULT_DIR/.opencode" ]] && warn " .opencode/ directory exists"
[[ -d "$VAULT_DIR/.gemini" ]] && warn " .gemini/ directory exists"
[[ -d "$VAULT_DIR/.codex" ]] && warn " .codex/ directory exists"
[[ -f "$VAULT_DIR/CLAUDE.md" ]] && warn " CLAUDE.md exists"
[[ -f "$VAULT_DIR/AGENTS.md" ]] && warn " AGENTS.md exists"
[[ -f "$VAULT_DIR/GEMINI.md" ]] && warn " GEMINI.md exists"
@@ -162,11 +164,26 @@ case "$PLATFORM" in
MCP_SRC=""
MCP_DST=""
HAS_PLUGINS=0
DIST_SKILLS_DIR="$DIST_COMPONENTS_DIR/skills"
VAULT_SKILLS_DIR="$VAULT_COMPONENTS_DIR/skills"
;;
codex-cli)
DIST_COMPONENTS_DIR="$DIST_DIR/.codex"
VAULT_COMPONENTS_DIR="$VAULT_DIR/.codex"
DISPATCHER_SRC="$DIST_DIR/AGENTS.md"
DISPATCHER_DST="$VAULT_DIR/AGENTS.md"
MCP_SRC="$DIST_DIR/.codex/config.toml"
MCP_DST="$VAULT_DIR/.codex/config.toml"
HAS_PLUGINS=0
DIST_SKILLS_DIR="$DIST_DIR/.agents/skills"
VAULT_SKILLS_DIR="$VAULT_DIR/.agents/skills"
;;
*)
die "Unknown platform: $PLATFORM (install layout not defined)"
;;
esac
[[ -n "${DIST_SKILLS_DIR:-}" ]] || DIST_SKILLS_DIR="$DIST_COMPONENTS_DIR/skills"
[[ -n "${VAULT_SKILLS_DIR:-}" ]] || VAULT_SKILLS_DIR="$VAULT_COMPONENTS_DIR/skills"
PLATFORM_VAULT_DIR="$VAULT_COMPONENTS_DIR"
# Load opencode-specific helpers when building for opencode
@@ -190,7 +207,11 @@ mkdir -p "$VAULT_DIR/Meta/states"
# ── Install components ────────────────────────────────────────────────────────
info "Installing agents..."
AGENT_COUNT=$(install_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
if [[ "$PLATFORM" == "codex-cli" ]]; then
AGENT_COUNT=$(install_toml_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
else
AGENT_COUNT=$(install_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
fi
success "Agents: $AGENT_COUNT installed/updated"
info "Installing references..."
@@ -198,7 +219,7 @@ REF_COUNT=$(install_refs "$DIST_COMPONENTS_DIR/references" "$VAULT_COMPONENTS_DI
success "References: $REF_COUNT installed/updated"
info "Installing skills..."
SKILL_COUNT=$(install_skills "$DIST_COMPONENTS_DIR/skills" "$VAULT_COMPONENTS_DIR/skills")
SKILL_COUNT=$(install_skills "$DIST_SKILLS_DIR" "$VAULT_SKILLS_DIR")
success "Skills: $SKILL_COUNT installed/updated"
info "Installing hooks..."
@@ -266,19 +287,30 @@ echo ""
echo -e " ${VAULT_DIR}/"
FW_DIR_NAME="$(basename "$VAULT_COMPONENTS_DIR")"
DISPATCHER_NAME="$(basename "$DISPATCHER_DST")"
echo -e " ├── ${FW_DIR_NAME}/"
echo -e " ├── agents/ ${DIM}← agents${NC}"
echo -e " │ ├── skills/ ${DIM}skills${NC}"
echo -e " │ ├── hooks/ ${DIM}hooks${NC}"
if [[ $HAS_PLUGINS -eq 1 ]]; then
echo -e " ├── plugins/ ${DIM}← hook plugins${NC}"
if [[ "$PLATFORM" == "codex-cli" ]]; then
echo -e " ├── .codex/"
echo -e " │ ├── agents/ ${DIM}custom agents${NC}"
echo -e " │ ├── references/ ${DIM}shared docs${NC}"
echo -e " │ └── config.toml ${DIM}← MCP + profiles${NC}"
echo -e " ├── .agents/"
echo -e " │ └── skills/ ${DIM}← repo skills${NC}"
else
echo -e " │ ├── settings.json ${DIM}← hooks configuration${NC}"
echo -e " ├── ${FW_DIR_NAME}/"
echo -e " │ ├── agents/ ${DIM}← agents${NC}"
echo -e " │ ├── skills/ ${DIM}← skills${NC}"
echo -e " │ ├── hooks/ ${DIM}← hooks${NC}"
if [[ $HAS_PLUGINS -eq 1 ]]; then
echo -e " │ ├── plugins/ ${DIM}← hook plugins${NC}"
else
echo -e " │ ├── settings.json ${DIM}← hooks configuration${NC}"
fi
echo -e " │ └── references/ ${DIM}← shared docs${NC}"
fi
echo -e " │ └── references/ ${DIM}← shared docs${NC}"
echo -e " ├── Meta/"
echo -e " │ └── scripts/ ${DIM}${ORCH_COUNT:-0} orchestra scripts${NC}"
if [[ -n "$MCP_SRC" && -f "$MCP_DST" ]]; then
if [[ "$PLATFORM" == "codex-cli" && -f "$MCP_DST" ]]; then
echo -e " └── ${DISPATCHER_NAME} ${DIM}← project instructions${NC}"
elif [[ -n "$MCP_SRC" && -f "$MCP_DST" ]]; then
echo -e " ├── ${DISPATCHER_NAME} ${DIM}← project instructions${NC}"
echo -e " └── $(basename "$MCP_DST") ${DIM}← MCP servers${NC}"
else
@@ -295,6 +327,7 @@ case "$PLATFORM" in
claude-code) echo -e " 1. Open Claude Code in your vault folder" ;;
opencode) echo -e " 1. Open OpenCode in your vault folder" ;;
gemini-cli) echo -e " 1. Open Gemini CLI in your vault folder" ;;
codex-cli) echo -e " 1. Open Codex CLI in your vault folder (run: codex)" ;;
*) echo -e " 1. Open your agent platform in your vault folder" ;;
esac
echo -e " 2. Say: ${BOLD}\"Initialize my vault\"${NC}"

View File

@@ -51,6 +51,20 @@ copy_if_changed() {
fi
}
# ── copy_tree_if_changed <src_dir> <dst_dir> ────────────────────────────────
# Overlays a source directory tree onto the destination when files differ or
# the destination does not exist. This preserves nested skill assets/scripts
# needed by platforms such as Codex CLI.
copy_tree_if_changed() {
local src="$1" dst="$2"
_LAST_CHANGED=0
if [[ ! -d "$dst" ]] || ! diff -qr "$src" "$dst" >/dev/null 2>&1; then
mkdir -p "$dst"
cp -R "$src/." "$dst/"
_LAST_CHANGED=1
fi
}
# ── _insert_after_start_marker <dst> <content> ───────────────────────────────
# Inserts <content> immediately after the MBIFC:CUSTOM_AGENTS_START line in dst.
# dst must already exist and contain the marker.
@@ -304,7 +318,32 @@ install_agents() {
local src_dir="$1" dst_dir="$2"
local count=0 manifest=()
mkdir -p "$dst_dir"
for src in "$src_dir/"*.md; do
for src in "$src_dir/"*; do
[[ -f "$src" ]] || continue
case "$src" in
*.md|*.toml) ;;
*) continue ;;
esac
local name; name="$(basename "$src")"
manifest+=("$name")
copy_if_changed "$src" "$dst_dir/$name"
if [[ $_LAST_CHANGED -eq 1 ]]; then
[[ $VERBOSE_COPY -eq 1 ]] && info "Updated agent: $name" || true
count=$((count + 1))
fi
done
manifest_write "agents" "${manifest[@]}"
printf '%d' "$count"
}
# install_toml_agents <src_dir> <dst_dir>
# Copies *.toml agent files from src to dst. Used by Codex CLI platform installs
# where agents are .toml rather than .md. Tracked in the manifest under "agents".
install_toml_agents() {
local src_dir="$1" dst_dir="$2"
local count=0 manifest=()
mkdir -p "$dst_dir"
for src in "$src_dir/"*.toml; do
[[ -f "$src" ]] || continue
local name; name="$(basename "$src")"
manifest+=("$name")
@@ -349,8 +388,7 @@ install_skills() {
[[ -f "${skill_src}SKILL.md" ]] || continue
local name; name="$(basename "$skill_src")"
manifest+=("$name")
mkdir -p "$dst_dir/$name"
copy_if_changed "${skill_src}SKILL.md" "$dst_dir/$name/SKILL.md"
copy_tree_if_changed "$skill_src" "$dst_dir/$name"
if [[ $_LAST_CHANGED -eq 1 ]]; then
[[ $VERBOSE_COPY -eq 1 ]] && info "Updated skill: $name" || true
count=$((count + 1))

View File

@@ -42,6 +42,7 @@ if [[ -z "$PLATFORM" ]]; then
[[ -d "$VAULT_DIR/.claude/agents" ]] && DETECTED+=("claude-code")
[[ -d "$VAULT_DIR/.opencode/agents" ]] && DETECTED+=("opencode")
[[ -d "$VAULT_DIR/.gemini/agents" ]] && DETECTED+=("gemini-cli")
[[ -d "$VAULT_DIR/.codex/agents" ]] && DETECTED+=("codex-cli")
if [[ ${#DETECTED[@]} -eq 0 ]]; then
die "No installed platform detected in $VAULT_DIR — run launchme.sh first"
@@ -73,6 +74,7 @@ case "$PLATFORM" in
claude-code) _SETUP_CHECK="$VAULT_DIR/.claude/agents" ;;
opencode) _SETUP_CHECK="$VAULT_DIR/.opencode/agents" ;;
gemini-cli) _SETUP_CHECK="$VAULT_DIR/.gemini/agents" ;;
codex-cli) _SETUP_CHECK="$VAULT_DIR/.codex/agents" ;;
*) die "Unknown platform: $PLATFORM" ;;
esac
[[ -d "$_SETUP_CHECK" ]] \
@@ -83,6 +85,7 @@ case "$PLATFORM" in
opencode) _DISP_NAME="AGENTS.md"; _FW_DIR_NAME="opencode" ;;
gemini-cli) _DISP_NAME="GEMINI.md"; _FW_DIR_NAME="gemini" ;;
claude-code) _DISP_NAME="CLAUDE.md"; _FW_DIR_NAME="claude" ;;
codex-cli) _DISP_NAME="AGENTS.md"; _FW_DIR_NAME="codex" ;;
*) die "Unknown platform: $PLATFORM" ;;
esac
echo -e "${BOLD}This will update core agents, skills, references, hooks, and ${_DISP_NAME}.${NC}"
@@ -132,11 +135,26 @@ case "$PLATFORM" in
MCP_SRC=""
MCP_DST=""
HAS_PLUGINS=0
DIST_SKILLS_DIR="$DIST_COMPONENTS_DIR/skills"
VAULT_SKILLS_DIR="$VAULT_COMPONENTS_DIR/skills"
;;
codex-cli)
DIST_COMPONENTS_DIR="$DIST_DIR/.codex"
VAULT_COMPONENTS_DIR="$VAULT_DIR/.codex"
DISPATCHER_SRC="$DIST_DIR/AGENTS.md"
DISPATCHER_DST="$VAULT_DIR/AGENTS.md"
MCP_SRC="$DIST_DIR/.codex/config.toml"
MCP_DST="$VAULT_DIR/.codex/config.toml"
HAS_PLUGINS=0
DIST_SKILLS_DIR="$DIST_DIR/.agents/skills"
VAULT_SKILLS_DIR="$VAULT_DIR/.agents/skills"
;;
*)
die "Unknown platform: $PLATFORM (install layout not defined)"
;;
esac
[[ -n "${DIST_SKILLS_DIR:-}" ]] || DIST_SKILLS_DIR="$DIST_COMPONENTS_DIR/skills"
[[ -n "${VAULT_SKILLS_DIR:-}" ]] || VAULT_SKILLS_DIR="$VAULT_COMPONENTS_DIR/skills"
PLATFORM_VAULT_DIR="$VAULT_COMPONENTS_DIR"
# Load opencode-specific helpers when building for opencode
@@ -158,9 +176,13 @@ mkdir -p "$VAULT_DIR/Meta/states"
# ── Update components (per-file logging enabled) ─────────────────────────────
VERBOSE_COPY=1
AGENT_COUNT=$(install_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
if [[ "$PLATFORM" == "codex-cli" ]]; then
AGENT_COUNT=$(install_toml_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
else
AGENT_COUNT=$(install_agents "$DIST_COMPONENTS_DIR/agents" "$VAULT_COMPONENTS_DIR/agents")
fi
REF_COUNT=$(install_refs "$DIST_COMPONENTS_DIR/references" "$VAULT_COMPONENTS_DIR/references")
SKILL_COUNT=$(install_skills "$DIST_COMPONENTS_DIR/skills" "$VAULT_COMPONENTS_DIR/skills")
SKILL_COUNT=$(install_skills "$DIST_SKILLS_DIR" "$VAULT_SKILLS_DIR")
HOOK_COUNT=$(install_hooks "$DIST_COMPONENTS_DIR/hooks" "$VAULT_COMPONENTS_DIR/hooks")
PLUGIN_COUNT=0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Tests for Codex CLI install/update flows in scripts/launchme.sh and scripts/updateme.sh
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
test_codex_cli_scripts_define_explicit_platform_cases() {
local result=0
grep -q 'codex-cli)' "$ROOT/scripts/launchme.sh" \
|| { echo 'launchme.sh should define a codex-cli) case'; result=1; }
grep -q 'codex-cli)' "$ROOT/scripts/updateme.sh" \
|| { echo 'updateme.sh should define a codex-cli) case'; result=1; }
grep -q '\.codex/agents' "$ROOT/scripts/updateme.sh" \
|| { echo 'updateme.sh should detect .codex/agents installs'; result=1; }
return $result
}
test_launchme_installs_codex_cli_layout() {
local vault; vault="$(mktemp -d)"
local log; log="$(mktemp)"
if ! bash "$ROOT/scripts/launchme.sh" --platform codex-cli --target "$vault" >"$log" 2>&1; then
cat "$log"
rm -rf "$vault" "$log"
return 1
fi
local result=0
[[ -f "$vault/AGENTS.md" ]] || { echo 'AGENTS.md missing after codex-cli install'; result=1; }
[[ -f "$vault/.codex/config.toml" ]] || { echo '.codex/config.toml missing after codex-cli install'; result=1; }
[[ -d "$vault/.codex/agents" ]] || { echo '.codex/agents missing after codex-cli install'; result=1; }
[[ -f "$vault/.codex/agents/transcriber.toml" || -f "$vault/.codex/agents/architect.toml" ]] \
|| { echo 'core codex agent TOML missing after install'; result=1; }
[[ -f "$vault/.agents/skills/onboarding/SKILL.md" ]] || { echo '.agents/skills/onboarding/SKILL.md missing after install'; result=1; }
if [[ -f "$vault/AGENTS.md" ]]; then
grep -q 'Codex CLI' "$vault/AGENTS.md" \
|| { echo 'AGENTS.md should contain Codex CLI guidance'; result=1; }
fi
if [[ -f "$vault/.codex/config.toml" ]]; then
grep -q '\[agents\]' "$vault/.codex/config.toml" \
|| { echo '.codex/config.toml should contain [agents]'; result=1; }
fi
rm -rf "$vault" "$log"
return $result
}
test_updateme_auto_detects_and_refreshes_codex_cli_install() {
local vault; vault="$(mktemp -d)"
local install_log; install_log="$(mktemp)"
local update_log; update_log="$(mktemp)"
if ! bash "$ROOT/scripts/launchme.sh" --platform codex-cli --target "$vault" >"$install_log" 2>&1; then
cat "$install_log"
rm -rf "$vault" "$install_log" "$update_log"
return 1
fi
printf 'stale dispatcher\n' > "$vault/AGENTS.md"
printf 'stale config\n' > "$vault/.codex/config.toml"
if ! printf 'c\n' | bash "$ROOT/scripts/updateme.sh" --target "$vault" >"$update_log" 2>&1; then
cat "$update_log"
rm -rf "$vault" "$install_log" "$update_log"
return 1
fi
local result=0
grep -q 'Detected platform: codex-cli' "$update_log" \
|| { echo 'updateme.sh should auto-detect codex-cli'; result=1; }
grep -q 'Codex CLI' "$vault/AGENTS.md" \
|| { echo 'update should refresh AGENTS.md content'; result=1; }
grep -q '\[agents\]' "$vault/.codex/config.toml" \
|| { echo 'update should refresh .codex/config.toml content'; result=1; }
[[ -d "$vault/.codex/agents" ]] || { echo '.codex/agents should remain after update'; result=1; }
[[ -d "$vault/.agents/skills" ]] || { echo '.agents/skills should remain after update'; result=1; }
rm -rf "$vault" "$install_log" "$update_log"
return $result
}

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# =============================================================================
# tests/scripts/platform-parity.test.sh — Four-platform build parity suite
# =============================================================================
# Proves that Codex CLI changes did not regress Claude Code, Gemini CLI,
# OpenCode, or Codex CLI build artifacts. Runs as part of tests/run.sh.
# =============================================================================
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# ---------------------------------------------------------------------------
# test_platform_build_matrix_produces_expected_dispatchers_and_roots
#
# Builds all four platforms and asserts each produces the correct dispatcher,
# platform config directory, and at least one canonical agent file.
# ---------------------------------------------------------------------------
test_platform_build_matrix_produces_expected_dispatchers_and_roots() {
local result=0
# Platform → expected artifacts map
# Format: "<dispatcher>|<agent_file>|<config_file>"
declare -A EXPECTED
EXPECTED["claude-code"]="dist/claude-code/CLAUDE.md|dist/claude-code/.claude/agents/architect.md|dist/claude-code/.mcp.json"
EXPECTED["gemini-cli"]="dist/gemini-cli/GEMINI.md|dist/gemini-cli/.gemini/agents/architect.md|dist/gemini-cli/.gemini/settings.json"
EXPECTED["opencode"]="dist/opencode/AGENTS.md|dist/opencode/.opencode/agents/architect.md|dist/opencode/opencode.json"
EXPECTED["codex-cli"]="dist/codex-cli/AGENTS.md|dist/codex-cli/.codex/agents/architect.toml|dist/codex-cli/.codex/config.toml"
for platform in claude-code gemini-cli opencode codex-cli; do
if ! bash "$ROOT/scripts/build.sh" --platform "$platform" >/dev/null 2>&1; then
echo "FAIL: build failed for platform: $platform"
result=1
continue
fi
IFS='|' read -r dispatcher agent_file config_file <<< "${EXPECTED[$platform]}"
[[ -f "$ROOT/$dispatcher" ]] \
|| { echo "FAIL [$platform]: dispatcher missing: $dispatcher"; result=1; }
[[ -f "$ROOT/$agent_file" ]] \
|| { echo "FAIL [$platform]: agent file missing: $agent_file"; result=1; }
[[ -f "$ROOT/$config_file" ]] \
|| { echo "FAIL [$platform]: config file missing: $config_file"; result=1; }
done
# Additional Codex-specific: skills directory
[[ -f "$ROOT/dist/codex-cli/.agents/skills/onboarding/SKILL.md" ]] \
|| { echo "FAIL [codex-cli]: .agents/skills/onboarding/SKILL.md missing"; result=1; }
return $result
}
# ---------------------------------------------------------------------------
# test_claude_snapshot_regression_still_passes_after_codex_changes
#
# Runs the Claude Code snapshot regression test. Fails immediately if the
# snapshot diff reports any drift — Codex changes must not touch Claude output.
# ---------------------------------------------------------------------------
test_claude_snapshot_regression_still_passes_after_codex_changes() {
local log; log="$(mktemp)"
if ! bash "$ROOT/tests/regression/run.sh" >"$log" 2>&1; then
echo "FAIL: Claude snapshot regression reported drift:"
cat "$log"
rm -f "$log"
return 1
fi
rm -f "$log"
return 0
}
# ---------------------------------------------------------------------------
# test_codex_install_update_gate_remains_in_the_full_suite
#
# Asserts that the Codex CLI install/update test file still exists and
# defines both required test function names. This gate ensures the parity
# suite does not accidentally exclude Codex install regression coverage.
# ---------------------------------------------------------------------------
test_codex_install_update_gate_remains_in_the_full_suite() {
local install_test="$ROOT/tests/scripts/codex-cli-install.test.sh"
local result=0
[[ -f "$install_test" ]] \
|| { echo "FAIL: tests/scripts/codex-cli-install.test.sh does not exist"; return 1; }
grep -q 'test_launchme_installs_codex_cli_layout' "$install_test" \
|| { echo "FAIL: test_launchme_installs_codex_cli_layout not found in codex-cli-install.test.sh"; result=1; }
grep -q 'test_updateme_auto_detects_and_refreshes_codex_cli_install' "$install_test" \
|| { echo "FAIL: test_updateme_auto_detects_and_refreshes_codex_cli_install not found in codex-cli-install.test.sh"; result=1; }
return $result
}

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
tests/support/toml_smoke_check.py — Validate TOML files generated by Phase 1 adapters.
Usage:
python tests/support/toml_smoke_check.py path/to/file.toml
Exit 0 if the file parses under the subset of TOML features used in Phase 1:
- Standard [table] headers
- Quoted table names: [mcp_servers."Google Calendar"]
- key = "string"
- key = true / false
- key = ["a", "b"] (array of strings)
- env = { KEY = "VALUE" } (inline table)
- Comments (# ...)
- Blank lines
- key = '''...''' (multiline literal string, any content)
- key = \"\"\"...\"\"\" (multiline basic string, any content)
Exit 1 with a clear error message if parsing fails.
"""
import re
import sys
# ---------------------------------------------------------------------------
# Token patterns for Phase 1 TOML subset
# ---------------------------------------------------------------------------
# Blank line or comment
RE_BLANK = re.compile(r'^\s*(#.*)?$')
# Standard table header: [agents] or [mcp_servers.Gmail]
RE_TABLE = re.compile(r'^\[([A-Za-z0-9_.-]+)\]\s*(#.*)?$')
# TOML basic string content used inside quoted table keys for this test suite.
# The generated keys only need literal characters plus escaped quote/backslash.
# Keeping the subset narrow helps catch accidental raw backslashes such as
# [mcp_servers."foo\bar"], which would change the key meaning.
_TOML_ESCAPE = r'\\(?:["\\])'
_TOML_SAFE_CHAR = r'[^"\\]'
_TOML_QSTR = rf'(?:{_TOML_SAFE_CHAR}|{_TOML_ESCAPE})*'
# Quoted table header: [mcp_servers."Google Calendar"]
RE_TABLE_QUOTED = re.compile(
rf'^\[([A-Za-z0-9_.-]+"{_TOML_QSTR}")\]\s*(#.*)?$'
)
# key = "string"
RE_KEY_STRING = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*"([^"\\]|\\.)*"\s*(#.*)?$')
# key = true or key = false
RE_KEY_BOOL = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*(true|false)\s*(#.*)?$')
# key = integer
RE_KEY_INT = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*-?\d+\s*(#.*)?$')
# key = ["a", "b", ...] (array of strings — may span a single line only)
RE_KEY_ARRAY = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*\[.*\]\s*(#.*)?$')
# key = { KEY = "VALUE", ... } (inline table — single line)
RE_KEY_INLINE_TABLE = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*\{.*\}\s*(#.*)?$')
# key = ''' (start of multiline literal string)
RE_MULTILINE_LIT_START = re.compile(r"^[A-Za-z0-9_.-]+\s*=\s*'''")
# key = """ (start of multiline basic string)
RE_MULTILINE_BASIC_START = re.compile(r'^[A-Za-z0-9_.-]+\s*=\s*"""')
# Closing delimiter for multiline literal: line that is exactly ''' or ends with '''
RE_MULTILINE_LIT_END = re.compile(r"'''$")
# Closing delimiter for multiline basic: line that is exactly """ or ends with """
RE_MULTILINE_BASIC_END = re.compile(r'"""$')
# Collect all line-level patterns (order matters — most specific first)
LINE_PATTERNS = [
RE_BLANK,
RE_TABLE_QUOTED,
RE_TABLE,
RE_KEY_STRING,
RE_KEY_BOOL,
RE_KEY_INT,
RE_KEY_ARRAY,
RE_KEY_INLINE_TABLE,
RE_MULTILINE_LIT_START,
RE_MULTILINE_BASIC_START,
]
def check_file(path: str) -> None:
try:
with open(path, encoding='utf-8') as fh:
lines = fh.readlines()
except FileNotFoundError:
print(f"ERROR: File not found: {path}", file=sys.stderr)
sys.exit(1)
except OSError as exc:
print(f"ERROR: Cannot read {path}: {exc}", file=sys.stderr)
sys.exit(1)
errors = []
in_multiline_lit = False # inside ''' ... '''
in_multiline_basic = False # inside """ ... """
for lineno, raw in enumerate(lines, start=1):
line = raw.rstrip('\n')
# Inside a multiline literal string: any content is valid until '''
if in_multiline_lit:
if RE_MULTILINE_LIT_END.search(line):
in_multiline_lit = False
# All lines inside a multiline literal are valid — no error
continue
# Inside a multiline basic string: any content is valid until """
if in_multiline_basic:
if RE_MULTILINE_BASIC_END.search(line):
in_multiline_basic = False
# All lines inside a multiline basic string are valid — no error
continue
# Check if this line opens a multiline string
if RE_MULTILINE_LIT_START.match(line):
# Check if it's also closed on the same line (inline)
rest = re.sub(r"^[A-Za-z0-9_.-]+\s*=\s*'''", "", line)
if rest.endswith("'''"):
pass # Single-line multiline literal (unusual but valid)
else:
in_multiline_lit = True
continue
if RE_MULTILINE_BASIC_START.match(line):
rest = re.sub(r'^[A-Za-z0-9_.-]+\s*=\s*"""', "", line)
if rest.endswith('"""'):
pass # Single-line multiline basic (unusual but valid)
else:
in_multiline_basic = True
continue
matched = any(pat.match(line) for pat in LINE_PATTERNS)
if not matched:
errors.append(f" line {lineno}: unexpected syntax: {line!r}")
if in_multiline_lit:
errors.append(" Unterminated multiline literal string (''' not closed)")
if in_multiline_basic:
errors.append(' Unterminated multiline basic string (""" not closed)')
if errors:
print(f"TOML parse error in {path}:", file=sys.stderr)
for err in errors:
print(err, file=sys.stderr)
sys.exit(1)
print(f"OK: {path}")
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <file.toml>", file=sys.stderr)
sys.exit(2)
check_file(sys.argv[1])