codexmgr 0.4.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codexmgr-0.4.0 → codexmgr-0.4.1}/PKG-INFO +14 -9
- {codexmgr-0.4.0 → codexmgr-0.4.1}/README.md +13 -8
- {codexmgr-0.4.0 → codexmgr-0.4.1}/pyproject.toml +1 -1
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/__init__.py +1 -1
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/codex_launch.py +12 -8
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/paths.py +12 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_codex_cli.py +26 -12
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_paths.py +12 -1
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_project_gitignore.py +3 -2
- {codexmgr-0.4.0 → codexmgr-0.4.1}/uv.lock +1 -1
- {codexmgr-0.4.0 → codexmgr-0.4.1}/.github/actions/install_package/action.yml +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/.github/workflows/py_test.yml +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/.github/workflows/version_publish_main.yml +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/.gitignore +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/pytest.toml +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/agents/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/agents/file.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/agents/manager.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/agents/renderer.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/codex.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/codex_jit.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/config_mutations.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/health.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/navigation.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/commands/rules.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/errors.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/managed_text.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/options.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/core/toml_io.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/copies.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/listing.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/copies.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/listing.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/merge.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/hooks/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/parser.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/agents.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/mcp.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/apply.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/discovery.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/fields.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/project.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/mcp/tables.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/listing.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/mutation.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/packages/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/apply.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/generated.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/gitignore.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/snapshot.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/state.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/project/sync.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/py.typed +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/copies.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/listing.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/rules/tree.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/config.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/copies.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/listing.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/resolution.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/skills/sources.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/__init__.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/app.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/diff.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/focus.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/items.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/mcp_items.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/models.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/mutations.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_refs.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_selection.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_state.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/panels.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/rendering.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/rule_items.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/rule_tree.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/sections.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/staged_packages.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/src/codexmgr/tui/state.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/conftest.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_agents_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_agents_file.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_agentsmd_list_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_agentsmd_tools_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_apply_check_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_cd_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_empty_skill_config_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_home_resolution_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_hooks_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_mcp_apply_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_mcp_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_package_metadata.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_packages_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_python_compatibility.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_renderer.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_rules_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_skill_copies_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_skill_home_sources_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_skill_list_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_skills_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_status_doctor_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_sync_cli.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_toml_io.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_tui_app.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_tui_selection_focus.py +0 -0
- {codexmgr-0.4.0 → codexmgr-0.4.1}/tests/test_tui_state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codexmgr
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Manage project-local Codex configuration from reusable templates
|
|
5
5
|
Project-URL: Repository, https://github.com/JulianKimmig/codexmgr/
|
|
6
6
|
Keywords: agents,cli,codex,configuration,skills
|
|
@@ -474,14 +474,19 @@ codexmgr codex --simple <args...>
|
|
|
474
474
|
```
|
|
475
475
|
|
|
476
476
|
By default, `codexmgr codex` applies the current project config, launches the
|
|
477
|
-
real `codex` command with `CODEX_HOME` set to
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
477
|
+
real `codex` command with `CODEX_HOME` set to
|
|
478
|
+
`<project>/.codex/.runtime`, and forwards Codex arguments unchanged. For a
|
|
479
|
+
trusted project, Codex discovers the tracked `.codex/config.toml` as its
|
|
480
|
+
project configuration independently of `CODEX_HOME`. This keeps generated MCP
|
|
481
|
+
servers and other shared project settings in Git while mutable Codex state is
|
|
482
|
+
written below the ignored `.codex/.runtime` directory. User-provided `-c` and
|
|
483
|
+
`--config` arguments retain their normal Codex behavior.
|
|
484
|
+
|
|
485
|
+
The wrapper links the global authentication file into
|
|
486
|
+
`.codex/.runtime/auth.json`. It uses `CODEX_GLOBAL_AUTH` when that variable is
|
|
487
|
+
non-empty and otherwise uses `$HOME/.codex/auth.json`. A missing auth source
|
|
488
|
+
produces a warning but does not stop Codex, which can then authenticate within
|
|
489
|
+
the project runtime home.
|
|
485
490
|
|
|
486
491
|
Use `--simple` immediately after `codex` to run the basic command without
|
|
487
492
|
applying project state, changing the child `CODEX_HOME`, or managing an auth
|
|
@@ -450,14 +450,19 @@ codexmgr codex --simple <args...>
|
|
|
450
450
|
```
|
|
451
451
|
|
|
452
452
|
By default, `codexmgr codex` applies the current project config, launches the
|
|
453
|
-
real `codex` command with `CODEX_HOME` set to
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
453
|
+
real `codex` command with `CODEX_HOME` set to
|
|
454
|
+
`<project>/.codex/.runtime`, and forwards Codex arguments unchanged. For a
|
|
455
|
+
trusted project, Codex discovers the tracked `.codex/config.toml` as its
|
|
456
|
+
project configuration independently of `CODEX_HOME`. This keeps generated MCP
|
|
457
|
+
servers and other shared project settings in Git while mutable Codex state is
|
|
458
|
+
written below the ignored `.codex/.runtime` directory. User-provided `-c` and
|
|
459
|
+
`--config` arguments retain their normal Codex behavior.
|
|
460
|
+
|
|
461
|
+
The wrapper links the global authentication file into
|
|
462
|
+
`.codex/.runtime/auth.json`. It uses `CODEX_GLOBAL_AUTH` when that variable is
|
|
463
|
+
non-empty and otherwise uses `$HOME/.codex/auth.json`. A missing auth source
|
|
464
|
+
produces a warning but does not stop Codex, which can then authenticate within
|
|
465
|
+
the project runtime home.
|
|
461
466
|
|
|
462
467
|
Use `--simple` immediately after `codex` to run the basic command without
|
|
463
468
|
applying project state, changing the child `CODEX_HOME`, or managing an auth
|
|
@@ -6,6 +6,8 @@ from dataclasses import dataclass
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import TextIO
|
|
8
8
|
|
|
9
|
+
from ..core.paths import project_codex_dir, project_codex_runtime_dir
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
@dataclass(frozen=True)
|
|
11
13
|
class CodexLaunchRequest:
|
|
@@ -47,19 +49,21 @@ def build_project_codex_environment(
|
|
|
47
49
|
environ: Source process environment, or ``os.environ`` when omitted.
|
|
48
50
|
|
|
49
51
|
Returns:
|
|
50
|
-
A copied child environment. ``CODEX_HOME`` points to
|
|
51
|
-
|
|
52
|
+
A copied child environment. ``CODEX_HOME`` points to
|
|
53
|
+
``.codex/.runtime`` when the project ``.codex`` directory contains
|
|
54
|
+
``config.toml``.
|
|
52
55
|
"""
|
|
53
56
|
environment = dict(os.environ if environ is None else environ)
|
|
54
|
-
|
|
55
|
-
if not (
|
|
57
|
+
config_dir = project_codex_dir(cwd)
|
|
58
|
+
if not (config_dir / "config.toml").is_file():
|
|
56
59
|
return environment
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
runtime_dir = project_codex_runtime_dir(cwd)
|
|
62
|
+
runtime_dir.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
environment["CODEX_HOME"] = str(runtime_dir)
|
|
60
64
|
global_auth = _global_auth_path(environment)
|
|
61
65
|
if global_auth.is_file():
|
|
62
|
-
_replace_auth_link(
|
|
66
|
+
_replace_auth_link(runtime_dir / "auth.json", global_auth)
|
|
63
67
|
else:
|
|
64
68
|
stderr.write(
|
|
65
69
|
"codexmgr codex: warning: no global auth found at "
|
|
@@ -87,7 +91,7 @@ def _replace_auth_link(auth_link: Path, global_auth: Path) -> None:
|
|
|
87
91
|
"""Replace the project auth entry with a link to global authentication.
|
|
88
92
|
|
|
89
93
|
Args:
|
|
90
|
-
auth_link: Project-local ``.codex/auth.json`` link path.
|
|
94
|
+
auth_link: Project-local ``.codex/.runtime/auth.json`` link path.
|
|
91
95
|
global_auth: Existing global authentication source file.
|
|
92
96
|
|
|
93
97
|
Returns:
|
|
@@ -36,6 +36,18 @@ def project_codex_dir(cwd: Path) -> Path:
|
|
|
36
36
|
return cwd / ".codex"
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def project_codex_runtime_dir(cwd: Path) -> Path:
|
|
40
|
+
"""Return the project-local Codex runtime-home path.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
cwd: Project directory.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The ignored ``.codex/.runtime`` directory used as ``CODEX_HOME``.
|
|
47
|
+
"""
|
|
48
|
+
return project_codex_dir(cwd) / ".runtime"
|
|
49
|
+
|
|
50
|
+
|
|
39
51
|
def config_path(cwd: Path) -> Path:
|
|
40
52
|
"""Return the project codexmgr.toml path.
|
|
41
53
|
|
|
@@ -9,7 +9,7 @@ from codexmgr.interface.cli import main
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def test_build_codex_command_proxies_args_without_flattening_project_config(workspace):
|
|
12
|
-
"""The command relies on project
|
|
12
|
+
"""The command relies on project config instead of injected overrides."""
|
|
13
13
|
project, _ = workspace
|
|
14
14
|
(project / ".codex").mkdir()
|
|
15
15
|
(project / ".codex" / "config.toml").write_text(
|
|
@@ -111,21 +111,26 @@ def test_build_codex_command_keeps_repeated_user_lists(workspace):
|
|
|
111
111
|
]
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def
|
|
114
|
+
def test_codex_subcommand_uses_project_runtime_home_auth_and_return_code(
|
|
115
115
|
workspace,
|
|
116
116
|
monkeypatch,
|
|
117
117
|
):
|
|
118
|
-
"""Default launch
|
|
118
|
+
"""Default launch isolates runtime state and links the global auth file."""
|
|
119
119
|
project, codex_home = workspace
|
|
120
120
|
(project / ".codex").mkdir()
|
|
121
|
-
(project / ".codex" / "codexmgr.toml").write_text(
|
|
121
|
+
(project / ".codex" / "codexmgr.toml").write_text(
|
|
122
|
+
'[mcp.servers.example]\ncommand = "example"\n',
|
|
123
|
+
encoding="utf-8",
|
|
124
|
+
)
|
|
122
125
|
user_home = project.parent / "user-home"
|
|
123
126
|
global_auth = user_home / ".codex" / "auth.json"
|
|
124
127
|
global_auth.parent.mkdir(parents=True)
|
|
125
128
|
global_auth.write_text('{"token": "test"}\n', encoding="utf-8")
|
|
126
129
|
stale_auth = project.parent / "stale-auth.json"
|
|
127
130
|
stale_auth.write_text('{"token": "stale"}\n', encoding="utf-8")
|
|
128
|
-
|
|
131
|
+
runtime_home = project / ".codex" / ".runtime"
|
|
132
|
+
runtime_home.mkdir()
|
|
133
|
+
(runtime_home / "auth.json").symlink_to(stale_auth)
|
|
129
134
|
monkeypatch.delenv("CODEX_GLOBAL_AUTH", raising=False)
|
|
130
135
|
monkeypatch.setenv("HOME", str(user_home))
|
|
131
136
|
monkeypatch.setenv("CODEXMGR_LAUNCH_TEST", "forwarded")
|
|
@@ -136,6 +141,9 @@ def test_codex_subcommand_uses_project_home_auth_and_return_code(
|
|
|
136
141
|
captured["cwd"] = cwd
|
|
137
142
|
captured["codex_home"] = env["CODEX_HOME"]
|
|
138
143
|
captured["environment_marker"] = env["CODEXMGR_LAUNCH_TEST"]
|
|
144
|
+
captured["runtime_config_exists"] = (
|
|
145
|
+
project / ".codex" / ".runtime" / "config.toml"
|
|
146
|
+
).exists()
|
|
139
147
|
return SimpleNamespace(returncode=42)
|
|
140
148
|
|
|
141
149
|
monkeypatch.setattr("codexmgr.commands.codex.subprocess.run", fake_run)
|
|
@@ -155,10 +163,15 @@ def test_codex_subcommand_uses_project_home_auth_and_return_code(
|
|
|
155
163
|
assert captured == {
|
|
156
164
|
"command": ["codex", "--help"],
|
|
157
165
|
"cwd": project,
|
|
158
|
-
"codex_home": str(
|
|
166
|
+
"codex_home": str(runtime_home),
|
|
159
167
|
"environment_marker": "forwarded",
|
|
168
|
+
"runtime_config_exists": False,
|
|
160
169
|
}
|
|
161
|
-
|
|
170
|
+
assert runtime_home.is_dir()
|
|
171
|
+
assert tomllib.loads(
|
|
172
|
+
(project / ".codex" / "config.toml").read_text(encoding="utf-8")
|
|
173
|
+
)["mcp_servers"]["example"] == {"command": "example"}
|
|
174
|
+
auth_link = runtime_home / "auth.json"
|
|
162
175
|
assert auth_link.is_symlink()
|
|
163
176
|
assert auth_link.samefile(global_auth)
|
|
164
177
|
|
|
@@ -199,9 +212,10 @@ def test_codex_subcommand_warns_when_global_auth_is_missing(workspace, monkeypat
|
|
|
199
212
|
assert captured == {
|
|
200
213
|
"command": ["codex", "exec", "hello"],
|
|
201
214
|
"cwd": project,
|
|
202
|
-
"codex_home": str(project / ".codex"),
|
|
215
|
+
"codex_home": str(project / ".codex" / ".runtime"),
|
|
203
216
|
}
|
|
204
|
-
assert
|
|
217
|
+
assert (project / ".codex" / ".runtime").is_dir()
|
|
218
|
+
assert not (project / ".codex" / ".runtime" / "auth.json").exists()
|
|
205
219
|
|
|
206
220
|
|
|
207
221
|
def test_codex_subcommand_simple_mode_runs_basic_codex(workspace, monkeypatch):
|
|
@@ -282,7 +296,7 @@ enabled = ["example"]
|
|
|
282
296
|
assert captured == {
|
|
283
297
|
"command": ["codex", "exec", "hello"],
|
|
284
298
|
"cwd": project,
|
|
285
|
-
"codex_home": str(project / ".codex"),
|
|
299
|
+
"codex_home": str(project / ".codex" / ".runtime"),
|
|
286
300
|
}
|
|
287
301
|
|
|
288
302
|
|
|
@@ -385,7 +399,7 @@ skills = ["strict-skill"]
|
|
|
385
399
|
assert stdout.getvalue() == ""
|
|
386
400
|
assert stderr.getvalue() == ""
|
|
387
401
|
assert captured["cwd"] == project
|
|
388
|
-
assert captured["codex_home"] == str(project / ".codex")
|
|
402
|
+
assert captured["codex_home"] == str(project / ".codex" / ".runtime")
|
|
389
403
|
assert captured["codexmgr_toml"] == ""
|
|
390
404
|
assert captured["config"]["skills"]["config"] == [
|
|
391
405
|
{"path": str(base_skill.resolve()), "enabled": True},
|
|
@@ -442,7 +456,7 @@ def test_codex_jit_overlay_snapshots_and_restores_rules(
|
|
|
442
456
|
assert stdout.getvalue() == ""
|
|
443
457
|
assert stderr.getvalue() == ""
|
|
444
458
|
assert captured["command"] == ["codex", "exec", "hello"]
|
|
445
|
-
assert captured["codex_home"] == str(project / ".codex")
|
|
459
|
+
assert captured["codex_home"] == str(project / ".codex" / ".runtime")
|
|
446
460
|
assert captured["rule"] == "# Components\n"
|
|
447
461
|
assert not (project / ".rules").exists()
|
|
448
462
|
|
|
@@ -2,7 +2,11 @@ from pathlib import Path
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from codexmgr.core.paths import
|
|
5
|
+
from codexmgr.core.paths import (
|
|
6
|
+
global_codex_dir,
|
|
7
|
+
global_codexmgr_dir,
|
|
8
|
+
project_codex_runtime_dir,
|
|
9
|
+
)
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def test_defaults_to_home_codex_directory(monkeypatch: pytest.MonkeyPatch):
|
|
@@ -33,3 +37,10 @@ def test_codexmgr_home_uses_environment_variable(monkeypatch: pytest.MonkeyPatch
|
|
|
33
37
|
monkeypatch.setenv("CODEXMGR_HOME", "/tmp/custom-codexmgr-home")
|
|
34
38
|
|
|
35
39
|
assert global_codexmgr_dir() == Path("/tmp/custom-codexmgr-home")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_project_codex_runtime_directory_is_nested_below_project_config():
|
|
43
|
+
"""Mutable Codex state has a dedicated home below the project config."""
|
|
44
|
+
project = Path("/tmp/example-project")
|
|
45
|
+
|
|
46
|
+
assert project_codex_runtime_dir(project) == project / ".codex" / ".runtime"
|
|
@@ -75,7 +75,8 @@ def test_managed_gitignore_ignores_unknown_runtime_state_only(workspace, run_cli
|
|
|
75
75
|
"""Git ignores future runtime names but exposes every codexmgr-owned path."""
|
|
76
76
|
project, codex_home = workspace
|
|
77
77
|
run_cli(["setup"], project, codex_home)
|
|
78
|
-
runtime_file = project / ".codex" / "future-runtime-state.bin"
|
|
78
|
+
runtime_file = project / ".codex" / ".runtime" / "future-runtime-state.bin"
|
|
79
|
+
runtime_file.parent.mkdir()
|
|
79
80
|
runtime_file.write_text("runtime\n", encoding="utf-8")
|
|
80
81
|
agent_file = project / ".codex" / "agents" / "local.toml"
|
|
81
82
|
agent_file.parent.mkdir()
|
|
@@ -91,7 +92,7 @@ def test_managed_gitignore_ignores_unknown_runtime_state_only(workspace, run_cli
|
|
|
91
92
|
text=True,
|
|
92
93
|
)
|
|
93
94
|
|
|
94
|
-
assert _git_check_ignore(project, ".codex/future-runtime-state.bin") == 0
|
|
95
|
+
assert _git_check_ignore(project, ".codex/.runtime/future-runtime-state.bin") == 0
|
|
95
96
|
for managed_path in [
|
|
96
97
|
".codex/.gitignore",
|
|
97
98
|
".codex/codexmgr.toml",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|