Yaml frontmatter fix#111
Conversation
…ntax helpers parseFrontmatter detects YAML frontmatter only when --- is line 1 and a closing --- exists, preventing horizontal rules in note bodies from being misidentified. formatTagsAsInlineSyntax extracts the tag-formatting logic from applyNoteConventions so it can be reused by tool handlers. Also adds 'replace_all' to BearUrlParams.mode for the upcoming bear-add-tag frontmatter fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tTagsAsInlineSyntax Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When text starts with ---\n...\n---, assemble the final note as frontmatter → title (H1) → tags → body and pass it as a single text payload so Bear does not insert the title or tags outside the block. Notes without frontmatter follow the existing code path unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a note contains YAML frontmatter, rebuild the note body with tags inserted after the closing --- so a blind prepend cannot clobber the block. Notes without frontmatter keep the original prepend behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dd-tag Tests require Bear to be running. They create throwaway notes with unique prefixes, verify frontmatter is preserved correctly, then trash the notes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
API validation confirmed mode=replace_all is documented; no code changes needed. Commit 26fd648 split into two atomic commits (bear-create-note and bear-add-tag). FINDINGS.md documents the API verification result and the discrepancy in TASK.md's expected commit count (7 vs 8). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@stefanstr is attempting to deploy a commit to the vasylenko's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
|
Hi @stefanstr ! Thank you for contributing! I will do my best to review this promptly. Meanwhile, could you please explain in detail how this pain surfaces to you? How can I reproduce it from my end?
Also, please clean up the leftovers from the coding agent (unless you wanted that to remain on purpose, e.g., for me to review? LMK if so), such as progress file or summary (concise version of that might go to the PR description, btw). Thanks! |
There was a problem hiding this comment.
Pull request overview
This PR addresses a correctness issue where Bear x-callback-url operations could disrupt notes that start with YAML frontmatter, by detecting frontmatter and ensuring subsequent title/tag insertion preserves the frontmatter block at the top of the note.
Changes:
- Added frontmatter parsing + tag formatting/insertion helpers in note conventions, with unit tests.
- Updated
bear-create-noteto assemble a singletextpayload when frontmatter is present to prevent Bear from inserting content above the YAML block. - Updated
bear-add-tagto detect frontmatter and avoid clobbering it (usingmode=replace_allwhen needed), plus added system tests and documentation/changelog updates.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/tools/note-tools.ts |
Adds frontmatter-aware handling in bear-create-note and bear-add-tag to preserve YAML structure. |
src/operations/note-conventions.ts |
Introduces parseFrontmatter, formatTagsAsInlineSyntax, and insertInlineTags helpers. |
src/operations/note-conventions.test.ts |
Adds unit tests covering new helpers and insertion behaviors. |
src/infra/bear-urls.ts |
Extends Bear URL mode typing to include replace_all. |
tests/system/frontmatter.test.ts |
Adds end-to-end/system coverage for frontmatter interactions in create-note and add-tag. |
README.md |
Documents frontmatter behavior and conventions. |
CHANGELOG.md |
Adds unreleased entries describing the fixes. |
SUMMARY.md |
Branch/implementation summary added (reviewed for maintainability impact). |
PROGRESS.md |
Branch progress log added (reviewed for maintainability impact). |
| # Progress: yaml-frontmatter-fix | ||
|
|
||
| ## Status: Complete | ||
|
|
||
| ## What was done | ||
|
|
||
| ### Task 1: API Validation (FINDINGS.md) | ||
| - Fetched official Bear x-callback-url docs | ||
| - Confirmed `mode=replace_all` is a documented, supported value for `/add-text` | ||
| - Confirmed `/replace-note` does not exist (BRIEF.md was mistaken about this alternative) | ||
| - No code changes required | ||
|
|
||
| ### Task 2: Commit split | ||
| - Commit 26fd648 (mixed bear-create-note + bear-add-tag changes) split into two atomic commits | ||
| - Note: TASK.md expected 3 commits from 26fd648, but the bear-urls.ts change was already in d86067b; the correct split yielded 2 commits from that mixed change | ||
| - Final branch structure: 8 commits on top of main, including this follow-up documentation refresh | ||
|
|
||
| ### Task 3: Documentation refresh | ||
| - SUMMARY.md updated with new commit SHAs, API validation result, resolved blockers | ||
| - PROGRESS.md updated (this file) | ||
|
|
||
| ## Remaining work (for PR author) | ||
| - Run system tests with Bear open: `npm run test:system` | ||
| - Manual verification: create a frontmatter note via MCP, add a tag, confirm structure in Bear |
There was a problem hiding this comment.
PROGRESS.md is tracking branch-specific execution status and references internal task artifacts (e.g., FINDINGS.md) that aren’t present in the repo. This kind of progress log tends to go stale quickly once merged; consider removing it from the repository root or relocating it to a more appropriate place (e.g., PR description or contributor docs) if you want to preserve the narrative.
| # Progress: yaml-frontmatter-fix | |
| ## Status: Complete | |
| ## What was done | |
| ### Task 1: API Validation (FINDINGS.md) | |
| - Fetched official Bear x-callback-url docs | |
| - Confirmed `mode=replace_all` is a documented, supported value for `/add-text` | |
| - Confirmed `/replace-note` does not exist (BRIEF.md was mistaken about this alternative) | |
| - No code changes required | |
| ### Task 2: Commit split | |
| - Commit 26fd648 (mixed bear-create-note + bear-add-tag changes) split into two atomic commits | |
| - Note: TASK.md expected 3 commits from 26fd648, but the bear-urls.ts change was already in d86067b; the correct split yielded 2 commits from that mixed change | |
| - Final branch structure: 8 commits on top of main, including this follow-up documentation refresh | |
| ### Task 3: Documentation refresh | |
| - SUMMARY.md updated with new commit SHAs, API validation result, resolved blockers | |
| - PROGRESS.md updated (this file) | |
| ## Remaining work (for PR author) | |
| - Run system tests with Bear open: `npm run test:system` | |
| - Manual verification: create a frontmatter note via MCP, add a tag, confirm structure in Bear | |
| # Progress | |
| This repository does not keep branch-specific progress logs in versioned documentation. | |
| Short-lived implementation notes, task checklists, commit-splitting details, and manual verification steps should be recorded in the pull request description or other contributor workflow materials instead of this file. | |
| Keep repository-root documentation focused on stable, long-term project information. |
| const parsed = text ? parseFrontmatter(text) : null; | ||
|
|
||
| let url: string; | ||
| let pollTitle: string | undefined; | ||
|
|
||
| if (parsed?.frontmatter !== null && parsed !== null) { |
There was a problem hiding this comment.
The frontmatter detection branch uses a confusing/nullish pattern (const parsed = text ? parseFrontmatter(text) : null and if (parsed?.frontmatter !== null && parsed !== null)). Since parseFrontmatter() always returns an object, consider keeping parsed non-null (e.g., always call it when text is provided) and simplify the condition to a straightforward parsed.frontmatter !== null check to reduce cognitive load and avoid subtle truthiness pitfalls later.
| const parsed = text ? parseFrontmatter(text) : null; | |
| let url: string; | |
| let pollTitle: string | undefined; | |
| if (parsed?.frontmatter !== null && parsed !== null) { | |
| const parsed = parseFrontmatter(text ?? ''); | |
| let url: string; | |
| let pollTitle: string | undefined; | |
| if (parsed.frontmatter !== null) { |
| // Frontmatter path: assemble the full note content so Bear doesn't | ||
| // insert a title H1 or tags outside the frontmatter block. | ||
| const tagLine = tags ? formatTagsAsInlineSyntax(tags) : ''; | ||
| const bodySegments: string[] = []; | ||
| if (title) bodySegments.push(`# ${title}`); | ||
| if (parsed.body) bodySegments.push(parsed.body); |
There was a problem hiding this comment.
In the frontmatter path, the tool constructs its own # <title> line and sends only text to Bear (no title URL param). That makes the current inputSchema description for text (“Do not include a title heading — Bear adds it automatically from the title parameter.”) inaccurate for frontmatter notes. Update the tool’s schema/description so callers know that when frontmatter is present the server will manage the H1/title itself (and what to do if the user’s text already contains a heading).
Hey. First off, apologies, I didn't mean to push it to main just yet, hence the leftovers. Misclicked. Feel free to reject this one or pull from it whatever is useful. The painpoint is that Bear's X-Callback-URL actions were written before they added YAML support and they break notes with frontmatter. YAML blocks need to be at the start of the note whereas currently, new tags, titles, etc. get injected before them. My additions check whether a note contains YAML, and if it does, it makes sure it stays note-initial. Frontmatter is useful for keeping info on web excerpts and such. |
Interesting... Does it behave the same with and without the note structure convention enforced? There is an ENV variable that enforces the MCP server to put tags after the H1 note title. |
|


The original MCP breaks notes using YAML front matter. I added logic ensuring that YAML remains at the beginning of the note for the notes using it.