-
Notifications
You must be signed in to change notification settings - Fork 1
Multi Project Mode
By default the server is pinned to one project: --work-dir is the project root, and every tool resolves against it. Multi-project mode turns --work-dir into an access root — a directory beneath which each conversation selects its own project — so one running server can serve many repositories without a process per repo.
Enable it with --multi-project or "multiProject": true.
codexify --work-dir /path/to/projects --multi-projectOne static codexify.config.json is still read once at startup; selection changes only the effective work directory the project tools use, never the server configuration itself.
Each conversation binds a workspace exactly once through set_project_root. An ordinary new binding selects either a project (path) or a private scratch workspace (withoutProject: true); a continuation binding instead reuses an exact previously recorded workspace (resumePath). When the intended new project isn't known, list_projects provides a search step first. The setup app presents the ordinary choices as a searchable picker, with Chat without a project first and a worktree checkbox before the project results.
-
Ordinary path rules.
pathis relative to the access root or absolute, but its canonical target must be an existing directory inside that root. Traversal (..) and symlink escapes are rejected after canonicalization, so a link pointing outside can't smuggle a selection past the check. -
Git repository URLs.
pathmay instead be a provider-agnostic HTTPS or SSH clone URL ending in.git, including GitLab and SCP-style SSH forms such asgit@gitlab.com:group/repository.git. GitHub additionally accepts repository-root URLs without.gitplus HTTPS branch (/tree/<branch>), pull-request (/pull/<number>), and full-40-hex commit (/commit/<sha>) URLs. Codexify reuses exactly one matching local checkout when possible, otherwise it clones intoprojectCloneDir/--project-clone-dir(default: the access root). Provider-agnostic URLs select the repository's default checkout. Targeted GitHub URLs fetch the exact branch/PR/commit; when an existing source checkout is on another commit, Codexify leaves it untouched and normally binds a detached managed worktree at the requested target. IfcreateWorktree: falseor modeneverforbids that worktree, selection succeeds only when the source checkout is already at the exact target. Credential-bearing HTTPS URLs, query strings/fragments,file://, plain HTTP,git://, and other arbitrary transports are rejected. -
Binding scope. The binding belongs to the ChatGPT conversation, keyed from
_meta["openai/session"](hashed, never stored raw). Simultaneous chats can hold different projects, and a later turn recovers its own root after MCP reconnects or a server restart. A client with no ChatGPT conversation metadata falls back to a binding that lasts only the current MCP transport session. -
Per-selection worktree override. A
pathrequest may addcreateWorktree: trueto force a managed worktree orfalseto force the source checkout. Omitting it appliesworktrees.mode. The override affects only that binding and never rewrites configuration. Repeating an immutable binding with a conflicting placement request is rejected. - No switching. A conversation cannot switch roots once bound — start another chat for another project. Re-selecting the same canonical workspace and placement is idempotent.
-
Before selection. Project-scoped tools are unavailable and say why.
list_projectsandset_project_rootare the two project-independent tools present for this workflow.
Pass withoutProject: true to set_project_root, or choose Chat without a project in the setup app, when the task needs files and commands but no repository. This choice is mutually exclusive with path.
- A ChatGPT conversation receives a durable private scratch directory outside the configured project access root. Its files and binding survive MCP reconnects and Codexify restarts.
- A generic MCP client without stable ChatGPT conversation metadata receives a transport-scoped scratch directory that is removed when that transport session ends.
- Filesystem, command, memory, skill, and project-instruction tools resolve against the scratch directory. The project catalogue and configured access-root contents are not exposed through it.
- The scratch choice is immutable, just like a project binding. Start a new chat to choose a project later.
After either choice, call get_agent_brief before using workspace-scoped tools. set_project_root reports the active scratch path, or both the active managed worktree and source checkout when they differ.
resumePath is reserved for a continuation prompt produced from an older-schema ChatGPT conversation. It must be the exact absolute active path of a workspace already present in Codexify's saved bindings under the same access root, and it cannot be combined with path, withoutProject, or createWorktree:
{ "resumePath": "/absolute/path/to/the/existing/workspace" }Call it before any ordinary project selection. Codexify canonicalizes the path, locks the destination conversation's binding, and accepts only one of these previously validated targets:
- the exact project root from an existing project binding, retaining whether it is a direct checkout or managed worktree and its source/worktree metadata; or
- the exact persistent scratch root from an existing ChatGPT scratch marker in the same access-root namespace.
This is intentionally not ordinary access-root selection. A recorded managed worktree or scratch root may live outside the access root, but an arbitrary outside path cannot be resumed. Resumption never clones, fetches, pulls, creates a worktree, runs a worktree setup script, resets, or switches branches. Relative, unrecorded, missing, deleted, wrong-scope, malformed, or tampered targets fail without binding anything; the caller must not fall back to path or allocate a replacement workspace. Transport-scoped clients cannot resume because they lack stable ChatGPT conversation metadata.
Resumption preserves the filesystem and project-root-keyed memory exactly, including staged, unstaged, and untracked changes. It does not transfer conversation messages, in-memory command sessions, or conversation-keyed diff checkpoints. The original binding remains valid, so the two conversations must not edit the shared workspace concurrently. Repeating the same resume in the destination conversation is idempotent; trying to resume a different workspace into an already-bound conversation is rejected.
list_projects searches a read-only catalogue assembled from two sources:
-
Native Codex trust table — Codex records trust decisions in its user config:
[projects."/absolute/path/to/project"] trust_level = "trusted"
Codexify reads those paths as candidates. It doesn't treat the table as exhaustive — entries may be stale or lack descriptions.
-
Explicit
projectCatalog.entries— operator-authored providers that can add aliases/descriptions or supply projects absent from the native table:{ "multiProject": true, "projectCatalog": { "codexConfig": { "enabled": true, "trustedOnly": true }, "entries": [ { "path": "codexify", "name": "Codexify", "aliases": ["ChatGPT MCP bridge"], "description": "Rust MCP bridge exposing local programming tools to ChatGPT" } ] } }
Every candidate still passes Codexify's own checks: it must exist, resolve to a directory, and canonicalize to the access root itself or a descendant. Missing entries, files, and symlink escapes are skipped; duplicate canonical targets are merged. Native Codex trust is only catalogue metadata plus the default trustedOnly filter — it never grants access outside --work-dir, and neither does an explicit entry.
list_projects returns a selector relative to the access root, passable unchanged as set_project_root.path. Its optional query matches names, aliases, descriptions, and selectors case-insensitively with exact/prefix/substring ranking. It never binds automatically — if several results are plausible, the agent's instructions require it to ask you rather than guess, because a wrong binding can't be changed in that conversation.
The native table is read live on every list_projects call, so newly trusted projects become discoverable without restarting. Project-local .codex/config.toml layers are not scanned — they matter only after selection. Catalogue construction never opens a candidate's README, source, .codex/, or AGENTS.md; contents stay unread until the conversation selects that project.
Git URL selection is separate from catalogue listing. Before cloning, Codexify checks the conventional destination, catalogue candidates, and immediate children of projectCloneDir, comparing normalized remotes at each Git top level. Exactly one match is reused; multiple matches are rejected as ambiguous. Non-GitHub URLs must end in .git unless a matching checkout already exists. GitHub branch names may contain /; everything after /tree/ is treated as the branch ref.
-
Worktrees. With the default worktree mode, the first ordinary conversation selecting a Git project uses the source checkout directly; a second ordinary selection gets a detached managed worktree. Explicit
createWorktreeoverrides this placement. AresumePathcontinuation deliberately reuses the recorded tree and therefore must not run concurrently with its source conversation. See Worktree Isolation. - Scratch workspaces. Projectless chats do not create or reuse Git worktrees. Their private scratch roots have the conversation or transport lifetime described above.
-
Saved state. With an explicit
memory.dir, each selected project gets its own hashed child directory. Conversation bindings stay enabled even whenmemory.enabledisfalse. See Context and Memory. - Diff checkpoints. Namespaced per conversation/project pair. See Diff Checkpoints.
To clear a stray binding, delete its file under ~/.codexify/conversation-projects/. There is no tool to re-point an already-bound conversation; resumePath only creates a new conversation binding to an exact workspace already authorized by another saved binding. A managed worktree stays referenced while any binding exists; startup cleanup skips referenced or dirty worktrees and only removes older clean, unreferenced entries beyond keepCount.
codexify projects list --work-dir /path/to/projects
codexify projects list --work-dir /path/to/projects --query "codexify"
codexify projects list --work-dir /path/to/projects --json
codexify projects list --work-dir /path/to/projects --show-skipped--show-skipped (local-only) also prints paths rejected as missing, untrusted, or outside the access root, plus merged duplicates. See CLI Reference.
Multi-project selection isolates Codexify's structured tools and per-conversation state — it is not an OS sandbox. The entire access-root subtree is intentionally selectable by a new conversation, and resumePath can alias an exact previously authorized managed worktree or scratch path outside it. Treat every reachable workspace as sensitive. See Security Model.
-
Worktree Isolation · Connecting to ChatGPT (multi-project onboarding) · Configuration (
projectCatalog,multiProject).
Repository · Releases · Report an issue · MIT License
Getting started
Reference
How it works
Multi-project
Extending
Operations