code-agnostic 0.3.14__tar.gz → 0.3.16__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.14 → code_agnostic-0.3.16}/PKG-INFO +17 -9
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/README.md +16 -8
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/__init__.py +1 -1
- code_agnostic-0.3.16/code_agnostic/agents/compilers.py +87 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/claude/mapper.py +5 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/mapper.py +31 -2
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/schema.json +112 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/models.py +2 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/utils.py +8 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/mapper.py +6 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/schema.json +2 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/mapper.py +5 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/schema.json +4 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/agents.py +6 -1
- code_agnostic-0.3.16/code_agnostic/cli/commands/apply.py +86 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/import_.py +10 -2
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/mcp.py +3 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/restore.py +10 -5
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/rules.py +6 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/skills.py +74 -17
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/status.py +1 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/helpers.py +21 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/options.py +9 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/core/repository.py +43 -5
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/executor.py +21 -6
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/imports/service.py +1 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/lossiness.py +59 -2
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/mcp_service.py +5 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/loaders.py +2 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/schemas/mcp.base.schema.json +22 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/schemas/mcp.v1.schema.json +9 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/status.py +2 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/renderers.py +66 -14
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/tables.py +3 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/PKG-INFO +17 -9
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/pyproject.toml +1 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_agents.py +20 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_apply_cursor.py +1 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_apps.py +33 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_explain_lossiness.py +55 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_import.py +25 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_import_interactive.py +2 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_mcp.py +42 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_projects.py +26 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_restore.py +58 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_rules.py +16 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_skills.py +128 -5
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_status.py +26 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_workspaces.py +67 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_common_mcp_to_dto.py +12 -1
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_common_repository.py +5 -4
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_dto_to_common_mcp.py +4 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_mcp_service.py +20 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_project_config_sync.py +31 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_transactional_executor.py +80 -0
- code_agnostic-0.3.14/code_agnostic/agents/compilers.py +0 -45
- code_agnostic-0.3.14/code_agnostic/cli/commands/apply.py +0 -42
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/LICENSE +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/__main__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/claude.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/codex.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/models.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/opencode.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/agents/parser.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/app_id.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/apps_service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/claude/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/claude/config_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/claude/service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/config_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/schema_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/codex/service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/compiled_planning.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/framework.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/interfaces/repositories.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/interfaces/service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/loader.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/schema.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/common/symlink_planning.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/config_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/schema_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/cursor/service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/config_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/schema_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/apps/opencode/service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/aliases.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/apps.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/plan.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/projects.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/validate.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/cli/commands/workspaces.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/constants.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/core/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/core/project_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/core/workspace_repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/errors.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/generated_artifacts.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/git_exclude_service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/imports/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/imports/adapters.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/imports/filesystem.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/imports/models.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/models.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/planner.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/project_artifacts.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/rules/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/rules/compilers.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/rules/models.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/rules/parser.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/rules/repository.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/skills/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/skills/compilers.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/skills/install_sources.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/skills/models.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/skills/parser.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/schemas/agent.v1.schema.json +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/schemas/rule.v1.schema.json +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/spec/schemas/skill.v1.schema.json +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/__init__.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/enums.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/import_selector.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/tui/sections.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/utils.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/validation.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/workspace_artifacts.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic/workspaces.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/SOURCES.txt +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/dependency_links.txt +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/entry_points.txt +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/requires.txt +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/code_agnostic.egg-info/top_level.txt +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/setup.cfg +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_aliases.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_apply_apps.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_apply_codex.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_apply_target.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_flags.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_git_exclude.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_module_organization.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_plan.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_validate.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_cli_workspace_resolution.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_compiled_planning.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_git_exclude_service.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_planner_executor.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_planner_rules.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_skill_install_sources.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_symlink_planning.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_sync_plan_model.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_utils.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_version.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_workspace_config_sync.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/tests/test_workspace_repo_status.py +0 -0
- {code_agnostic-0.3.14 → code_agnostic-0.3.16}/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.16
|
|
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
|
|
@@ -171,6 +171,7 @@ If managed outputs need repair after an apply, restore the active synced revisio
|
|
|
171
171
|
```bash
|
|
172
172
|
code-agnostic restore
|
|
173
173
|
code-agnostic restore -w myproject
|
|
174
|
+
code-agnostic restore --project myproject
|
|
174
175
|
```
|
|
175
176
|
|
|
176
177
|
### MCP management
|
|
@@ -179,11 +180,14 @@ Add, remove, and list MCP servers without editing JSON by hand.
|
|
|
179
180
|
|
|
180
181
|
```bash
|
|
181
182
|
code-agnostic mcp add github --command npx --args @modelcontextprotocol/server-github --env GITHUB_TOKEN
|
|
183
|
+
code-agnostic mcp add local-docs --command uvx --args docs-mcp --cwd ~/code/docs
|
|
182
184
|
code-agnostic mcp list
|
|
183
185
|
code-agnostic mcp remove github
|
|
184
186
|
```
|
|
185
187
|
|
|
186
188
|
Env vars without a value (`--env GITHUB_TOKEN`) are stored as `${GITHUB_TOKEN}` references.
|
|
189
|
+
Cursor-local MCP `envFile` can be represented in source config and is reported
|
|
190
|
+
as lossy for targets that do not support environment files.
|
|
187
191
|
|
|
188
192
|
### Rules with metadata
|
|
189
193
|
|
|
@@ -260,12 +264,14 @@ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local
|
|
|
260
264
|
skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be
|
|
261
265
|
inspected with `code-agnostic skills list -w <name>`. Project-local skills live
|
|
262
266
|
under `~/.config/code-agnostic/projects/<name>/skills` and are generated into
|
|
263
|
-
the registered project directory by `plan` / `apply`.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
skills
|
|
267
|
-
|
|
268
|
-
`.
|
|
267
|
+
the registered project directory by `plan` / `apply`. The current first-class
|
|
268
|
+
list/remove commands cover global and workspace scopes; project skill inventory
|
|
269
|
+
and removal are not supported yet, and `code-agnostic projects list` only shows
|
|
270
|
+
a coarse `[skills]` marker today. Codex generated skill outputs are written to
|
|
271
|
+
`~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
|
|
272
|
+
set, defaulting to `~/.codex`. Claude Code generated skills and agents are
|
|
273
|
+
written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
|
|
274
|
+
copies under repo-local `.claude/skills` and `.claude/agents`.
|
|
269
275
|
|
|
270
276
|
If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, or `.claude/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.
|
|
271
277
|
|
|
@@ -317,7 +323,8 @@ code-agnostic import plan -a codex -i # interactive TUI picker
|
|
|
317
323
|
only the selected sections. Conflicts are skipped by default, so rerun with
|
|
318
324
|
`--on-conflict overwrite` only after reviewing the preview. Use `--include`,
|
|
319
325
|
`--exclude`, `--source-root`, and `--follow-symlinks` to narrow what gets
|
|
320
|
-
imported.
|
|
326
|
+
imported. `--source-root` means the source app config root to read from, not the
|
|
327
|
+
`code-agnostic` hub root.
|
|
321
328
|
|
|
322
329
|
### CLI conventions
|
|
323
330
|
|
|
@@ -349,7 +356,8 @@ The compiler migration is documented in:
|
|
|
349
356
|
- [x] Per-workspace git-exclude customization
|
|
350
357
|
- [x] Interactive TUI for import selection
|
|
351
358
|
- [x] Claude Code support
|
|
352
|
-
- [
|
|
359
|
+
- [x] Project-scoped skill installs and sync
|
|
360
|
+
- [ ] Project-scoped skill inventory and remove commands
|
|
353
361
|
- [ ] `rules add` / `skills add` / `agents add` commands (open `$EDITOR` with template)
|
|
354
362
|
- [ ] Shell auto-complete
|
|
355
363
|
- [ ] Full TUI mode (command palette + menus)
|
|
@@ -146,6 +146,7 @@ If managed outputs need repair after an apply, restore the active synced revisio
|
|
|
146
146
|
```bash
|
|
147
147
|
code-agnostic restore
|
|
148
148
|
code-agnostic restore -w myproject
|
|
149
|
+
code-agnostic restore --project myproject
|
|
149
150
|
```
|
|
150
151
|
|
|
151
152
|
### MCP management
|
|
@@ -154,11 +155,14 @@ Add, remove, and list MCP servers without editing JSON by hand.
|
|
|
154
155
|
|
|
155
156
|
```bash
|
|
156
157
|
code-agnostic mcp add github --command npx --args @modelcontextprotocol/server-github --env GITHUB_TOKEN
|
|
158
|
+
code-agnostic mcp add local-docs --command uvx --args docs-mcp --cwd ~/code/docs
|
|
157
159
|
code-agnostic mcp list
|
|
158
160
|
code-agnostic mcp remove github
|
|
159
161
|
```
|
|
160
162
|
|
|
161
163
|
Env vars without a value (`--env GITHUB_TOKEN`) are stored as `${GITHUB_TOKEN}` references.
|
|
164
|
+
Cursor-local MCP `envFile` can be represented in source config and is reported
|
|
165
|
+
as lossy for targets that do not support environment files.
|
|
162
166
|
|
|
163
167
|
### Rules with metadata
|
|
164
168
|
|
|
@@ -235,12 +239,14 @@ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local
|
|
|
235
239
|
skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be
|
|
236
240
|
inspected with `code-agnostic skills list -w <name>`. Project-local skills live
|
|
237
241
|
under `~/.config/code-agnostic/projects/<name>/skills` and are generated into
|
|
238
|
-
the registered project directory by `plan` / `apply`.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
skills
|
|
242
|
-
|
|
243
|
-
`.
|
|
242
|
+
the registered project directory by `plan` / `apply`. The current first-class
|
|
243
|
+
list/remove commands cover global and workspace scopes; project skill inventory
|
|
244
|
+
and removal are not supported yet, and `code-agnostic projects list` only shows
|
|
245
|
+
a coarse `[skills]` marker today. Codex generated skill outputs are written to
|
|
246
|
+
`~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
|
|
247
|
+
set, defaulting to `~/.codex`. Claude Code generated skills and agents are
|
|
248
|
+
written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
|
|
249
|
+
copies under repo-local `.claude/skills` and `.claude/agents`.
|
|
244
250
|
|
|
245
251
|
If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, or `.claude/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.
|
|
246
252
|
|
|
@@ -292,7 +298,8 @@ code-agnostic import plan -a codex -i # interactive TUI picker
|
|
|
292
298
|
only the selected sections. Conflicts are skipped by default, so rerun with
|
|
293
299
|
`--on-conflict overwrite` only after reviewing the preview. Use `--include`,
|
|
294
300
|
`--exclude`, `--source-root`, and `--follow-symlinks` to narrow what gets
|
|
295
|
-
imported.
|
|
301
|
+
imported. `--source-root` means the source app config root to read from, not the
|
|
302
|
+
`code-agnostic` hub root.
|
|
296
303
|
|
|
297
304
|
### CLI conventions
|
|
298
305
|
|
|
@@ -324,7 +331,8 @@ The compiler migration is documented in:
|
|
|
324
331
|
- [x] Per-workspace git-exclude customization
|
|
325
332
|
- [x] Interactive TUI for import selection
|
|
326
333
|
- [x] Claude Code support
|
|
327
|
-
- [
|
|
334
|
+
- [x] Project-scoped skill installs and sync
|
|
335
|
+
- [ ] Project-scoped skill inventory and remove commands
|
|
328
336
|
- [ ] `rules add` / `skills add` / `agents add` commands (open `$EDITOR` with template)
|
|
329
337
|
- [ ] Shell auto-complete
|
|
330
338
|
- [ ] Full TUI mode (command palette + menus)
|
|
@@ -0,0 +1,87 @@
|
|
|
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
|
+
|
|
20
|
+
|
|
21
|
+
class IAgentCompiler(ABC):
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def compile(self, agent: Agent) -> str:
|
|
24
|
+
"""Return compiled agent content for target editor."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OpenCodeAgentCompiler(IAgentCompiler):
|
|
28
|
+
"""Cross-compile for OpenCode agents."""
|
|
29
|
+
|
|
30
|
+
def compile(self, agent: Agent) -> str:
|
|
31
|
+
return serialize_opencode_agent(agent)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CursorAgentCompiler(IAgentCompiler):
|
|
35
|
+
"""Cross-compile for Cursor subagents."""
|
|
36
|
+
|
|
37
|
+
def compile(self, agent: Agent) -> str:
|
|
38
|
+
return _serialize_cursor_agent(agent)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _serialize_cursor_agent(agent: Agent) -> str:
|
|
42
|
+
overrides = dict(agent.metadata.app_overrides.get("cursor", {}))
|
|
43
|
+
fm: dict[str, Any] = {}
|
|
44
|
+
if agent.metadata.name:
|
|
45
|
+
fm["name"] = agent.metadata.name
|
|
46
|
+
if agent.metadata.description:
|
|
47
|
+
fm["description"] = agent.metadata.description
|
|
48
|
+
|
|
49
|
+
model = overrides.pop("model", agent.metadata.model)
|
|
50
|
+
if model:
|
|
51
|
+
fm["model"] = model
|
|
52
|
+
|
|
53
|
+
if agent.metadata.tools.write is False:
|
|
54
|
+
fm["readonly"] = True
|
|
55
|
+
|
|
56
|
+
for key in sorted(overrides):
|
|
57
|
+
if key not in _CURSOR_AGENT_FRONTMATTER_KEYS:
|
|
58
|
+
allowed = ", ".join(sorted(_CURSOR_AGENT_FRONTMATTER_KEYS))
|
|
59
|
+
raise InvalidConfigSchemaError(
|
|
60
|
+
agent.source_path,
|
|
61
|
+
f"x-cursor.{key} is not supported in cursor agent "
|
|
62
|
+
f"frontmatter; allowed keys: {allowed}",
|
|
63
|
+
)
|
|
64
|
+
fm[key] = overrides[key]
|
|
65
|
+
|
|
66
|
+
parts: list[str] = []
|
|
67
|
+
if fm:
|
|
68
|
+
parts.append("---")
|
|
69
|
+
parts.append(yaml.dump(fm, default_flow_style=False, sort_keys=False).rstrip())
|
|
70
|
+
parts.append("---")
|
|
71
|
+
parts.append("")
|
|
72
|
+
parts.append(agent.content)
|
|
73
|
+
return "\n".join(parts)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CodexAgentCompiler(IAgentCompiler):
|
|
77
|
+
"""Cross-compile for Codex subagents."""
|
|
78
|
+
|
|
79
|
+
def compile(self, agent: Agent) -> str:
|
|
80
|
+
return serialize_codex_agent(agent)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ClaudeAgentCompiler(IAgentCompiler):
|
|
84
|
+
"""Cross-compile for Claude Code subagents."""
|
|
85
|
+
|
|
86
|
+
def compile(self, agent: Agent) -> str:
|
|
87
|
+
return serialize_claude_agent(agent)
|
|
@@ -40,6 +40,9 @@ class ClaudeMCPMapper(IAppMCPMapper):
|
|
|
40
40
|
type=MCPServerType.STDIO,
|
|
41
41
|
command=command,
|
|
42
42
|
args=_as_list(server.get("args")),
|
|
43
|
+
cwd=server.get("cwd")
|
|
44
|
+
if isinstance(server.get("cwd"), str)
|
|
45
|
+
else None,
|
|
43
46
|
env=_as_str_dict(server.get("env")),
|
|
44
47
|
headers=_as_str_dict(server.get("headers")),
|
|
45
48
|
timeout_ms=_timeout(server.get("timeout")),
|
|
@@ -70,6 +73,8 @@ class ClaudeMCPMapper(IAppMCPMapper):
|
|
|
70
73
|
out["command"] = server.command
|
|
71
74
|
if server.args:
|
|
72
75
|
out["args"] = deepcopy(server.args)
|
|
76
|
+
if server.cwd is not None:
|
|
77
|
+
out["cwd"] = server.cwd
|
|
73
78
|
else:
|
|
74
79
|
if not server.url:
|
|
75
80
|
continue
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from copy import deepcopy
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
5
6
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
6
7
|
from code_agnostic.apps.common.models import MCPServerDTO, MCPServerType
|
|
8
|
+
from code_agnostic.errors import InvalidConfigSchemaError
|
|
7
9
|
|
|
8
10
|
_ENV_PATTERN = re.compile(r"^\$\{(?:env:)?([A-Z_][A-Z0-9_]*)\}$")
|
|
9
11
|
_BEARER_PATTERN = re.compile(r"^Bearer\s+\$\{(?:env:)?([A-Z_][A-Z0-9_]*)\}$")
|
|
@@ -34,6 +36,29 @@ def _encode_tool_timeout(timeout_ms: int) -> int | float:
|
|
|
34
36
|
return int(seconds) if timeout_ms % 1000 == 0 else seconds
|
|
35
37
|
|
|
36
38
|
|
|
39
|
+
def _codex_env_var_name(server_name: str, entry: Any) -> str | None:
|
|
40
|
+
if isinstance(entry, str):
|
|
41
|
+
return entry
|
|
42
|
+
if not isinstance(entry, dict):
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
name = entry.get("name")
|
|
46
|
+
if not isinstance(name, str):
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
source = entry.get("source", "local")
|
|
50
|
+
if source in (None, "local"):
|
|
51
|
+
return name
|
|
52
|
+
|
|
53
|
+
detail = (
|
|
54
|
+
"Codex env_vars entries with source = 'remote' cannot be imported "
|
|
55
|
+
"into common MCP config without losing remote-executor semantics"
|
|
56
|
+
if source == "remote"
|
|
57
|
+
else f"Unsupported Codex env_vars source: {source!r}"
|
|
58
|
+
)
|
|
59
|
+
raise InvalidConfigSchemaError(Path(f"mcp_servers.{server_name}.env_vars"), detail)
|
|
60
|
+
|
|
61
|
+
|
|
37
62
|
class CodexMCPMapper(IAppMCPMapper):
|
|
38
63
|
def to_common(self, payload: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
39
64
|
mapped: dict[str, MCPServerDTO] = {}
|
|
@@ -49,8 +74,9 @@ class CodexMCPMapper(IAppMCPMapper):
|
|
|
49
74
|
env: dict[str, str] = {}
|
|
50
75
|
env_vars = server.get("env_vars")
|
|
51
76
|
if isinstance(env_vars, list):
|
|
52
|
-
for
|
|
53
|
-
|
|
77
|
+
for entry in env_vars:
|
|
78
|
+
key = _codex_env_var_name(name, entry)
|
|
79
|
+
if key is not None:
|
|
54
80
|
env[key] = f"${{{key}}}"
|
|
55
81
|
env_table = server.get("env")
|
|
56
82
|
if isinstance(env_table, dict):
|
|
@@ -82,6 +108,7 @@ class CodexMCPMapper(IAppMCPMapper):
|
|
|
82
108
|
args=[str(item) for item in server.get("args", [])]
|
|
83
109
|
if isinstance(server.get("args"), list)
|
|
84
110
|
else [],
|
|
111
|
+
cwd=server.get("cwd") if isinstance(server.get("cwd"), str) else None,
|
|
85
112
|
url=url if isinstance(url, str) else None,
|
|
86
113
|
timeout_ms=_coerce_timeout_ms(server.get("tool_timeout_sec")),
|
|
87
114
|
headers=headers,
|
|
@@ -100,6 +127,8 @@ class CodexMCPMapper(IAppMCPMapper):
|
|
|
100
127
|
out["command"] = server.command
|
|
101
128
|
if server.args:
|
|
102
129
|
out["args"] = deepcopy(server.args)
|
|
130
|
+
if server.cwd is not None:
|
|
131
|
+
out["cwd"] = server.cwd
|
|
103
132
|
else:
|
|
104
133
|
if not server.url:
|
|
105
134
|
continue
|
|
@@ -210,6 +210,22 @@
|
|
|
210
210
|
"additionalProperties": false,
|
|
211
211
|
"description": "Default settings that apply to all apps.",
|
|
212
212
|
"properties": {
|
|
213
|
+
"approvals_reviewer": {
|
|
214
|
+
"allOf": [
|
|
215
|
+
{
|
|
216
|
+
"$ref": "#/definitions/ApprovalsReviewer"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"description": "Reviewer for approval prompts unless overridden by per-app settings."
|
|
220
|
+
},
|
|
221
|
+
"default_tools_approval_mode": {
|
|
222
|
+
"allOf": [
|
|
223
|
+
{
|
|
224
|
+
"$ref": "#/definitions/AppToolApproval"
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
"description": "Approval mode for tools unless overridden by per-app or per-tool settings."
|
|
228
|
+
},
|
|
213
229
|
"destructive_enabled": {
|
|
214
230
|
"description": "Whether tools with `destructive_hint = true` are allowed by default.",
|
|
215
231
|
"type": "boolean"
|
|
@@ -346,6 +362,13 @@
|
|
|
346
362
|
"CodeModeConfigToml": {
|
|
347
363
|
"additionalProperties": false,
|
|
348
364
|
"properties": {
|
|
365
|
+
"direct_only_tool_namespaces": {
|
|
366
|
+
"description": "Exact tool namespaces to expose only as direct model tools. These tools bypass deferral, remain top-level in code-mode-only sessions, and are omitted from the nested code-mode tool surface.",
|
|
367
|
+
"items": {
|
|
368
|
+
"type": "string"
|
|
369
|
+
},
|
|
370
|
+
"type": "array"
|
|
371
|
+
},
|
|
349
372
|
"enabled": {
|
|
350
373
|
"type": "boolean"
|
|
351
374
|
},
|
|
@@ -576,18 +599,30 @@
|
|
|
576
599
|
"request_rule": {
|
|
577
600
|
"type": "boolean"
|
|
578
601
|
},
|
|
602
|
+
"resize_all_images": {
|
|
603
|
+
"type": "boolean"
|
|
604
|
+
},
|
|
579
605
|
"responses_websockets": {
|
|
580
606
|
"type": "boolean"
|
|
581
607
|
},
|
|
582
608
|
"responses_websockets_v2": {
|
|
583
609
|
"type": "boolean"
|
|
584
610
|
},
|
|
611
|
+
"rollout_budget": {
|
|
612
|
+
"$ref": "#/definitions/FeatureToml_for_RolloutBudgetConfigToml"
|
|
613
|
+
},
|
|
614
|
+
"respect_system_proxy": {
|
|
615
|
+
"type": "boolean"
|
|
616
|
+
},
|
|
585
617
|
"runtime_metrics": {
|
|
586
618
|
"type": "boolean"
|
|
587
619
|
},
|
|
588
620
|
"search_tool": {
|
|
589
621
|
"type": "boolean"
|
|
590
622
|
},
|
|
623
|
+
"secret_auth_storage": {
|
|
624
|
+
"type": "boolean"
|
|
625
|
+
},
|
|
591
626
|
"shell_snapshot": {
|
|
592
627
|
"type": "boolean"
|
|
593
628
|
},
|
|
@@ -603,6 +638,9 @@
|
|
|
603
638
|
"skill_mcp_dependency_install": {
|
|
604
639
|
"type": "boolean"
|
|
605
640
|
},
|
|
641
|
+
"sleep_tool": {
|
|
642
|
+
"type": "boolean"
|
|
643
|
+
},
|
|
606
644
|
"sqlite": {
|
|
607
645
|
"type": "boolean"
|
|
608
646
|
},
|
|
@@ -621,6 +659,9 @@
|
|
|
621
659
|
"terminal_visualization_instructions": {
|
|
622
660
|
"type": "boolean"
|
|
623
661
|
},
|
|
662
|
+
"token_budget": {
|
|
663
|
+
"type": "boolean"
|
|
664
|
+
},
|
|
624
665
|
"tool_call_mcp_elicitation": {
|
|
625
666
|
"type": "boolean"
|
|
626
667
|
},
|
|
@@ -870,6 +911,16 @@
|
|
|
870
911
|
}
|
|
871
912
|
]
|
|
872
913
|
},
|
|
914
|
+
"FeatureToml_for_RolloutBudgetConfigToml": {
|
|
915
|
+
"anyOf": [
|
|
916
|
+
{
|
|
917
|
+
"type": "boolean"
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
"$ref": "#/definitions/RolloutBudgetConfigToml"
|
|
921
|
+
}
|
|
922
|
+
]
|
|
923
|
+
},
|
|
873
924
|
"FeedbackConfigToml": {
|
|
874
925
|
"additionalProperties": false,
|
|
875
926
|
"properties": {
|
|
@@ -2436,6 +2487,13 @@
|
|
|
2436
2487
|
},
|
|
2437
2488
|
"type": "object"
|
|
2438
2489
|
},
|
|
2490
|
+
"RealtimeConversationArchitecture": {
|
|
2491
|
+
"enum": [
|
|
2492
|
+
"realtimeapi",
|
|
2493
|
+
"avas"
|
|
2494
|
+
],
|
|
2495
|
+
"type": "string"
|
|
2496
|
+
},
|
|
2439
2497
|
"RealtimeConversationVersion": {
|
|
2440
2498
|
"enum": [
|
|
2441
2499
|
"v1",
|
|
@@ -2446,6 +2504,9 @@
|
|
|
2446
2504
|
"RealtimeToml": {
|
|
2447
2505
|
"additionalProperties": false,
|
|
2448
2506
|
"properties": {
|
|
2507
|
+
"architecture": {
|
|
2508
|
+
"$ref": "#/definitions/RealtimeConversationArchitecture"
|
|
2509
|
+
},
|
|
2449
2510
|
"transport": {
|
|
2450
2511
|
"$ref": "#/definitions/RealtimeTransport"
|
|
2451
2512
|
},
|
|
@@ -2524,6 +2585,35 @@
|
|
|
2524
2585
|
}
|
|
2525
2586
|
]
|
|
2526
2587
|
},
|
|
2588
|
+
"RolloutBudgetConfigToml": {
|
|
2589
|
+
"additionalProperties": false,
|
|
2590
|
+
"properties": {
|
|
2591
|
+
"enabled": {
|
|
2592
|
+
"type": "boolean"
|
|
2593
|
+
},
|
|
2594
|
+
"limit_tokens": {
|
|
2595
|
+
"format": "int64",
|
|
2596
|
+
"minimum": 1.0,
|
|
2597
|
+
"type": "integer"
|
|
2598
|
+
},
|
|
2599
|
+
"prefill_token_weight": {
|
|
2600
|
+
"format": "double",
|
|
2601
|
+
"minimum": 0.0,
|
|
2602
|
+
"type": "number"
|
|
2603
|
+
},
|
|
2604
|
+
"reminder_interval_tokens": {
|
|
2605
|
+
"format": "int64",
|
|
2606
|
+
"minimum": 1.0,
|
|
2607
|
+
"type": "integer"
|
|
2608
|
+
},
|
|
2609
|
+
"sampling_token_weight": {
|
|
2610
|
+
"format": "double",
|
|
2611
|
+
"minimum": 0.0,
|
|
2612
|
+
"type": "number"
|
|
2613
|
+
}
|
|
2614
|
+
},
|
|
2615
|
+
"type": "object"
|
|
2616
|
+
},
|
|
2527
2617
|
"SandboxMode": {
|
|
2528
2618
|
"enum": [
|
|
2529
2619
|
"read-only",
|
|
@@ -4482,6 +4572,10 @@
|
|
|
4482
4572
|
"description": "Experimental / do not use. Replaces the built-in realtime start instructions inserted into developer messages when realtime becomes active.",
|
|
4483
4573
|
"type": "string"
|
|
4484
4574
|
},
|
|
4575
|
+
"experimental_realtime_webrtc_call_base_url": {
|
|
4576
|
+
"description": "Experimental / do not use. Overrides only the WebRTC realtime call creation base URL. This is separate from `experimental_realtime_ws_base_url` because WebRTC call creation is HTTP, while sideband control is websocket.",
|
|
4577
|
+
"type": "string"
|
|
4578
|
+
},
|
|
4485
4579
|
"experimental_realtime_ws_backend_prompt": {
|
|
4486
4580
|
"description": "Experimental / do not use. Overrides only the realtime conversation websocket transport instructions (the `Op::RealtimeConversation` `/ws` session.update instructions) without changing normal prompts.",
|
|
4487
4581
|
"type": "string"
|
|
@@ -4708,18 +4802,30 @@
|
|
|
4708
4802
|
"request_rule": {
|
|
4709
4803
|
"type": "boolean"
|
|
4710
4804
|
},
|
|
4805
|
+
"resize_all_images": {
|
|
4806
|
+
"type": "boolean"
|
|
4807
|
+
},
|
|
4711
4808
|
"responses_websockets": {
|
|
4712
4809
|
"type": "boolean"
|
|
4713
4810
|
},
|
|
4714
4811
|
"responses_websockets_v2": {
|
|
4715
4812
|
"type": "boolean"
|
|
4716
4813
|
},
|
|
4814
|
+
"rollout_budget": {
|
|
4815
|
+
"$ref": "#/definitions/FeatureToml_for_RolloutBudgetConfigToml"
|
|
4816
|
+
},
|
|
4817
|
+
"respect_system_proxy": {
|
|
4818
|
+
"type": "boolean"
|
|
4819
|
+
},
|
|
4717
4820
|
"runtime_metrics": {
|
|
4718
4821
|
"type": "boolean"
|
|
4719
4822
|
},
|
|
4720
4823
|
"search_tool": {
|
|
4721
4824
|
"type": "boolean"
|
|
4722
4825
|
},
|
|
4826
|
+
"secret_auth_storage": {
|
|
4827
|
+
"type": "boolean"
|
|
4828
|
+
},
|
|
4723
4829
|
"shell_snapshot": {
|
|
4724
4830
|
"type": "boolean"
|
|
4725
4831
|
},
|
|
@@ -4735,6 +4841,9 @@
|
|
|
4735
4841
|
"skill_mcp_dependency_install": {
|
|
4736
4842
|
"type": "boolean"
|
|
4737
4843
|
},
|
|
4844
|
+
"sleep_tool": {
|
|
4845
|
+
"type": "boolean"
|
|
4846
|
+
},
|
|
4738
4847
|
"sqlite": {
|
|
4739
4848
|
"type": "boolean"
|
|
4740
4849
|
},
|
|
@@ -4753,6 +4862,9 @@
|
|
|
4753
4862
|
"terminal_visualization_instructions": {
|
|
4754
4863
|
"type": "boolean"
|
|
4755
4864
|
},
|
|
4865
|
+
"token_budget": {
|
|
4866
|
+
"type": "boolean"
|
|
4867
|
+
},
|
|
4756
4868
|
"tool_call_mcp_elicitation": {
|
|
4757
4869
|
"type": "boolean"
|
|
4758
4870
|
},
|
|
@@ -22,6 +22,8 @@ class MCPServerDTO:
|
|
|
22
22
|
type: MCPServerType
|
|
23
23
|
command: str | None = None
|
|
24
24
|
args: list[str] = field(default_factory=list)
|
|
25
|
+
cwd: str | None = None
|
|
26
|
+
env_file: str | None = None
|
|
25
27
|
url: str | None = None
|
|
26
28
|
timeout_ms: int | None = None
|
|
27
29
|
headers: dict[str, str] = field(default_factory=dict)
|
|
@@ -67,6 +67,8 @@ def common_mcp_to_dto(mcp_servers: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
|
67
67
|
|
|
68
68
|
command = raw.get("command")
|
|
69
69
|
args = raw.get("args")
|
|
70
|
+
cwd = raw.get("cwd")
|
|
71
|
+
env_file = raw.get("envFile")
|
|
70
72
|
url = raw.get("url")
|
|
71
73
|
timeout_ms = _coerce_timeout_ms(raw.get("timeout"))
|
|
72
74
|
|
|
@@ -96,6 +98,8 @@ def common_mcp_to_dto(mcp_servers: dict[str, Any]) -> dict[str, MCPServerDTO]:
|
|
|
96
98
|
type=MCPServerType.STDIO,
|
|
97
99
|
command=command,
|
|
98
100
|
args=[str(item) for item in args] if isinstance(args, list) else [],
|
|
101
|
+
cwd=cwd if isinstance(cwd, str) else None,
|
|
102
|
+
env_file=env_file if isinstance(env_file, str) else None,
|
|
99
103
|
timeout_ms=timeout_ms,
|
|
100
104
|
headers={str(k): str(v) for k, v in headers.items()}
|
|
101
105
|
if isinstance(headers, dict)
|
|
@@ -133,6 +137,10 @@ def dto_to_common_mcp(servers: dict[str, MCPServerDTO]) -> dict[str, Any]:
|
|
|
133
137
|
if server.command:
|
|
134
138
|
item["command"] = server.command
|
|
135
139
|
item["args"] = [str(arg) for arg in server.args]
|
|
140
|
+
if server.cwd is not None:
|
|
141
|
+
item["cwd"] = server.cwd
|
|
142
|
+
if server.env_file is not None:
|
|
143
|
+
item["envFile"] = server.env_file
|
|
136
144
|
elif server.url:
|
|
137
145
|
item["url"] = server.url
|
|
138
146
|
else:
|
|
@@ -22,6 +22,9 @@ class CursorMCPMapper(IAppMCPMapper):
|
|
|
22
22
|
for item in server.get("args", [])
|
|
23
23
|
if isinstance(item, (str, int, float, bool))
|
|
24
24
|
],
|
|
25
|
+
env_file=server.get("envFile")
|
|
26
|
+
if isinstance(server.get("envFile"), str)
|
|
27
|
+
else None,
|
|
25
28
|
timeout_ms=(
|
|
26
29
|
int(server["timeout"])
|
|
27
30
|
if isinstance(server.get("timeout"), int)
|
|
@@ -82,9 +85,12 @@ class CursorMCPMapper(IAppMCPMapper):
|
|
|
82
85
|
if server.type == MCPServerType.STDIO:
|
|
83
86
|
if not server.command:
|
|
84
87
|
continue
|
|
88
|
+
out["type"] = "stdio"
|
|
85
89
|
out["command"] = server.command
|
|
86
90
|
if server.args:
|
|
87
91
|
out["args"] = deepcopy(server.args)
|
|
92
|
+
if server.env_file is not None:
|
|
93
|
+
out["envFile"] = server.env_file
|
|
88
94
|
else:
|
|
89
95
|
if not server.url:
|
|
90
96
|
continue
|
|
@@ -23,12 +23,14 @@
|
|
|
23
23
|
"type": "object",
|
|
24
24
|
"required": ["command"],
|
|
25
25
|
"properties": {
|
|
26
|
+
"type": { "const": "stdio" },
|
|
26
27
|
"command": { "type": "string", "minLength": 1 },
|
|
27
28
|
"args": {
|
|
28
29
|
"type": "array",
|
|
29
30
|
"items": { "type": "string" }
|
|
30
31
|
},
|
|
31
32
|
"timeout": { "type": "integer", "minimum": 0 },
|
|
33
|
+
"envFile": { "type": "string", "minLength": 1 },
|
|
32
34
|
"env": { "$ref": "#/$defs/stringMap" },
|
|
33
35
|
"headers": { "$ref": "#/$defs/stringMap" },
|
|
34
36
|
"auth": { "$ref": "#/$defs/stringMap" }
|
|
@@ -32,6 +32,9 @@ class OpenCodeMCPMapper(IAppMCPMapper):
|
|
|
32
32
|
type=MCPServerType.STDIO,
|
|
33
33
|
command=command,
|
|
34
34
|
args=args,
|
|
35
|
+
cwd=server.get("cwd")
|
|
36
|
+
if isinstance(server.get("cwd"), str)
|
|
37
|
+
else None,
|
|
35
38
|
timeout_ms=(
|
|
36
39
|
int(server["timeout"])
|
|
37
40
|
if isinstance(server.get("timeout"), int)
|
|
@@ -90,6 +93,8 @@ class OpenCodeMCPMapper(IAppMCPMapper):
|
|
|
90
93
|
continue
|
|
91
94
|
out["type"] = "local"
|
|
92
95
|
out["command"] = [server.command, *server.args]
|
|
96
|
+
if server.cwd is not None:
|
|
97
|
+
out["cwd"] = server.cwd
|
|
93
98
|
else:
|
|
94
99
|
if not server.url:
|
|
95
100
|
continue
|
|
@@ -564,6 +564,10 @@
|
|
|
564
564
|
},
|
|
565
565
|
"description": "Command and arguments to run the MCP server"
|
|
566
566
|
},
|
|
567
|
+
"cwd": {
|
|
568
|
+
"type": "string",
|
|
569
|
+
"description": "Working directory for the MCP server process. Relative paths resolve from the workspace directory."
|
|
570
|
+
},
|
|
567
571
|
"environment": {
|
|
568
572
|
"type": "object",
|
|
569
573
|
"additionalProperties": {
|
|
@@ -5,7 +5,11 @@ import shutil
|
|
|
5
5
|
import click
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
|
|
8
|
-
from code_agnostic.cli.helpers import
|
|
8
|
+
from code_agnostic.cli.helpers import (
|
|
9
|
+
reject_symlinked_source_dir,
|
|
10
|
+
validate_resource_name,
|
|
11
|
+
workspace_config_root,
|
|
12
|
+
)
|
|
9
13
|
from code_agnostic.cli.options import workspace_option
|
|
10
14
|
from code_agnostic.core.repository import CoreRepository
|
|
11
15
|
from code_agnostic.tui import SyncConsoleUI
|
|
@@ -36,6 +40,7 @@ def agents_remove(obj: dict[str, str], name: str, workspace: str | None) -> None
|
|
|
36
40
|
validate_resource_name(name, "agent")
|
|
37
41
|
core = CoreRepository()
|
|
38
42
|
root = workspace_config_root(core, workspace)
|
|
43
|
+
reject_symlinked_source_dir(root / "agents", "agents")
|
|
39
44
|
agent_dir = root / "agents" / name
|
|
40
45
|
if agent_dir.is_dir():
|
|
41
46
|
shutil.rmtree(agent_dir)
|