Skip to content

feat: extend capa wrap to Cursor agent and other CLI providers - #154

Merged
Minitour merged 3 commits into
version-2.0from
feat/wrap-cli-agents
Aug 2, 2026
Merged

feat: extend capa wrap to Cursor agent and other CLI providers#154
Minitour merged 3 commits into
version-2.0from
feat/wrap-cli-agents

Conversation

@Minitour

@Minitour Minitour commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

  • Add wrap.aliases and resolveWrapTarget so a provider can expose alternate launch binaries (Cursor GUI vs agent CLI) while sharing the same shadow workspace/install paths.
  • Enable capa wrap agent for Cursor CLI, plus wrap for gemini-cli, opencode, qwen-code, kiro-cli, iflow-cli, and kimi-cli.
  • Wire launchProvider to take an explicit launch config; leave Claude Code and Codex wrap unchanged.
  • Not wrapping github-copilot: its owned paths live under shared .github / .vscode, and wrap exclusions are top-level only (would hide those entire trees).

Test plan

  • bun test src/shared/providers/__tests__/wrap.test.ts src/cli/utils/wrap/__tests__/
  • capa wrap agent prepares a cursor shadow workspace and launches the agent binary
  • capa wrap cursor still launches GUI (cursor --new-window --wait)
  • capa wrap github-copilot / capa wrap copilot are rejected (not wrappable)
  • capa wrap with a missing provider lists new ids and aliases (agent)
  • capa wrap gemini-cli / opencode launch their respective binaries when installed

Add wrap.aliases so Cursor GUI and agent CLI can share provider config, and declare wrap for gemini, opencode, copilot, qwen, kiro, iflow, and kimi.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Extend capa wrap to Cursor agent alias and additional CLI providers

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add wrap.aliases and resolveWrapTarget to launch alternate provider binaries from the same
 workspace.
• Enable capa wrap for Cursor agent plus new CLI providers (Gemini, OpenCode, Copilot, Qwen,
 Kiro, iFlow, Kimi).
• Refactor wrap launching to accept an explicit launch config and add coverage for alias resolution.
Diagram

graph TD
  U(("User")) --> W["capa wrap (wrapCommand)"] --> R["resolveWrapTarget"] --> D{"wrap.kind"}
  D -->|"gui"| G["startWrapWatchers"] --> L["launchProvider (WrapLaunchConfig)"] --> B{{"Provider binary"}}
  D -->|"cli"| C["startDetachedWatchWorker"] --> L --> B

  subgraph Legend
    direction LR
    _u(("User")) ~~~ _p["Process"] ~~~ _d{"Decision"} ~~~ _e{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Model each launchable binary as its own provider entry
  • ➕ Avoids introducing a second aliasing mechanism (wrap.aliases) beyond pluginProviderId.
  • ➕ Simplifies resolution logic (token → provider id) at the expense of more entries.
  • ➖ Duplicates provider metadata and increases maintenance burden.
  • ➖ Harder to guarantee shared wrap workspace/install paths across the related binaries (e.g., Cursor GUI vs agent CLI).
2. Keep provider model unchanged; implement token→binary mapping only in the CLI command
  • ➕ Minimizes shared library surface area changes and type churn.
  • ➕ Keeps alias complexity localized to the wrap command.
  • ➖ Other callers (tests, future commands) cannot reuse the resolution logic.
  • ➖ Provider metadata no longer declares its own wrap launch surface, making discovery/listing harder.
3. Extend `pluginProviderId` to accept multiple aliases (array)
  • ➕ Single alias mechanism for both plugin ids and wrap tokens.
  • ➕ Could reduce separate fields (wrap.aliases) if semantics align.
  • ➖ Conflates plugin manifest identity with wrap launch tokens; not all wrap aliases correspond to a plugin provider id.
  • ➖ Still needs a way to attach per-alias launch config (binary/kind/args).

Recommendation: Current approach (introducing WrapLaunchConfig + wrap.aliases and resolving via resolveWrapTarget) is the best fit: it keeps provider metadata canonical, allows multiple launch targets to share one provider/workspace, and makes alias discovery/listing consistent across CLI UX and tests. The added resolution helper cleanly centralizes the token mapping logic for future reuse.

Files changed (12) +202 / -38

Enhancement (9) +135 / -23
wrap.tsResolve wrap targets (including aliases) and launch via explicit wrap config +13/-18

Resolve wrap targets (including aliases) and launch via explicit wrap config

• Replaces direct provider lookup with 'resolveWrapTarget()' so 'capa wrap <token>' can map to either the provider’s primary wrap config or an alias launch config. Improves error messages by listing wrappable providers including their aliases. Updates GUI/CLI launch paths to call 'launchProvider()' with the resolved wrap launch config.

src/cli/commands/wrap.ts

cursor.tsAdd Cursor wrap alias for 'agent' CLI launch +8/-1

Add Cursor wrap alias for 'agent' CLI launch

• Extends Cursor’s 'wrap' integration to include an 'agent' alias that launches the 'agent' CLI while keeping Cursor’s primary GUI wrap behavior unchanged.

src/shared/providers/entries/cursor.ts

gemini-cli.tsEnable wrapping Gemini CLI via 'gemini' binary +1/-0

Enable wrapping Gemini CLI via 'gemini' binary

• Adds a 'wrap' declaration so 'capa wrap gemini-cli' launches the 'gemini' CLI from the prepared shadow workspace.

src/shared/providers/entries/gemini-cli.ts

github-copilot.tsEnable wrapping GitHub Copilot CLI and add 'copilot' alias token +7/-0

Enable wrapping GitHub Copilot CLI and add 'copilot' alias token

• Adds a 'wrap' declaration for the 'copilot' binary and exposes 'copilot' as an accepted wrap alias token for 'github-copilot'. This lets users run 'capa wrap copilot' while still attributing the workspace/provider metadata to 'github-copilot'.

src/shared/providers/entries/github-copilot.ts

opencode.tsEnable wrapping OpenCode via 'opencode' binary +1/-0

Enable wrapping OpenCode via 'opencode' binary

• Adds a 'wrap' declaration so 'capa wrap opencode' launches the 'opencode' CLI from the shadow workspace.

src/shared/providers/entries/opencode.ts

partial-integration.tsAdd wrap support for iFlow, Kiro, and Qwen partial-integration providers +3/-0

Add wrap support for iFlow, Kiro, and Qwen partial-integration providers

• Adds 'wrap' declarations for 'iflow-cli' (binary 'iflow'), 'kiro-cli' (binary 'kiro-cli'), and 'qwen-code' (binary 'qwen'), enabling 'capa wrap' for these providers.

src/shared/providers/entries/partial-integration.ts

skills-only.tsAdd wrap support for Kimi CLI provider +1/-0

Add wrap support for Kimi CLI provider

• Adds a 'wrap' declaration for 'kimi-cli' (binary 'kimi'), enabling 'capa wrap kimi-cli'.

src/shared/providers/entries/skills-only.ts

index.tsIntroduce 'resolveWrapTarget' and alias-inclusive provider list formatting +87/-1

Introduce 'resolveWrapTarget' and alias-inclusive provider list formatting

• Adds 'WrapTarget' and 'resolveWrapTarget()' to resolve a user token to a provider plus a concrete launch config (primary or alias). Introduces 'formatWrappableProviderList()' to present wrappable providers along with pluginProviderId and wrap alias tokens for better CLI errors and UX.

src/shared/providers/index.ts

providers.tsSplit wrap launch config from wrap integration and add alias support +14/-3

Split wrap launch config from wrap integration and add alias support

• Introduces 'WrapLaunchConfig' as the normalized launch shape (binary/kind/args) and updates 'WrapIntegration' to extend it while adding optional 'aliases' for alternate launch tokens. This enables multiple binaries per provider without duplicating provider integrations.

src/types/providers.ts

Refactor (1) +3 / -8
launch.tsDecouple 'launchProvider' from providers by accepting 'WrapLaunchConfig' +3/-8

Decouple 'launchProvider' from providers by accepting 'WrapLaunchConfig'

• Changes 'launchProvider()' signature from 'ProviderIntegration' to 'WrapLaunchConfig', removing the internal dependency on provider registry shape. Keeps existing GUI vs CLI behavior intact while making the launcher reusable for alias-based launches.

src/cli/utils/wrap/launch.ts

Tests (2) +64 / -7
launch.test.tsUpdate launch tests to use 'WrapLaunchConfig' and cover Cursor agent binary +11/-7

Update launch tests to use 'WrapLaunchConfig' and cover Cursor agent binary

• Refactors tests to pass 'WrapLaunchConfig' directly into 'launchProvider()' rather than a full provider integration. Adds a test ensuring the Cursor 'agent' CLI binary is launched when provided via wrap config.

src/cli/utils/wrap/tests/launch.test.ts

wrap.test.tsAdd tests for wrap alias resolution and provider list formatting +53/-0

Add tests for wrap alias resolution and provider list formatting

• Expands coverage to validate new wrappable providers are included, 'resolveWrapTarget()' behavior for the Cursor 'agent' alias and other CLI providers, and that 'formatWrappableProviderList()' prints both pluginProviderId aliases and wrap aliases.

src/shared/providers/tests/wrap.test.ts

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Copilot wrap hides .github ✓ Resolved 🐞 Bug ≡ Correctness
Description
Making github-copilot wrappable causes wrap to exclude the provider-owned top-level dirs (notably
.github and .vscode), so they are omitted from the shadow workspace and any edits under them
will never be promoted to the real project. In repos that already use these directories, `capa wrap
copilot` runs against an incomplete workspace and can silently drop changes under those directories.
Code

src/shared/providers/entries/github-copilot.ts[R35-41]

+	wrap: {
+		binary: "copilot",
+		kind: "cli",
+		aliases: {
+			copilot: { binary: "copilot", kind: "cli" },
+		},
+	},
Evidence
github-copilot declares MCP config under .vscode/... and instructions/rules/subagents under
.github/..., which getProviderOwnedTopLevelNames converts into excluded top-level names for
wrap. Wrap’s symlink workspace builder skips excluded top-level entries entirely, and the promotion
path returns early for excluded names, so .github/.vscode content is neither present via symlink
nor promoted back to the real project.

src/shared/providers/entries/github-copilot.ts[6-42]
src/shared/providers/index.ts[151-183]
src/cli/utils/wrap/symlink-workspace.ts[29-37]
src/cli/utils/wrap/symlink-workspace.ts[82-117]
src/cli/utils/wrap/symlink-workspace.ts[229-237]
src/cli/utils/wrap/workspace.ts[281-306]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`github-copilot` stores its integration files under shared, commonly-used top-level directories (`.github`, `.vscode`). Wrap’s exclusion model is **top-level only**: excluded names are not symlinked into the shadow workspace and are also skipped during promotion back to the real project.
By adding `wrap` to `github-copilot`, `capa wrap copilot/github-copilot` will now treat `.github`/`.vscode` as excluded provider-owned top-level names, resulting in an incomplete shadow workspace and non-promoted edits under those dirs.
## Issue Context
- Provider-owned top-level names are derived from `instructions.filename`, `mcp.configPath`, `rules.dir`, etc.
- Wrap workspace construction and promotion operate at the **top-level name** granularity.
## Fix Focus Areas
Choose one:
1) **Disable wrap for `github-copilot`** until wrap supports subpath-level exclusions/overlays for shared directories.
2) **Add per-provider wrap exclusion overrides** (e.g. `wrap.excludeTopLevels` / `wrap.shadowTopLevels`) so `github-copilot` can avoid excluding `.github` and `.vscode` (accepting that install/wrap may then write directly to the real project via symlinks), OR
3) **Implement subpath-level exclusions** so only specific Copilot-managed files under `.github`/`.vscode` are shadowed while the rest of those directories remain linked/synced.
- src/shared/providers/entries/github-copilot.ts[35-41]
- src/shared/providers/index.ts[151-183]
- src/cli/utils/wrap/workspace.ts[281-306]
- src/cli/utils/wrap/symlink-workspace.ts[29-37]
- src/cli/utils/wrap/symlink-workspace.ts[82-117]
- src/cli/utils/wrap/symlink-workspace.ts[229-237]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/shared/providers/entries/github-copilot.ts Outdated
Minitour and others added 2 commits August 2, 2026 03:00
Top-level wrap exclusions would hide entire .github/.vscode trees from the shadow workspace.

Co-authored-by: Cursor <cursoragent@cursor.com>
Check PATH before shadow workspace install, and interactively select a wrappable provider when none is specified.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Minitour
Minitour merged commit dd424c0 into version-2.0 Aug 2, 2026
@Minitour
Minitour deleted the feat/wrap-cli-agents branch August 2, 2026 00:11
@Minitour Minitour mentioned this pull request Aug 2, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant