code-agnostic 0.8.0__tar.gz → 0.9.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.8.0 → code_agnostic-0.9.0}/PKG-INFO +3 -3
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/README.md +2 -2
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/__init__.py +1 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/apps_service.py +2 -2
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/claude/mapper.py +12 -25
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/claude/service.py +0 -3
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/service.py +2 -6
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/interfaces/repositories.py +2 -2
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/interfaces/service.py +6 -2
- code_agnostic-0.9.0/code_agnostic/apps/common/mapper_utils.py +30 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/copilot/mapper.py +5 -10
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/copilot/service.py +0 -3
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/mapper.py +3 -12
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/service.py +0 -6
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/mapper.py +7 -21
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/service.py +0 -3
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/projects.py +2 -2
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/skills.py +8 -7
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/workspaces.py +7 -9
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/helpers.py +7 -6
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/constants.py +1 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/core/repository.py +22 -20
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/executor.py +12 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/git_exclude_service.py +1 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/mcp_service.py +1 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/models.py +10 -15
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/planner.py +148 -262
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/project_artifacts.py +2 -1
- code_agnostic-0.9.0/code_agnostic/rules/compilers.py +29 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/status.py +4 -4
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/utils.py +25 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/PKG-INFO +3 -3
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/SOURCES.txt +2 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/pyproject.toml +1 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_codex.py +1 -1
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_validate.py +1 -4
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_common_repository.py +6 -7
- code_agnostic-0.9.0/tests/test_file_lock.py +53 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_sync_plan_model.py +0 -4
- code_agnostic-0.8.0/code_agnostic/rules/compilers.py +0 -57
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/LICENSE +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/__main__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/claude.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/codex.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/compilers.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/copilot.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/models.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/opencode.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/agents/parser.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/app_id.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/claude/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/claude/config_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/config_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/mapper.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/codex/schema_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/compiled_planning.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/framework.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/loader.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/models.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/schema.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/symlink_planning.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/utils.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/copilot/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/copilot/config_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/config_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/cursor/schema_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/config_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/opencode/schema_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/aliases.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/agents.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/apply.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/apps.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/import_.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/mcp.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/plan.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/restore.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/rules.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/status.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/commands/validate.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/cli/options.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/core/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/core/project_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/core/workspace_repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/errors.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/generated_artifacts.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/imports/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/imports/adapters.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/imports/filesystem.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/imports/models.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/imports/service.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/lossiness.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/rules/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/rules/models.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/rules/parser.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/rules/repository.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/skills/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/skills/compilers.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/skills/install_sources.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/skills/models.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/skills/parser.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/loaders.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/schemas/agent.v1.schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/schemas/rule.v1.schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/spec/schemas/skill.v1.schema.json +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/__init__.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/enums.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/import_selector.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/renderers.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/sections.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/tui/tables.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/validation.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/workspace_artifacts.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/workspaces.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/dependency_links.txt +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/entry_points.txt +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/requires.txt +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic.egg-info/top_level.txt +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/setup.cfg +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_agents.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_aliases.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_apps.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_codex_agent_cleanup.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_cursor.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_mcp_cleanup.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apply_target.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_apps.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_disable_cleanup.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_explain_lossiness.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_flags.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_git_exclude.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_import.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_import_interactive.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_mcp.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_module_organization.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_plan.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_projects.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_restore.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_rules.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_skills.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_status.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_workspace_resolution.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_cli_workspaces.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_common_mcp_to_dto.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_compiled_planning.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_dto_to_common_mcp.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_git_exclude_service.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_mcp_service.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_planner_executor.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_planner_rules.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_project_config_sync.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_skill_install_sources.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_symlink_planning.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_transactional_executor.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_utils.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_version.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_workspace_config_sync.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_workspace_repo_status.py +0 -0
- {code_agnostic-0.8.0 → code_agnostic-0.9.0}/tests/test_workspaces.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-agnostic
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Centralized hub for LLM coding config: MCP, skills, rules, and agents.
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -59,7 +59,7 @@ AI coding tools each want config in a different place and format. When you use m
|
|
|
59
59
|
~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Each resource is cross-compiled to the target editor's native format. Rules become
|
|
62
|
+
Each resource is cross-compiled to the target editor's native format. Rules become `AGENTS.md` sections consumed by Cursor/OpenCode/Codex/Copilot, mirrored into `CLAUDE.local.md` memory for Claude Code.
|
|
63
63
|
|
|
64
64
|
Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are still supported for migration, but bundle directories are the preferred source format for new config.
|
|
65
65
|
|
|
@@ -216,7 +216,7 @@ always_apply: false
|
|
|
216
216
|
Always use type hints. Prefer dataclasses over dicts.
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
Compiled to `AGENTS.md` sections, consumed natively by Cursor/OpenCode/Codex/Copilot and mirrored into `CLAUDE.local.md` for Claude Code.
|
|
220
220
|
Legacy single-file rule markdown with YAML frontmatter remains supported for migration.
|
|
221
221
|
|
|
222
222
|
```bash
|
|
@@ -34,7 +34,7 @@ AI coding tools each want config in a different place and format. When you use m
|
|
|
34
34
|
~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
Each resource is cross-compiled to the target editor's native format. Rules become
|
|
37
|
+
Each resource is cross-compiled to the target editor's native format. Rules become `AGENTS.md` sections consumed by Cursor/OpenCode/Codex/Copilot, mirrored into `CLAUDE.local.md` memory for Claude Code.
|
|
38
38
|
|
|
39
39
|
Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are still supported for migration, but bundle directories are the preferred source format for new config.
|
|
40
40
|
|
|
@@ -191,7 +191,7 @@ always_apply: false
|
|
|
191
191
|
Always use type hints. Prefer dataclasses over dicts.
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
Compiled to `AGENTS.md` sections, consumed natively by Cursor/OpenCode/Codex/Copilot and mirrored into `CLAUDE.local.md` for Claude Code.
|
|
195
195
|
Legacy single-file rule markdown with YAML frontmatter remains supported for migration.
|
|
196
196
|
|
|
197
197
|
```bash
|
|
@@ -120,7 +120,7 @@ class AppsService:
|
|
|
120
120
|
# --- Workspace scopes (ws:<app>:*) ---
|
|
121
121
|
ws_prefix = f"ws:{app_id.value}:"
|
|
122
122
|
for workspace in core.load_workspaces():
|
|
123
|
-
ws_name = workspace
|
|
123
|
+
ws_name = workspace.name
|
|
124
124
|
ws_repo = WorkspaceConfigRepository(root=core.workspace_config_dir(ws_name))
|
|
125
125
|
ws_state = ws_repo.load_state()
|
|
126
126
|
ws_links = ws_state.managed_links
|
|
@@ -139,7 +139,7 @@ class AppsService:
|
|
|
139
139
|
# --- Project scopes (project:<app>:*) ---
|
|
140
140
|
project_prefix = f"project:{app_id.value}:"
|
|
141
141
|
for project in load_project_entries(core):
|
|
142
|
-
project_name = project
|
|
142
|
+
project_name = project.name
|
|
143
143
|
project_repo = ProjectConfigRepository(
|
|
144
144
|
root=project_config_dir(core, project_name)
|
|
145
145
|
)
|
|
@@ -2,27 +2,14 @@ from copy import deepcopy
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
5
|
+
from code_agnostic.apps.common.mapper_utils import (
|
|
6
|
+
as_command_list,
|
|
7
|
+
as_str_dict,
|
|
8
|
+
coerce_int_timeout_ms,
|
|
9
|
+
)
|
|
5
10
|
from code_agnostic.apps.common.models import MCPServerDTO, MCPServerType
|
|
6
11
|
|
|
7
12
|
|
|
8
|
-
def _as_list(value: Any) -> list[str]:
|
|
9
|
-
if isinstance(value, list):
|
|
10
|
-
return [str(item) for item in value]
|
|
11
|
-
if isinstance(value, str):
|
|
12
|
-
return [value]
|
|
13
|
-
return []
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _as_str_dict(value: Any) -> dict[str, str]:
|
|
17
|
-
return {str(k): str(v) for k, v in value.items()} if isinstance(value, dict) else {}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _timeout(value: Any) -> int | None:
|
|
21
|
-
if isinstance(value, int) and not isinstance(value, bool):
|
|
22
|
-
return value
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
|
|
26
13
|
class ClaudeMCPMapper(IAppMCPMapper):
|
|
27
14
|
def to_common(self, payload: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
28
15
|
mapped: dict[str, MCPServerDTO] = {}
|
|
@@ -39,13 +26,13 @@ class ClaudeMCPMapper(IAppMCPMapper):
|
|
|
39
26
|
name=name,
|
|
40
27
|
type=MCPServerType.STDIO,
|
|
41
28
|
command=command,
|
|
42
|
-
args=
|
|
29
|
+
args=as_command_list(server.get("args")),
|
|
43
30
|
cwd=server.get("cwd")
|
|
44
31
|
if isinstance(server.get("cwd"), str)
|
|
45
32
|
else None,
|
|
46
|
-
env=
|
|
47
|
-
headers=
|
|
48
|
-
timeout_ms=
|
|
33
|
+
env=as_str_dict(server.get("env")),
|
|
34
|
+
headers=as_str_dict(server.get("headers")),
|
|
35
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
49
36
|
)
|
|
50
37
|
continue
|
|
51
38
|
|
|
@@ -56,9 +43,9 @@ class ClaudeMCPMapper(IAppMCPMapper):
|
|
|
56
43
|
name=name,
|
|
57
44
|
type=MCPServerType.HTTP,
|
|
58
45
|
url=url,
|
|
59
|
-
env=
|
|
60
|
-
headers=
|
|
61
|
-
timeout_ms=
|
|
46
|
+
env=as_str_dict(server.get("env")),
|
|
47
|
+
headers=as_str_dict(server.get("headers")),
|
|
48
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
62
49
|
)
|
|
63
50
|
return mapped
|
|
64
51
|
|
|
@@ -56,9 +56,6 @@ class ClaudeConfigService(RegisteredAppConfigService):
|
|
|
56
56
|
def mcp_config_key(self) -> str:
|
|
57
57
|
return "mcpServers"
|
|
58
58
|
|
|
59
|
-
def build_action_payload(self, payload: dict[str, Any]) -> Any:
|
|
60
|
-
return payload
|
|
61
|
-
|
|
62
59
|
def derive_status(
|
|
63
60
|
self, existing: dict[str, Any], merged: dict[str, Any]
|
|
64
61
|
) -> ActionStatus:
|
|
@@ -102,14 +102,10 @@ class CodexConfigService(RegisteredAppConfigService):
|
|
|
102
102
|
def derive_status(
|
|
103
103
|
self, existing: dict[str, Any], merged: dict[str, Any]
|
|
104
104
|
) -> ActionStatus:
|
|
105
|
-
rendered = self.repository.serialize_config(merged)
|
|
106
|
-
existing_text = (
|
|
107
|
-
self.repository.config_path.read_text(encoding="utf-8")
|
|
108
|
-
if self.repository.config_path.exists()
|
|
109
|
-
else ""
|
|
110
|
-
)
|
|
111
105
|
if not self.repository.config_path.exists():
|
|
112
106
|
return ActionStatus.CREATE
|
|
107
|
+
rendered = self.repository.serialize_config(merged)
|
|
108
|
+
existing_text = self.repository.config_path.read_text(encoding="utf-8")
|
|
113
109
|
if existing_text == rendered:
|
|
114
110
|
return ActionStatus.NOOP
|
|
115
111
|
return ActionStatus.UPDATE
|
{code_agnostic-0.8.0 → code_agnostic-0.9.0}/code_agnostic/apps/common/interfaces/repositories.py
RENAMED
|
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from code_agnostic.models import SyncState
|
|
5
|
+
from code_agnostic.models import SyncState, WorkspaceConfig
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class ISchemaRepository(ABC):
|
|
@@ -93,7 +93,7 @@ class ISourceRepository(IConfigRepository):
|
|
|
93
93
|
raise NotImplementedError
|
|
94
94
|
|
|
95
95
|
@abstractmethod
|
|
96
|
-
def load_workspaces(self) -> list[
|
|
96
|
+
def load_workspaces(self) -> list[WorkspaceConfig]:
|
|
97
97
|
raise NotImplementedError
|
|
98
98
|
|
|
99
99
|
def workspace_config_dir(self, name: str) -> Path:
|
|
@@ -79,9 +79,13 @@ class IAppConfigService(ABC):
|
|
|
79
79
|
"""Editor-specific validation hook. Default: no schema."""
|
|
80
80
|
return
|
|
81
81
|
|
|
82
|
-
@abstractmethod
|
|
83
82
|
def build_action_payload(self, payload: dict[str, Any]) -> Any:
|
|
84
|
-
|
|
83
|
+
"""Transform the merged config before it becomes the action payload.
|
|
84
|
+
|
|
85
|
+
Defaults to the merged dict as-is; editors that persist a non-JSON
|
|
86
|
+
rendering (e.g. Codex TOML) override this.
|
|
87
|
+
"""
|
|
88
|
+
return payload
|
|
85
89
|
|
|
86
90
|
def set_mcp_payload(
|
|
87
91
|
self,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Shared coercion helpers for MCP mappers.
|
|
2
|
+
|
|
3
|
+
Editors whose MCP config uses the same shapes share these instead of
|
|
4
|
+
re-implementing them per mapper. Editors with genuinely different semantics
|
|
5
|
+
(e.g. Copilot's float/negative timeout handling, Codex's seconds-based timeout)
|
|
6
|
+
keep their own local helpers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def as_command_list(value: Any) -> list[str]:
|
|
13
|
+
"""A command/args value that may be a list or a single string."""
|
|
14
|
+
if isinstance(value, list):
|
|
15
|
+
return [str(item) for item in value]
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
return [value]
|
|
18
|
+
return []
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def as_str_dict(value: Any) -> dict[str, str]:
|
|
22
|
+
"""A string-to-string mapping (env/headers), or empty if not a dict."""
|
|
23
|
+
return {str(k): str(v) for k, v in value.items()} if isinstance(value, dict) else {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def coerce_int_timeout_ms(value: Any) -> int | None:
|
|
27
|
+
"""A plain integer millisecond timeout; rejects bools and non-ints."""
|
|
28
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
29
|
+
return int(value)
|
|
30
|
+
return None
|
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
6
|
+
from code_agnostic.apps.common.mapper_utils import as_str_dict
|
|
6
7
|
from code_agnostic.apps.common.models import MCPServerDTO, MCPServerType
|
|
7
8
|
from code_agnostic.errors import InvalidConfigSchemaError
|
|
8
9
|
|
|
@@ -23,12 +24,6 @@ def _timeout_ms(value: Any) -> int | None:
|
|
|
23
24
|
return None
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def _str_dict(value: Any) -> dict[str, str]:
|
|
27
|
-
if not isinstance(value, dict):
|
|
28
|
-
return {}
|
|
29
|
-
return {str(k): str(v) for k, v in value.items()}
|
|
30
|
-
|
|
31
|
-
|
|
32
27
|
class CopilotMCPMapper(IAppMCPMapper):
|
|
33
28
|
def to_common(self, payload: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
34
29
|
mapped: dict[str, MCPServerDTO] = {}
|
|
@@ -47,8 +42,8 @@ class CopilotMCPMapper(IAppMCPMapper):
|
|
|
47
42
|
command=command,
|
|
48
43
|
args=_as_string_list(server.get("args")),
|
|
49
44
|
timeout_ms=_timeout_ms(server.get("timeout")),
|
|
50
|
-
env=
|
|
51
|
-
headers=
|
|
45
|
+
env=as_str_dict(server.get("env")),
|
|
46
|
+
headers=as_str_dict(server.get("headers")),
|
|
52
47
|
)
|
|
53
48
|
continue
|
|
54
49
|
|
|
@@ -59,8 +54,8 @@ class CopilotMCPMapper(IAppMCPMapper):
|
|
|
59
54
|
type=MCPServerType.HTTP,
|
|
60
55
|
url=url,
|
|
61
56
|
timeout_ms=_timeout_ms(server.get("timeout")),
|
|
62
|
-
env=
|
|
63
|
-
headers=
|
|
57
|
+
env=as_str_dict(server.get("env")),
|
|
58
|
+
headers=as_str_dict(server.get("headers")),
|
|
64
59
|
)
|
|
65
60
|
return mapped
|
|
66
61
|
|
|
@@ -62,9 +62,6 @@ class CopilotConfigService(RegisteredAppConfigService):
|
|
|
62
62
|
self.repository.config_path, "mcpServers must be a JSON object"
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
def build_action_payload(self, payload: dict[str, Any]) -> Any:
|
|
66
|
-
return payload
|
|
67
|
-
|
|
68
65
|
def derive_status(
|
|
69
66
|
self, existing: dict[str, Any], merged: dict[str, Any]
|
|
70
67
|
) -> ActionStatus:
|
|
@@ -2,6 +2,7 @@ from copy import deepcopy
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
5
|
+
from code_agnostic.apps.common.mapper_utils import coerce_int_timeout_ms
|
|
5
6
|
from code_agnostic.apps.common.models import MCPAuthDTO, MCPServerDTO, MCPServerType
|
|
6
7
|
|
|
7
8
|
|
|
@@ -25,12 +26,7 @@ class CursorMCPMapper(IAppMCPMapper):
|
|
|
25
26
|
env_file=server.get("envFile")
|
|
26
27
|
if isinstance(server.get("envFile"), str)
|
|
27
28
|
else None,
|
|
28
|
-
timeout_ms=(
|
|
29
|
-
int(server["timeout"])
|
|
30
|
-
if isinstance(server.get("timeout"), int)
|
|
31
|
-
and not isinstance(server.get("timeout"), bool)
|
|
32
|
-
else None
|
|
33
|
-
),
|
|
29
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
34
30
|
env={k: str(v) for k, v in (server.get("env") or {}).items()},
|
|
35
31
|
headers={
|
|
36
32
|
k: str(v) for k, v in (server.get("headers") or {}).items()
|
|
@@ -66,12 +62,7 @@ class CursorMCPMapper(IAppMCPMapper):
|
|
|
66
62
|
name=name,
|
|
67
63
|
type=server_type,
|
|
68
64
|
url=url,
|
|
69
|
-
timeout_ms=(
|
|
70
|
-
int(server["timeout"])
|
|
71
|
-
if isinstance(server.get("timeout"), int)
|
|
72
|
-
and not isinstance(server.get("timeout"), bool)
|
|
73
|
-
else None
|
|
74
|
-
),
|
|
65
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
75
66
|
headers={k: str(v) for k, v in (server.get("headers") or {}).items()},
|
|
76
67
|
env={k: str(v) for k, v in (server.get("env") or {}).items()},
|
|
77
68
|
auth=auth,
|
|
@@ -75,9 +75,6 @@ class CursorConfigService(RegisteredAppConfigService):
|
|
|
75
75
|
self.repository.config_path, format_schema_error(error)
|
|
76
76
|
)
|
|
77
77
|
|
|
78
|
-
def build_action_payload(self, payload: dict[str, Any]) -> Any:
|
|
79
|
-
return payload
|
|
80
|
-
|
|
81
78
|
def derive_status(
|
|
82
79
|
self, existing: dict[str, Any], merged: dict[str, Any]
|
|
83
80
|
) -> ActionStatus:
|
|
@@ -97,9 +94,6 @@ class CursorConfigService(RegisteredAppConfigService):
|
|
|
97
94
|
return ActionStatus.NOOP
|
|
98
95
|
return ActionStatus.UPDATE
|
|
99
96
|
|
|
100
|
-
def agent_action_removable_links(self, removable_links: list[Path]) -> list[Path]:
|
|
101
|
-
return removable_links
|
|
102
|
-
|
|
103
97
|
def plan_skill_actions(
|
|
104
98
|
self,
|
|
105
99
|
sources: list[Path],
|
|
@@ -2,17 +2,13 @@ from copy import deepcopy
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
5
|
+
from code_agnostic.apps.common.mapper_utils import (
|
|
6
|
+
as_command_list,
|
|
7
|
+
coerce_int_timeout_ms,
|
|
8
|
+
)
|
|
5
9
|
from code_agnostic.apps.common.models import MCPAuthDTO, MCPServerDTO, MCPServerType
|
|
6
10
|
|
|
7
11
|
|
|
8
|
-
def _as_list(value: Any) -> list[str]:
|
|
9
|
-
if isinstance(value, list):
|
|
10
|
-
return [str(item) for item in value]
|
|
11
|
-
if isinstance(value, str):
|
|
12
|
-
return [value]
|
|
13
|
-
return []
|
|
14
|
-
|
|
15
|
-
|
|
16
12
|
class OpenCodeMCPMapper(IAppMCPMapper):
|
|
17
13
|
def to_common(self, payload: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
18
14
|
mapped: dict[str, MCPServerDTO] = {}
|
|
@@ -22,7 +18,7 @@ class OpenCodeMCPMapper(IAppMCPMapper):
|
|
|
22
18
|
|
|
23
19
|
server_type = server.get("type")
|
|
24
20
|
if server_type == "local" or "command" in server:
|
|
25
|
-
command_parts =
|
|
21
|
+
command_parts = as_command_list(server.get("command"))
|
|
26
22
|
command = command_parts[0] if command_parts else None
|
|
27
23
|
args = command_parts[1:] if command_parts else []
|
|
28
24
|
if not command:
|
|
@@ -35,12 +31,7 @@ class OpenCodeMCPMapper(IAppMCPMapper):
|
|
|
35
31
|
cwd=server.get("cwd")
|
|
36
32
|
if isinstance(server.get("cwd"), str)
|
|
37
33
|
else None,
|
|
38
|
-
timeout_ms=(
|
|
39
|
-
int(server["timeout"])
|
|
40
|
-
if isinstance(server.get("timeout"), int)
|
|
41
|
-
and not isinstance(server.get("timeout"), bool)
|
|
42
|
-
else None
|
|
43
|
-
),
|
|
34
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
44
35
|
env={
|
|
45
36
|
k: str(v) for k, v in (server.get("environment") or {}).items()
|
|
46
37
|
},
|
|
@@ -72,12 +63,7 @@ class OpenCodeMCPMapper(IAppMCPMapper):
|
|
|
72
63
|
name=name,
|
|
73
64
|
type=normalized_type,
|
|
74
65
|
url=url,
|
|
75
|
-
timeout_ms=(
|
|
76
|
-
int(server["timeout"])
|
|
77
|
-
if isinstance(server.get("timeout"), int)
|
|
78
|
-
and not isinstance(server.get("timeout"), bool)
|
|
79
|
-
else None
|
|
80
|
-
),
|
|
66
|
+
timeout_ms=coerce_int_timeout_ms(server.get("timeout")),
|
|
81
67
|
env={k: str(v) for k, v in (server.get("environment") or {}).items()},
|
|
82
68
|
headers={k: str(v) for k, v in (server.get("headers") or {}).items()},
|
|
83
69
|
auth=auth,
|
|
@@ -112,9 +112,6 @@ class OpenCodeConfigService(RegisteredAppConfigService):
|
|
|
112
112
|
self.repository.config_path, format_schema_error(error)
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
-
def build_action_payload(self, payload: dict[str, Any]) -> Any:
|
|
116
|
-
return payload
|
|
117
|
-
|
|
118
115
|
def _load_base_config(self) -> dict[str, Any]:
|
|
119
116
|
from code_agnostic.errors import InvalidJsonFormatError
|
|
120
117
|
from code_agnostic.utils import read_json_safe
|
|
@@ -63,7 +63,7 @@ def projects_list(obj: dict[str, str]) -> None:
|
|
|
63
63
|
|
|
64
64
|
for item in items:
|
|
65
65
|
project_source = ProjectConfigRepository(
|
|
66
|
-
root=core.project_config_dir(item
|
|
66
|
+
root=core.project_config_dir(item.name)
|
|
67
67
|
)
|
|
68
68
|
markers = []
|
|
69
69
|
if project_source.has_mcp():
|
|
@@ -75,4 +75,4 @@ def projects_list(obj: dict[str, str]) -> None:
|
|
|
75
75
|
if project_source.has_agents():
|
|
76
76
|
markers.append("agents")
|
|
77
77
|
suffix = f" [{', '.join(markers)}]" if markers else ""
|
|
78
|
-
click.echo(f"{item
|
|
78
|
+
click.echo(f"{item.name}: {item.path}{suffix}")
|
|
@@ -14,6 +14,7 @@ from code_agnostic.cli.helpers import (
|
|
|
14
14
|
from code_agnostic.cli.options import workspace_option
|
|
15
15
|
from code_agnostic.core.repository import CoreRepository
|
|
16
16
|
from code_agnostic.errors import SyncAppError
|
|
17
|
+
from code_agnostic.models import WorkspaceConfig
|
|
17
18
|
from code_agnostic.skills.install_sources import (
|
|
18
19
|
SkillInstallSourceError,
|
|
19
20
|
cleanup_skill_install_resolution,
|
|
@@ -25,18 +26,18 @@ from code_agnostic.utils import compact_home_path
|
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def _entries_containing_cwd(
|
|
28
|
-
entries: list[
|
|
29
|
-
) -> list[
|
|
29
|
+
entries: list[WorkspaceConfig], cwd: Path
|
|
30
|
+
) -> list[WorkspaceConfig]:
|
|
30
31
|
matches = []
|
|
31
32
|
for entry in entries:
|
|
32
|
-
root =
|
|
33
|
+
root = entry.path.expanduser().resolve()
|
|
33
34
|
if cwd == root or cwd.is_relative_to(root):
|
|
34
35
|
matches.append(entry)
|
|
35
36
|
return matches
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def _project_entries_by_name(core: CoreRepository) -> dict[str,
|
|
39
|
-
return {item
|
|
39
|
+
def _project_entries_by_name(core: CoreRepository) -> dict[str, WorkspaceConfig]:
|
|
40
|
+
return {item.name: item for item in core.load_projects()}
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
def _install_root(
|
|
@@ -72,12 +73,12 @@ def _install_root(
|
|
|
72
73
|
cwd = Path.cwd().resolve()
|
|
73
74
|
project_matches = _entries_containing_cwd(core.load_projects(), cwd)
|
|
74
75
|
if len(project_matches) == 1:
|
|
75
|
-
name = project_matches[0]
|
|
76
|
+
name = project_matches[0].name
|
|
76
77
|
return core.project_config_dir(name), f"project:{name}", None
|
|
77
78
|
|
|
78
79
|
workspace_matches = _entries_containing_cwd(core.load_workspaces(), cwd)
|
|
79
80
|
if len(project_matches) == 0 and len(workspace_matches) == 1:
|
|
80
|
-
name = workspace_matches[0]
|
|
81
|
+
name = workspace_matches[0].name
|
|
81
82
|
return core.workspace_config_dir(name), f"workspace:{name}", None
|
|
82
83
|
except SyncAppError as exc:
|
|
83
84
|
raise click.ClickException(str(exc)) from exc
|
|
@@ -48,7 +48,7 @@ def workspaces_remove(obj: dict[str, str], name: str) -> None:
|
|
|
48
48
|
ui = SyncConsoleUI(Console())
|
|
49
49
|
core = CoreRepository()
|
|
50
50
|
try:
|
|
51
|
-
existing = {item
|
|
51
|
+
existing = {item.name: str(item.path) for item in core.load_workspaces()}
|
|
52
52
|
removed = core.remove_workspace(name)
|
|
53
53
|
except SyncAppError as exc:
|
|
54
54
|
raise click.ClickException(str(exc))
|
|
@@ -70,20 +70,18 @@ def workspaces_list(obj: dict[str, str]) -> None:
|
|
|
70
70
|
except SyncAppError as exc:
|
|
71
71
|
raise click.ClickException(str(exc))
|
|
72
72
|
for item in workspaces:
|
|
73
|
-
workspace_path =
|
|
73
|
+
workspace_path = item.path
|
|
74
74
|
repos: list[str] = []
|
|
75
75
|
if workspace_path.exists() and workspace_path.is_dir():
|
|
76
76
|
repos = [
|
|
77
77
|
str(path.relative_to(workspace_path))
|
|
78
78
|
for path in workspace_service.discover_git_repos(workspace_path)
|
|
79
79
|
]
|
|
80
|
-
ws_source = WorkspaceConfigRepository(
|
|
81
|
-
root=core.workspace_config_dir(item["name"])
|
|
82
|
-
)
|
|
80
|
+
ws_source = WorkspaceConfigRepository(root=core.workspace_config_dir(item.name))
|
|
83
81
|
overview.append(
|
|
84
82
|
{
|
|
85
|
-
"name": item
|
|
86
|
-
"path": item
|
|
83
|
+
"name": item.name,
|
|
84
|
+
"path": str(item.path),
|
|
87
85
|
"repos": repos,
|
|
88
86
|
"has_mcp": ws_source.has_mcp(),
|
|
89
87
|
"has_rules": ws_source.has_rules(),
|
|
@@ -123,7 +121,7 @@ def workspaces_git_exclude(obj: dict[str, str], workspace: str | None) -> None:
|
|
|
123
121
|
added_lines = 0
|
|
124
122
|
|
|
125
123
|
for item in ws_list:
|
|
126
|
-
workspace_path =
|
|
124
|
+
workspace_path = item.path
|
|
127
125
|
if not workspace_path.exists() or not workspace_path.is_dir():
|
|
128
126
|
continue
|
|
129
127
|
repos = workspace_service.discover_git_repos(workspace_path)
|
|
@@ -132,7 +130,7 @@ def workspaces_git_exclude(obj: dict[str, str], workspace: str | None) -> None:
|
|
|
132
130
|
if git_dir is None:
|
|
133
131
|
continue
|
|
134
132
|
entries = exclude_service.compute_entries_for_repo(
|
|
135
|
-
item
|
|
133
|
+
item.name,
|
|
136
134
|
enabled_apps,
|
|
137
135
|
workspace_path=workspace_path,
|
|
138
136
|
repo_path=repo,
|
|
@@ -12,18 +12,19 @@ from code_agnostic.models import (
|
|
|
12
12
|
EditorStatusRow,
|
|
13
13
|
EditorSyncStatus,
|
|
14
14
|
SyncPlan,
|
|
15
|
+
WorkspaceConfig,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def _workspace_entries_by_name(core: CoreRepository) -> dict[str,
|
|
19
|
-
return {item
|
|
19
|
+
def _workspace_entries_by_name(core: CoreRepository) -> dict[str, WorkspaceConfig]:
|
|
20
|
+
return {item.name: item for item in core.load_workspaces()}
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def _project_entries_by_name(core: CoreRepository) -> dict[str,
|
|
23
|
-
return {item
|
|
23
|
+
def _project_entries_by_name(core: CoreRepository) -> dict[str, WorkspaceConfig]:
|
|
24
|
+
return {item.name: item for item in core.load_projects()}
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def require_workspace_entry(core: CoreRepository, workspace: str) ->
|
|
27
|
+
def require_workspace_entry(core: CoreRepository, workspace: str) -> WorkspaceConfig:
|
|
27
28
|
try:
|
|
28
29
|
entry = _workspace_entries_by_name(core).get(workspace)
|
|
29
30
|
except SyncAppError as exc:
|
|
@@ -33,7 +34,7 @@ def require_workspace_entry(core: CoreRepository, workspace: str) -> dict[str, s
|
|
|
33
34
|
return entry
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def require_project_entry(core: CoreRepository, project: str) ->
|
|
37
|
+
def require_project_entry(core: CoreRepository, project: str) -> WorkspaceConfig:
|
|
37
38
|
try:
|
|
38
39
|
entry = _project_entries_by_name(core).get(project)
|
|
39
40
|
except SyncAppError as exc:
|
|
@@ -9,6 +9,7 @@ GIT_DIRNAME: Final[str] = ".git"
|
|
|
9
9
|
SYNC_STATE_FILENAME: Final[str] = ".sync-state.json"
|
|
10
10
|
SYNC_REVISIONS_DIRNAME: Final[str] = ".sync-revisions"
|
|
11
11
|
SYNC_STAGING_DIRNAME: Final[str] = ".sync-staging"
|
|
12
|
+
SYNC_LOCK_FILENAME: Final[str] = ".sync.lock"
|
|
12
13
|
|
|
13
14
|
RULES_DIRNAME: Final[str] = "rules"
|
|
14
15
|
SKILLS_DIRNAME: Final[str] = "skills"
|