code-agnostic 0.3.15__tar.gz → 0.3.17__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.3.15 → code_agnostic-0.3.17}/PKG-INFO +39 -30
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/README.md +38 -29
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/__init__.py +1 -1
- code_agnostic-0.3.17/code_agnostic/agents/compilers.py +176 -0
- code_agnostic-0.3.17/code_agnostic/agents/copilot.py +117 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/parser.py +1 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/app_id.py +14 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/mapper.py +28 -2
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/schema.json +72 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/loader.py +1 -0
- code_agnostic-0.3.17/code_agnostic/apps/copilot/config_repository.py +65 -0
- code_agnostic-0.3.17/code_agnostic/apps/copilot/mapper.py +97 -0
- code_agnostic-0.3.17/code_agnostic/apps/copilot/service.py +140 -0
- code_agnostic-0.3.17/code_agnostic/apps/opencode/__init__.py +1 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/agents.py +6 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/apply.py +9 -6
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/rules.py +6 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/skills.py +63 -13
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/status.py +1 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/helpers.py +7 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/constants.py +4 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/core/repository.py +24 -8
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/imports/adapters.py +12 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/imports/service.py +75 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/lossiness.py +32 -12
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/models.py +3 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/planner.py +19 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/project_artifacts.py +3 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/skills/compilers.py +12 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/skills/parser.py +1 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/spec/loaders.py +2 -2
- code_agnostic-0.3.17/code_agnostic/spec/schemas/agent.v1.schema.json +148 -0
- code_agnostic-0.3.17/code_agnostic/spec/schemas/rule.v1.schema.json +49 -0
- code_agnostic-0.3.17/code_agnostic/spec/schemas/skill.v1.schema.json +78 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/status.py +2 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/renderers.py +57 -13
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/tables.py +3 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/workspace_artifacts.py +17 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/PKG-INFO +39 -30
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/SOURCES.txt +5 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/pyproject.toml +1 -1
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_agents.py +20 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_apply_apps.py +252 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_apps.py +5 -3
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_explain_lossiness.py +71 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_import.py +68 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_rules.py +16 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_skills.py +123 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_status.py +26 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_workspaces.py +41 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_common_repository.py +3 -4
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_project_config_sync.py +31 -0
- code_agnostic-0.3.15/code_agnostic/agents/compilers.py +0 -45
- code_agnostic-0.3.15/code_agnostic/spec/schemas/agent.v1.schema.json +0 -70
- code_agnostic-0.3.15/code_agnostic/spec/schemas/rule.v1.schema.json +0 -22
- code_agnostic-0.3.15/code_agnostic/spec/schemas/skill.v1.schema.json +0 -33
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/LICENSE +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/__main__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/claude.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/codex.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/models.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/agents/opencode.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/apps_service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/claude/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/claude/config_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/claude/mapper.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/claude/service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/config_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/schema_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/codex/service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/compiled_planning.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/framework.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/interfaces/repositories.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/interfaces/service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/models.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/schema.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/symlink_planning.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/common/utils.py +0 -0
- {code_agnostic-0.3.15/code_agnostic/apps/cursor → code_agnostic-0.3.17/code_agnostic/apps/copilot}/__init__.py +0 -0
- {code_agnostic-0.3.15/code_agnostic/apps/opencode → code_agnostic-0.3.17/code_agnostic/apps/cursor}/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/cursor/config_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/cursor/mapper.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/cursor/schema.json +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/cursor/schema_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/cursor/service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/opencode/config_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/opencode/mapper.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/opencode/schema.json +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/opencode/schema_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/apps/opencode/service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/aliases.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/apps.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/import_.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/mcp.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/plan.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/projects.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/restore.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/validate.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/commands/workspaces.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/cli/options.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/core/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/core/project_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/core/workspace_repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/errors.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/executor.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/generated_artifacts.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/git_exclude_service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/imports/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/imports/filesystem.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/imports/models.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/mcp_service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/rules/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/rules/compilers.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/rules/models.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/rules/parser.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/rules/repository.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/skills/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/skills/install_sources.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/skills/models.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/spec/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/__init__.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/enums.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/import_selector.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/tui/sections.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/utils.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/validation.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic/workspaces.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/dependency_links.txt +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/entry_points.txt +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/requires.txt +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/code_agnostic.egg-info/top_level.txt +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/setup.cfg +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_aliases.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_apply_codex.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_apply_cursor.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_apply_target.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_flags.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_git_exclude.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_import_interactive.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_mcp.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_module_organization.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_plan.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_projects.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_restore.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_validate.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_cli_workspace_resolution.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_common_mcp_to_dto.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_compiled_planning.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_dto_to_common_mcp.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_git_exclude_service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_mcp_service.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_planner_executor.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_planner_rules.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_skill_install_sources.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_symlink_planning.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_sync_plan_model.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_transactional_executor.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_utils.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_version.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_workspace_config_sync.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/tests/test_workspace_repo_status.py +0 -0
- {code_agnostic-0.3.15 → code_agnostic-0.3.17}/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.
|
|
3
|
+
Version: 0.3.17
|
|
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
|
|
@@ -56,9 +56,10 @@ AI coding tools each want config in a different place and format. When you use m
|
|
|
56
56
|
~/.cursor/ Compiled & synced for Cursor
|
|
57
57
|
~/.codex/ Compiled & synced for Codex (or CODEX_HOME)
|
|
58
58
|
~/.claude.json and ~/.claude/ Compiled & synced for Claude Code
|
|
59
|
+
~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
|
|
59
60
|
```
|
|
60
61
|
|
|
61
|
-
Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, and `CLAUDE.local.md` memory for Claude Code.
|
|
62
|
+
Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex/Copilot, and `CLAUDE.local.md` memory for Claude Code.
|
|
62
63
|
|
|
63
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.
|
|
64
65
|
|
|
@@ -77,8 +78,9 @@ Today the implementation is still mixed: some assets are compiled and some are s
|
|
|
77
78
|
|
|
78
79
|
Workspace sync may generate repo-local outputs, but those outputs are not
|
|
79
80
|
source. Project-local skill folders generated inside a repo, such as
|
|
80
|
-
`.agents/skills
|
|
81
|
-
skills into managed project source first, then run `plan` /
|
|
81
|
+
`.agents/skills`, `.opencode/skills`, or `.github/skills`, are also generated
|
|
82
|
+
outputs; install skills into managed project source first, then run `plan` /
|
|
83
|
+
`apply`.
|
|
82
84
|
|
|
83
85
|
## Install
|
|
84
86
|
|
|
@@ -114,6 +116,7 @@ code-agnostic import apply -a codex
|
|
|
114
116
|
code-agnostic apps enable -a cursor
|
|
115
117
|
code-agnostic apps enable -a opencode
|
|
116
118
|
code-agnostic apps enable -a claude
|
|
119
|
+
code-agnostic apps enable -a copilot
|
|
117
120
|
|
|
118
121
|
# Preview and apply
|
|
119
122
|
code-agnostic validate
|
|
@@ -123,19 +126,19 @@ code-agnostic apply
|
|
|
123
126
|
|
|
124
127
|
## Editor compatibility
|
|
125
128
|
|
|
126
|
-
| Feature | OpenCode | Cursor | Codex | Claude Code |
|
|
127
|
-
|
|
128
|
-
| MCP sync | yes | yes | yes | yes |
|
|
129
|
-
| Rules sync (cross-compiled) | yes | yes | yes | yes |
|
|
130
|
-
| Skills sync | yes | yes | yes | yes |
|
|
131
|
-
| Agents sync | yes | yes | yes | yes |
|
|
132
|
-
| Workspace root `AGENTS.md` link | yes | yes | yes | yes |
|
|
133
|
-
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
|
|
134
|
-
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
|
|
135
|
-
| Nested `AGENTS.md` discovery | -- | yes | yes | -- |
|
|
136
|
-
| Workspace propagation | yes | yes | yes | yes |
|
|
137
|
-
| Import from | yes | yes | yes | yes |
|
|
138
|
-
| Interactive import (TUI) | yes | yes | yes | yes |
|
|
129
|
+
| Feature | OpenCode | Cursor | Codex | Claude Code | GitHub Copilot |
|
|
130
|
+
|---------|:--------:|:------:|:-----:|:-----------:|:--------------:|
|
|
131
|
+
| MCP sync | yes | yes | yes | yes | yes |
|
|
132
|
+
| Rules sync (cross-compiled) | yes | yes | yes | yes | yes |
|
|
133
|
+
| Skills sync | yes | yes | yes | yes | yes |
|
|
134
|
+
| Agents sync | yes | yes | yes | yes | yes |
|
|
135
|
+
| Workspace root `AGENTS.md` link | yes | yes | yes | yes | yes |
|
|
136
|
+
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- | -- |
|
|
137
|
+
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes | yes |
|
|
138
|
+
| Nested `AGENTS.md` discovery | -- | yes | yes | -- | yes |
|
|
139
|
+
| Workspace propagation | yes | yes | yes | yes | yes |
|
|
140
|
+
| Import from | yes | yes | yes | yes | yes |
|
|
141
|
+
| Interactive import (TUI) | yes | yes | yes | yes | yes |
|
|
139
142
|
|
|
140
143
|
`yes` means the resource type is synced for that editor. Some metadata is still
|
|
141
144
|
target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
|
|
@@ -143,7 +146,7 @@ that are omitted or rejected for a selected target.
|
|
|
143
146
|
|
|
144
147
|
Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
|
|
145
148
|
|
|
146
|
-
OpenCode workspace configs write project-root `opencode.json` files that include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
|
|
149
|
+
OpenCode workspace configs write project-root `opencode.json` files that include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`. Copilot repo-shared MCP, skills, and agents are written under `.github/`.
|
|
147
150
|
|
|
148
151
|
Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load `AGENTS.md` files that already exist in the opened project. Codex documents nested `AGENTS.md` discovery, but not a native config include for an extra workspace file.
|
|
149
152
|
|
|
@@ -157,14 +160,13 @@ Plan-then-apply workflow. Preview every change before it touches disk.
|
|
|
157
160
|
code-agnostic validate # check canonical source files
|
|
158
161
|
code-agnostic plan -a cursor # dry-run for one editor
|
|
159
162
|
code-agnostic plan # dry-run for all
|
|
160
|
-
code-agnostic apply # apply changes for all enabled editors
|
|
161
163
|
code-agnostic status # check drift and disabled app states
|
|
162
164
|
code-agnostic explain-lossiness # show fields omitted or rejected per editor
|
|
163
165
|
```
|
|
164
166
|
|
|
165
167
|
Bare `plan` and `apply` target every enabled editor; bare `status` also shows
|
|
166
|
-
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`,
|
|
167
|
-
when you want one editor at a time.
|
|
168
|
+
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, `-a claude`, or
|
|
169
|
+
`-a copilot` when you want one editor at a time.
|
|
168
170
|
|
|
169
171
|
If managed outputs need repair after an apply, restore the active synced revision:
|
|
170
172
|
|
|
@@ -264,14 +266,19 @@ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local
|
|
|
264
266
|
skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be
|
|
265
267
|
inspected with `code-agnostic skills list -w <name>`. Project-local skills live
|
|
266
268
|
under `~/.config/code-agnostic/projects/<name>/skills` and are generated into
|
|
267
|
-
the registered project directory by `plan` / `apply`.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
skills
|
|
271
|
-
|
|
272
|
-
`.
|
|
273
|
-
|
|
274
|
-
|
|
269
|
+
the registered project directory by `plan` / `apply`. The current first-class
|
|
270
|
+
list/remove commands cover global and workspace scopes; project skill inventory
|
|
271
|
+
and removal are not supported yet, and `code-agnostic projects list` only shows
|
|
272
|
+
a coarse `[skills]` marker today. Codex generated skill outputs are written to
|
|
273
|
+
`~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
|
|
274
|
+
set, defaulting to `~/.codex`. Claude Code generated skills and agents are
|
|
275
|
+
written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
|
|
276
|
+
copies under repo-local `.claude/skills` and `.claude/agents`. GitHub Copilot
|
|
277
|
+
generated skills and agents are written under `~/.copilot/skills` and
|
|
278
|
+
`~/.copilot/agents`, with repo-shared copies under `.github/skills` and
|
|
279
|
+
`.github/agents`.
|
|
280
|
+
|
|
281
|
+
If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, `.claude/skills`, or `.github/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
|
|
275
282
|
|
|
276
283
|
Planned convenience command:
|
|
277
284
|
|
|
@@ -285,7 +292,7 @@ first implementation slice.
|
|
|
285
292
|
|
|
286
293
|
### Workspaces
|
|
287
294
|
|
|
288
|
-
Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, and
|
|
295
|
+
Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Copilot receives repo-shared `.github/mcp.json`, `.github/skills`, and `.github/agents`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, Claude, and Copilot; user-created target app skill folders remain unmanaged unless they were generated from managed source.
|
|
289
296
|
|
|
290
297
|
Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
|
|
291
298
|
|
|
@@ -313,6 +320,7 @@ Migrate existing config from any supported editor into the hub.
|
|
|
313
320
|
code-agnostic import plan -a codex
|
|
314
321
|
code-agnostic import apply -a codex
|
|
315
322
|
code-agnostic import plan -a claude
|
|
323
|
+
code-agnostic import plan -a copilot
|
|
316
324
|
code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
|
|
317
325
|
code-agnostic import plan -a codex -i # interactive TUI picker
|
|
318
326
|
```
|
|
@@ -355,6 +363,7 @@ The compiler migration is documented in:
|
|
|
355
363
|
- [x] Interactive TUI for import selection
|
|
356
364
|
- [x] Claude Code support
|
|
357
365
|
- [x] Project-scoped skill installs and sync
|
|
366
|
+
- [ ] Project-scoped skill inventory and remove commands
|
|
358
367
|
- [ ] `rules add` / `skills add` / `agents add` commands (open `$EDITOR` with template)
|
|
359
368
|
- [ ] Shell auto-complete
|
|
360
369
|
- [ ] Full TUI mode (command palette + menus)
|
|
@@ -31,9 +31,10 @@ AI coding tools each want config in a different place and format. When you use m
|
|
|
31
31
|
~/.cursor/ Compiled & synced for Cursor
|
|
32
32
|
~/.codex/ Compiled & synced for Codex (or CODEX_HOME)
|
|
33
33
|
~/.claude.json and ~/.claude/ Compiled & synced for Claude Code
|
|
34
|
+
~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, and `CLAUDE.local.md` memory for Claude Code.
|
|
37
|
+
Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex/Copilot, and `CLAUDE.local.md` memory for Claude Code.
|
|
37
38
|
|
|
38
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.
|
|
39
40
|
|
|
@@ -52,8 +53,9 @@ Today the implementation is still mixed: some assets are compiled and some are s
|
|
|
52
53
|
|
|
53
54
|
Workspace sync may generate repo-local outputs, but those outputs are not
|
|
54
55
|
source. Project-local skill folders generated inside a repo, such as
|
|
55
|
-
`.agents/skills
|
|
56
|
-
skills into managed project source first, then run `plan` /
|
|
56
|
+
`.agents/skills`, `.opencode/skills`, or `.github/skills`, are also generated
|
|
57
|
+
outputs; install skills into managed project source first, then run `plan` /
|
|
58
|
+
`apply`.
|
|
57
59
|
|
|
58
60
|
## Install
|
|
59
61
|
|
|
@@ -89,6 +91,7 @@ code-agnostic import apply -a codex
|
|
|
89
91
|
code-agnostic apps enable -a cursor
|
|
90
92
|
code-agnostic apps enable -a opencode
|
|
91
93
|
code-agnostic apps enable -a claude
|
|
94
|
+
code-agnostic apps enable -a copilot
|
|
92
95
|
|
|
93
96
|
# Preview and apply
|
|
94
97
|
code-agnostic validate
|
|
@@ -98,19 +101,19 @@ code-agnostic apply
|
|
|
98
101
|
|
|
99
102
|
## Editor compatibility
|
|
100
103
|
|
|
101
|
-
| Feature | OpenCode | Cursor | Codex | Claude Code |
|
|
102
|
-
|
|
103
|
-
| MCP sync | yes | yes | yes | yes |
|
|
104
|
-
| Rules sync (cross-compiled) | yes | yes | yes | yes |
|
|
105
|
-
| Skills sync | yes | yes | yes | yes |
|
|
106
|
-
| Agents sync | yes | yes | yes | yes |
|
|
107
|
-
| Workspace root `AGENTS.md` link | yes | yes | yes | yes |
|
|
108
|
-
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
|
|
109
|
-
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
|
|
110
|
-
| Nested `AGENTS.md` discovery | -- | yes | yes | -- |
|
|
111
|
-
| Workspace propagation | yes | yes | yes | yes |
|
|
112
|
-
| Import from | yes | yes | yes | yes |
|
|
113
|
-
| Interactive import (TUI) | yes | yes | yes | yes |
|
|
104
|
+
| Feature | OpenCode | Cursor | Codex | Claude Code | GitHub Copilot |
|
|
105
|
+
|---------|:--------:|:------:|:-----:|:-----------:|:--------------:|
|
|
106
|
+
| MCP sync | yes | yes | yes | yes | yes |
|
|
107
|
+
| Rules sync (cross-compiled) | yes | yes | yes | yes | yes |
|
|
108
|
+
| Skills sync | yes | yes | yes | yes | yes |
|
|
109
|
+
| Agents sync | yes | yes | yes | yes | yes |
|
|
110
|
+
| Workspace root `AGENTS.md` link | yes | yes | yes | yes | yes |
|
|
111
|
+
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- | -- |
|
|
112
|
+
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes | yes |
|
|
113
|
+
| Nested `AGENTS.md` discovery | -- | yes | yes | -- | yes |
|
|
114
|
+
| Workspace propagation | yes | yes | yes | yes | yes |
|
|
115
|
+
| Import from | yes | yes | yes | yes | yes |
|
|
116
|
+
| Interactive import (TUI) | yes | yes | yes | yes | yes |
|
|
114
117
|
|
|
115
118
|
`yes` means the resource type is synced for that editor. Some metadata is still
|
|
116
119
|
target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
|
|
@@ -118,7 +121,7 @@ that are omitted or rejected for a selected target.
|
|
|
118
121
|
|
|
119
122
|
Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
|
|
120
123
|
|
|
121
|
-
OpenCode workspace configs write project-root `opencode.json` files that include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
|
|
124
|
+
OpenCode workspace configs write project-root `opencode.json` files that include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`. Copilot repo-shared MCP, skills, and agents are written under `.github/`.
|
|
122
125
|
|
|
123
126
|
Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load `AGENTS.md` files that already exist in the opened project. Codex documents nested `AGENTS.md` discovery, but not a native config include for an extra workspace file.
|
|
124
127
|
|
|
@@ -132,14 +135,13 @@ Plan-then-apply workflow. Preview every change before it touches disk.
|
|
|
132
135
|
code-agnostic validate # check canonical source files
|
|
133
136
|
code-agnostic plan -a cursor # dry-run for one editor
|
|
134
137
|
code-agnostic plan # dry-run for all
|
|
135
|
-
code-agnostic apply # apply changes for all enabled editors
|
|
136
138
|
code-agnostic status # check drift and disabled app states
|
|
137
139
|
code-agnostic explain-lossiness # show fields omitted or rejected per editor
|
|
138
140
|
```
|
|
139
141
|
|
|
140
142
|
Bare `plan` and `apply` target every enabled editor; bare `status` also shows
|
|
141
|
-
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`,
|
|
142
|
-
when you want one editor at a time.
|
|
143
|
+
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, `-a claude`, or
|
|
144
|
+
`-a copilot` when you want one editor at a time.
|
|
143
145
|
|
|
144
146
|
If managed outputs need repair after an apply, restore the active synced revision:
|
|
145
147
|
|
|
@@ -239,14 +241,19 @@ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local
|
|
|
239
241
|
skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be
|
|
240
242
|
inspected with `code-agnostic skills list -w <name>`. Project-local skills live
|
|
241
243
|
under `~/.config/code-agnostic/projects/<name>/skills` and are generated into
|
|
242
|
-
the registered project directory by `plan` / `apply`.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
skills
|
|
246
|
-
|
|
247
|
-
`.
|
|
248
|
-
|
|
249
|
-
|
|
244
|
+
the registered project directory by `plan` / `apply`. The current first-class
|
|
245
|
+
list/remove commands cover global and workspace scopes; project skill inventory
|
|
246
|
+
and removal are not supported yet, and `code-agnostic projects list` only shows
|
|
247
|
+
a coarse `[skills]` marker today. Codex generated skill outputs are written to
|
|
248
|
+
`~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
|
|
249
|
+
set, defaulting to `~/.codex`. Claude Code generated skills and agents are
|
|
250
|
+
written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
|
|
251
|
+
copies under repo-local `.claude/skills` and `.claude/agents`. GitHub Copilot
|
|
252
|
+
generated skills and agents are written under `~/.copilot/skills` and
|
|
253
|
+
`~/.copilot/agents`, with repo-shared copies under `.github/skills` and
|
|
254
|
+
`.github/agents`.
|
|
255
|
+
|
|
256
|
+
If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, `.claude/skills`, or `.github/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
|
|
250
257
|
|
|
251
258
|
Planned convenience command:
|
|
252
259
|
|
|
@@ -260,7 +267,7 @@ first implementation slice.
|
|
|
260
267
|
|
|
261
268
|
### Workspaces
|
|
262
269
|
|
|
263
|
-
Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, and
|
|
270
|
+
Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Copilot receives repo-shared `.github/mcp.json`, `.github/skills`, and `.github/agents`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, Claude, and Copilot; user-created target app skill folders remain unmanaged unless they were generated from managed source.
|
|
264
271
|
|
|
265
272
|
Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
|
|
266
273
|
|
|
@@ -288,6 +295,7 @@ Migrate existing config from any supported editor into the hub.
|
|
|
288
295
|
code-agnostic import plan -a codex
|
|
289
296
|
code-agnostic import apply -a codex
|
|
290
297
|
code-agnostic import plan -a claude
|
|
298
|
+
code-agnostic import plan -a copilot
|
|
291
299
|
code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
|
|
292
300
|
code-agnostic import plan -a codex -i # interactive TUI picker
|
|
293
301
|
```
|
|
@@ -330,6 +338,7 @@ The compiler migration is documented in:
|
|
|
330
338
|
- [x] Interactive TUI for import selection
|
|
331
339
|
- [x] Claude Code support
|
|
332
340
|
- [x] Project-scoped skill installs and sync
|
|
341
|
+
- [ ] Project-scoped skill inventory and remove commands
|
|
333
342
|
- [ ] `rules add` / `skills add` / `agents add` commands (open `$EDITOR` with template)
|
|
334
343
|
- [ ] Shell auto-complete
|
|
335
344
|
- [ ] Full TUI mode (command palette + menus)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Per-editor agent compilers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from code_agnostic.agents.claude import serialize_claude_agent
|
|
11
|
+
from code_agnostic.agents.codex import serialize_codex_agent
|
|
12
|
+
from code_agnostic.agents.models import Agent
|
|
13
|
+
from code_agnostic.agents.opencode import serialize_opencode_agent
|
|
14
|
+
from code_agnostic.errors import InvalidConfigSchemaError
|
|
15
|
+
|
|
16
|
+
_CURSOR_AGENT_FRONTMATTER_KEYS = frozenset(
|
|
17
|
+
{"name", "description", "model", "readonly", "is_background"}
|
|
18
|
+
)
|
|
19
|
+
_COPILOT_AGENT_FRONTMATTER_KEYS = frozenset(
|
|
20
|
+
{
|
|
21
|
+
"name",
|
|
22
|
+
"description",
|
|
23
|
+
"model",
|
|
24
|
+
"target",
|
|
25
|
+
"disable-model-invocation",
|
|
26
|
+
"user-invocable",
|
|
27
|
+
"infer",
|
|
28
|
+
"mcp-servers",
|
|
29
|
+
"metadata",
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IAgentCompiler(ABC):
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def compile(self, agent: Agent) -> str:
|
|
37
|
+
"""Return compiled agent content for target editor."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OpenCodeAgentCompiler(IAgentCompiler):
|
|
41
|
+
"""Cross-compile for OpenCode agents."""
|
|
42
|
+
|
|
43
|
+
def compile(self, agent: Agent) -> str:
|
|
44
|
+
return serialize_opencode_agent(agent)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CursorAgentCompiler(IAgentCompiler):
|
|
48
|
+
"""Cross-compile for Cursor subagents."""
|
|
49
|
+
|
|
50
|
+
def compile(self, agent: Agent) -> str:
|
|
51
|
+
return _serialize_cursor_agent(agent)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _serialize_cursor_agent(agent: Agent) -> str:
|
|
55
|
+
overrides = dict(agent.metadata.app_overrides.get("cursor", {}))
|
|
56
|
+
fm: dict[str, Any] = {}
|
|
57
|
+
if agent.metadata.name:
|
|
58
|
+
fm["name"] = agent.metadata.name
|
|
59
|
+
if agent.metadata.description:
|
|
60
|
+
fm["description"] = agent.metadata.description
|
|
61
|
+
|
|
62
|
+
model = overrides.pop("model", agent.metadata.model)
|
|
63
|
+
if model:
|
|
64
|
+
fm["model"] = model
|
|
65
|
+
|
|
66
|
+
if agent.metadata.tools.write is False:
|
|
67
|
+
fm["readonly"] = True
|
|
68
|
+
|
|
69
|
+
for key in sorted(overrides):
|
|
70
|
+
if key not in _CURSOR_AGENT_FRONTMATTER_KEYS:
|
|
71
|
+
allowed = ", ".join(sorted(_CURSOR_AGENT_FRONTMATTER_KEYS))
|
|
72
|
+
raise InvalidConfigSchemaError(
|
|
73
|
+
agent.source_path,
|
|
74
|
+
f"x-cursor.{key} is not supported in cursor agent "
|
|
75
|
+
f"frontmatter; allowed keys: {allowed}",
|
|
76
|
+
)
|
|
77
|
+
fm[key] = overrides[key]
|
|
78
|
+
|
|
79
|
+
parts: list[str] = []
|
|
80
|
+
if fm:
|
|
81
|
+
parts.append("---")
|
|
82
|
+
parts.append(yaml.dump(fm, default_flow_style=False, sort_keys=False).rstrip())
|
|
83
|
+
parts.append("---")
|
|
84
|
+
parts.append("")
|
|
85
|
+
parts.append(agent.content)
|
|
86
|
+
return "\n".join(parts)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class CodexAgentCompiler(IAgentCompiler):
|
|
90
|
+
"""Cross-compile for Codex subagents."""
|
|
91
|
+
|
|
92
|
+
def compile(self, agent: Agent) -> str:
|
|
93
|
+
return serialize_codex_agent(agent)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class CopilotAgentCompiler(IAgentCompiler):
|
|
97
|
+
"""Cross-compile for GitHub Copilot custom agents."""
|
|
98
|
+
|
|
99
|
+
def compile(self, agent: Agent) -> str:
|
|
100
|
+
return _serialize_copilot_agent(agent)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _serialize_copilot_agent(agent: Agent) -> str:
|
|
104
|
+
overrides = dict(agent.metadata.app_overrides.get("copilot", {}))
|
|
105
|
+
fm: dict[str, Any] = {}
|
|
106
|
+
|
|
107
|
+
name = overrides.pop("name", agent.metadata.name or agent.name)
|
|
108
|
+
if name:
|
|
109
|
+
fm["name"] = name
|
|
110
|
+
|
|
111
|
+
description = overrides.pop("description", agent.metadata.description)
|
|
112
|
+
if not description:
|
|
113
|
+
raise InvalidConfigSchemaError(
|
|
114
|
+
agent.source_path,
|
|
115
|
+
"Copilot agents require a description",
|
|
116
|
+
)
|
|
117
|
+
fm["description"] = description
|
|
118
|
+
|
|
119
|
+
model = overrides.pop("model", agent.metadata.model)
|
|
120
|
+
if model:
|
|
121
|
+
fm["model"] = model
|
|
122
|
+
|
|
123
|
+
tools = _copilot_tools(agent)
|
|
124
|
+
if tools is not None:
|
|
125
|
+
fm["tools"] = tools
|
|
126
|
+
|
|
127
|
+
for key in sorted(overrides):
|
|
128
|
+
if key not in _COPILOT_AGENT_FRONTMATTER_KEYS:
|
|
129
|
+
allowed = ", ".join(sorted(_COPILOT_AGENT_FRONTMATTER_KEYS))
|
|
130
|
+
raise InvalidConfigSchemaError(
|
|
131
|
+
agent.source_path,
|
|
132
|
+
f"x-copilot.{key} is not supported in copilot agent "
|
|
133
|
+
f"frontmatter; allowed keys: {allowed}",
|
|
134
|
+
)
|
|
135
|
+
if key in fm:
|
|
136
|
+
continue
|
|
137
|
+
fm[key] = overrides[key]
|
|
138
|
+
|
|
139
|
+
parts: list[str] = []
|
|
140
|
+
if fm:
|
|
141
|
+
parts.append("---")
|
|
142
|
+
parts.append(yaml.dump(fm, default_flow_style=False, sort_keys=False).rstrip())
|
|
143
|
+
parts.append("---")
|
|
144
|
+
parts.append("")
|
|
145
|
+
parts.append(agent.content)
|
|
146
|
+
return "\n".join(parts)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _copilot_tools(agent: Agent) -> list[str] | None:
|
|
150
|
+
permissions = agent.metadata.tools
|
|
151
|
+
if permissions.read is True and permissions.write is True and not permissions.mcp:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
tools: list[str] = []
|
|
155
|
+
if permissions.read:
|
|
156
|
+
tools.append("read")
|
|
157
|
+
if permissions.write:
|
|
158
|
+
tools.append("edit")
|
|
159
|
+
|
|
160
|
+
for item in agent.metadata.tools.mcp:
|
|
161
|
+
server = item.get("server")
|
|
162
|
+
tool = item.get("tool")
|
|
163
|
+
if not server:
|
|
164
|
+
raise InvalidConfigSchemaError(
|
|
165
|
+
agent.source_path,
|
|
166
|
+
"Copilot agent MCP tools require a server",
|
|
167
|
+
)
|
|
168
|
+
tools.append(f"{server}/{tool}" if tool else f"{server}/*")
|
|
169
|
+
return tools
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ClaudeAgentCompiler(IAgentCompiler):
|
|
173
|
+
"""Cross-compile for Claude Code subagents."""
|
|
174
|
+
|
|
175
|
+
def compile(self, agent: Agent) -> str:
|
|
176
|
+
return serialize_claude_agent(agent)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""GitHub Copilot custom agent Markdown conversion."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from code_agnostic.agents.models import Agent, AgentMetadata, AgentToolPermissions
|
|
13
|
+
|
|
14
|
+
_FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
15
|
+
_READ_ALIASES = {"read", "notebookread", "view"}
|
|
16
|
+
_WRITE_ALIASES = {"edit", "multiedit", "write", "notebookedit"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class CopilotAgentParseResult:
|
|
21
|
+
agent: Agent
|
|
22
|
+
warnings: list[str]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_copilot_agent(path: Path) -> CopilotAgentParseResult:
|
|
26
|
+
text = path.read_text(encoding="utf-8")
|
|
27
|
+
raw: dict[str, Any]
|
|
28
|
+
match = _FRONTMATTER_RE.match(text)
|
|
29
|
+
if match:
|
|
30
|
+
raw = yaml.safe_load(match.group(1)) or {}
|
|
31
|
+
content = text[match.end() :]
|
|
32
|
+
else:
|
|
33
|
+
raw = {}
|
|
34
|
+
content = text
|
|
35
|
+
if not isinstance(raw, dict):
|
|
36
|
+
raw = {}
|
|
37
|
+
|
|
38
|
+
fallback_name = _copilot_agent_stem(path)
|
|
39
|
+
tools, warnings = _parse_copilot_tools(raw.get("tools"), path)
|
|
40
|
+
agent = Agent(
|
|
41
|
+
name=fallback_name,
|
|
42
|
+
source_path=path,
|
|
43
|
+
metadata=AgentMetadata(
|
|
44
|
+
name=str(raw.get("name", fallback_name)),
|
|
45
|
+
description=str(raw.get("description", "")),
|
|
46
|
+
model=str(raw.get("model", "")),
|
|
47
|
+
tools=tools,
|
|
48
|
+
),
|
|
49
|
+
content=content,
|
|
50
|
+
)
|
|
51
|
+
return CopilotAgentParseResult(agent=agent, warnings=warnings)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def copilot_agent_filename(path: Path) -> str:
|
|
55
|
+
return f"{_copilot_agent_stem(path)}.agent.md"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _copilot_agent_stem(path: Path) -> str:
|
|
59
|
+
name = path.name
|
|
60
|
+
if name.endswith(".agent.md"):
|
|
61
|
+
return name[: -len(".agent.md")]
|
|
62
|
+
if name.endswith(".md"):
|
|
63
|
+
return name[: -len(".md")]
|
|
64
|
+
return path.stem
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _parse_copilot_tools(
|
|
68
|
+
raw: Any, path: Path
|
|
69
|
+
) -> tuple[AgentToolPermissions, list[str]]:
|
|
70
|
+
if raw is None:
|
|
71
|
+
return AgentToolPermissions(), []
|
|
72
|
+
|
|
73
|
+
tokens = _coerce_tool_tokens(raw)
|
|
74
|
+
if tokens is None:
|
|
75
|
+
return AgentToolPermissions(), [
|
|
76
|
+
f"Skipped unrepresentable Copilot tools: {path}"
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
if "*" in tokens:
|
|
80
|
+
return AgentToolPermissions(), []
|
|
81
|
+
|
|
82
|
+
read = False
|
|
83
|
+
write = False
|
|
84
|
+
mcp: list[dict[str, str]] = []
|
|
85
|
+
unsupported: list[str] = []
|
|
86
|
+
for token in tokens:
|
|
87
|
+
normalized = token.lower()
|
|
88
|
+
if normalized in _READ_ALIASES:
|
|
89
|
+
read = True
|
|
90
|
+
continue
|
|
91
|
+
if normalized in _WRITE_ALIASES:
|
|
92
|
+
write = True
|
|
93
|
+
continue
|
|
94
|
+
if "/" in token:
|
|
95
|
+
server, tool = token.split("/", 1)
|
|
96
|
+
if server and tool:
|
|
97
|
+
item = {"server": server}
|
|
98
|
+
if tool != "*":
|
|
99
|
+
item["tool"] = tool
|
|
100
|
+
mcp.append(item)
|
|
101
|
+
continue
|
|
102
|
+
unsupported.append(token)
|
|
103
|
+
|
|
104
|
+
if unsupported:
|
|
105
|
+
return AgentToolPermissions(), [
|
|
106
|
+
f"Skipped unrepresentable Copilot tools in {path}: {', '.join(unsupported)}"
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
return AgentToolPermissions(read=read, write=write, mcp=mcp), []
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _coerce_tool_tokens(raw: Any) -> list[str] | None:
|
|
113
|
+
if isinstance(raw, str):
|
|
114
|
+
return [item.strip() for item in raw.split(",") if item.strip()]
|
|
115
|
+
if isinstance(raw, list):
|
|
116
|
+
return [item for item in raw if isinstance(item, str)]
|
|
117
|
+
return None
|
|
@@ -19,7 +19,7 @@ from code_agnostic.agents.models import (
|
|
|
19
19
|
from code_agnostic.spec.loaders import load_agent_bundle
|
|
20
20
|
|
|
21
21
|
_FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
22
|
-
_APP_OVERRIDE_PREFIXES = ("cursor", "codex", "opencode", "claude")
|
|
22
|
+
_APP_OVERRIDE_PREFIXES = ("cursor", "codex", "opencode", "claude", "copilot")
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def parse_agent(path: Path) -> Agent:
|
|
@@ -6,6 +6,8 @@ from code_agnostic.constants import (
|
|
|
6
6
|
CLAUDE_PROJECT_DIRNAME,
|
|
7
7
|
CODEX_CONFIG_FILENAME,
|
|
8
8
|
CODEX_PROJECT_DIRNAME,
|
|
9
|
+
COPILOT_PROJECT_CONFIG_FILENAME,
|
|
10
|
+
COPILOT_PROJECT_DIRNAME,
|
|
9
11
|
CURSOR_CONFIG_FILENAME,
|
|
10
12
|
CURSOR_PROJECT_DIRNAME,
|
|
11
13
|
OPENCODE_CONFIG_FILENAME,
|
|
@@ -19,6 +21,7 @@ class AppId(str, Enum):
|
|
|
19
21
|
CURSOR = "cursor"
|
|
20
22
|
CODEX = "codex"
|
|
21
23
|
CLAUDE = "claude"
|
|
24
|
+
COPILOT = "copilot"
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
@dataclass(frozen=True)
|
|
@@ -89,6 +92,17 @@ APP_CATALOG: dict[AppId, AppMetadata] = {
|
|
|
89
92
|
project_dir_name=CLAUDE_PROJECT_DIRNAME,
|
|
90
93
|
config_filename=CLAUDE_CONFIG_FILENAME,
|
|
91
94
|
),
|
|
95
|
+
AppId.COPILOT: AppMetadata(
|
|
96
|
+
app_id=AppId.COPILOT,
|
|
97
|
+
label="GitHub Copilot",
|
|
98
|
+
targetable=True,
|
|
99
|
+
toggleable=True,
|
|
100
|
+
importable=True,
|
|
101
|
+
supports_import_agents=True,
|
|
102
|
+
supports_workspace_propagation=True,
|
|
103
|
+
project_dir_name=COPILOT_PROJECT_DIRNAME,
|
|
104
|
+
config_filename=COPILOT_PROJECT_CONFIG_FILENAME,
|
|
105
|
+
),
|
|
92
106
|
}
|
|
93
107
|
|
|
94
108
|
|