Skip to content
Open
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ overwrite user-modified agents.
| Agent | Activation | MCP config | Durable context / augmentation |
|-------|------------|------------|--------------------------------|
| Claude Code | Detected | `~/.claude.json` | Skill + three exact-tool graph agents; `SessionStart`, `SubagentStart`, non-blocking `PreToolUse` for `Grep`/`Glob`/`Bash`, and post-`Read` coverage |
| Codex CLI | Detected | `$CODEX_HOME/config.toml` | `AGENTS.md`, skill, three read-only agents; `SessionStart` + `SubagentStart` |
| Codex CLI | Detected | `$CODEX_HOME/config.toml` | Managed `AGENTS.md` activation pointer, skill, three read-only agents; `SessionStart` + `SubagentStart` |
| Gemini CLI | Detected | `.gemini/settings.json` | `GEMINI.md`, three explicit read/graph-tool subagents; `BeforeTool`, `AfterTool` `read_file` coverage, and `SessionStart` |
| Zed | Detected | platform `settings.json` (JSONC) | `AGENTS.md` + shared skill |
| OpenCode | Detected | `$OPENCODE_CONFIG` or resolved global config | `AGENTS.md`, skill, three deny-by-default read-only agents; plugin adds grep/glob graph lookup, post-`read` coverage, first-tool-result session context, and post-compaction reinjection |
Expand Down Expand Up @@ -536,6 +536,11 @@ overwrite user-modified agents.
| Oh My Pi (omp) | Detected | Effective agent directory (`OMP_PROFILE` / `PI_CODING_AGENT_DIR`; default `~/.omp/agent/mcp.json`) | Skill and three direct-MCP graph-tool subagents (Scout/Verify/Auditor); preserves user `AGENTS.md` |
| Sourcegraph Cody | Explicit opt-in | Existing `$CBM_CODY_CONFIG_PATH` | MCP only |

For Codex, install keeps only a tiny managed activation pointer in global
`$CODEX_HOME/AGENTS.md`; all detailed behavior lives in the installed `codebase-memory` skill.
Fresh installs create the pointer, upgrades replace the legacy full managed block while preserving
all user-owned bytes, and uninstall removes only the managed pointer.

### Sessions, compaction, and subagents

Hooks installed by this project are fail-open and context-only. Claude Code's
Expand Down
144 changes: 137 additions & 7 deletions scripts/smoke-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,12 @@ run_no_crash() {

TMPDIR=$(smoke_mktemp_dir)
DRYRUN_HOME=""
CODEX_LIFECYCLE_HOME=""
# On MSYS2/Windows, convert POSIX path to native Windows path for the binary
if command -v cygpath &>/dev/null; then
TMPDIR=$(cygpath -m "$TMPDIR")
fi
trap 'smoke_rmtree "$TMPDIR" "${DRYRUN_HOME:-}"' EXIT
trap 'smoke_rmtree "$TMPDIR" "${DRYRUN_HOME:-}" "${CODEX_LIFECYCLE_HOME:-}"' EXIT

CLI_STDERR=$(smoke_mktemp_file)
# 10 of the cli call sites assign directly (VAR=$(cli ...)). Under
Expand Down Expand Up @@ -1432,6 +1433,7 @@ echo '# Personal Rovo guidance' > "$ROVO_INSTRUCTIONS"
PHASE8_INSTALL_RC=0
PHASE8_INSTALL_LOG=$(smoke_mktemp_file)
HOME="$FAKE_HOME" \
CODEX_HOME="$FAKE_HOME/.codex" \
XDG_CONFIG_HOME="$FAKE_HOME/.config" \
APPDATA="$FAKE_HOME/AppData/Roaming" \
LOCALAPPDATA="$FAKE_HOME/AppData/Local" \
Expand Down Expand Up @@ -1704,12 +1706,139 @@ if ! grep -q 'existing_section' "$FAKE_HOME/.codex/config.toml"; then
fi
echo "OK 8f-h: Codex TOML (MCP + preserved existing)"

# 8i: Codex instructions
if [ ! -f "$FAKE_HOME/.codex/AGENTS.md" ] || ! grep -q 'codebase-memory-mcp' "$FAKE_HOME/.codex/AGENTS.md"; then
echo "FAIL 8i: Codex AGENTS.md missing"
exit 1
fi
echo "OK 8i: Codex instructions"
# 8i: Codex keeps only a tiny global activation pointer; the installed skill
# owns all detailed behavior. Exercise the complete lifecycle in a second HOME
# so Codex-only reinstalls cannot perturb the all-agent fixture below.
CODEX_POINTER_EXPECTED=$(smoke_mktemp_file)
printf '%s\n' \
'<!-- codebase-memory-mcp:start -->' \
'For structural codebase exploration, use the installed `codebase-memory` skill.' \
'<!-- codebase-memory-mcp:end -->' > "$CODEX_POINTER_EXPECTED"
if [ ! -f "$FAKE_HOME/.codex/AGENTS.md" ] ||
[ "$(smoke_file_sha256 "$FAKE_HOME/.codex/AGENTS.md")" != \
"$(smoke_file_sha256 "$CODEX_POINTER_EXPECTED")" ]; then
echo "FAIL 8i: fresh Codex install did not create the exact activation pointer"
exit 1
fi

CODEX_LIFECYCLE_HOME=$(smoke_mktemp_dir)
CODEX_LIFECYCLE_ROOT="$CODEX_LIFECYCLE_HOME/.codex"
CODEX_INSTRUCTIONS="$CODEX_LIFECYCLE_ROOT/AGENTS.md"
mkdir -p "$CODEX_LIFECYCLE_ROOT"

CODEX_FRESH_LOG=$(smoke_mktemp_file)
HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" install --skip-binary --clients=codex -y > "$CODEX_FRESH_LOG" 2>&1
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
"$(smoke_file_sha256 "$CODEX_POINTER_EXPECTED")" ] ||
! grep -q '\[mcp_servers.codebase-memory-mcp\]' "$CODEX_LIFECYCLE_ROOT/config.toml" ||
! grep -q 'search_graph' "$CODEX_LIFECYCLE_ROOT/skills/codebase-memory/SKILL.md" ||
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory-scout.toml" ] ||
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory.toml" ] ||
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory-auditor.toml" ] ||
! grep -q 'SessionStart' "$CODEX_LIFECYCLE_ROOT/config.toml" ||
! grep -q 'SubagentStart' "$CODEX_LIFECYCLE_ROOT/config.toml"; then
echo "FAIL 8i: isolated fresh Codex install lost the pointer or another surface"
exit 1
fi

printf '%s\n' \
'# Personal Codex guidance' \
'<!-- codebase-memory-mcp:start -->' \
'legacy managed guidance' \
'<!-- codebase-memory-mcp:end -->' \
'# Keep this line' > "$CODEX_INSTRUCTIONS"
CODEX_EXPECTED_MIGRATED=$(smoke_mktemp_file)
printf '%s\n' \
'# Personal Codex guidance' \
'<!-- codebase-memory-mcp:start -->' \
'For structural codebase exploration, use the installed `codebase-memory` skill.' \
'<!-- codebase-memory-mcp:end -->' \
'# Keep this line' > "$CODEX_EXPECTED_MIGRATED"
CODEX_EXPECTED_USER=$(smoke_mktemp_file)
printf '%s\n' '# Personal Codex guidance' '# Keep this line' > "$CODEX_EXPECTED_USER"
CODEX_LEGACY_SHA=$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")

CODEX_PLAN=$(smoke_mktemp_file)
HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" install --plan --skip-binary --clients=codex > "$CODEX_PLAN"
CODEX_INSTRUCTION_PATH=$(json_get "$CODEX_PLAN" \
"next((str(x) for x in d.get('instruction_files_planned', []) if str(x).replace('\\\\','/').endswith('/.codex/AGENTS.md')), '')")
CODEX_CLEANUP_COUNT=$(json_get "$CODEX_PLAN" "len(d.get('cleanup_actions_planned', []))")
if ! exact_path_match "$CODEX_INSTRUCTION_PATH" "$CODEX_INSTRUCTIONS" ||
[ "$CODEX_CLEANUP_COUNT" != "0" ] ||
[ "$CODEX_LEGACY_SHA" != "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" ]; then
echo "FAIL 8i: Codex plan did not describe a non-mutating pointer upsert"
exit 1
fi

CODEX_DRY_LOG=$(smoke_mktemp_file)
HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" install --dry-run --skip-binary --clients=codex -y > "$CODEX_DRY_LOG" 2>&1
if ! grep -q 'managed activation pointer' "$CODEX_DRY_LOG" ||
[ "$CODEX_LEGACY_SHA" != "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" ]; then
echo "FAIL 8i: Codex dry-run did not preview a byte-identical pointer migration"
exit 1
fi

CODEX_MIGRATE_LOG=$(smoke_mktemp_file)
HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" install --skip-binary --clients=codex -y > "$CODEX_MIGRATE_LOG" 2>&1
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
"$(smoke_file_sha256 "$CODEX_EXPECTED_MIGRATED")" ]; then
echo "FAIL 8i: Codex migration did not replace only the legacy managed block"
exit 1
fi

HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" install --skip-binary --clients=codex -y > /dev/null 2>&1
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
"$(smoke_file_sha256 "$CODEX_EXPECTED_MIGRATED")" ] ||
[ "$(grep -c '<!-- codebase-memory-mcp:start -->' "$CODEX_INSTRUCTIONS")" -ne 1 ]; then
echo "FAIL 8i: Codex reinstall changed or duplicated the activation pointer"
exit 1
fi
CODEX_UNINSTALL_LOG=$(smoke_mktemp_file)
if ! HOME="$CODEX_LIFECYCLE_HOME" \
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
"$BINARY" uninstall -y -n > "$CODEX_UNINSTALL_LOG" 2>&1; then
echo "FAIL 8i: Codex uninstall returned nonzero"
cat "$CODEX_UNINSTALL_LOG"
exit 1
fi
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
"$(smoke_file_sha256 "$CODEX_EXPECTED_USER")" ]; then
echo "FAIL 8i: Codex uninstall did not remove only the activation pointer"
exit 1
fi
echo "OK 8i: isolated Codex activation-pointer lifecycle"

# 8j-l: Gemini MCP + hooks + merge
CMD=$(json_get "$FAKE_HOME/.gemini/settings.json" "d['mcpServers']['codebase-memory-mcp']['command']")
Expand Down Expand Up @@ -2728,6 +2857,7 @@ if [[ "$BINARY" == *.exe ]]; then
UNINSTALL_BINARY="$SELF_PATH"
fi
HOME="$FAKE_HOME" \
CODEX_HOME="$FAKE_HOME/.codex" \
XDG_CONFIG_HOME="$FAKE_HOME/.config" \
APPDATA="$FAKE_HOME/AppData/Roaming" \
LOCALAPPDATA="$FAKE_HOME/AppData/Local" \
Expand Down
57 changes: 43 additions & 14 deletions src/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -1393,18 +1393,7 @@ static const char skill_content[] =
"5. `search_graph` results default to 50 per page — check `has_more` and use `offset`.\n";

static const char codex_instructions_content[] =
"# Codebase Knowledge Graph\n"
"\n"
"This project uses codebase-memory-mcp to maintain a knowledge graph of the codebase.\n"
"Use the MCP tools to explore and understand the code:\n"
"\n"
"- `search_graph` — find functions, classes, routes by pattern\n"
"- `trace_path` — trace who calls a function or what it calls\n"
"- `get_code_snippet` — read function source code\n"
"- `query_graph` — run Cypher queries for complex patterns\n"
"- `get_architecture` — high-level project summary\n"
"\n"
"Always prefer graph tools over grep for code discovery.\n";
"For structural codebase exploration, use the installed `codebase-memory` skill.\n";

/* Old skill names — cleaned up during install to remove stale directories. */
static const char *old_skill_names[] = {
Expand Down Expand Up @@ -8054,6 +8043,24 @@ static bool install_generic_agent_config(const char *label, const char *binary_p
return mcp_installed;
}

static bool install_codex_activation_pointer(const char *path, bool dry_run) {
if (!path) {
return false;
}
if (g_install_plan) {
plan_record("Codex CLI", "instructions", path);
return true;
}
return dry_run || cbm_upsert_instructions(path, codex_instructions_content) == CLI_OK;
Comment thread
astandrik marked this conversation as resolved.
}

static void report_codex_activation_pointer_install(const char *path, bool installed) {
printf(" instructions: %s (managed activation pointer)\n", path);
if (!installed) {
record_agent_config_error(false, "Codex CLI", "instructions_install", path);
}
}

static void install_windsurf_config(const char *binary_path, const char *config_path,
const char *rules_path, bool dry_run) {
if (g_install_plan) {
Expand Down Expand Up @@ -8997,6 +9004,8 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
snprintf(ip, sizeof(ip), "%s/AGENTS.md", config_dir);
snprintf(skills_dir, sizeof(skills_dir), "%s/skills", config_dir);
snprintf(ap, sizeof(ap), "%s/agents/codebase-memory.toml", config_dir);
/* A broken hook config must not keep legacy full guidance active. */
bool pointer_installed = install_codex_activation_pointer(ip, dry_run);
char command[CLI_BUF_8K];
char command_windows[CLI_BUF_8K];
char hooks_json[CLI_BUF_1K];
Expand Down Expand Up @@ -9024,10 +9033,16 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
record_agent_config_error_with_reason(
false, "Codex CLI", commands_ok ? "hook_preflight" : "hook_command_build", cp,
reason);
if (!g_install_plan) {
report_codex_activation_pointer_install(ip, pointer_installed);
}
goto codex_install_done;
}
install_generic_agent_config("Codex CLI", binary_path, cp, ip, dry_run,
install_generic_agent_config("Codex CLI", binary_path, cp, NULL, dry_run,
cbm_upsert_codex_mcp);
if (!g_install_plan) {
report_codex_activation_pointer_install(ip, pointer_installed);
}
install_agent_skill("Codex CLI", skills_dir, force, dry_run);
Comment thread
astandrik marked this conversation as resolved.
install_tiered_agent_profiles(
(cbm_tiered_profile_set_t){
Expand Down Expand Up @@ -10953,6 +10968,17 @@ static void uninstall_agent_mcp_instr(mcp_uninstall_args_t paths, bool dry_run,
}
}

static bool uninstall_codex_activation_pointer(const char *path, bool dry_run) {
return path && (dry_run || cbm_remove_instructions(path) == CLI_OK);
}

static void report_codex_activation_pointer_uninstall(const char *path, bool removed) {
printf(" instructions: removed managed activation pointer\n");
if (!removed) {
record_agent_config_error(true, "Codex CLI", "instructions_uninstall", path);
}
}

static void uninstall_agent_skill(const char *label, const char *skills_dir, bool dry_run) {
int removed = cbm_remove_skills(skills_dir, dry_run);
printf(" %s skill: %d removed\n", label, removed);
Expand Down Expand Up @@ -11387,6 +11413,7 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
snprintf(ip, sizeof(ip), "%s/AGENTS.md", config_dir);
snprintf(skills_dir, sizeof(skills_dir), "%s/skills", config_dir);
snprintf(ap, sizeof(ap), "%s/agents/codebase-memory.toml", config_dir);
bool pointer_removed = uninstall_codex_activation_pointer(ip, dry_run);
cbm_agent_installed_binary_path(home, installed_binary, sizeof(installed_binary));
char hook_command[CLI_BUF_8K];
char hook_command_windows[CLI_BUF_8K];
Expand All @@ -11407,10 +11434,12 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
? NULL
: cbm_toml_codex_hook_failure_name(preflight_failure);
record_agent_config_error_with_reason(true, "Codex CLI", "hook_preflight", cp, reason);
report_codex_activation_pointer_uninstall(ip, pointer_removed);
goto codex_toml_done;
}
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Codex CLI", cp, ip}, dry_run,
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Codex CLI", cp, NULL}, dry_run,
cbm_remove_codex_mcp_owned);
report_codex_activation_pointer_uninstall(ip, pointer_removed);
if (!dry_run &&
cbm_reconcile_codex_hooks_command(cp, hook_command, hook_command_windows,
CBM_TOML_CODEX_HOOK_REMOVE, false) != CLI_OK) {
Expand Down
Loading
Loading