Skip to content

fix: expand @-imports when syncing personalization from CLAUDE.md#3405

Merged
trunk-io[bot] merged 3 commits into
mainfrom
matt/personalization-expand-imports
Jul 14, 2026
Merged

fix: expand @-imports when syncing personalization from CLAUDE.md#3405
trunk-io[bot] merged 3 commits into
mainfrom
matt/personalization-expand-imports

Conversation

@MattBro

@MattBro MattBro commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

PostHog Code's Settings → Personalization "Sync from AGENTS.md / CLAUDE.md" toggle (added in #3286) reads the first non-empty of ~/.agents/AGENTS.md, ~/.codex/AGENTS.md, ~/.claude/AGENTS.md, ~/.claude/CLAUDE.md and mirrors it into personalization.

Many users' ~/.claude/CLAUDE.md is a stub that only composes Claude Code @-imports (e.g. @./claude/engineering.md). Reading the file plainly ships the stub and none of the actual rules the imports pull in, so those users sync an almost-empty personalization.

Two wrinkles make a naive fix wrong:

  • ~/.claude/CLAUDE.md is often a symlink into a dotfiles repo. Its relative imports resolve against the symlink target's directory, not ~/.claude.
  • Imports can be recursive and can form cycles, and a mention of a path inside a code span/block is not an import.

Changes

getUserAgentInstructions in packages/workspace-server/src/services/os/os.ts now expands @-imports before applying the length cap, matching Claude Code's memory-import behavior (docs: https://code.claude.com/docs/en/memory):

  • Resolves the realpath of the instructions file first, and resolves each @path relative to the directory of the file that contains it (after realpath), so symlinked stubs resolve their relative imports against the real dotfiles directory.
  • Supports relative, absolute, and ~/ import paths.
  • Recursively inlines imported file contents, capped at 4 hops deep, with a visited-set cycle guard keyed on realpaths.
  • A missing, unreadable, or cyclic import leaves its @path reference in place rather than throwing.
  • Skips imports inside fenced code blocks and single-backtick inline code spans.
  • Applies the existing 20k USER_AGENT_INSTRUCTIONS_MAX_LENGTH cap after expansion, preserving the truncated flag semantics.
  • Files with no imports are byte-for-byte unchanged.

Divergences from Claude Code's import spec

  • Code handling follows CommonMark closely: fenced blocks track marker character and length (a closer must be at least as long, same character, no info string), indented code blocks (4+ spaces or a tab after a blank line) stay literal, and inline code spans match equal-length backtick runs, so multi-backtick spans keep their contents literal. One simplification: an indented line without a preceding blank line (e.g. a list continuation) is treated as prose and its imports expand, since distinguishing lazy continuation from code needs a full block parser.
  • Import tokens are recognized as @ preceded by start-of-line or whitespace, then a run of non-whitespace characters. This intentionally avoids treating foo@bar (e.g. emails) as imports; a trailing punctuation character on a bare path is included in the path rather than stripped.

How did you test this?

Added unit tests in packages/workspace-server/src/services/os/os.test.ts covering: no-imports passthrough, single import, nested import, cycle guard, missing import, inline-code-span literal, fenced-code-block literal, max-depth cutoff, symlinked root file with a relative import resolving against the real directory, and the length cap applied after expansion, fence length/info-string rules (shorter fence inside a longer one, tilde vs backtick isolation), indented code blocks (blank-line context, tab indent, list continuations still expanding, three-space boundary), and multi-backtick / unmatched-run inline spans.

  • pnpm exec vitest run src/services/os/os.test.ts — 30 passed
  • Full @posthog/workspace-server suite — 697 passed
  • pnpm --filter @posthog/workspace-server typecheck — clean
  • biome check on the changed files — clean

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@trunk-io

trunk-io Bot commented Jul 13, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit f9386f2.

@MattBro MattBro requested review from MattPua and pauldambra July 13, 2026 20:39
@pauldambra pauldambra marked this pull request as ready for review July 14, 2026 08:41
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "Merge branch 'main' into matt/personaliz..." | Re-trigger Greptile

Comment thread packages/workspace-server/src/services/os/os.ts Outdated
Comment thread packages/workspace-server/src/services/os/os.ts
Comment thread packages/workspace-server/src/services/os/os.ts Outdated
…mport expansion

A closing fence must now match the opening character, be at least as
long, and carry no info string, so a three-backtick line inside a
four-backtick block no longer closes it. Indented code blocks (4+
spaces or tab after a blank line) stay literal without skipping list
continuations. Inline spans match equal-length backtick runs per
CommonMark, so double-backtick spans stay literal and an unmatched
backtick run no longer swallows the rest of the line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MattBro

MattBro commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io trunk-io Bot merged commit 3cf6615 into main Jul 14, 2026
28 checks passed
@trunk-io trunk-io Bot deleted the matt/personalization-expand-imports branch July 14, 2026 18:38
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.

2 participants