Skip to content

fix(cursor): deliver user-scope rules to workspaces outside the home directory (#1197) - #1198

Merged
jeff-r2026 merged 6 commits into
Tencent:mainfrom
SaulMoro:fix/cursor-user-rules-outside-home
Oct 17, 2026
Merged

jeff-r2026 merged 6 commits into
Tencent:mainfrom
SaulMoro:fix/cursor-user-rules-outside-home

Conversation

@SaulMoro

Copy link
Copy Markdown
Collaborator

Fixes #1197

Summary

Cursor never loaded the user-scope team rules in a workspace outside the home directory, and doctor still reported them delivered. Cursor (IDE and CLI) has no direct read of ~/.cursor/rules. LocalCursorRulesService.loadRulesFromDirAndAncestors walks up from the workspace root and loads <ancestor>/.cursor/rules at each step, so the home copy loads only when $HOME is an ancestor. On Windows, repositories under C:\Work or D:\src are common.

The fix keeps the file for workspaces under home and uses the sessionStart hook teamai already installs in ~/.cursor/hooks.json for the rest:

Cursor sessionStart (workspace W)
  walk from W reaches ~/.cursor/rules (directly or through a link)?  → add nothing, the file loads
  otherwise                                                          → add the user-scope rules and instruction blocks
  • cursor-user-rules is a new session-start handler. It compares the canonical path of each <ancestor>/.cursor/rules with the user rules directory. A junction or symlink on the walk (the workaround in the report) therefore counts as reached, and the rules do not arrive twice. In a teamai project outside home it loads the user scope separately, since the project delivers its own rules into the workspace.
  • Cursor caps a hook reply at 10,000 characters of additional_context and drops the whole reply when over (verified in the bundle, counted as the trimmed text's .length). The dispatcher now leaves the rules out when the merged session-start reply would not fit and the rest then does. The env, MR and package hints still arrive. The MR and package hints are claimed once, so they keep priority over the rules, which come back every session.
  • resolveHookCwd turns Cursor's /c:/... workspace roots into c:/.... The IDE sends folders.map((f) => f.uri.path), which no path API resolves on Windows. Before this, a Windows Cursor hook could not chdir into its workspace or find its project scope.
  • doctor (user scope) gains two checks. "Team rules reach Cursor workspaces outside the home directory" passes when the hook is registered for --tool cursor and the rules fit the limit. Its fix text names the real cause: invalid JSON (repair by hand), a missing hook (naming a team builtin.disabled entry when there is one), or length. "Team rules and the team env hint fit one Cursor hook reply" is informational. It runs the env hint handler itself and flags the case where the dispatcher would keep the hint and drop the rules.

Evidence

Unit tests: the handler through the hook registry (outside home, under home, a link on the walk, a project outside home, excluded tool, over the limit), the dispatcher guard, the /c:/ root, and the doctor checks. Each was observed failing before its implementation. npx tsc --noEmit, npm run lint and npx vitest run pass (10859 passed, 21 skipped).

Real CLI with an isolated HOME, a local bare remote (git provider), init --scope user --agent cursor and pull. The steps dispatch Cursor's sessionStart payload (workspace_roots) for a workspace under home and one outside it, then run doctor:

  • Before (main at fe3b906):
    hook-dispatch, workspace under home     → (no output)
    hook-dispatch, workspace outside home   → (no output)
    doctor  ✔ Rules delivered to cursor
    
  • After (this branch):
    hook-dispatch, workspace under home     → (no output)
    hook-dispatch, workspace outside home   → {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"Team rules (from teamai):\n\nThe team codeword is PELICAN-42."}}
    doctor  ✔ Team rules reach Cursor workspaces outside the home directory
            ✔ Team rules and the team env hint fit one Cursor hook reply
            ✔ Rules delivered to cursor
    
  • With the sessionStart entry removed from ~/.cursor/hooks.json:
    ✖ Team rules reach Cursor workspaces outside the home directory → ... registers no teamai sessionStart hook for Cursor, so those workspaces get none of them. Run teamai hooks inject to rewrite it.
  • Rules of 8,900 characters plus ten described env variables: doctor shows ✖ Team rules and the team env hint fit one Cursor hook reply (10,208 characters), and the dispatched reply holds only the env hint (length=1246). Rules of 9,500 characters plus one variable: both checks pass, and the reply holds the rules and the hint (length=9818).

Not checked here:

  • A Cursor session reading the hook's context. The reply shape (hookSpecificOutput.additionalContext) is the one teamai's other Cursor hints already use, and the IDE and CLI bundles enable Claude-nested output compatibility. I did not observe it in a live session.
  • Windows. The walk, /c:/ roots and junctions are covered by reading Cursor's source and by POSIX tests only. CI has no Windows runner.
  • Whether Cursor fires sessionStart on resume, and whether the context survives compaction. The handler has no resume guard, because I could not confirm that Cursor's sessionStart payload carries a source field, and a guard on a missing field would drop the rules on a real start.

Only the git provider and the Cursor agent ran locally. CI covers gitlab/github and the other agents.

Merge Danger

Door: two-way

Nothing is written to disk by the new handler. A revert removes the hook text and the doctor checks.

One observable shift on Windows: Cursor IDE hook events record cwd as c:/... instead of /c:/..., so session ids derived from the cwd (hosts that send no conversation id) and recorded dashboard cwd values change at the upgrade. The old values pointed at a path that did not exist.

…directory (Tencent#1197)

Cursor loads ~/.cursor/rules only by walking up from the workspace, so a
workspace outside the home directory never got the user-scope rules while
doctor reported them delivered. The sessionStart hook now adds them there,
and doctor checks that hook.
… Windows workspace roots

Cursor caps a hook reply at 10,000 characters and drops all of it when
over, so the dispatcher now leaves the user-scope rules out when the
merged reply would not fit, keeping the other session-start hints.
The Cursor IDE sends each workspace root as a URI path (/c:/...), which
no path API resolves on Windows; resolveHookCwd now drops the slash.
Doctor names the real next step for invalid JSON, a team-disabled
session-start built-in, and a dispatch for another tool.
… in doctor

Doctor now fails past 9,000 characters, where the session's own hints can
push the rules out of Cursor's 10,000-character reply. The dispatcher
leaves the rules out only when the rest then fits. Doctor reads the
--tool value whole and orders its causes: invalid JSON, a missing hook
(naming a team builtin.disabled entry), then length.
…f a fixed headroom

The rules check fails only past Cursor's real 10,000-character limit. An
informational check measures the rules together with the team env hint,
which shares every session's reply, using the hint handler itself, so it
flags exactly the case where the dispatcher leaves the rules out.
@jeff-r2026 jeff-r2026 self-assigned this Oct 17, 2026
@github-actions

Copy link
Copy Markdown

No confident findings in the specified PR diff.

The description documents sufficient testing: a representative real-CLI run covers user-scope initialization, pull, hook dispatch, and doctor checks. The disclosed Windows, live-Cursor, and extra-provider/agent limitations are not blocking under the review rules.

No PR code was run, built, or installed.

@jeff-r2026
jeff-r2026 merged commit 1e145bf into Tencent:main Oct 17, 2026
14 checks passed
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.

[bug] Cursor never loads user-scope rules in a workspace outside the home directory

2 participants