Skip to content

Commit 35de33e

Browse files
committed
feat(cursor): improves for avoid bugs in path/cwd
1 parent e2fc274 commit 35de33e

21 files changed

Lines changed: 744 additions & 74 deletions

.archcore/.sync-state.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,66 @@
32513251
"source": "plugin/stack-and-tooling.rule.md",
32523252
"target": "plugin/multi-host-plugin-architecture.adr.md",
32533253
"type": "related"
3254+
},
3255+
{
3256+
"source": "plugin/cursor-mcp-architecture.adr.md",
3257+
"target": "plugin/multi-host-plugin-architecture.adr.md",
3258+
"type": "extends"
3259+
},
3260+
{
3261+
"source": "plugin/cursor-mcp-architecture.adr.md",
3262+
"target": "plugin/cwd-guard-for-cursor-and-claude.idea.md",
3263+
"type": "related"
3264+
},
3265+
{
3266+
"source": "plugin/cursor-mcp-architecture.adr.md",
3267+
"target": "plugin/codex-path-resolution.adr.md",
3268+
"type": "related"
3269+
},
3270+
{
3271+
"source": "plugin/cursor-mcp-architecture.adr.md",
3272+
"target": "plugin/remove-bundled-launcher-global-cli.idea.md",
3273+
"type": "related"
3274+
},
3275+
{
3276+
"source": "plugin/cursor-mcp-architecture.adr.md",
3277+
"target": "plugin/multi-host-compatibility-layer.spec.md",
3278+
"type": "related"
3279+
},
3280+
{
3281+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3282+
"target": "plugin/always-use-mcp-tools.adr.md",
3283+
"type": "extends"
3284+
},
3285+
{
3286+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3287+
"target": "plugin/mcp-only-operations.rule.md",
3288+
"type": "related"
3289+
},
3290+
{
3291+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3292+
"target": "plugin/hooks-validation-system.spec.md",
3293+
"type": "extends"
3294+
},
3295+
{
3296+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3297+
"target": "plugin/remove-bundled-launcher-global-cli.idea.md",
3298+
"type": "related"
3299+
},
3300+
{
3301+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3302+
"target": "plugin/cursor-mcp-architecture.adr.md",
3303+
"type": "related"
3304+
},
3305+
{
3306+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3307+
"target": "plugin/multi-host-compatibility-layer.spec.md",
3308+
"type": "related"
3309+
},
3310+
{
3311+
"source": "plugin/plugin-as-mcp-enforcement-boundary.idea.md",
3312+
"target": "plugin/cli-integration-tests.rule.md",
3313+
"type": "related"
32543314
}
32553315
]
32563316
}

.archcore/plugin/cli-integration-tests.rule.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Any change to plugin code that invokes the `archcore` CLI MUST be accompanied by
1616
In particular:
1717

1818
1. Every shell-out from a `bin/*` script to the CLI (`archcore <subcmd> [args...]`) MUST be covered by a unit test that asserts the invoked subcommand via the `MOCK_ARCHCORE_LOG` mechanism (see `mock_archcore_logging` in `test/helpers/common.bash`).
19-
2. Every `args` array in `.mcp.json`, `.codex.mcp.json`, and `cursor.mcp.json`, and every subcommand in any new `hooks/*.json`-referenced script, MUST name only canonical subcommands. The canonical surface as of plugin v0.4.0 is `config | doctor | help | hooks | init | mcp | status | update`.
19+
2. Every `args` array in `.mcp.json`, `.codex.mcp.json`, and `docs/cursor.mcp.example.json`, and every subcommand in any new `hooks/*.json`-referenced script, MUST name only canonical subcommands. The canonical surface as of plugin v0.4.0 is `config | doctor | help | hooks | init | mcp | status | update`. Note that `docs/cursor.mcp.example.json` may also include `--project <path>` after `mcp` — this is a flag, not a subcommand, and is locked by `test/structure/cursor-plugin.bats`.
2020
3. Every prescriptive `` `archcore <subcmd>` `` reference in `README.md` MUST be guarded by `test/structure/readme-cli-references.bats`. Internal `.archcore/` design docs are intentionally excluded from this guard — they hold historical spec text that may legitimately reference renamed commands.
2121
4. Skill or agent prose that instructs the agent to run `archcore <subcmd>` as a shell command MUST be reviewed against the canonical CLI surface; prefer routing CLI work through MCP tools (`mcp__archcore__*`) rather than shell-outs, so the agent stays inside the validated path.
2222

@@ -104,15 +104,16 @@ assert_output ""
104104
The rule is enforced by these tests, which ship in the plugin:
105105

106106
- **`test/structure/readme-cli-references.bats`** — every code-quoted `` `archcore <subcmd>` `` in `README.md` must name a canonical subcommand. The allowlist is hardcoded in the test file and tracks the CLI's `archcore --help` surface.
107+
- **`test/structure/cursor-plugin.bats`** — locks the contents of `docs/cursor.mcp.example.json`: command is `archcore`, args contain `mcp` followed by `--project ${workspaceFolder}`, no `cwd` field, and no legacy `cursor.mcp.json` at the plugin root.
107108
- **`test/unit/validate-archcore.bats`** — invocation-log assertions using `MOCK_ARCHCORE_LOG` for `validate-archcore`. The two relevant tests are `validate-archcore calls archcore doctor (not validate)` and `validate-archcore invokes only allowlisted subcommands`.
108-
- **`test/unit/session-start.bats`** — covers the missing-CLI fallback (the hook must exit 0 and emit install guidance, not block the session) and verifies `session-start invokes only the 'hooks' subcommand`.
109+
- **`test/unit/session-start.bats`** — covers the missing-CLI fallback (the hook must exit 0 and emit install guidance, not block the session), verifies `session-start invokes only the 'hooks' subcommand`, and asserts the plugin-install-dir guard (silent exit when sibling `.cursor-plugin/`, `.claude-plugin/`, or `.codex-plugin/` manifests are present).
109110

110111
When the canonical CLI surface changes upstream (new subcommand added, renamed, or removed), update `ARCHCORE_SUBCOMMANDS` in `readme-cli-references.bats` and the equivalent constant in any unit test that asserts on it. Any new subcommand the plugin starts invoking from a bin/ script gets its own invocation-log assertion before merge.
111112

112113
A change that does not satisfy this rule is rejected in code review. The rule applies to:
113114

114115
- Any script under `bin/` that calls `archcore`
115-
- `args` arrays in `.mcp.json`, `.codex.mcp.json`, `cursor.mcp.json`
116+
- `args` arrays in `.mcp.json`, `.codex.mcp.json`, `docs/cursor.mcp.example.json`
116117
- Any new hook config (`hooks/*.json`) referencing a CLI-invoking script
117118
- README and other user-facing prescriptive docs naming `` `archcore <subcmd>` `` invocations
118119
- Skill or agent prompt text that instructs the agent to run an `archcore` shell command

.archcore/plugin/component-registry.doc.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,16 @@ Hook 6 (`check-precision`) is the Phase 1 implementation of the Precision Initia
112112

113113
Historical note: a prior revision had a `PostToolUse` entry with matcher `Write|Edit` invoking `validate-archcore`. It was removed because PreToolUse already blocks all Write/Edit to `.archcore/*.md` (PostToolUse fires only on success), so the matcher was dead weight forking a shell on every Write/Edit anywhere in the repo. See `hooks-validation-system.spec.md` for the rationale. Structure tests guard against its re-introduction.
114114

115+
`bin/session-start` carries a plugin-install-dir guard (see `cursor-mcp-architecture.adr.md`): when cwd contains a sibling `.cursor-plugin/`, `.claude-plugin/`, or `.codex-plugin/` manifest, the hook exits silently. This prevents the hook from emitting the plugin's bundled `.archcore/` (if any future regression reintroduces it) as the user's knowledge base.
116+
115117
### Bin Scripts
116118

117119
The `bin/` tree contains hook scripts and the shared stdin-normalization library. The plugin **does not bundle the Archcore CLI binary or any launcher wrapper** — it invokes `archcore` directly from PATH. Users install the CLI globally via the official installer at https://docs.archcore.ai/cli/install/. See `remove-bundled-launcher-global-cli.idea.md` for the rationale.
118120

119121
| Script | Hook Event | Purpose |
120122
| ---------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121123
| `bin/lib/normalize-stdin.sh` | (library) | Multi-host stdin normalization. Detects host (Claude Code/Cursor/Copilot/Codex), extracts fields (tool_name, file_path, path), normalizes MCP tool names, provides output helpers (archcore_hook_block, archcore_hook_info, archcore_hook_pretool_info, archcore_hook_allow). Sourced by all hook scripts except check-staleness. |
122-
| `bin/session-start` | SessionStart | Sources the normalizer; if `archcore` is not on PATH, prints an install message pointing at https://docs.archcore.ai/cli/install/ and exits 0. Otherwise detects missing `.archcore/` and emits init guidance (instructs the agent to call `mcp__archcore__init_project`), or invokes `archcore hooks <host> session-start` directly, then calls `bin/check-staleness`. Always exits 0. |
124+
| `bin/session-start` | SessionStart | Sources the normalizer; if `archcore` is not on PATH, prints an install message pointing at https://docs.archcore.ai/cli/install/ and exits 0. Refuses to run when cwd contains a sibling plugin manifest (see `cursor-mcp-architecture.adr.md` — Layer 3 defense). Otherwise detects missing `.archcore/` and emits init guidance (instructs the agent to call `mcp__archcore__init_project`), or invokes `archcore hooks <host> session-start` directly, then calls `bin/check-staleness`. Always exits 0. |
123125
| `bin/check-archcore-write` | PreToolUse | Blocks direct Write/Edit to `.archcore/**/*.md` with exit 2 + stderr message redirecting to MCP tools. Allows `.archcore/settings.json` and `.archcore/.sync-state.json`. Allows all paths outside `.archcore/`. |
124126
| `bin/check-code-alignment` | PreToolUse | Injects relevant `.archcore/` context for source-file Write/Edit. Greps `.archcore/**/*.md` for documents referencing the edit path (directory prefixes, longest-first). Ranks by specificity → type priority (rule > cpat > adr > spec > guide). Emits top-3 via `hookSpecificOutput.additionalContext` (Claude Code/Codex/Copilot) or `additional_context` (Cursor). Never blocks; always exits 0. Honors `ARCHCORE_DISABLE_INJECTION=1` escape hatch and `.archcore/settings.json → codeAlignment.sourceRoots` override. |
125127
| `bin/validate-archcore` | PostToolUse | Runs `archcore doctor` directly (timeout 2s) after MCP document operations (by tool_name prefix). The subcommand invocation is locked by `test/structure/readme-cli-references.bats` against the canonical CLI surface and a `MOCK_ARCHCORE_LOG`-backed assertion in `test/unit/validate-archcore.bats`. The legacy Write/Edit branch in the script is retained as defensive code but is never reached from the current hooks config. Outputs JSON `hookSpecificOutput` when issues found, empty otherwise. Silently exits 0 if the CLI is unavailable. Always exits 0. |
@@ -131,12 +133,13 @@ The `bin/` tree contains hook scripts and the shared stdin-normalization library
131133

132134
| Component | Location | Tests | Description |
133135
| --------------- | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------- |
134-
| Unit tests | `test/unit/` | 112 | Test each bin script: stdin parsing, host detection, exit codes, output format, edge cases. Includes `check-staleness.bats` (24h rate limit, corrupt-stamp recovery) and `check-code-alignment.bats` (source-root filter, specificity ranking, top-3 cap, settings override, Cursor JSON shape, non-blocking safety). `session-start.bats` covers the missing-CLI fallback path now that the launcher is gone. |
135-
| Structure tests | `test/structure/` | 100 | Validate JSON configs, skill frontmatter, agent frontmatter, hook references, script permissions, rules. `hooks.bats` includes anti-regression invariants: no Write/Edit matcher on PostToolUse, no postToolUse event on Cursor, exact event-set invariants per host. `codex-plugin.bats` enforces Codex manifest, marketplace schema, hooks shape, MCP wiring (`command: "archcore"` on PATH), TOML agents, and parity between `commands/*.md` wrappers and `skills/<name>/SKILL.md`. `readme-cli-references.bats` validates every `archcore <subcmd>` reference in README against the canonical surface (`config doctor help hooks init mcp status update`). |
136+
| Unit tests | `test/unit/` | 115 | Test each bin script: stdin parsing, host detection, exit codes, output format, edge cases. Includes `check-staleness.bats` (24h rate limit, corrupt-stamp recovery), `check-code-alignment.bats` (source-root filter, specificity ranking, top-3 cap, settings override, Cursor JSON shape, non-blocking safety), and `session-start.bats` (covers the missing-CLI fallback path and the plugin-install-dir guard for cursor/claude/codex sibling manifests). |
137+
| Structure tests | `test/structure/` | 105 | Validate JSON configs, skill frontmatter, agent frontmatter, hook references, script permissions, rules. `hooks.bats` includes anti-regression invariants: no Write/Edit matcher on PostToolUse, no postToolUse event on Cursor, exact event-set invariants per host. `cursor-plugin.bats` locks `docs/cursor.mcp.example.json` shape (command, `--project ${workspaceFolder}` in args, no `cwd` field, no legacy `cursor.mcp.json` at root). `no-bundled-archcore-refs.bats` ensures no distributable file references concrete paths under `.archcore/<category>/<slug>.<type>.md` (which would only be plugin-internal docs). `codex-plugin.bats` enforces Codex manifest, marketplace schema, hooks shape, MCP wiring (`command: "archcore"` on PATH), TOML agents, and parity between `commands/*.md` wrappers and `skills/<name>/SKILL.md`. `readme-cli-references.bats` validates every `archcore <subcmd>` reference in README against the canonical surface (`config doctor help hooks init mcp status update`). |
136138
| Fixtures | `test/fixtures/stdin/` | 12 files | Mock stdin JSON for Claude Code, Cursor, Copilot, Codex CLI, and malformed inputs |
137139
| Helpers | `test/helpers/` || common.bash (setup, mocks, timeout shim), bats-support, bats-assert (git submodules) |
138-
| Makefile | `Makefile` || Targets: `test`, `test-unit`, `test-structure`, `lint`, `check-json`, `check-perms`, `verify` |
139-
| CI | `.github/workflows/test.yml` || GitHub Actions: macOS + Linux matrix, bats + shellcheck |
140+
| Makefile | `Makefile` || Targets: `test`, `test-unit`, `test-structure`, `lint`, `check-json`, `check-perms`, `verify`. Dev-only — stripped from `main` distribution. |
141+
| CI | `.github/workflows/test.yml` || GitHub Actions on push/PR to `dev`: macOS + Linux matrix, bats + shellcheck |
142+
| Release | `.github/workflows/release.yml` || GitHub Actions on tag push: strips dev-only artifacts, force-pushes the clean tree to `main`. See `docs/release.md` for the blocklist. |
140143

141144
Run `make verify` for full check. Run `make test` for tests only. See `plugin-testing.guide.md` for details.
142145

@@ -159,7 +162,7 @@ The `command` resolves through PATH — users must have the Archcore CLI install
159162

160163
Codex CLI uses `.codex-plugin/plugin.json` to point at plugin-root `.codex.mcp.json`, which uses the same shape — `command: "archcore"`, `args: ["mcp"]`. No `cwd` indirection, no `env_vars` allowlist gymnastics: with the launcher gone, Codex's plugin-cache rebase of `cwd: "."` is no longer needed.
161164

162-
Cursor users still register MCP externally via Cursor's MCP settings or a project `mcp.json`. The bundled `cursor.mcp.json` template adds `cwd: "${workspaceFolder}"` to scope the MCP to the active project — required because Cursor does not auto-inject a project CWD into plugin-spawned processes.
165+
Cursor is the exception. The plugin **does not** ship a plugin-MCP for Cursor — no `mcp.json` at the plugin root and no `mcpServers` field in `.cursor-plugin/plugin.json`. Cursor 2.5+ would auto-detect a plugin-root `mcp.json` and register it under "Plugin MCP Servers," but it spawns plugin-MCPs with cwd = plugin install directory, and its MCP stdio schema has no `cwd` field to redirect to the workspace ([forum #74861](https://forum.cursor.com/t/allow-workspacefolder-in-mcp-project-configration/74861), [forum #99215](https://forum.cursor.com/t/how-get-the-correct-current-work-directory-in-mcp-server/99215)). Shipping a plugin-MCP would cause the server to serve the plugin install dir instead of the user's workspace. Cursor users instead copy `docs/cursor.mcp.example.json` into `~/.cursor/mcp.json` (user-scoped) or `.cursor/mcp.json` (project-scoped); the template passes `--project ${workspaceFolder}` in `args` to make the workspace explicit. Full rationale and three-layer defense in `cursor-mcp-architecture.adr.md`.
163166

164167
Rationale: see `remove-bundled-launcher-global-cli.idea.md`. The previous bundled launcher (download-on-first-use, checksum-verified, cached per host) is removed; CLI lifecycle now decouples cleanly from plugin releases.
165168

@@ -168,14 +171,14 @@ Rationale: see `remove-bundled-launcher-global-cli.idea.md`. The previous bundle
168171
| File | Host | Purpose |
169172
| --------------------------------- | ----------- | ---------------------------------------------------------------------- |
170173
| `.claude-plugin/plugin.json` | Claude Code | Plugin manifest |
171-
| `.cursor-plugin/plugin.json` | Cursor | Plugin manifest (with explicit component paths; no `mcpServers` field) |
174+
| `.cursor-plugin/plugin.json` | Cursor | Plugin manifest (with explicit component paths; **no `mcpServers` field** — deliberately disabled, see `cursor-mcp-architecture.adr.md`) |
172175
| `.codex-plugin/plugin.json` | Codex CLI | Plugin manifest with `skills`, `hooks`, and `mcpServers` pointers |
173176
| `.claude-plugin/marketplace.json` | Claude Code | Marketplace metadata |
174177
| `.cursor-plugin/marketplace.json` | Cursor | Marketplace metadata |
175178
| `.agents/plugins/marketplace.json` | Codex CLI | Marketplace metadata and default-install policy |
176179
| `.mcp.json` | Claude Code | Plugin-provided MCP registration (`command: "archcore"` on PATH) |
177180
| `.codex.mcp.json` | Codex CLI | Plugin-provided MCP registration (`command: "archcore"` on PATH) |
178-
| `cursor.mcp.json` | Cursor | Reference MCP config for users to copy into `~/.cursor/mcp.json` or `.cursor/mcp.json` (Cursor does not auto-register plugin MCP) |
181+
| `docs/cursor.mcp.example.json` | Cursor | Reference MCP config for users to copy into `~/.cursor/mcp.json` or `.cursor/mcp.json`. Lives under `docs/` (not the plugin root) so it cannot trigger Cursor's plugin-MCP auto-detection. Passes `--project ${workspaceFolder}` in `args`. |
179182
| `hooks/hooks.json` | Claude Code | Hook event config (PascalCase) |
180183
| `hooks/cursor.hooks.json` | Cursor | Hook event config (camelCase + afterMCPExecution) |
181184
| `hooks/codex.hooks.json` | Codex CLI | Hook event config (PascalCase + apply_patch matcher) |

0 commit comments

Comments
 (0)