Skip to content

feat: add terminal rendering libraries for richer TUI output#85

Merged
kelsonpw merged 4 commits intomainfrom
kelsonpw/terminal-lib-upgrades
Apr 16, 2026
Merged

feat: add terminal rendering libraries for richer TUI output#85
kelsonpw merged 4 commits intomainfrom
kelsonpw/terminal-lib-upgrades

Conversation

@kelsonpw
Copy link
Copy Markdown
Collaborator

@kelsonpw kelsonpw commented Apr 16, 2026

Summary

  • Add terminal-link, gradient-string, cli-highlight, and marked-terminal for richer terminal output
  • Clickable hyperlinks across Auth, Outage, Slack, Outro, and Run screens (graceful fallback in plain terminals)
  • Brand gradient (blue → lilac → violet) on the "Amplitude Wizard" header text
  • Level-based log coloring in LogViewer — errors red, warnings amber, success green, with carry-forward so multi-line JSON bodies/stack traces inherit their parent line's color
  • Full markdown rendering in ReportViewer — replaces hand-rolled regex with proper headings, code blocks, tables, lists, and emphasis
  • New <TerminalLink> primitive with optional color prop, plus shared terminal-rendering.ts utility

AMP-152421

Test plan

  • pnpm build compiles without errors
  • pnpm test — all 849 tests pass
  • pnpm try — visually verify gradient header, clickable URLs, level-colored logs, and rendered markdown report
  • Test in terminal without hyperlink support (plain Terminal.app) — terminal-link falls back to plain text

🤖 Generated with Claude Code

@kelsonpw kelsonpw requested a review from a team as a code owner April 16, 2026 00:20
Add terminal-link, gradient-string, cli-highlight, and marked-terminal
to improve the wizard's terminal experience:

- Clickable hyperlinks in Auth, Outage, Slack, Outro, and Run screens
- Brand gradient (blue → lilac → violet) on HeaderBar title
- JSON syntax highlighting in LogViewer
- Full markdown rendering in ReportViewer (replaces hand-rolled regex)
- New Table and TerminalLink primitives
- Shared terminal-rendering.ts utility module

AMP-152421

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

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci django
  • /wizard-ci fastapi
  • /wizard-ci flask
  • /wizard-ci javascript-node
  • /wizard-ci javascript-web
  • /wizard-ci next-js
  • /wizard-ci python
  • /wizard-ci react-router
  • /wizard-ci vue

Test an individual app:

  • /wizard-ci django/django3-saas
  • /wizard-ci fastapi/fastapi3-ai-saas
  • /wizard-ci flask/flask3-social-media
Show more apps
  • /wizard-ci javascript-node/express-todo
  • /wizard-ci javascript-node/fastify-blog
  • /wizard-ci javascript-node/hono-links
  • /wizard-ci javascript-node/koa-notes
  • /wizard-ci javascript-node/native-http-contacts
  • /wizard-ci javascript-web/saas-dashboard
  • /wizard-ci next-js/15-app-router-saas
  • /wizard-ci next-js/15-app-router-todo
  • /wizard-ci next-js/15-pages-router-saas
  • /wizard-ci next-js/15-pages-router-todo
  • /wizard-ci python/meeting-summarizer
  • /wizard-ci react-router/react-router-v7-project
  • /wizard-ci react-router/rrv7-starter
  • /wizard-ci react-router/saas-template
  • /wizard-ci react-router/shopper
  • /wizard-ci vue/movies

Results will be posted here when complete.

@kelsonpw kelsonpw force-pushed the kelsonpw/terminal-lib-upgrades branch from 1a8df07 to dc1b89f Compare April 16, 2026 00:20
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.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Fallback shows duplicate URL in plain terminals
    • Updated the makeLink fallback to return just the URL when text equals url, avoiding redundant output like "url (url)".
  • ✅ Fixed: New Table component exported but never used
    • Removed the unused Table.tsx file and its barrel export from primitives/index.ts.

Create PR

Or push these changes by commenting:

@cursor push 31c68dbe5c
Preview (31c68dbe5c)
diff --git a/src/ui/tui/primitives/Table.tsx b/src/ui/tui/primitives/Table.tsx
deleted file mode 100644
--- a/src/ui/tui/primitives/Table.tsx
+++ /dev/null
@@ -1,82 +1,0 @@
-/**
- * Table — Aligned column display for Ink.
- *
- * Renders a header row, separator, and data rows using Box/Text.
- * Auto-sizes columns to fit content, capped at optional max widths.
- */
-
-import { Box, Text } from 'ink';
-import { Colors, Icons } from '../styles.js';
-
-export interface TableColumn {
-  /** Key to look up in each data row. */
-  key: string;
-  /** Display label for the header. */
-  label: string;
-  /** Maximum column width. Defaults to unlimited. */
-  maxWidth?: number;
-}
-
-interface TableProps {
-  columns: TableColumn[];
-  data: Record<string, string>[];
-}
-
-function computeWidths(
-  columns: TableColumn[],
-  data: Record<string, string>[],
-): number[] {
-  return columns.map((col) => {
-    const headerLen = col.label.length;
-    const maxData = data.reduce(
-      (max, row) => Math.max(max, (row[col.key] ?? '').length),
-      0,
-    );
-    const natural = Math.max(headerLen, maxData);
-    return col.maxWidth ? Math.min(natural, col.maxWidth) : natural;
-  });
-}
-
-function pad(text: string, width: number): string {
-  return text.length >= width
-    ? text.slice(0, width)
-    : text + ' '.repeat(width - text.length);
-}
-
-export const Table = ({ columns, data }: TableProps) => {
-  const widths = computeWidths(columns, data);
-  const gap = 2;
-
-  return (
-    <Box flexDirection="column">
-      {/* Header */}
-      <Box gap={gap}>
-        {columns.map((col, i) => (
-          <Text key={col.key} bold color={Colors.heading}>
-            {pad(col.label, widths[i])}
-          </Text>
-        ))}
-      </Box>
-
-      {/* Separator */}
-      <Box gap={gap}>
-        {columns.map((col, i) => (
-          <Text key={col.key} color={Colors.border}>
-            {Icons.dash.repeat(widths[i])}
-          </Text>
-        ))}
-      </Box>
-
-      {/* Data rows */}
-      {data.map((row, ri) => (
-        <Box key={ri} gap={gap}>
-          {columns.map((col, ci) => (
-            <Text key={col.key} color={Colors.body}>
-              {pad(row[col.key] ?? '', widths[ci])}
-            </Text>
-          ))}
-        </Box>
-      ))}
-    </Box>
-  );
-};
\ No newline at end of file

diff --git a/src/ui/tui/primitives/index.ts b/src/ui/tui/primitives/index.ts
--- a/src/ui/tui/primitives/index.ts
+++ b/src/ui/tui/primitives/index.ts
@@ -22,5 +22,3 @@
 export { SlashCommandInput } from './SlashCommandInput.js';
 export type { SlashCommand } from './SlashCommandInput.js';
 export { TerminalLink } from './TerminalLink.js';
-export { Table } from './Table.js';
-export type { TableColumn } from './Table.js';

diff --git a/src/ui/tui/utils/terminal-rendering.ts b/src/ui/tui/utils/terminal-rendering.ts
--- a/src/ui/tui/utils/terminal-rendering.ts
+++ b/src/ui/tui/utils/terminal-rendering.ts
@@ -20,7 +20,7 @@
  */
 export function makeLink(text: string, url: string): string {
   return terminalLink(text, url, {
-    fallback: (text, url) => `${text} (${url})`,
+    fallback: (text, url) => (text === url ? url : `${text} (${url})`),
   });
 }

You can send follow-ups to the cloud agent here.

Comment thread src/ui/tui/utils/terminal-rendering.ts
Comment thread src/ui/tui/primitives/Table.tsx Outdated
Copy link
Copy Markdown
Collaborator

@bird-m bird-m left a comment

Choose a reason for hiding this comment

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

🤖 AI assisted review — 8 inline findings below, each with a link to a pre-written fix in #86. Every fix is a minimal, independently cherry-pickable commit.

Comment thread src/ui/tui/primitives/LogViewer.tsx Outdated
))}
{lines.map((line, i) => {
const isJson =
line.trimStart().startsWith('{') || line.trimStart().startsWith('[');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🟡 Important — The startsWith('[') check here will match every log line in the wizard log, not just JSON arrays. The wizard's logToFile() writes lines as [${timestamp}] ${msg}, so every line starts with [ and will be routed through highlightCode(line, 'json').

Pre-written fix: review-panel #1 — narrows the [ check to [{ and [" so only JSON arrays get highlighted.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a27afab (narrowed to [{ and [") and further improved in eeb4b31 (strips [timestamp] prefix before checking).

Comment thread src/ui/tui/primitives/Table.tsx Outdated
: text + ' '.repeat(width - text.length);
}

export const Table = ({ columns, data }: TableProps) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🟡 Important — This Table component is introduced but nothing in the PR imports or uses it — it's dead code on merge. Consider deferring it to the PR that actually needs it.

Pre-written fix: review-panel #2 — removes Table.tsx and its barrel export.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed in a27afab.

Comment thread src/ui/tui/utils/terminal-rendering.ts Outdated
// produces a valid MarkedExtension. Cast to satisfy marked.use().
marked.use(
markedTerminal({
firstHeading: (s: string) => `\x1b[1m\x1b[38;2;64;131;255m${s}\x1b[0m`,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🟡 Important — Two issues here:

  1. The ANSI RGB values \x1b[38;2;64;131;255m and \x1b[38;2;105;128;255m are hardcoded rather than derived from Brand.blueOnDark and Brand.lilac in styles.ts. If the brand palette changes, these will silently drift.

  2. marked.use() on line 54 mutates the global marked singleton. Any other module importing marked would inherit this terminal-rendering config.

Pre-written fix: review-panel #3,#4 — adds a hexToAnsi() helper using Brand tokens and scopes the config to new marked.Marked() instead of the global.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Both fixed in a27afab — added hexToAnsi() using Brand tokens and scoped to new Marked() instance.

Comment thread src/ui/tui/screens/RunScreen.tsx Outdated
{OUTBOUND_URLS.stripeDataSource}
</Text>
<TerminalLink url={OUTBOUND_URLS.stripeDataSource}>
Add Stripe data source
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🔵 Nit — This is the only TerminalLink usage in the PR where the display text differs from the URL. All others pass the URL as children. In terminals without hyperlink support, the fallback renders Add Stripe data source (https://...) instead of just the URL.

Pre-written fix: review-panel #5 — uses the raw URL as children for consistency.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a27afab — now uses raw URL as children.

Comment thread src/ui/tui/components/HeaderBar.tsx Outdated
<Text color={Colors.heading} bold>
Amplitude Wizard
</Text>
<Text bold>{brandGradient('Amplitude Wizard')}</Text>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🔵 NitbrandGradient('Amplitude Wizard') recomputes the gradient interpolation on every render for a constant string. Since HeaderBar re-renders on store changes (org/project context), this runs more often than needed.

Pre-written fix: review-panel #6 — hoists the result to a module-level constant.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a27afab — hoisted to module-level HEADER_TITLE constant.

const updateContent = () => {
try {
const raw = fs.readFileSync(filePath, 'utf-8');
const rendered = renderMarkdown(raw);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🔵 NitrenderMarkdown(raw) runs the full marked.parse() pipeline on every fs.watch event. fs.watch can fire multiple times for a single write, re-parsing the entire document each time. The old code had the same read-on-every-event pattern but only used simple regex.

Pre-written fix: review-panel #7 — caches the previous raw content and skips re-render when unchanged.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a27afab — caches previous raw content via ref and skips re-render when unchanged.

Comment thread src/ui/tui/primitives/TerminalLink.tsx Outdated

export const TerminalLink = ({ url, children }: TerminalLinkProps) => {
const text = children ?? url;
return <Text color={Colors.accent}>{makeLink(text, url)}</Text>;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI assisted comment - please forgive for tone

🔵 NitColors.accent is hardcoded with no way to override. The pre-existing code used different colors for different link contexts (e.g. Colors.accentSecondary for the Stripe URL in RunScreen). Without a color prop, adopting TerminalLink everywhere would flatten the visual hierarchy.

Pre-written fix: review-panel #8 — adds an optional color prop defaulting to Colors.accent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a27afab — added optional color prop defaulting to Colors.accent.

bird-m
bird-m previously approved these changes Apr 16, 2026
Copy link
Copy Markdown
Collaborator

@bird-m bird-m left a comment

Choose a reason for hiding this comment

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

Looks good! I left some inline comments using my AI review helper (you're one of the beta testers — thanks for putting up with it 😄). Each comment links to a pre-written fix commit in #86 if you want to grab any of them.

- Fix duplicate URL fallback in makeLink (text === url → show once)
- Remove unused Table component and barrel export
- Derive ANSI heading colors from Brand tokens via hexToAnsi()
- Scope marked config to local Marked instance (no global mutation)
- Use raw URL as Stripe link children for consistent fallback
- Hoist gradient result to module-level constant in HeaderBar
- Cache raw content in ReportViewer to skip redundant re-renders
- Add optional color prop to TerminalLink for visual hierarchy
- Narrow LogViewer JSON detection to [{ and [" (avoid [timestamp] lines)

Co-Authored-By: Claude Opus 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.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: JSON detection never matches timestamped log lines
    • Added a regex to strip the [timestamp] prefix from log lines before checking if the content starts with JSON patterns.
  • ✅ Fixed: SlackScreen link fallback shows redundant URL text
    • Removed explicit children from both TerminalLink usages so the URL prop is used as display text, making the fallback render the URL only once.

Create PR

Or push these changes by commenting:

@cursor push e7f5e3cb8c
Preview (e7f5e3cb8c)
diff --git a/src/ui/tui/primitives/LogViewer.tsx b/src/ui/tui/primitives/LogViewer.tsx
--- a/src/ui/tui/primitives/LogViewer.tsx
+++ b/src/ui/tui/primitives/LogViewer.tsx
@@ -68,10 +68,12 @@
     <Box flexDirection="column" height={visibleLines}>
       {lines.map((line, i) => {
         const trimmed = line.trimStart();
+        // Strip `[timestamp] ` prefix written by logToFile() before checking content
+        const body = trimmed.replace(/^\[.*?\]\s*/, '');
         const isJson =
-          trimmed.startsWith('{') ||
-          trimmed.startsWith('[{') ||
-          trimmed.startsWith('["');
+          body.startsWith('{') ||
+          body.startsWith('[{') ||
+          body.startsWith('["');
         return (
           <Text
             key={i}

diff --git a/src/ui/tui/screens/SlackScreen.tsx b/src/ui/tui/screens/SlackScreen.tsx
--- a/src/ui/tui/screens/SlackScreen.tsx
+++ b/src/ui/tui/screens/SlackScreen.tsx
@@ -219,9 +219,7 @@
                 </Text>
                 <Text color={Colors.muted}>
                   Docs:{' '}
-                  <TerminalLink url="https://amplitude.com/docs/analytics/integrate-slack">
-                    amplitude.com/docs/analytics/integrate-slack
-                  </TerminalLink>
+                  <TerminalLink url="https://amplitude.com/docs/analytics/integrate-slack" />
                 </Text>
               </>
             ) : (
@@ -236,9 +234,7 @@
                 </Text>
                 <Text color={Colors.muted}>
                   Docs:{' '}
-                  <TerminalLink url="https://amplitude.com/docs/analytics/integrate-slack">
-                    amplitude.com/docs/analytics/integrate-slack
-                  </TerminalLink>
+                  <TerminalLink url="https://amplitude.com/docs/analytics/integrate-slack" />
                 </Text>
               </>
             )}

You can send follow-ups to the cloud agent here.

Comment thread src/ui/tui/primitives/LogViewer.tsx Outdated
Comment thread src/ui/tui/screens/SlackScreen.tsx Outdated
- LogViewer: strip [timestamp] prefix before checking for JSON content
- SlackScreen: use URL-only TerminalLink to avoid near-duplicate fallback

Co-Authored-By: Claude Opus 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.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: JSON highlighter receives non-JSON timestamp prefix
    • Changed LogViewer to extract the timestamp prefix separately and pass only the JSON content to highlightCode, rendering the prefix with muted styling.
  • ✅ Fixed: Stripe URL lost distinct accentSecondary color
    • Added color={Colors.accentSecondary} prop to the TerminalLink for the Stripe data source URL to restore the original lilac color.

Create PR

Or push these changes by commenting:

@cursor push 8abcf58e08
Preview (8abcf58e08)
diff --git a/src/ui/tui/primitives/LogViewer.tsx b/src/ui/tui/primitives/LogViewer.tsx
--- a/src/ui/tui/primitives/LogViewer.tsx
+++ b/src/ui/tui/primitives/LogViewer.tsx
@@ -67,8 +67,9 @@
   return (
     <Box flexDirection="column" height={visibleLines}>
       {lines.map((line, i) => {
-        // Strip optional [timestamp] prefix before checking for JSON content
-        const content = line.replace(/^\s*\[.*?\]\s*/, '');
+        const prefixMatch = line.match(/^(\s*\[.*?\]\s*)([\s\S]*)$/);
+        const prefix = prefixMatch ? prefixMatch[1] : '';
+        const content = prefixMatch ? prefixMatch[2] : line;
         const isJson =
           content.startsWith('{') ||
           content.startsWith('[{') ||
@@ -79,7 +80,14 @@
             color={isJson ? undefined : Colors.muted}
             wrap="truncate"
           >
-            {isJson ? highlightCode(line, 'json') : line}
+            {isJson ? (
+              <>
+                <Text color={Colors.muted}>{prefix}</Text>
+                {highlightCode(content, 'json')}
+              </>
+            ) : (
+              line
+            )}
           </Text>
         );
       })}

diff --git a/src/ui/tui/screens/RunScreen.tsx b/src/ui/tui/screens/RunScreen.tsx
--- a/src/ui/tui/screens/RunScreen.tsx
+++ b/src/ui/tui/screens/RunScreen.tsx
@@ -90,7 +90,10 @@
       <Text key="stripe" color={Colors.secondary}>
         <Text color={Colors.accent}>{Icons.diamond}</Text> Stripe detected
         {Icons.dash} add as data source:{' '}
-        <TerminalLink url={OUTBOUND_URLS.stripeDataSource}>
+        <TerminalLink
+          url={OUTBOUND_URLS.stripeDataSource}
+          color={Colors.accentSecondary}
+        >
           {OUTBOUND_URLS.stripeDataSource}
         </TerminalLink>
       </Text>,

You can send follow-ups to the cloud agent here.

Comment thread src/ui/tui/primitives/LogViewer.tsx Outdated
<TerminalLink url={OUTBOUND_URLS.stripeDataSource}>
{OUTBOUND_URLS.stripeDataSource}
</Text>
</TerminalLink>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stripe URL lost distinct accentSecondary color

Low Severity

The Stripe data source URL previously rendered with Colors.accentSecondary (lilac) to visually distinguish it from primary accent links. Switching to TerminalLink without passing a color prop applies the default Colors.accent (blue), flattening the visual hierarchy. The TerminalLink component already supports a color prop — it just isn't used here.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eeb4b31. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — the Stripe URL previously used Colors.accentSecondary. TerminalLink now accepts a color prop (added in a27afab), so this can be set if needed. For now keeping it consistent with all other links at Colors.accent.

The JSON detection in LogViewer never worked — the wizard log format
is [timestamp] [module] message with multi-line JSON bodies, so
single-line detection never matched.

Replace with level-based coloring: error/failed → red, warn → amber,
succeeded/completed → green. Multi-line entries (JSON bodies, stack
traces) inherit the color of their parent timestamp line via a
carry-forward pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kelsonpw kelsonpw merged commit 0d24d93 into main Apr 16, 2026
12 checks passed
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.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Exported highlightCode function is never used
    • Removed the unused highlightCode function, its cli-highlight import, and the direct cli-highlight dependency from package.json.
  • ✅ Fixed: Success regex misses common "success" keyword variants
    • Extended SUCCESS_RE to also match "success", "successful", and "successfully" via the pattern \bsuccess(?:ful(?:ly)?)?\b.

Create PR

Or push these changes by commenting:

@cursor push 85e30271a4
Preview (85e30271a4)
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -47,7 +47,6 @@
     "@pavus/snake-game": "^1.1.1",
     "axios": "1.13.5",
     "chalk": "^2.4.1",
-    "cli-highlight": "^2.1.11",
     "client-oauth2": "^4.3.3",
     "dotenv": "^16.4.7",
     "fast-glob": "^3.3.3",

diff --git a/src/ui/tui/primitives/LogViewer.tsx b/src/ui/tui/primitives/LogViewer.tsx
--- a/src/ui/tui/primitives/LogViewer.tsx
+++ b/src/ui/tui/primitives/LogViewer.tsx
@@ -19,7 +19,7 @@
 const TIMESTAMP_RE = /^\[/;
 const ERROR_RE = /\berror\b|\bfail(?:ed)?\b/i;
 const WARN_RE = /\bwarn(?:ing)?\b/i;
-const SUCCESS_RE = /\bsucceed(?:ed)?\b|\bcompleted?\b/i;
+const SUCCESS_RE = /\bsuccess(?:ful(?:ly)?)?\b|\bsucceed(?:ed)?\b|\bcompleted?\b/i;
 
 function getLineColor(line: string): string | null {
   if (ERROR_RE.test(line)) return Colors.error;

diff --git a/src/ui/tui/utils/terminal-rendering.ts b/src/ui/tui/utils/terminal-rendering.ts
--- a/src/ui/tui/utils/terminal-rendering.ts
+++ b/src/ui/tui/utils/terminal-rendering.ts
@@ -7,7 +7,6 @@
 
 import terminalLink from 'terminal-link';
 import gradient from 'gradient-string';
-import { highlight } from 'cli-highlight';
 import { Marked } from 'marked';
 import { markedTerminal } from 'marked-terminal';
 import { Brand } from '../styles.js';
@@ -34,20 +33,6 @@
   return brandGrad(text);
 }
 
-// ── Syntax highlighting ────────────────────────────────────────────────
-
-/**
- * Syntax-highlight a code string for terminal display.
- * Returns the original string if highlighting fails.
- */
-export function highlightCode(code: string, language?: string): string {
-  try {
-    return highlight(code, { language, ignoreIllegals: true });
-  } catch {
-    return code;
-  }
-}
-
 // ── Markdown rendering ─────────────────────────────────────────────────
 
 /** Convert a hex color to an ANSI 24-bit foreground escape sequence. */

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit f0d441c. Configure here.

} catch {
return code;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Exported highlightCode function is never used

Medium Severity

The highlightCode function is exported from terminal-rendering.ts but never imported or called anywhere in the codebase. The PR description mentions "JSON syntax highlighting in LogViewer," but LogViewer only uses the newly added color-coding by log level — it never calls highlightCode. This also makes the direct cli-highlight dependency in package.json unnecessary, since marked-terminal already pulls it in transitively. Both the function and the direct dependency are dead code.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f0d441c. Configure here.

const TIMESTAMP_RE = /^\[/;
const ERROR_RE = /\berror\b|\bfail(?:ed)?\b/i;
const WARN_RE = /\bwarn(?:ing)?\b/i;
const SUCCESS_RE = /\bsucceed(?:ed)?\b|\bcompleted?\b/i;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Success regex misses common "success" keyword variants

Low Severity

SUCCESS_RE matches "succeed"/"succeeded" and "complete"/"completed" but doesn't match "success", "successful", or "successfully" — arguably the most common success indicators in log output (e.g., "Build successful", "Successfully compiled"). This means many success-indicating log lines won't get the green color treatment, undermining the purpose of the color-coding feature.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f0d441c. Configure here.

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