Skip to content

Add inline @mention support for comments, messages, and chat#288

Merged
jeremy merged 7 commits intobasecamp:mainfrom
nnemirovsky:feat/mention-support
Mar 13, 2026
Merged

Add inline @mention support for comments, messages, and chat#288
jeremy merged 7 commits intobasecamp:mainfrom
nnemirovsky:feat/mention-support

Conversation

@nnemirovsky
Copy link
Contributor

@nnemirovsky nnemirovsky commented Mar 13, 2026

Summary

Adds @Name and @First.Last syntax for inline mentions in all content-posting commands. Mentions are resolved to clickable <bc-attachment> tags using the person's attachable_sgid.

  • Comments: basecamp comment 789 "@Jane.Smith, please review" — create and update
  • Messages: basecamp message "Title" "cc @Jane" --in project — create and update
  • Chat: basecamp chat post "@Jane, done!" --in project — auto-promotes to text/html
  • Ambiguous names return a helpful error with suggestions; use @First.Last to disambiguate

Implementation

  • names.Resolver.ResolvePersonByName() — returns full Person with AttachableSGID (exact > case-insensitive > partial > pingable fallback)
  • richtext.ResolveMentions() — finds @mentions in HTML, calls lookup function, replaces with <bc-attachment> tags
  • resolveMentions() helper in commands wires the two together
  • Person.AttachableSGID field added — populated from both people and pingable endpoints

Files changed

File Change
internal/names/resolver.go AttachableSGID field, ResolvePersonByName, deduplicatePeople helper
internal/names/resolver_test.go Tests for ResolvePersonByName + pingable fallback
internal/richtext/richtext.go MentionToHTML, ResolveMentions, reMentionInput regex
internal/richtext/richtext_test.go 8 test cases for mention resolution
internal/commands/helpers.go resolveMentions glue function
internal/commands/comment.go Mention resolution in create + update
internal/commands/messages.go Mention resolution in create + update
internal/commands/chat.go Mention resolution in post (auto text/html promotion)
skills/basecamp/SKILL.md Document mention syntax in quick ref + workflows
README.md Add mention example to usage
API-COVERAGE.md Note mention support on comments/messages/chat rows

Test plan

  • go test ./internal/names/... — all pass (including ResolvePersonByName + pingable fallback)
  • go test ./internal/richtext/... -run "TestMentionToHTML|TestResolveMentions" — 8 subtests pass
  • go build ./... — clean
  • go vet ./... — clean
  • Manual test: posted comment with @First.Last mention — resolved correctly with avatar
  • Manual test: ambiguous @Name returns error with suggestions

Summary by cubic

Adds inline @mention support across comments, messages, and chat. Converts @Name and @First.Last into clickable Basecamp mentions; chat auto-promotes to HTML when mentions are present.

  • New Features

    • @Name or @First.Last<bc-attachment> using each person’s attachable_sgid via names.Resolver.ResolvePersonByName() + richtext.ResolveMentions().
    • Resolution: exact > case-insensitive > partial; ambiguous names return suggestions — use @First.Last to disambiguate.
    • Chat with mentions auto-promotes to text/html; comments and messages keep Markdown-to-HTML.
  • Bug Fixes

    • Skip mention resolution when a non-HTML --content-type is set; otherwise auto-promote chat safely.
    • Hardened parsing: escape plain text, skip inside HTML tags and <code>/<pre>, and ignore/handle existing or self-closing <bc-attachment> tags.
    • Improved <code>/<pre> detection to avoid missing valid tags after partial matches like <preview>; lowercasing hoisted to the caller to reduce allocations.
    • Support Unicode names (e.g., @José, @Zoë); docs note using <br><br> for visible empty lines in rich text.

Written for commit 8152905. Summary will update on new commits.

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.
@nnemirovsky nnemirovsky requested a review from a team as a code owner March 13, 2026 04:43
Copilot AI review requested due to automatic review settings March 13, 2026 04:43
@github-actions github-actions bot added commands CLI command implementations tests Tests (unit and e2e) skills Agent skills docs enhancement New feature or request labels Mar 13, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 Person records (including AttachableSGID) 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.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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-ai with guidance or docs links (including llms.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.
Copilot AI review requested due to automatic review settings March 13, 2026 05:29
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 Person records including AttachableSGID (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
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.
Copilot AI review requested due to automatic review settings March 13, 2026 06:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 populates Person.AttachableSGID from 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/html when 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.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.
Copy link
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

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

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.

@jeremy jeremy merged commit 243815f into basecamp:main Mar 13, 2026
23 checks passed
nnemirovsky added a commit to nnemirovsky/basecamp-cli that referenced this pull request Mar 14, 2026
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.
jeremy added a commit that referenced this pull request Mar 15, 2026
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
jeremy added a commit that referenced this pull request Mar 15, 2026
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
jeremy added a commit that referenced this pull request Mar 15, 2026
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
jeremy added a commit that referenced this pull request Mar 15, 2026
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
jeremy added a commit that referenced this pull request Mar 15, 2026
* 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations docs enhancement New feature or request skills Agent skills tests Tests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants