Skip to content

feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7326 (#426) - #482

Merged
ScriptedAlchemy merged 4 commits into
mainfrom
feat/cursor-host-gaps-in-framework
Sep 4, 2026
Merged

feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7326 (#426)#482
ScriptedAlchemy merged 4 commits into
mainfrom
feat/cursor-host-gaps-in-framework

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

Framework-side support for the Cursor half of #426: Cursor 3.18.25 loads Agent Plugins 1.0.0 packages from ~/.cursor/plugins/local/<name> but performs none of the specification's §7.2.1 / §9 path resolution, so every spec-shaped stdio server fails to spawn there. The emitted portable install.mjs now performs that resolution itself, for the Cursor copy only, and Doctor proves it. Nothing is claimed as Cursor behaviour (provenance derived), and nothing is sent to the vendor.

Per the maintainer's direction (support host-side gaps from inside the framework where possible; no vendor feedback).

Gap (captured evidence)

docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md §3, Cursor 3.18.25 (realCommit 280eca29…af3), isolated HOME / --user-data-dir, five probe plugins:

Spec form Observed
"cwd": "${PLUGIN_ROOT}" Connection failed: spawn node ENOENT — the literal string is handed to spawn as the working directory
"args": ["${PLUGIN_ROOT}/mcp/report.mjs"] Cannot find module '/home/<user>/${PLUGIN_ROOT}/mcp/report.mjs'
omitted cwd child cwd = $HOME, not the plugin root
"env": { "X": "${PLUGIN_ROOT}" } arrives literal
PLUGIN_ROOT / PLUGIN_DATA (§9.1) undefined in the child
"command": "./mcp/launch.sh" spawn <workspace>/mcp/launch.sh ENOENT
control: same server with absolute paths / ${CURSOR_PLUGIN_ROOT} connect_success, stable heartbeat

The control row is what makes this feasible: Cursor hands command, args, cwd and env to spawn verbatim, so absolute paths written at install time launch.

What changes

  • install.mjs (emitted with portable bundles)packages/agent-bundle/src/install/surface.ts: detects an Agent Plugins pack (root plugin.json with an agent-plugins.org $schema, no .cursor-plugin/plugin.json) and rewrites mcp.json in the ~/.cursor/plugins/local/<name> copy, every stdio server: ${PLUGIN_ROOT} → absolute plugin root; ${PLUGIN_DATA}~/.cursor/agent-bundle/plugin-data/<name> (created before install, §9.1); omitted cwd → plugin root; ./ in command/cwd → resolved against the plugin root; PLUGIN_ROOT / PLUGIN_DATA added to env. Remote servers, skills-only packs and cursor-target bundles are untouched. The artifact inventory hashes the expanded form, so reruns are Already installed, same-version rebuilds Replaced, and an older unexpanded receipt-managed copy is replaced on the next run. The receipt gains cursorExpansion { pluginRoot, pluginData, documents: { "mcp.json": <shipped text> } }.
  • Doctor — new install/cursor-agent-plugins-launch.ts, AB7326: expanded (info; recorded root matches, data directory exists, no placeholder left, absolute cwd / plugin-root command / args paths exist, env carries the recorded values), unexpanded (warning; spec forms Cursor cannot launch, listed per server), drifted (error, entry corrupt; moved / duplicated / edited copy). AB7320 validates the Agent Plugins contract against the receipt's shipped document (validatePortablePluginFiles({ documents })), since the expanded copy is conformant only in that form.
  • Capability tableportable-1.0.0.json: plugin.cursorLocalInstall.placeholderExpansion (supported, provenance: derived) and a dated mcp.evidence line; the hosts page renders it. Also drops the reference to the audit file removed in docs(audits): remove the vendor-feedback docs and sections #467.
  • Docsdocs/diagnostics.md (AB7326), website/docs/{en,zh}/guide/distribution/installation.mdx, website/docs/{en,zh}/reference/cli.mdx, docs/framework-mode.md, package README, emitted INSTALL.md (“Cursor placeholder expansion”), audit §5.
  • adapterRevision: cursor 1.12.0, plugin 1.28.0, portable 1.9.0 (the shared installer source changed).

Tests

  • tests/install-surface.test.ts: the observations' pack (./ command, ${PLUGIN_ROOT} cwd/args, ${PLUGIN_DATA} env/args, remote server) — bundle untouched, copy expanded, data dir created, receipt records the shipped document, idempotent rerun, same-version re-expansion, older unexpanded copy repaired, remote-only pack byte-identical, Cursor Plugin bundle never rewritten.
  • tests/doctor.test.ts: AB7326 unexpanded → expanded (driven by the real emitted install.mjs) → drifted (data dir and script removed) → moved copy; AB7320 stays clean throughout.
  • tests/host-install-proof.test.ts / tests/support/host-install.ts (pnpm test:host-install:build): the built portable fixture in an isolated Cursor home — installed copy expanded, receipt verbatim, Doctor AB7326 info, byte lane clean against the shipped document.
  • pnpm typecheck, pnpm lint, pnpm test:unit, pnpm docs:site:build green locally.

Review status

  • Codex reviewed 6ac5bbf (PR opened): one P2 thread — Doctor did not compare the installed mcp.json with the recorded expansion. Addressed in 0d837cde: AB7326 now recomputes the expansion from the receipt (expandAgentPluginsMcpForCursor, mirrored from the emitted installer) and requires the installed bytes to match before trusting the recorded document; edited/replaced/removed documents report drifted/corrupt (tests: AB7326 doctor test steps 5–6).
  • Heads not re-reviewed by Codex: 5bae7fc1 (changeset ref), 0d837cde (the fix above). Per maintainer instruction no review re-request is posted; merging on green CI.

Refs #426.

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 50679e3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T23:47:19.550202Z 6ac5bbf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ac5bbfe25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

launch: Object.freeze({ servers: names, state: 'unexpanded' }),
});
}
const issues = await driftIssues(pluginRoot, expansion, servers, caseInsensitive);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare installed MCP bytes with the recorded expansion

When a receipt is present, driftIssues examines only the current servers for generic properties and never verifies that they equal the transformation of the recorded pre-expansion document. Consequently, after a normal install, editing a server's bare command, removing a server (or all servers), or replacing mcp.json with unparsable JSON can still make plain doctor --host cursor report expanded or omit AB7325 entirely; AB7320 also validates the receipt's original document instead of these installed bytes. Recompute the expected expanded document from the receipt and compare it with the installed file so the documented “edited → drifted/corrupt” behavior is actually enforced.

AGENTS.md reference: AGENTS.md:L78-L81

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0d837cd. Doctor now reproduces the installer's expansion (expandAgentPluginsMcpForCursor, mirrored line for line from the emitted install.mjs) from the receipt's recorded document and its recorded pluginRoot/pluginData, and requires the installed mcp.json to be byte-identical to that output before anything else: an edit (a bare command renamed), a replaced or unparsable document, a removed mcp.json, or a receipt whose recorded document has no stdio server all report drifted (error, entry corrupt). The recorded document is handed to the AB7320 byte lane only when the bytes match; a drifted copy is validated as the bytes on disk (so the §9.1 env keys and absolute cwd surface as AB6035). The path/env checks stay on top for the moved-copy case. Tests added to the AB7325 doctor test (steps 5–6: edited → drifted + AB7320 errors + corrupt; removed → drifted); the step that installs through the real emitted install.mjs pins the two implementations to each other. docs/diagnostics.md updated.

ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/cursor-host-gaps-in-framework branch from 8fc4b01 to 5bae7fc Compare September 3, 2026 23:55
@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@482
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@482
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@482

commit: 50679e3

ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/cursor-host-gaps-in-framework branch from 0d837cd to 60b7a96 Compare September 4, 2026 00:44
@ScriptedAlchemy ScriptedAlchemy changed the title feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7325 (#426) feat(install): expand Agent Plugins placeholders for the Cursor copy at install time; doctor AB7326 (#426) Sep 4, 2026
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/cursor-host-gaps-in-framework branch from 60b7a96 to a8266cd Compare September 4, 2026 00:51
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/cursor-host-gaps-in-framework branch from a8266cd to 50679e3 Compare September 4, 2026 00:51
@ScriptedAlchemy
ScriptedAlchemy merged commit b351f4a into main Sep 4, 2026
13 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the feat/cursor-host-gaps-in-framework branch September 4, 2026 01:07
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…and doctor activation states (#101)

Squash of the #452 branch (review rounds 1-7 folded in) ahead of the
rebase onto main, which now carries #482's AB7326.
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…and doctor activation states (#101)

Squash of the #452 branch (review rounds 1-7 folded in) ahead of the
rebase onto main, which now carries #482's AB7326.
ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
…and doctor activation states (#101) (#452)

* feat(install): receipt-owned uninstall lifecycle, format/2 receipts, and doctor activation states (#101)

Squash of the #452 branch (review rounds 1-7 folded in) ahead of the
rebase onto main, which now carries #482's AB7326.

* fix(install): roll back a marketplace this install created when the plugin install fails; exact standalone marketplace plans

- install: when marketplace add succeeded but plugin install / receipt write fails, remove the marketplace this run registered (its claim lived only in memory); a pre-existing marketplace is never touched
- install.mjs --uninstall --mode marketplace --plan names the receipt store, marketplaces root, and agent-bundle namespace it would prune, matching the completed run

* fix(install): refuse dirty staged marketplaces, reverse the plugin on receipt-write failure, aggregate Claude lifecycle across scopes

- uninstall (core + install.mjs): a staged Cursor marketplace whose working tree differs from the receipted commit (or cannot be verified) is refused with AB7007 until --force; HEAD alone proved only the commit
- install: when the host install succeeded but the receipt write failed, the plugin registration is reversed before the marketplace this run created
- doctor AB7328: Claude multi-scope rows aggregate — a stage holds only for every listed copy; evidence names the disabled/unplaced/flagless scopes
- tests and docs for each

* chore(adapters): advance the Claude (1.27.0), Cursor (1.13.0), and portable (1.10.0) adapter revisions for the lifecycle capability rows and install-surface changes

* fix(install): treat the Cursor PLUGIN_DATA directory as receipt-owned durable state during uninstall

Uninstalling a local Cursor copy (core uninstallBundle and the emitted
install.mjs --uninstall) now keeps a written PLUGIN_DATA directory behind a
remnant receipt that carries the cursorExpansion so a later
--purge-data --confirm-purge still finds it, prunes an empty installer-created
one together with its agent-bundle parents, and never touches a recorded path
outside this home's plugin-data. Doctor's AB7307 remnant diagnostic names the
PLUGIN_DATA directory. Restores the byte-identical host-install proofs for
cursor/portable and documents the behaviour in docs/diagnostics.md.

* fix(uninstall): read Claude's plugins/installed_plugins.json so hand-made project/local installs elsewhere retain the marketplace and refuse a shared purge

* docs(uninstall): document the PLUGIN_DATA data policy and the Claude installed_plugins.json dependents in INSTALL.md, README, site (en/zh), and the changeset
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