Add inline @mention support for comments, messages, and chat#288
Add inline @mention support for comments, messages, and chat#288jeremy merged 7 commits intobasecamp:mainfrom
Conversation
Resolve @name and @First.Last syntax to Basecamp mention tags using attachable SGIDs. Works across all content-posting commands: comment create/update, message create/update, and campfire chat.
Update agent_notes, Long descriptions, SKILL.md quick reference and workflows, README usage examples, and API-COVERAGE.md notes for the new @name / @First.Last mention syntax in comments, messages, and chat.
There was a problem hiding this comment.
Pull request overview
Adds inline @mention resolution across Basecamp CLI content-posting commands by converting @Name / @First.Last in rich text into Basecamp <bc-attachment> mention tags (via attachable_sgid lookup).
Changes:
- Add person lookup that returns full
Personrecords (includingAttachableSGID) and use it to resolve mentions. - Add richtext mention parsing + replacement (
ResolveMentions,MentionToHTML) and wire it into comments/messages/chat commands. - Update documentation (skill docs, README, API coverage) to reflect mention support.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
skills/basecamp/SKILL.md |
Documents mention syntax and adds examples to quick reference/workflows. |
internal/richtext/richtext.go |
Introduces mention regex + HTML replacement utilities for <bc-attachment> mentions. |
internal/richtext/richtext_test.go |
Adds tests for mention tag generation and mention resolution behavior. |
internal/names/resolver.go |
Extends Person with AttachableSGID, adds ResolvePersonByName, and dedup helper. |
internal/names/resolver_test.go |
Adds tests for ResolvePersonByName, including pingable fallback. |
internal/commands/helpers.go |
Adds resolveMentions helper to glue name resolution into richtext. |
internal/commands/comment.go |
Applies mention resolution to comment create/update flows. |
internal/commands/messages.go |
Applies mention resolution to message create/update flows and updates help text. |
internal/commands/chat.go |
Adds mention resolution for chat posts and auto-promotes to text/html on mention presence. |
README.md |
Adds an example showing mentions in comments. |
API-COVERAGE.md |
Notes mention support in comments/messages/chat coverage rows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 11 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/commands/chat.go">
<violation number="1" location="internal/commands/chat.go:365">
P1: When mentions auto-promote chat content to `text/html`, the non-mention text is not HTML-escaped. Characters like `<`, `>`, and `&` are sent raw, which can break rendering or inject unintended HTML. Comments and messages avoid this by calling `richtext.MarkdownToHTML()` before resolving mentions — chat should do the same.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- HTML-escape plain text in chat before mention resolution to prevent raw HTML injection when auto-promoting to text/html - Skip @mentions inside HTML tags and existing <bc-attachment> elements - Support Unicode names in mention regex (e.g., @josé, @zoë) - Fix doc comment to mention @First.Last syntax
Markdown blank lines become <p> tags which Basecamp renders without visible spacing. Add guidance to use <br><br> for empty lines in agent_notes and SKILL.md.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/basecamp/SKILL.md">
<violation number="1" location="skills/basecamp/SKILL.md:78">
P2: Duplicate numbering: the new rule 6 ("Line breaks in rich text") and the existing rule 6 ("Project scope is mandatory") both have the same number. Renumber the project-scope rule to `7.`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Pull request overview
Adds inline @mention support across Basecamp CLI content-posting commands by resolving @Name / @First.Last into Basecamp <bc-attachment> mention tags using each person’s attachable_sgid.
Changes:
- Add mention parsing + replacement in rich text HTML (
richtext.ResolveMentions,MentionToHTML) and wire it into comments/messages/chat posting flows. - Extend name resolution to return full
Personrecords includingAttachableSGID(Resolver.ResolvePersonByName) with pingable fallback and tests. - Update CLI docs/coverage matrices with mention syntax and examples.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
internal/richtext/richtext.go |
Implements mention detection and HTML replacement utilities. |
internal/richtext/richtext_test.go |
Adds unit tests for mention HTML generation and resolution behavior. |
internal/names/resolver.go |
Adds AttachableSGID to Person and introduces ResolvePersonByName + dedup helper. |
internal/names/resolver_test.go |
Adds tests for name resolution returning attachable SGIDs and pingable fallback. |
internal/commands/helpers.go |
Adds resolveMentions glue between name resolution and richtext mention replacement. |
internal/commands/comment.go |
Resolves mentions when creating/updating comment content. |
internal/commands/messages.go |
Resolves mentions when creating/updating message content. |
internal/commands/chat.go |
Resolves mentions in chat posts and auto-promotes to text/html when needed. |
skills/basecamp/SKILL.md |
Documents mention syntax and adds examples to skill reference. |
README.md |
Adds a usage example featuring @mention in comments. |
API-COVERAGE.md |
Notes mention support in relevant communication commands. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Skip mention resolution when explicit non-HTML content type is set - Skip mentions inside <code> and <pre> blocks - Handle self-closing <bc-attachment /> tags correctly - Fix duplicate numbering in SKILL.md agent invariants
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/richtext/richtext.go">
<violation number="1" location="internal/richtext/richtext.go:699">
P2: `isInsideCodeBlock` matches partial tag names like `<preview>` or `<codeblock>` because it only checks the prefix `"<"+tag` without verifying a tag boundary. Add a boundary check (next char is `>`, ` `, or end-of-string) after the opening-tag match.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Prevent partial tag name matches (e.g., <preview> matching <pre>) by verifying the character after the tag name is a boundary.
There was a problem hiding this comment.
Pull request overview
Adds inline @mention resolution across Basecamp CLI content-posting commands by turning @Name / @First.Last into Basecamp <bc-attachment> mention tags using each person’s attachable_sgid.
Changes:
- Introduces person-resolution support for mentions via
names.Resolver.ResolvePersonByName()and populatesPerson.AttachableSGIDfrom people + pingable endpoints. - Adds richtext mention parsing/replacement (
ResolveMentions,MentionToHTML) with skips for HTML tags, code/pre blocks, and existing<bc-attachment>elements. - Wires mention resolution into comments/messages create+update and chat post (with chat auto-promoting to
text/htmlwhen mentions are present), plus documentation updates.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
skills/basecamp/SKILL.md |
Documents mention syntax and rich-text line-break behavior; adds usage examples. |
internal/richtext/richtext.go |
Adds mention regex + HTML replacement pipeline and helper “inside tag/block” checks. |
internal/richtext/richtext_test.go |
Adds unit tests for mention HTML generation and mention resolution behavior/edge cases. |
internal/names/resolver.go |
Extends Person with AttachableSGID, adds ResolvePersonByName, and dedup helper for suggestions. |
internal/names/resolver_test.go |
Adds tests for ResolvePersonByName and pingable fallback behavior. |
internal/commands/helpers.go |
Adds shared resolveMentions glue to connect name resolution with richtext replacement. |
internal/commands/comment.go |
Applies mention resolution to comment create and update flows. |
internal/commands/messages.go |
Applies mention resolution to message create and update flows; updates help text. |
internal/commands/chat.go |
Resolves mentions for chat post and promotes to text/html when needed; updates help text. |
README.md |
Adds a quick example demonstrating @First.Last mention usage. |
API-COVERAGE.md |
Notes mention support in relevant communication command rows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/richtext/richtext.go">
<violation number="1" location="internal/richtext/richtext.go:706">
P2: When the boundary check fails, `continue` skips to the next tag type, discarding earlier valid `<pre>`/`<code>` occurrences. If e.g. raw `<preview>` appears inside a `<pre>` block, `LastIndex` finds `<preview>`, the boundary check rejects it, and the real `<pre>` is never examined — the mention inside the code block would be incorrectly resolved.
Consider searching backward through all occurrences rather than just the last one.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
When LastIndex found <pre inside <preview>, the boundary check correctly rejected it but continue skipped to the next tag type, missing any valid <pre> at an earlier position. Now iteratively searches backwards past failed matches. Also hoists ToLower to the caller to avoid repeated allocation per mention.
There was a problem hiding this comment.
Great work on this @nnemirovsky! Thank you.
I have a few things to add for unambiguous sgid mentions and markdown mentions. And considering UX for resolving ambiguous @firstname mentions.
Update: actually, this is forward-compatible; I can introduce deterministic mentions + ambiguity resolution in follow-up PRs.
The paragraph spacing PR handles blank lines properly now, so the <br><br> workaround guidance added in basecamp#288 is no longer needed. Also extracts the duplicated pendingBreak flush into a flushPendingBreak() helper and adds test coverage for blockquote and hr spacing.
Introduce three deterministic mention paths that bypass fuzzy name matching, giving agents and pipelines unambiguous control: - [@name](mention:SGID) — zero API calls, caller-trusted SGID - [@name](person:ID) — one API call, resolves against pingable set - @sgid:VALUE — inline SGID embed for composability ResolveMentions is restructured into three sequential passes (anchor → @sgid → fuzzy @name), each producing <bc-attachment> tags that subsequent passes skip. Also hardens the existing fuzzy @name path with review fixes from #288: - ResolvePersonByName resolves against pingable set only - Expanded prefix regex to match after (, [, ", ' - Trailing-character bailout for hyphens and word-internal apostrophes - Case-insensitive isInsideBcAttachment guard - Chat auto-promotes text/plain → text/html when mentions resolve - Fix double-escaping of HTML entities in mention: display text
Introduce three deterministic mention paths that bypass fuzzy name matching, giving agents and pipelines unambiguous control: - [@name](mention:SGID) — zero API calls, caller-trusted SGID - [@name](person:ID) — one API call, resolves against pingable set - @sgid:VALUE — inline SGID embed for composability ResolveMentions is restructured into three sequential passes (anchor → @sgid → fuzzy @name), each producing <bc-attachment> tags that subsequent passes skip. Also hardens the existing fuzzy @name path with review fixes from #288: - ResolvePersonByName resolves against pingable set only - Expanded prefix regex to match after (, [, ", ' - Trailing-character bailout for hyphens and word-internal apostrophes - Case-insensitive isInsideBcAttachment guard - Chat auto-promotes text/plain → text/html when mentions resolve - Fix double-escaping of HTML entities in mention: display text
Introduce three deterministic mention paths that bypass fuzzy name matching, giving agents and pipelines unambiguous control: - [@name](mention:SGID) — zero API calls, caller-trusted SGID - [@name](person:ID) — one API call, resolves against pingable set - @sgid:VALUE — inline SGID embed for composability ResolveMentions is restructured into three sequential passes (anchor → @sgid → fuzzy @name), each producing <bc-attachment> tags that subsequent passes skip. Also hardens the existing fuzzy @name path with review fixes from #288: - ResolvePersonByName resolves against pingable set only - Expanded prefix regex to match after (, [, ", ' - Trailing-character bailout for hyphens and word-internal apostrophes - Case-insensitive isInsideBcAttachment guard - Chat auto-promotes text/plain → text/html when mentions resolve - Fix double-escaping of HTML entities in mention: display text
Introduce three deterministic mention paths that bypass fuzzy name matching, giving agents and pipelines unambiguous control: - [@name](mention:SGID) — zero API calls, caller-trusted SGID - [@name](person:ID) — one API call, resolves against pingable set - @sgid:VALUE — inline SGID embed for composability ResolveMentions is restructured into three sequential passes (anchor → @sgid → fuzzy @name), each producing <bc-attachment> tags that subsequent passes skip. Also hardens the existing fuzzy @name path with review fixes from #288: - ResolvePersonByName resolves against pingable set only - Expanded prefix regex to match after (, [, ", ' - Trailing-character bailout for hyphens and word-internal apostrophes - Case-insensitive isInsideBcAttachment guard - Chat auto-promotes text/plain → text/html when mentions resolve - Fix double-escaping of HTML entities in mention: display text
* Add deterministic mention syntax and harden fuzzy resolution Introduce three deterministic mention paths that bypass fuzzy name matching, giving agents and pipelines unambiguous control: - [@name](mention:SGID) — zero API calls, caller-trusted SGID - [@name](person:ID) — one API call, resolves against pingable set - @sgid:VALUE — inline SGID embed for composability ResolveMentions is restructured into three sequential passes (anchor → @sgid → fuzzy @name), each producing <bc-attachment> tags that subsequent passes skip. Also hardens the existing fuzzy @name path with review fixes from #288: - ResolvePersonByName resolves against pingable set only - Expanded prefix regex to match after (, [, ", ' - Trailing-character bailout for hyphens and word-internal apostrophes - Case-insensitive isInsideBcAttachment guard - Chat auto-promotes text/plain → text/html when mentions resolve - Fix double-escaping of HTML entities in mention: display text * Update agent notes and SKILL.md for deterministic mention syntax Document all four mention syntaxes in agent_notes annotations (comment, message, chat commands) and update SKILL.md invariant #5 with the full syntax list and recommended workflow for agents. * Clarify reMentionInput prefix comment to enumerate allowed characters The comment said "punctuation" which was vague — enumerate the specific characters (whitespace, >, (, [, ", ') to match the actual regex.
Summary
Adds
@Nameand@First.Lastsyntax for inline mentions in all content-posting commands. Mentions are resolved to clickable<bc-attachment>tags using the person'sattachable_sgid.basecamp comment 789 "@Jane.Smith, please review"— create and updatebasecamp message "Title" "cc @Jane" --in project— create and updatebasecamp chat post "@Jane, done!" --in project— auto-promotes totext/html@First.Lastto disambiguateImplementation
names.Resolver.ResolvePersonByName()— returns fullPersonwithAttachableSGID(exact > case-insensitive > partial > pingable fallback)richtext.ResolveMentions()— finds@mentionsin HTML, calls lookup function, replaces with<bc-attachment>tagsresolveMentions()helper in commands wires the two togetherPerson.AttachableSGIDfield added — populated from both people and pingable endpointsFiles changed
internal/names/resolver.goAttachableSGIDfield,ResolvePersonByName,deduplicatePeoplehelperinternal/names/resolver_test.goResolvePersonByName+ pingable fallbackinternal/richtext/richtext.goMentionToHTML,ResolveMentions,reMentionInputregexinternal/richtext/richtext_test.gointernal/commands/helpers.goresolveMentionsglue functioninternal/commands/comment.gointernal/commands/messages.gointernal/commands/chat.goskills/basecamp/SKILL.mdREADME.mdAPI-COVERAGE.mdTest plan
go test ./internal/names/...— all pass (includingResolvePersonByName+ pingable fallback)go test ./internal/richtext/... -run "TestMentionToHTML|TestResolveMentions"— 8 subtests passgo build ./...— cleango vet ./...— clean@First.Lastmention — resolved correctly with avatar@Namereturns error with suggestionsSummary by cubic
Adds inline @mention support across comments, messages, and chat. Converts
@Nameand@First.Lastinto clickable Basecamp mentions; chat auto-promotes to HTML when mentions are present.New Features
@Nameor@First.Last→<bc-attachment>using each person’sattachable_sgidvianames.Resolver.ResolvePersonByName()+richtext.ResolveMentions().@First.Lastto disambiguate.text/html; comments and messages keep Markdown-to-HTML.Bug Fixes
--content-typeis set; otherwise auto-promote chat safely.<code>/<pre>, and ignore/handle existing or self-closing<bc-attachment>tags.<code>/<pre>detection to avoid missing valid tags after partial matches like<preview>; lowercasing hoisted to the caller to reduce allocations.@José,@Zoë); docs note using<br><br>for visible empty lines in rich text.Written for commit 8152905. Summary will update on new commits.