Skip to content

feat(init): show AI-generated feature blurbs in setup summary#982

Open
betegon wants to merge 11 commits into
mainfrom
feat/feature-blurbs
Open

feat(init): show AI-generated feature blurbs in setup summary#982
betegon wants to merge 11 commits into
mainfrom
feat/feature-blurbs

Conversation

@betegon
Copy link
Copy Markdown
Member

@betegon betegon commented May 19, 2026

Summary

Ends the wizard with a "Here's what we set up" two-column table — one row per enabled feature with a sentence specific to the project that was just instrumented. The blurbs come from a new server-side agent that uses the tech-stack description already captured during platform detection (e.g. "Next.js 14 with Drizzle ORM and Cloudflare D1") to produce context-aware copy rather than generic marketing text.

Depends on getsentry/cli-init-api#156 for the server-side agent.

Changes

  • WizardSummary and WizardOutput gain an optional featureBlurbs field
  • buildSummary() populates it from the workflow output and suppresses the plain "Features" row when blurbs are present (the table makes it redundant)
  • Both ink-report.ts (post-dispose chalk output) and logging-ui.ts render the blurbs as a proper two-column table via renderTextTable — same renderer used by sentry issue list / sentry project list, so wrapping and column fitting are handled automatically
  • SummaryPanel in ink-app.tsx renders the live Ink variant with flex layout
  • Removed "Nice, setup made it through." preamble so the feedback prompt follows the summary directly

Test Plan

  • Start bun run dev in cli-init-api, then run MASTRA_API_URL=http://localhost:8787 bun run src/bin.ts init against a real project
  • Confirm the blurbs table appears after the key/value fields and before changed files
  • Run with --no-tui and confirm the plain-text table renders the same content
  • If the blurb agent fails, wizard completes normally with no blurbs shown (try/catch in open-sentry-ui.ts)

Adds a "Here's what we set up" two-column table at the end of sentry
init — one row per enabled feature with a project-specific sentence
generated by a new feature-blurb-writer agent.

The blurb column wraps to terminal width using the same renderTextTable
renderer that sentry issue list / project list use. The Features
key-value row is suppressed when blurbs are present (the table already
covers it). The success preamble "Nice, setup made it through." is
removed so the feedback prompt follows the summary directly.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-982/

Built to branch gh-pages at 2026-05-19 20:26 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

- Replace O(n²) find-in-map loop in buildSummary with a Map for blurb lookup
- Remove redundant marginTop={0} from featureBlurbs row in SummaryPanel
- Drop trailing colon from "Here's what we set up" in LoggingUI to match ink-report and ink-app
- Update feedback.test.ts to reflect removed success preamble

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

Codecov Results 📊

7009 passed | Total: 7009 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +15
Passed Tests 📈 +15
Failed Tests
Skipped Tests

All tests are passing successfully.

✅ Patch coverage is 95.12%. Project has 14173 uncovered lines.
✅ Project coverage is 77.12%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
src/lib/init/formatters.ts 84.00% ⚠️ 4 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    77.04%    77.12%    +0.08%
==========================================
  Files          320       320         —
  Lines        61872     61940       +68
  Branches         0         0         —
==========================================
+ Hits         47664     47767      +103
- Misses       14208     14173       -35
- Partials         0         0         —

Generated by Codecov Action

The LLM can echo back arbitrary strings in the feature field of the
blurbs response. Instead of keying labels off the LLM output, pair
blurbs positionally against output.features — the canonical ordered
list of selected feature IDs already in the workflow output. Labels
are always resolved via featureLabel() on the canonical IDs.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/lib/init/formatters.ts Outdated
Comment thread src/lib/init/ui/ink-report.ts
betegon and others added 2 commits May 19, 2026 21:06
…urbs

- Fix Biome import order in logging-ui.ts (renderTextTable after markdown)
- Fix Biome format: collapse single-item success array, split long JSX condition
- Fix LoggingUI.summary() early-return guard to account for featureBlurbs
- Fix buildSummary null guard to include featureBlurbs in the emptiness check
- Add test coverage: formatters (blurb population, suppressed Features row,
  positional label matching, display-order sort), ink-report (blurbs section),
  logging-ui (blurbs table, no section when absent)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@sentry-warden sentry-warden Bot left a comment

Choose a reason for hiding this comment

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

Features silently disappear when server returns blurbs with empty strings (src/lib/init/formatters.ts:44)

The plain "Features" field is suppressed whenever output.featureBlurbs is non-empty (line 44), but the blurbs table is only rendered after filtering out null/empty entries. If the server returns any featureBlurbs entries where every blurb is an empty string, the Features row is suppressed yet the blurbs table is also empty — no feature information appears in the summary at all.

Verification

Trace: output.featureBlurbs = [{feature:'errorMonitoring', blurb:''}]output.featureBlurbs?.length = 1!1 = false → guard at line 44 fails → 'Features' field NOT added. Then line 72: blurbsInOrder[0]?.blurb = '' which is falsy → .map() returns null.filter() produces featureBlurbs = []. Line 88: featureBlurbs.length === 0 → featureBlurbs NOT included in summary. Neither the Features field row nor the featureBlurbs table is rendered. The suppression guard (raw count) and the rendering gate (processed count) are out of sync.

Identified by Warden find-bugs

betegon and others added 2 commits May 19, 2026 21:41
- Add snapshot test: SummaryPanel renders featureBlurbs section
- Add formatters test: blurbs array shorter than features drops extras
- Fix LoggingUI.summary() early-return guard to include featureBlurbs

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon marked this pull request as ready for review May 19, 2026 19:48
- Revert to Map-by-feature-ID lookup (positional matching silently
  assigns wrong blurbs when agent omits or reorders entries; Map skips
  the missing feature instead)
- Strip ANSI sequences from server-supplied blurbs in buildSummary to
  prevent terminal injection
- Update tests: wrong-ID case now expects undefined (omitted), add
  omitted-feature case and stripAnsi sanitization case

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5fa2f88. Configure here.

Comment thread src/lib/init/formatters.ts Outdated
Comment thread src/lib/init/ui/ink-app.tsx
- Move featureBlurbs computation before fields in buildSummary so the
  Features row check uses the resolved length; when the agent echoes
  wrong IDs all blurbs drop out and the Features row renders correctly
- Move "Here's what we set up" section before "Changed files" in all
  three renderers (ink-report, logging-ui, ink-app) to match the
  intended layout: key/value → blurbs → changed files

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/lib/init/formatters.ts
Previous regex only stripped SGR colour codes (\x1b[...m); cursor
movement and screen-manipulation sequences (\x1b[2J, \x1b[A, etc.)
passed through to the terminal. Extend to match any CSI sequence
(\x1b[...LETTER) so server-supplied blurb strings are fully sanitised.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/lib/formatters/plain-detect.ts
… bytes

Previous regex missed sequences with intermediate bytes (0x20-0x2F)
such as \x1b[!p (Soft Terminal Reset) or \x1b[1 q (Set Cursor Style).
Use the correct CSI structure: parameter bytes (0x30-0x3F) + optional
intermediate bytes (0x20-0x2F) + final byte (0x40-0x7E).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Comment on lines 82 to 92
export function stripAnsi(text: string): string {
return (
text
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape detection requires matching \x1b and \x07
.replace(/\x1b\[[0-9;]*m/g, "")
// Full CSI spec: \x1b[ + parameter bytes (0x30-0x3F) + intermediate
// bytes (0x20-0x2F, e.g. space, !, ") + final byte (0x40-0x7E).
// The narrower [0-9;?]*[A-Za-z] missed sequences with intermediate
// bytes like \x1b[!p (Soft Terminal Reset) or \x1b[1 q (cursor style).
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape detection requires matching \x1b
.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g, "")
// biome-ignore lint/suspicious/noControlCharactersInRegex: OSC 8 hyperlink sequences use \x1b and \x07
.replace(/\x1b\]8;;[^\x07]*\x07/g, "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The stripAnsi function's regex is incomplete, allowing unsanitized terminal escape sequences from LLM-generated text to be executed, leading to a terminal injection vulnerability.
Severity: HIGH

Suggested Fix

Update the regex in the stripAnsi function to correctly strip all ANSI escape sequences, not just CSI and OSC 8 hyperlinks. Consider using a more comprehensive pattern or a dedicated third-party library for robust ANSI stripping to prevent terminal injection.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/lib/formatters/plain-detect.ts#L82-L92

Potential issue: The `stripAnsi` function, intended to sanitize LLM-supplied feature
blurbs, uses an incomplete regular expression. While it handles some CSI sequences and
OSC 8 hyperlinks, it fails to remove other terminal escape sequences, such as OSC
commands for changing a window title (`\x1b]0;...\x07`), Device Control Strings
(`\x1bP...`), or terminal resets (`\x1bc`). Because the output of `stripAnsi` is later
printed directly to `process.stdout` via `LoggingUI.summary()`, an LLM providing a
feature blurb with these unhandled sequences could execute them on the user's terminal,
creating a terminal injection vulnerability.

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.

1 participant