codexmgr 0.3.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.3.0 → codexmgr-0.4.1}/PKG-INFO +35 -11
- {codexmgr-0.3.0 → codexmgr-0.4.1}/README.md +34 -10
- {codexmgr-0.3.0 → codexmgr-0.4.1}/pyproject.toml +1 -1
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/__init__.py +1 -1
- codexmgr-0.4.1/src/codexmgr/agents/file.py +43 -0
- codexmgr-0.4.1/src/codexmgr/commands/codex.py +114 -0
- codexmgr-0.4.1/src/codexmgr/commands/codex_launch.py +102 -0
- codexmgr-0.4.1/src/codexmgr/core/managed_text.py +78 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/core/paths.py +24 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/cli.py +10 -2
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/generated.py +2 -1
- codexmgr-0.4.1/src/codexmgr/project/gitignore.py +43 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_codex_cli.py +157 -74
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_paths.py +12 -1
- codexmgr-0.4.1/tests/test_project_gitignore.py +156 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/uv.lock +1 -1
- codexmgr-0.3.0/src/codexmgr/agents/file.py +0 -95
- codexmgr-0.3.0/src/codexmgr/commands/codex.py +0 -224
- {codexmgr-0.3.0 → codexmgr-0.4.1}/.github/actions/install_package/action.yml +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/.github/workflows/py_test.yml +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/.github/workflows/version_publish_main.yml +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/.gitignore +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/pytest.toml +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/agents/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/agents/manager.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/agents/renderer.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/codex_jit.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/config_mutations.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/health.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/navigation.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/commands/rules.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/core/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/core/errors.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/core/options.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/core/toml_io.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/copies.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/listing.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/custom_agents/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/copies.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/listing.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/merge.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/hooks/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/parser.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/agents.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/interface/parsers/mcp.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/apply.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/discovery.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/fields.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/project.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/mcp/tables.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/listing.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/mutation.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/packages/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/apply.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/snapshot.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/state.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/project/sync.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/py.typed +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/copies.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/listing.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/rules/tree.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/config.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/copies.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/listing.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/resolution.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/skills/sources.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/__init__.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/app.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/diff.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/focus.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/items.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/mcp_items.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/models.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/mutations.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_refs.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_selection.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/package_state.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/panels.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/rendering.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/rule_items.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/rule_tree.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/sections.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/staged_packages.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/src/codexmgr/tui/state.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/conftest.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_agents_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_agents_file.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_agentsmd_list_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_agentsmd_tools_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_apply_check_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_cd_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_empty_skill_config_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_home_resolution_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_hooks_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_mcp_apply_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_mcp_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_package_metadata.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_packages_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_python_compatibility.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_renderer.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_rules_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_skill_copies_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_skill_home_sources_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_skill_list_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_skills_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_status_doctor_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_sync_cli.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_toml_io.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_tui_app.py +0 -0
- {codexmgr-0.3.0 → codexmgr-0.4.1}/tests/test_tui_selection_focus.py +0 -0
- {codexmgr-0.3.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.
|
|
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
|
|
@@ -40,7 +40,7 @@ Use `codexmgr` when a project should:
|
|
|
40
40
|
- enable packaged Codex setups made from those reusable pieces
|
|
41
41
|
- keep reusable MCP server definitions out of the user-level Codex config
|
|
42
42
|
- check whether generated Codex files match the project config
|
|
43
|
-
- run `codex` with project `.codex
|
|
43
|
+
- run `codex` with the project `.codex` directory as its local Codex home
|
|
44
44
|
|
|
45
45
|
The basic model has three parts:
|
|
46
46
|
|
|
@@ -92,7 +92,8 @@ codexmgr setup
|
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
`setup` creates `.codex/`, creates `.codex/codexmgr.toml` if it is missing, and
|
|
95
|
-
runs `apply`. Existing project config is preserved.
|
|
95
|
+
runs `apply`. Existing project config is preserved. Apply also creates or
|
|
96
|
+
refreshes codexmgr's managed block in `.codex/.gitignore`.
|
|
96
97
|
|
|
97
98
|
Create a reusable `AGENTS.md` snippet in the manager home. This command creates
|
|
98
99
|
`$CODEXMGR_HOME/agentsmd/coding.toml` and refuses to overwrite an existing file:
|
|
@@ -146,6 +147,9 @@ file for you, and you can also edit it by hand when that is clearer.
|
|
|
146
147
|
|
|
147
148
|
`apply` resolves the source config and may write or update these managed files:
|
|
148
149
|
|
|
150
|
+
- `.codex/.gitignore`: preserves manual rules outside its managed block,
|
|
151
|
+
ignores every unknown `.codex` entry by default, and explicitly exposes the
|
|
152
|
+
codexmgr-owned files and directories listed below
|
|
149
153
|
- `.codex/codexmgr.lock`: resolved AGENTS.md, agent, skill, hook, rule, and MCP
|
|
150
154
|
state
|
|
151
155
|
- `.codex/config.toml`: project-local Codex config, including generated
|
|
@@ -368,6 +372,12 @@ managed project files, and refreshes generated state.
|
|
|
368
372
|
writing them. `apply --diff` also avoids writing and prints unified diffs for
|
|
369
373
|
the expected generated-file changes.
|
|
370
374
|
|
|
375
|
+
The `.codex/.gitignore` managed block uses a default-ignore rule rather than a
|
|
376
|
+
list of known runtime filenames. New caches, databases, sessions, and other
|
|
377
|
+
state created by future Codex versions therefore remain outside Git without a
|
|
378
|
+
codexmgr update. Project configuration, custom agents, and hook support files
|
|
379
|
+
remain visible to Git.
|
|
380
|
+
|
|
371
381
|
`doctor` checks project setup, home environment variables, project TOML syntax,
|
|
372
382
|
referenced snippets, enabled skills, enabled custom agents, enabled hook
|
|
373
383
|
bundles, enabled rules, and stale generated files.
|
|
@@ -460,14 +470,28 @@ Codex wrapper command:
|
|
|
460
470
|
|
|
461
471
|
```bash
|
|
462
472
|
codexmgr codex <args...>
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
473
|
+
codexmgr codex --simple <args...>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
By default, `codexmgr codex` applies the current project config, launches the
|
|
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.
|
|
490
|
+
|
|
491
|
+
Use `--simple` immediately after `codex` to run the basic command without
|
|
492
|
+
applying project state, changing the child `CODEX_HOME`, or managing an auth
|
|
493
|
+
link. This launch wrapper is intentionally CLI-only because the TUI does not
|
|
494
|
+
embed or proxy external terminal sessions.
|
|
471
495
|
|
|
472
496
|
The wrapper can run with a just-in-time package/profile overlay without changing
|
|
473
497
|
`.codex/codexmgr.toml`. Put Codex arguments after `--` when using this syntax:
|
|
@@ -16,7 +16,7 @@ Use `codexmgr` when a project should:
|
|
|
16
16
|
- enable packaged Codex setups made from those reusable pieces
|
|
17
17
|
- keep reusable MCP server definitions out of the user-level Codex config
|
|
18
18
|
- check whether generated Codex files match the project config
|
|
19
|
-
- run `codex` with project `.codex
|
|
19
|
+
- run `codex` with the project `.codex` directory as its local Codex home
|
|
20
20
|
|
|
21
21
|
The basic model has three parts:
|
|
22
22
|
|
|
@@ -68,7 +68,8 @@ codexmgr setup
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
`setup` creates `.codex/`, creates `.codex/codexmgr.toml` if it is missing, and
|
|
71
|
-
runs `apply`. Existing project config is preserved.
|
|
71
|
+
runs `apply`. Existing project config is preserved. Apply also creates or
|
|
72
|
+
refreshes codexmgr's managed block in `.codex/.gitignore`.
|
|
72
73
|
|
|
73
74
|
Create a reusable `AGENTS.md` snippet in the manager home. This command creates
|
|
74
75
|
`$CODEXMGR_HOME/agentsmd/coding.toml` and refuses to overwrite an existing file:
|
|
@@ -122,6 +123,9 @@ file for you, and you can also edit it by hand when that is clearer.
|
|
|
122
123
|
|
|
123
124
|
`apply` resolves the source config and may write or update these managed files:
|
|
124
125
|
|
|
126
|
+
- `.codex/.gitignore`: preserves manual rules outside its managed block,
|
|
127
|
+
ignores every unknown `.codex` entry by default, and explicitly exposes the
|
|
128
|
+
codexmgr-owned files and directories listed below
|
|
125
129
|
- `.codex/codexmgr.lock`: resolved AGENTS.md, agent, skill, hook, rule, and MCP
|
|
126
130
|
state
|
|
127
131
|
- `.codex/config.toml`: project-local Codex config, including generated
|
|
@@ -344,6 +348,12 @@ managed project files, and refreshes generated state.
|
|
|
344
348
|
writing them. `apply --diff` also avoids writing and prints unified diffs for
|
|
345
349
|
the expected generated-file changes.
|
|
346
350
|
|
|
351
|
+
The `.codex/.gitignore` managed block uses a default-ignore rule rather than a
|
|
352
|
+
list of known runtime filenames. New caches, databases, sessions, and other
|
|
353
|
+
state created by future Codex versions therefore remain outside Git without a
|
|
354
|
+
codexmgr update. Project configuration, custom agents, and hook support files
|
|
355
|
+
remain visible to Git.
|
|
356
|
+
|
|
347
357
|
`doctor` checks project setup, home environment variables, project TOML syntax,
|
|
348
358
|
referenced snippets, enabled skills, enabled custom agents, enabled hook
|
|
349
359
|
bundles, enabled rules, and stale generated files.
|
|
@@ -436,14 +446,28 @@ Codex wrapper command:
|
|
|
436
446
|
|
|
437
447
|
```bash
|
|
438
448
|
codexmgr codex <args...>
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
449
|
+
codexmgr codex --simple <args...>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
By default, `codexmgr codex` applies the current project config, launches the
|
|
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.
|
|
466
|
+
|
|
467
|
+
Use `--simple` immediately after `codex` to run the basic command without
|
|
468
|
+
applying project state, changing the child `CODEX_HOME`, or managing an auth
|
|
469
|
+
link. This launch wrapper is intentionally CLI-only because the TUI does not
|
|
470
|
+
embed or proxy external terminal sessions.
|
|
447
471
|
|
|
448
472
|
The wrapper can run with a just-in-time package/profile overlay without changing
|
|
449
473
|
`.codex/codexmgr.toml`. Put Codex arguments after `--` when using this syntax:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Helpers for preserving manual AGENTS.md content around a managed block."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ..core.managed_text import render_managed_block
|
|
6
|
+
|
|
7
|
+
BEGIN_MARKER = "<!-- BEGIN CODEXMGR GENERATED -->"
|
|
8
|
+
END_MARKER = "<!-- END CODEXMGR GENERATED -->"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def write_managed_agents_md(path: Path, generated_markdown: str) -> None:
|
|
12
|
+
"""Write generated markdown into the CODEXMGR managed block.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
path: Project AGENTS.md path to create or update.
|
|
16
|
+
generated_markdown: Markdown content to place inside the managed block.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
None. The file is written with UTF-8 encoding.
|
|
20
|
+
"""
|
|
21
|
+
current = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
22
|
+
updated = render_managed_agents_md(current, generated_markdown)
|
|
23
|
+
path.write_text(updated, encoding="utf-8")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def render_managed_agents_md(current: str, generated_markdown: str) -> str:
|
|
27
|
+
"""Render AGENTS.md content with an updated managed block.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
current: Existing AGENTS.md content, or an empty string for a new file.
|
|
31
|
+
generated_markdown: Markdown content for the managed block.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Full AGENTS.md content with the codexmgr managed block replaced or
|
|
35
|
+
appended.
|
|
36
|
+
"""
|
|
37
|
+
return render_managed_block(
|
|
38
|
+
current,
|
|
39
|
+
generated_markdown,
|
|
40
|
+
begin_marker=BEGIN_MARKER,
|
|
41
|
+
end_marker=END_MARKER,
|
|
42
|
+
artifact_name="AGENTS.md",
|
|
43
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Orchestration and subprocess execution for the external Codex command."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TextIO
|
|
6
|
+
|
|
7
|
+
from ..core.errors import CommandError
|
|
8
|
+
from .codex_jit import parse_codex_jit_request, run_with_jit_overlay
|
|
9
|
+
from .codex_launch import (
|
|
10
|
+
build_project_codex_environment,
|
|
11
|
+
parse_codex_launch_request,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_codex_command(
|
|
16
|
+
cwd: Path,
|
|
17
|
+
codex_home: Path,
|
|
18
|
+
codexmgr_home: Path,
|
|
19
|
+
codex_args: list[str],
|
|
20
|
+
stderr: TextIO,
|
|
21
|
+
) -> int:
|
|
22
|
+
"""Select a launch mode and run the external Codex executable.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
cwd: Project working directory for the external Codex process.
|
|
26
|
+
codex_home: Codex home used to resolve project skills.
|
|
27
|
+
codexmgr_home: codexmgr home containing package sources.
|
|
28
|
+
codex_args: Raw arguments after ``codexmgr codex``.
|
|
29
|
+
stderr: Stream that receives launch warnings.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The external Codex process return code.
|
|
33
|
+
"""
|
|
34
|
+
launch_request = parse_codex_launch_request(codex_args)
|
|
35
|
+
if launch_request.simple:
|
|
36
|
+
return run_codex(cwd, launch_request.codex_args)
|
|
37
|
+
|
|
38
|
+
request = parse_codex_jit_request(launch_request.codex_args)
|
|
39
|
+
|
|
40
|
+
def run_project_codex(project_cwd: Path, args: list[str]) -> int:
|
|
41
|
+
"""Launch Codex with the prepared project-local environment.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
project_cwd: Project working directory for the child process.
|
|
45
|
+
args: Arguments to proxy to the Codex executable.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
The external Codex process return code.
|
|
49
|
+
"""
|
|
50
|
+
environment = build_project_codex_environment(project_cwd, stderr)
|
|
51
|
+
return _run_subprocess(project_cwd, args, environment)
|
|
52
|
+
|
|
53
|
+
if request.enabled:
|
|
54
|
+
return run_with_jit_overlay(
|
|
55
|
+
request,
|
|
56
|
+
cwd,
|
|
57
|
+
codex_home,
|
|
58
|
+
codexmgr_home,
|
|
59
|
+
run_project_codex,
|
|
60
|
+
)
|
|
61
|
+
from ..project.apply import apply_project_config
|
|
62
|
+
|
|
63
|
+
apply_project_config(cwd, codex_home, codexmgr_home)
|
|
64
|
+
return run_project_codex(cwd, launch_request.codex_args)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_codex(cwd: Path, codex_args: list[str]) -> int:
|
|
68
|
+
"""Run the basic external Codex command without project modifications.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
cwd: Project working directory for the external codex process.
|
|
72
|
+
codex_args: Arguments to pass through to codex.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
The external codex process return code.
|
|
76
|
+
"""
|
|
77
|
+
return _run_subprocess(cwd, codex_args)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_codex_command(cwd: Path, codex_args: list[str]) -> list[str]:
|
|
81
|
+
"""Build an argument-preserving external Codex command invocation.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
cwd: Project working directory, retained for the public helper contract.
|
|
85
|
+
codex_args: Arguments to pass through to codex.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
The complete argv for the external codex process.
|
|
89
|
+
"""
|
|
90
|
+
return ["codex", *codex_args]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _run_subprocess(
|
|
94
|
+
cwd: Path,
|
|
95
|
+
codex_args: list[str],
|
|
96
|
+
environment: dict[str, str] | None = None,
|
|
97
|
+
) -> int:
|
|
98
|
+
"""Run Codex and translate executable lookup failures.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
cwd: Working directory for the external Codex process.
|
|
102
|
+
codex_args: Arguments to pass through to Codex.
|
|
103
|
+
environment: Optional explicit child process environment.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
The external Codex process return code.
|
|
107
|
+
"""
|
|
108
|
+
command = build_codex_command(cwd, codex_args)
|
|
109
|
+
try:
|
|
110
|
+
if environment is None:
|
|
111
|
+
return subprocess.run(command, cwd=cwd).returncode
|
|
112
|
+
return subprocess.run(command, cwd=cwd, env=environment).returncode
|
|
113
|
+
except FileNotFoundError as exc:
|
|
114
|
+
raise CommandError("codex command not found") from exc
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Launch-mode parsing and project-local Codex environment preparation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TextIO
|
|
8
|
+
|
|
9
|
+
from ..core.paths import project_codex_dir, project_codex_runtime_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class CodexLaunchRequest:
|
|
14
|
+
"""A parsed request to launch Codex in managed or simple mode.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
simple: Whether to bypass all codexmgr project preparation.
|
|
18
|
+
codex_args: Arguments to proxy to the external Codex executable.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
simple: bool
|
|
22
|
+
codex_args: list[str]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_codex_launch_request(codex_args: list[str]) -> CodexLaunchRequest:
|
|
26
|
+
"""Parse the optional leading codexmgr ``--simple`` launch flag.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
codex_args: Raw arguments following ``codexmgr codex``.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The selected launch mode and external Codex arguments.
|
|
33
|
+
"""
|
|
34
|
+
if codex_args[:1] == ["--simple"]:
|
|
35
|
+
return CodexLaunchRequest(simple=True, codex_args=codex_args[1:])
|
|
36
|
+
return CodexLaunchRequest(simple=False, codex_args=list(codex_args))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_project_codex_environment(
|
|
40
|
+
cwd: Path,
|
|
41
|
+
stderr: TextIO,
|
|
42
|
+
environ: Mapping[str, str] | None = None,
|
|
43
|
+
) -> dict[str, str]:
|
|
44
|
+
"""Build the child environment for a project-local Codex launch.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
cwd: Project directory whose local Codex home may be selected.
|
|
48
|
+
stderr: Stream that receives a missing-auth warning.
|
|
49
|
+
environ: Source process environment, or ``os.environ`` when omitted.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
A copied child environment. ``CODEX_HOME`` points to
|
|
53
|
+
``.codex/.runtime`` when the project ``.codex`` directory contains
|
|
54
|
+
``config.toml``.
|
|
55
|
+
"""
|
|
56
|
+
environment = dict(os.environ if environ is None else environ)
|
|
57
|
+
config_dir = project_codex_dir(cwd)
|
|
58
|
+
if not (config_dir / "config.toml").is_file():
|
|
59
|
+
return environment
|
|
60
|
+
|
|
61
|
+
runtime_dir = project_codex_runtime_dir(cwd)
|
|
62
|
+
runtime_dir.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
environment["CODEX_HOME"] = str(runtime_dir)
|
|
64
|
+
global_auth = _global_auth_path(environment)
|
|
65
|
+
if global_auth.is_file():
|
|
66
|
+
_replace_auth_link(runtime_dir / "auth.json", global_auth)
|
|
67
|
+
else:
|
|
68
|
+
stderr.write(
|
|
69
|
+
"codexmgr codex: warning: no global auth found at "
|
|
70
|
+
f"{global_auth}; you may need to auth once.\n"
|
|
71
|
+
)
|
|
72
|
+
return environment
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _global_auth_path(environment: Mapping[str, str]) -> Path:
|
|
76
|
+
"""Resolve the read-only global authentication source.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
environment: Launch environment containing HOME and optional override.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
``CODEX_GLOBAL_AUTH`` when non-empty, otherwise HOME's Codex auth path.
|
|
83
|
+
"""
|
|
84
|
+
configured_auth = environment.get("CODEX_GLOBAL_AUTH")
|
|
85
|
+
if configured_auth:
|
|
86
|
+
return Path(configured_auth)
|
|
87
|
+
return Path(environment["HOME"]) / ".codex" / "auth.json"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _replace_auth_link(auth_link: Path, global_auth: Path) -> None:
|
|
91
|
+
"""Replace the project auth entry with a link to global authentication.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
auth_link: Project-local ``.codex/.runtime/auth.json`` link path.
|
|
95
|
+
global_auth: Existing global authentication source file.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
None. Any existing file or symbolic link at ``auth_link`` is replaced.
|
|
99
|
+
"""
|
|
100
|
+
if auth_link.exists() or auth_link.is_symlink():
|
|
101
|
+
auth_link.unlink()
|
|
102
|
+
auth_link.symlink_to(global_auth)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Reusable rendering for marker-delimited codexmgr-managed text blocks."""
|
|
2
|
+
|
|
3
|
+
from ..core.errors import CommandError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def render_managed_block(
|
|
7
|
+
current: str,
|
|
8
|
+
generated: str,
|
|
9
|
+
*,
|
|
10
|
+
begin_marker: str,
|
|
11
|
+
end_marker: str,
|
|
12
|
+
artifact_name: str,
|
|
13
|
+
) -> str:
|
|
14
|
+
"""Replace or append one marker-delimited generated text block.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
current: Existing artifact content, or an empty string when missing.
|
|
18
|
+
generated: Generated content to place between the markers.
|
|
19
|
+
begin_marker: Exact line marking the beginning of managed content.
|
|
20
|
+
end_marker: Exact line marking the end of managed content.
|
|
21
|
+
artifact_name: Human-readable artifact name for validation errors.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Full artifact content with one current codexmgr-managed block.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
CommandError: If existing managed-block markers are malformed.
|
|
28
|
+
"""
|
|
29
|
+
begin_count = current.count(begin_marker)
|
|
30
|
+
end_count = current.count(end_marker)
|
|
31
|
+
if begin_count != end_count:
|
|
32
|
+
raise CommandError(
|
|
33
|
+
f"Incomplete CODEXMGR generated block in {artifact_name}"
|
|
34
|
+
)
|
|
35
|
+
if begin_count > 1:
|
|
36
|
+
raise CommandError(f"Multiple CODEXMGR generated blocks in {artifact_name}")
|
|
37
|
+
|
|
38
|
+
block = _format_block(generated, begin_marker, end_marker)
|
|
39
|
+
if begin_count == 0:
|
|
40
|
+
return _append_block(current, block)
|
|
41
|
+
|
|
42
|
+
begin_index = current.index(begin_marker)
|
|
43
|
+
end_index = current.index(end_marker, begin_index)
|
|
44
|
+
after_index = end_index + len(end_marker)
|
|
45
|
+
return f"{current[:begin_index]}{block}{current[after_index:]}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _append_block(current: str, block: str) -> str:
|
|
49
|
+
"""Append a formatted managed block to existing content.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
current: Existing artifact content.
|
|
53
|
+
block: Fully formatted block including both markers.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Content ending in the appended managed block and a newline.
|
|
57
|
+
"""
|
|
58
|
+
if not current:
|
|
59
|
+
return f"{block}\n"
|
|
60
|
+
trimmed = current.rstrip("\n")
|
|
61
|
+
return f"{trimmed}\n\n{block}\n"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _format_block(generated: str, begin_marker: str, end_marker: str) -> str:
|
|
65
|
+
"""Wrap generated content in its managed-block markers.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
generated: Generated text to normalize and wrap.
|
|
69
|
+
begin_marker: Beginning marker line.
|
|
70
|
+
end_marker: Ending marker line.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Marker-delimited content without a trailing newline.
|
|
74
|
+
"""
|
|
75
|
+
body = generated.rstrip("\n")
|
|
76
|
+
if not body:
|
|
77
|
+
return f"{begin_marker}\n{end_marker}"
|
|
78
|
+
return f"{begin_marker}\n{body}\n{end_marker}"
|
|
@@ -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
|
|
|
@@ -72,6 +84,18 @@ def codex_config_path(cwd: Path) -> Path:
|
|
|
72
84
|
return project_codex_dir(cwd) / "config.toml"
|
|
73
85
|
|
|
74
86
|
|
|
87
|
+
def codex_gitignore_path(cwd: Path) -> Path:
|
|
88
|
+
"""Return the managed project Codex ignore-file path.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
cwd: Project directory.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Path to .codex/.gitignore.
|
|
95
|
+
"""
|
|
96
|
+
return project_codex_dir(cwd) / ".gitignore"
|
|
97
|
+
|
|
98
|
+
|
|
75
99
|
def agents_md_path(cwd: Path) -> Path:
|
|
76
100
|
"""Return the project AGENTS.md path.
|
|
77
101
|
|
|
@@ -65,7 +65,7 @@ def main(
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
try:
|
|
68
|
-
return _dispatch(args, project_dir, codex_dir, codexmgr_dir, out)
|
|
68
|
+
return _dispatch(args, project_dir, codex_dir, codexmgr_dir, out, err)
|
|
69
69
|
except CommandError as exc:
|
|
70
70
|
err.write(f"{exc}\n")
|
|
71
71
|
return 1
|
|
@@ -100,6 +100,7 @@ def _dispatch(
|
|
|
100
100
|
codex_home: Path,
|
|
101
101
|
codexmgr_home: Path,
|
|
102
102
|
stdout: TextIO,
|
|
103
|
+
stderr: TextIO,
|
|
103
104
|
) -> int:
|
|
104
105
|
"""Run the parsed command.
|
|
105
106
|
|
|
@@ -109,6 +110,7 @@ def _dispatch(
|
|
|
109
110
|
codex_home: Global Codex home for resolving named skills.
|
|
110
111
|
codexmgr_home: codexmgr home for resolving named AGENTS.md templates.
|
|
111
112
|
stdout: Stream for command output.
|
|
113
|
+
stderr: Stream for command warnings.
|
|
112
114
|
|
|
113
115
|
Returns:
|
|
114
116
|
A process-style exit code where zero means success.
|
|
@@ -136,7 +138,13 @@ def _dispatch(
|
|
|
136
138
|
return run_codexmgr_home_action(codexmgr_home, args.cd_action, stdout)
|
|
137
139
|
|
|
138
140
|
if args.command == "codex":
|
|
139
|
-
return run_codex_command(
|
|
141
|
+
return run_codex_command(
|
|
142
|
+
cwd,
|
|
143
|
+
codex_home,
|
|
144
|
+
codexmgr_home,
|
|
145
|
+
args.codex_args,
|
|
146
|
+
stderr,
|
|
147
|
+
)
|
|
140
148
|
|
|
141
149
|
if args.command == "agentsmd" and args.agentsmd_command == "list":
|
|
142
150
|
options = list_agentsmd_options(codexmgr_home)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from .gitignore import build_project_gitignore_file
|
|
6
7
|
from .state import GeneratedFile
|
|
7
8
|
from ..agents.file import render_managed_agents_md
|
|
8
9
|
from ..agents.renderer import render_agents_markdown
|
|
@@ -67,7 +68,7 @@ def build_generated_files(
|
|
|
67
68
|
Returns:
|
|
68
69
|
Expected generated files in write order.
|
|
69
70
|
"""
|
|
70
|
-
files: list[GeneratedFile] = []
|
|
71
|
+
files: list[GeneratedFile] = [build_project_gitignore_file(cwd)]
|
|
71
72
|
if lock_data:
|
|
72
73
|
files.append(GeneratedFile(lock_path(cwd), dump_toml(lock_data)))
|
|
73
74
|
if "agents_md" in config:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Managed ignore rules for project-local Codex runtime state."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .state import GeneratedFile
|
|
6
|
+
from ..core.managed_text import render_managed_block
|
|
7
|
+
from ..core.paths import codex_gitignore_path
|
|
8
|
+
|
|
9
|
+
BEGIN_MARKER = "# BEGIN CODEXMGR GENERATED"
|
|
10
|
+
END_MARKER = "# END CODEXMGR GENERATED"
|
|
11
|
+
MANAGED_IGNORE_RULES = """*
|
|
12
|
+
!/.gitignore
|
|
13
|
+
!/codexmgr.toml
|
|
14
|
+
!/codexmgr.lock
|
|
15
|
+
!/config.toml
|
|
16
|
+
!/hooks.json
|
|
17
|
+
!/agents/
|
|
18
|
+
!/agents/**
|
|
19
|
+
!/hooks/
|
|
20
|
+
!/hooks/**
|
|
21
|
+
__pycache__/
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_project_gitignore_file(cwd: Path) -> GeneratedFile:
|
|
26
|
+
"""Build the future-proof project Codex ignore-file state.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
cwd: Project directory containing the local ``.codex`` home.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Generated file preserving manual content outside codexmgr's block.
|
|
33
|
+
"""
|
|
34
|
+
path = codex_gitignore_path(cwd)
|
|
35
|
+
current = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
36
|
+
content = render_managed_block(
|
|
37
|
+
current,
|
|
38
|
+
MANAGED_IGNORE_RULES,
|
|
39
|
+
begin_marker=BEGIN_MARKER,
|
|
40
|
+
end_marker=END_MARKER,
|
|
41
|
+
artifact_name=".codex/.gitignore",
|
|
42
|
+
)
|
|
43
|
+
return GeneratedFile(path, content)
|