code-agnostic 0.5.1__tar.gz → 0.6.0__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.
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/PKG-INFO +1 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/__init__.py +1 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/apps_service.py +21 -10
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/service.py +17 -5
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/service.py +8 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/service.py +4 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/executor.py +8 -6
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/models.py +1 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/planner.py +1 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/PKG-INFO +1 -1
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/SOURCES.txt +1 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/pyproject.toml +1 -1
- code_agnostic-0.6.0/tests/test_cli_apply_codex_agent_cleanup.py +71 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/LICENSE +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/README.md +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/__main__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/claude.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/codex.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/compilers.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/copilot.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/models.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/opencode.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/agents/parser.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/app_id.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/claude/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/claude/config_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/claude/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/claude/service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/config_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/codex/schema_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/compiled_planning.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/framework.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/repositories.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/loader.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/models.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/schema.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/symlink_planning.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/utils.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/copilot/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/copilot/config_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/copilot/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/copilot/service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/config_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/schema_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/cursor/service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/config_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/mapper.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/schema_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/aliases.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/agents.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/apply.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/apps.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/import_.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/mcp.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/plan.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/projects.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/restore.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/rules.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/skills.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/status.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/validate.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/commands/workspaces.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/helpers.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/cli/options.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/constants.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/core/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/core/project_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/core/repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/core/workspace_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/errors.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/generated_artifacts.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/git_exclude_service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/imports/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/imports/adapters.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/imports/filesystem.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/imports/models.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/imports/service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/lossiness.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/mcp_service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/project_artifacts.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/rules/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/rules/compilers.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/rules/models.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/rules/parser.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/rules/repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/skills/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/skills/compilers.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/skills/install_sources.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/skills/models.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/skills/parser.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/loaders.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/schemas/agent.v1.schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/schemas/rule.v1.schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/spec/schemas/skill.v1.schema.json +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/status.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/__init__.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/enums.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/import_selector.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/renderers.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/sections.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/tui/tables.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/utils.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/validation.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/workspace_artifacts.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/workspaces.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/dependency_links.txt +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/entry_points.txt +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/requires.txt +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic.egg-info/top_level.txt +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/setup.cfg +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_agents.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_aliases.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apply_apps.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apply_codex.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apply_cursor.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apply_mcp_cleanup.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apply_target.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_apps.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_disable_cleanup.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_explain_lossiness.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_flags.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_git_exclude.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_import.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_import_interactive.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_mcp.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_module_organization.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_plan.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_projects.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_restore.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_rules.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_skills.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_status.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_validate.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_workspace_resolution.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_cli_workspaces.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_common_mcp_to_dto.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_common_repository.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_compiled_planning.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_dto_to_common_mcp.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_git_exclude_service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_mcp_service.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_planner_executor.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_planner_rules.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_project_config_sync.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_skill_install_sources.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_symlink_planning.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_sync_plan_model.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_transactional_executor.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_utils.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_version.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_workspace_config_sync.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_workspace_repo_status.py +0 -0
- {code_agnostic-0.5.1 → code_agnostic-0.6.0}/tests/test_workspaces.py +0 -0
|
@@ -201,14 +201,11 @@ class AppsService:
|
|
|
201
201
|
def _cleanup_global_mcp(
|
|
202
202
|
self, app_id: AppId, managed_mcp: dict[str, Any]
|
|
203
203
|
) -> list[Action]:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
{item for item in raw if isinstance(item, str)}
|
|
208
|
-
if isinstance(raw, list)
|
|
209
|
-
else set()
|
|
204
|
+
managed = self._names_for_scope(managed_mcp, app_scope(app_id, "mcp"))
|
|
205
|
+
managed_agents = self._names_for_scope(
|
|
206
|
+
managed_mcp, app_scope(app_id, "agents_registry")
|
|
210
207
|
)
|
|
211
|
-
if not managed:
|
|
208
|
+
if not managed and not managed_agents:
|
|
212
209
|
return []
|
|
213
210
|
try:
|
|
214
211
|
service = create_registered_app_service(app_id)
|
|
@@ -216,9 +213,23 @@ class AppsService:
|
|
|
216
213
|
return []
|
|
217
214
|
if not service.repository.config_path.exists():
|
|
218
215
|
return []
|
|
219
|
-
# Empty desired + our previously-managed names → prune only
|
|
220
|
-
# keep the user's, and clear
|
|
221
|
-
|
|
216
|
+
# Empty desired + our previously-managed names → prune only what we wrote
|
|
217
|
+
# (MCP servers and agent-registry entries), keep the user's, and clear the
|
|
218
|
+
# ownership state (managed_entries becomes empty).
|
|
219
|
+
return [
|
|
220
|
+
service.build_action(
|
|
221
|
+
{},
|
|
222
|
+
previously_managed=managed,
|
|
223
|
+
previously_managed_agents=managed_agents,
|
|
224
|
+
)
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def _names_for_scope(group: dict[str, Any], scope: str) -> set[str]:
|
|
229
|
+
raw = group.get(scope, [])
|
|
230
|
+
if not isinstance(raw, list):
|
|
231
|
+
return set()
|
|
232
|
+
return {item for item in raw if isinstance(item, str)}
|
|
222
233
|
|
|
223
234
|
def list_status_rows(self) -> list[AppStatusRow]:
|
|
224
235
|
apps = self.load_apps()
|
|
@@ -16,6 +16,7 @@ from code_agnostic.apps.codex.config_repository import CodexConfigRepository
|
|
|
16
16
|
from code_agnostic.apps.codex.mapper import CodexMCPMapper
|
|
17
17
|
from code_agnostic.apps.codex.schema_repository import CodexSchemaRepository
|
|
18
18
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
19
|
+
from code_agnostic.apps.common.utils import apply_mcp_servers
|
|
19
20
|
from code_agnostic.apps.common.interfaces.repositories import (
|
|
20
21
|
IAppConfigRepository,
|
|
21
22
|
ISchemaRepository,
|
|
@@ -118,6 +119,7 @@ class CodexConfigService(RegisteredAppConfigService):
|
|
|
118
119
|
common_servers: dict[str, MCPServerDTO],
|
|
119
120
|
agent_sources: list[Path] | None = None,
|
|
120
121
|
previously_managed: set[str] | None = None,
|
|
122
|
+
previously_managed_agents: set[str] | None = None,
|
|
121
123
|
*,
|
|
122
124
|
replace_mcp: bool = False,
|
|
123
125
|
) -> Action:
|
|
@@ -144,10 +146,17 @@ class CodexConfigService(RegisteredAppConfigService):
|
|
|
144
146
|
self.set_mcp_payload(
|
|
145
147
|
merged, desired_mcp, previously_managed, replace=replace_mcp
|
|
146
148
|
)
|
|
147
|
-
if agent_sources
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
self.
|
|
149
|
+
registry = self._build_agent_registry(agent_sources) if agent_sources else {}
|
|
150
|
+
if replace_mcp:
|
|
151
|
+
if registry:
|
|
152
|
+
merged["agents"] = self._merge_agents_payload(
|
|
153
|
+
merged.get("agents"), registry
|
|
154
|
+
)
|
|
155
|
+
elif registry or previously_managed_agents:
|
|
156
|
+
# Ownership-aware: prune agent-registry entries we previously wrote
|
|
157
|
+
# that are gone from the source, keep base-config/user agents.
|
|
158
|
+
merged["agents"] = apply_mcp_servers(
|
|
159
|
+
merged.get("agents"), registry, previously_managed_agents
|
|
151
160
|
)
|
|
152
161
|
self.validate_config(merged)
|
|
153
162
|
|
|
@@ -161,7 +170,10 @@ class CodexConfigService(RegisteredAppConfigService):
|
|
|
161
170
|
)
|
|
162
171
|
if not replace_mcp:
|
|
163
172
|
action.scope = app_scope(self.app_id, "mcp")
|
|
164
|
-
action.
|
|
173
|
+
action.managed_entries = {
|
|
174
|
+
app_scope(self.app_id, "mcp"): sorted(desired_mcp),
|
|
175
|
+
app_scope(self.app_id, "agents_registry"): sorted(registry),
|
|
176
|
+
}
|
|
165
177
|
return action
|
|
166
178
|
|
|
167
179
|
def _merge_agents_payload(
|
|
@@ -270,6 +270,7 @@ class IAppConfigService(ABC):
|
|
|
270
270
|
common_servers: dict[str, MCPServerDTO],
|
|
271
271
|
agent_sources: list[Path] | None = None,
|
|
272
272
|
previously_managed: set[str] | None = None,
|
|
273
|
+
previously_managed_agents: set[str] | None = None,
|
|
273
274
|
*,
|
|
274
275
|
replace_mcp: bool = False,
|
|
275
276
|
) -> Action:
|
|
@@ -296,7 +297,9 @@ class IAppConfigService(ABC):
|
|
|
296
297
|
# User-shared global config: track which servers we own so a later
|
|
297
298
|
# source removal can be pruned without touching the user's servers.
|
|
298
299
|
action.scope = app_scope(self.app_id, "mcp")
|
|
299
|
-
action.
|
|
300
|
+
action.managed_entries = {
|
|
301
|
+
app_scope(self.app_id, "mcp"): sorted(desired_mcp)
|
|
302
|
+
}
|
|
300
303
|
return action
|
|
301
304
|
|
|
302
305
|
def build_plan(
|
|
@@ -313,6 +316,9 @@ class IAppConfigService(ABC):
|
|
|
313
316
|
previously_managed_mcp = self._load_state_names(
|
|
314
317
|
managed_mcp_group, app_scope(self.app_id, "mcp")
|
|
315
318
|
)
|
|
319
|
+
previously_managed_agents = self._load_state_names(
|
|
320
|
+
managed_mcp_group, app_scope(self.app_id, "agents_registry")
|
|
321
|
+
)
|
|
316
322
|
|
|
317
323
|
skill_actions, skill_skipped = self._build_compiled_group(
|
|
318
324
|
sources=source_repository.list_skill_sources(),
|
|
@@ -341,6 +347,7 @@ class IAppConfigService(ABC):
|
|
|
341
347
|
common_servers,
|
|
342
348
|
agent_sources=source_repository.list_agent_sources(),
|
|
343
349
|
previously_managed=previously_managed_mcp,
|
|
350
|
+
previously_managed_agents=previously_managed_agents,
|
|
344
351
|
),
|
|
345
352
|
*skill_actions,
|
|
346
353
|
*agent_actions,
|
|
@@ -137,6 +137,7 @@ class OpenCodeConfigService(RegisteredAppConfigService):
|
|
|
137
137
|
common_servers: dict[str, MCPServerDTO],
|
|
138
138
|
agent_sources: list[Path] | None = None,
|
|
139
139
|
previously_managed: set[str] | None = None,
|
|
140
|
+
previously_managed_agents: set[str] | None = None,
|
|
140
141
|
*,
|
|
141
142
|
replace_mcp: bool = False,
|
|
142
143
|
) -> Action:
|
|
@@ -174,7 +175,9 @@ class OpenCodeConfigService(RegisteredAppConfigService):
|
|
|
174
175
|
)
|
|
175
176
|
if not replace_mcp:
|
|
176
177
|
action.scope = app_scope(self.app_id, "mcp")
|
|
177
|
-
action.
|
|
178
|
+
action.managed_entries = {
|
|
179
|
+
app_scope(self.app_id, "mcp"): sorted(desired_mcp)
|
|
180
|
+
}
|
|
178
181
|
return action
|
|
179
182
|
|
|
180
183
|
def _validate_common_mcp(self, common_servers: dict[str, MCPServerDTO]) -> None:
|
|
@@ -770,12 +770,14 @@ class SyncExecutor:
|
|
|
770
770
|
if action.scope is None:
|
|
771
771
|
continue
|
|
772
772
|
|
|
773
|
-
if action.
|
|
774
|
-
# User-shared
|
|
775
|
-
#
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
773
|
+
if action.managed_entries is not None:
|
|
774
|
+
# User-shared config: track the named entries we own (MCP servers,
|
|
775
|
+
# agent-registry names, …) by scope — never the shared config file
|
|
776
|
+
# itself, so cleanup prunes only what we wrote.
|
|
777
|
+
for entry_scope, names in action.managed_entries.items():
|
|
778
|
+
global_mcp_touched.add(entry_scope)
|
|
779
|
+
if names:
|
|
780
|
+
global_mcp[entry_scope] = sorted(set(names))
|
|
779
781
|
continue
|
|
780
782
|
|
|
781
783
|
if action.workspace is not None:
|
|
@@ -291,7 +291,7 @@ class SyncPlanner:
|
|
|
291
291
|
# Carry global MCP ownership tracking onto the merged Claude action so
|
|
292
292
|
# top-level `mcpServers` orphans still get pruned on later applies.
|
|
293
293
|
project_action.scope = existing_action.scope
|
|
294
|
-
project_action.
|
|
294
|
+
project_action.managed_entries = existing_action.managed_entries
|
|
295
295
|
actions = [
|
|
296
296
|
action
|
|
297
297
|
for action in plan.actions
|
|
@@ -128,6 +128,7 @@ tests/test_cli_agents.py
|
|
|
128
128
|
tests/test_cli_aliases.py
|
|
129
129
|
tests/test_cli_apply_apps.py
|
|
130
130
|
tests/test_cli_apply_codex.py
|
|
131
|
+
tests/test_cli_apply_codex_agent_cleanup.py
|
|
131
132
|
tests/test_cli_apply_cursor.py
|
|
132
133
|
tests/test_cli_apply_mcp_cleanup.py
|
|
133
134
|
tests/test_cli_apply_target.py
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Codex agent-registry ownership: an agent removed from the source is pruned
|
|
2
|
+
from `[agents.*]` in ~/.codex/config.toml on the next apply, while base-config
|
|
3
|
+
agent settings and user-added entries survive."""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from code_agnostic.__main__ import cli
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import tomllib
|
|
11
|
+
except ModuleNotFoundError: # pragma: no cover
|
|
12
|
+
import tomli as tomllib # type: ignore
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _write_agent(core_root: Path, name: str) -> None:
|
|
16
|
+
agent = core_root / "agents" / f"{name}.md"
|
|
17
|
+
agent.parent.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
agent.write_text(
|
|
19
|
+
f"---\nname: {name}\ndescription: {name} specialist\n---\n\nDo {name}.\n",
|
|
20
|
+
encoding="utf-8",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_apply_codex_prunes_removed_agent_from_registry(
|
|
25
|
+
minimal_shared_config: Path,
|
|
26
|
+
core_root: Path,
|
|
27
|
+
tmp_path: Path,
|
|
28
|
+
cli_runner,
|
|
29
|
+
enable_app,
|
|
30
|
+
) -> None:
|
|
31
|
+
enable_app("codex")
|
|
32
|
+
# Base-config agent settings + a user-managed agent entry must be preserved.
|
|
33
|
+
(core_root / "config" / "codex.base.json").write_text(
|
|
34
|
+
'{"agents": {"max_depth": 2}}', encoding="utf-8"
|
|
35
|
+
)
|
|
36
|
+
(tmp_path / ".codex").mkdir(parents=True, exist_ok=True)
|
|
37
|
+
(tmp_path / ".codex" / "config.toml").write_text(
|
|
38
|
+
'[agents.mine]\ndescription = "user agent"\nconfig_file = "agents/mine.toml"\n',
|
|
39
|
+
encoding="utf-8",
|
|
40
|
+
)
|
|
41
|
+
_write_agent(core_root, "planner")
|
|
42
|
+
_write_agent(core_root, "reviewer")
|
|
43
|
+
|
|
44
|
+
assert cli_runner.invoke(cli, ["apply", "-a", "codex"]).exit_code == 0
|
|
45
|
+
cfg = tomllib.loads(
|
|
46
|
+
(tmp_path / ".codex" / "config.toml").read_text(encoding="utf-8")
|
|
47
|
+
)
|
|
48
|
+
assert cfg["agents"]["max_depth"] == 2
|
|
49
|
+
assert set(cfg["agents"]) >= {"planner", "reviewer", "mine", "max_depth"}
|
|
50
|
+
|
|
51
|
+
state_path = core_root / ".sync-state.json"
|
|
52
|
+
import json
|
|
53
|
+
|
|
54
|
+
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
55
|
+
assert state["managed_mcp"]["app:codex:agents_registry"] == ["planner", "reviewer"]
|
|
56
|
+
|
|
57
|
+
# Remove reviewer from the source and re-apply.
|
|
58
|
+
(core_root / "agents" / "reviewer.md").unlink()
|
|
59
|
+
assert cli_runner.invoke(cli, ["apply", "-a", "codex"]).exit_code == 0
|
|
60
|
+
|
|
61
|
+
cfg = tomllib.loads(
|
|
62
|
+
(tmp_path / ".codex" / "config.toml").read_text(encoding="utf-8")
|
|
63
|
+
)
|
|
64
|
+
agents = cfg["agents"]
|
|
65
|
+
assert "reviewer" not in agents, "our removed agent must be pruned from registry"
|
|
66
|
+
assert "planner" in agents
|
|
67
|
+
assert agents["mine"]["description"] == "user agent", "user entry preserved"
|
|
68
|
+
assert agents["max_depth"] == 2, "base-config agent settings preserved"
|
|
69
|
+
|
|
70
|
+
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
71
|
+
assert state["managed_mcp"]["app:codex:agents_registry"] == ["planner"]
|
|
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
|
{code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/common/interfaces/repositories.py
RENAMED
|
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
|
{code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/config_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_agnostic-0.5.1 → code_agnostic-0.6.0}/code_agnostic/apps/opencode/schema_repository.py
RENAMED
|
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
|