Glamourous agentic coding for all 💘
This report summarizes scanner evidence for charmbracelet/crush, a Go project with 28,894 GitHub stars. SynthScan examined 208,171 lines of code across 924 source files, recording 325 pattern matches distributed across 5 syntactic categories. The legacy adjusted pattern signal is 2.3 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 290 finding(s).
Are imports declared and package/API names verified? 35 finding(s).
Are wrappers, guards, and repeated structures necessary? 0 finding(s).
Which changes need focused human review? 325 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 325 distinct pattern matches across 5 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .golangci.yml | 1 | version: "2" | COMMENT |
| LOW | internal/ui/anim/anim.go | 21 | fps = 20 | COMMENT |
| LOW | internal/ui/chat/applyhighlight_callback_test.go | 61 | COMMENT | |
| LOW | internal/ui/chat/assistant_thinking_window_test.go | 21 | // threshold deterministically. Each paragraph is tagged with its | COMMENT |
| LOW | internal/ui/chat/assistant_thinking_window_test.go | 101 | // width and is not safe for concurrent Render calls). | COMMENT |
| LOW | internal/ui/chat/assistant_thinking_window_test.go | 301 | // thinking block sits in tail-window mode must NOT invalidate the | COMMENT |
| LOW | internal/ui/chat/assistant.go | 21 | // truncated in the collapsed state. | COMMENT |
| LOW | internal/ui/chat/assistant.go | 41 | refusalTitle = "Model refused to continue" | COMMENT |
| LOW | internal/ui/chat/assistant.go | 181 | message *message.Message | COMMENT |
| LOW | internal/ui/chat/assistant.go | 201 | thinkingHashLen int | COMMENT |
| LOW | internal/ui/chat/assistant.go | 301 | return highlightedContent + spinner | COMMENT |
| LOW | internal/ui/chat/assistant.go | 681 | } | COMMENT |
| LOW | internal/ui/chat/assistant.go | 801 | isFinished := a.message.IsFinished() | COMMENT |
| LOW | internal/ui/chat/assistant.go | 821 | // cache valid while a changed section forces a miss naturally. | COMMENT |
| LOW | internal/ui/chat/assistant.go | 881 | a.thinkingViewMode = thinkingFullExpanded | COMMENT |
| LOW | internal/ui/chat/assistant.go | 901 | // underlying length), and re-running glamour just to count lines | COMMENT |
| LOW | internal/ui/chat/messages.go | 201 | // | COMMENT |
| LOW | internal/ui/chat/user_render_test.go | 41 | // TestUserMessagePreservesSingleLineBreaks is the regression test for | COMMENT |
| LOW | internal/ui/chat/agent.go | 101 | } | COMMENT |
| LOW | internal/ui/chat/incremental_glamour_test.go | 61 | } | COMMENT |
| LOW | internal/ui/chat/incremental_glamour_test.go | 261 | want: len("Para.\n\n"), | COMMENT |
| LOW | internal/ui/chat/incremental_glamour_test.go | 561 | fresh := freshRender(t, doc, width) | COMMENT |
| LOW | internal/ui/chat/incremental_glamour_test.go | 641 | COMMENT | |
| LOW | internal/ui/chat/incremental_glamour_test.go | 661 | // ----------------------------------------------------------------------- | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 1 | package chat | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 21 | // between calls. The boundary check is therefore deliberately | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 41 | // without re-scanning the entire prefix from the start. | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 181 | // already paid the cost of a full render of `content`; if there is | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 241 | // No new boundary found after the stable prefix. Return the | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 261 | // pass. | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 441 | // returned offset always points immediately after a blank-line | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 461 | // reasoning behind these "anywhere in prefix" rejects. | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 481 | // Iterate every blank-line position from latest to earliest. | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 581 | } | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 601 | // forces -1. This is overly conservative — it forfeits | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 621 | // "[label]: <url>" defines a link reference that the suffix | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 641 | // B1 (loose lists, refined). A loose list has a blank line between | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 661 | // that ever contained a list — the dominant case for LLM | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 721 | // appeared earlier AND the last non-blank line is indented but | COMMENT |
| LOW | internal/ui/chat/streaming_markdown.go | 981 | return true | COMMENT |
| LOW | internal/ui/notification/notification.go | 1 | // Package notification provides desktop notification support for the UI. | COMMENT |
| LOW | internal/ui/notification/native_stub.go | 1 | //go:build solaris || illumos | COMMENT |
| LOW | internal/ui/dialog/inline_editor.go | 81 | // MouseClickableEditor is an optional interface for inline editors | COMMENT |
| LOW | internal/ui/dialog/inline_editor.go | 101 | // HandleMouseDown begins a selection when the press lands in selectable | COMMENT |
| LOW | internal/ui/dialog/mcp_toggles.go | 21 | // Disabled is the repository-scoped override: when true the server's | COMMENT |
| LOW | internal/ui/dialog/common.go | 41 | // so callers don't have to repeat the arithmetic. The scrollbar column is | COMMENT |
| LOW | internal/ui/dialog/common.go | 281 | // and paddings. | COMMENT |
| LOW | internal/ui/styles/host_theme.go | 181 | } | COMMENT |
| LOW | internal/ui/common/markdown.go | 21 | const formatterName = "crush" | COMMENT |
| LOW | internal/ui/common/markdown.go | 141 | ) | COMMENT |
| LOW | internal/ui/common/markdown.go | 161 | glamour.WithStyles(codeBlockConfig(sty.Markdown)), | COMMENT |
| LOW | internal/ui/common/markdown.go | 241 | // AND every per-renderer mutex in a single atomic critical | COMMENT |
| LOW | internal/ui/common/markdown.go | 261 | planMDCache = map[int]*glamour.TermRenderer{} | COMMENT |
| LOW | internal/ui/common/markdown.go | 281 | // stable for the lifetime of the renderer (i.e. until | COMMENT |
| LOW | internal/ui/common/common.go | 261 | func CopyToClipboard(text, successMessage string) tea.Cmd { | COMMENT |
| LOW | internal/ui/common/capabilities.go | 21 | // Columns is the number of character columns in the terminal. | COMMENT |
| LOW | internal/ui/list/list.go | 41 | totalHeightValid bool | COMMENT |
| LOW | internal/ui/list/list.go | 301 | } | COMMENT |
| LOW | internal/ui/list/item.go | 1 | package list | COMMENT |
| LOW | internal/ui/list/item.go | 21 | type Item interface { | COMMENT |
| 212 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | internal/ui/dialog/theme_editor.go | 305 | line = ed.com.Styles.Dialog.NormalItem.Width(width).Render(label + ed.input.View()) | CODE |
| CRITICAL | internal/ui/dialog/theme_editor.go | 309 | line = ed.com.Styles.Dialog.NormalItem.Width(width).Render(label) | CODE |
| CRITICAL | internal/ui/dialog/plan_handoff_inline.go | 235 | question := iconPrompt + p.com.Styles.Editor.QuestionUnselected.Render( | CODE |
| CRITICAL | internal/ui/dialog/plan_handoff_inline.go | 315 | questionText := p.com.Styles.Editor.QuestionUnselected.Render( | CODE |
| CRITICAL | internal/ui/dialog/plan_handoff_inline.go | 321 | promptPrefix := p.com.Styles.Editor.QuestionBody.Render("> ") | CODE |
| CRITICAL | internal/ui/dialog/plan_handoff_inline.go | 363 | promptPrefix := p.com.Styles.Editor.QuestionBody.Render("> ") | CODE |
| CRITICAL | internal/ui/dialog/quit_mouse_test.go | 180 | q.com.Styles.Dialog.Quit.Hint.Render(quitHintLineOne), | CODE |
| CRITICAL | internal/ui/dialog/quit_mouse_test.go | 181 | q.com.Styles.Dialog.Quit.Hint.Render(quitHintLineTwo), | CODE |
| CRITICAL | internal/ui/dialog/models_item.go | 49 | configuredText := m.t.Dialog.Models.ConfiguredText.Render("Configured") | CODE |
| CRITICAL | internal/ui/dialog/commands.go | 308 | innerWidth := width - c.com.Styles.Dialog.View.GetHorizontalFrameSize() | CODE |
| CRITICAL | internal/ui/model/status_test.go | 48 | wantHintsCol := badgeLeftInset + lipgloss.Width(badge) + 1 + u.com.Styles.Status.Help.GetPaddingLeft() | CODE |
| CRITICAL | internal/ui/model/status_test.go | 70 | wantCol := u.com.Styles.Status.Help.GetPaddingLeft() | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | internal/ui/chat/tools.go | 1741 | // formatAgenticFetchResultForCopy formats agentic fetch tool results for clipboard. | COMMENT |
| MEDIUM | internal/ui/chat/agent.go | 212 | // AgenticFetchToolMessageItem is a message item that represents an agentic fetch tool call. | COMMENT |
| MEDIUM | internal/ui/chat/agent.go | 233 | // For the agentic fetch tool we keep spinning until the tool call is finished. | COMMENT |
| MEDIUM | internal/ui/chat/agent.go | 290 | // AgenticFetchToolRenderContext renders agentic fetch tool messages. | COMMENT |
| MEDIUM | internal/ui/chat/version_bump_test.go | 301 | // the agentic-fetch counterpart to the agent-tool nested mutator | COMMENT |
| MEDIUM | internal/ui/chat/version_bump_test.go | 369 | // is the agentic-fetch counterpart of the T5 regression test. | COMMENT |
| MEDIUM | internal/ui/chat/version_bump_test.go | 479 | // agentic-fetch counterpart of the agent-tool Advance bump test. | COMMENT |
| MEDIUM | internal/ui/dialog/question_single.go | 18 | // rendering scaffold. | COMMENT |
| MEDIUM | internal/ui/dialog/question_multi.go | 18 | // rendering scaffold. | COMMENT |
| MEDIUM | internal/server/events.go | 270 | // (agent tool, agentic fetch) resolve to the top-level session so the | COMMENT |
| MEDIUM | internal/backend/events.go | 47 | // agentic fetch) carry derived IDs; their interactive prompts are | COMMENT |
| MEDIUM | internal/agent/tools/fetch_types.go | 3 | // AgenticFetchToolName is the name of the agentic fetch tool. | COMMENT |
| MEDIUM | internal/agent/tools/fetch_types.go | 15 | // AgenticFetchParams defines the parameters for the agentic fetch tool. | COMMENT |
| MEDIUM | internal/agent/tools/fetch_types.go | 21 | // AgenticFetchPermissionsParams defines the permission parameters for the agentic fetch tool. | COMMENT |
| MEDIUM | internal/agent/agenttest/coordinator.go | 61 | // Keep buildTools light: no sub-agent or agentic-fetch construction. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | internal/ui/chat/assistant_plan_test.go | 85 | paragraph := strings.TrimSpace(strings.Repeat("lorem ipsum dolor sit amet ", 30)) | CODE |
| LOW | internal/ui/chat/assistant_plan_test.go | 85 | paragraph := strings.TrimSpace(strings.Repeat("lorem ipsum dolor sit amet ", 30)) | CODE |
| LOW | internal/gitutil/branch_test.go | 37 | runGit(t, testDir, "config", "user.email", "test@test.com") | CODE |
| LOW | internal/gitutil/branch_test.go | 61 | runGit(t, testDir, "config", "user.email", "test@test.com") | CODE |
| LOW | internal/gitutil/branch_test.go | 77 | runGit(t, testDir, "config", "user.email", "test@test.com") | CODE |
| LOW | internal/gitutil/branch_test.go | 107 | runGit(t, testDir, "config", "user.email", "test@test.com") | CODE |
| LOW | internal/gitutil/branch_test.go | 126 | runGit(t, testDir, "config", "user.email", "test@test.com") | CODE |
| LOW | internal/agent/agent_test.go | 267 | Prompt: "fetch the content from https://example-files.online-convert.com/website/html/example.html and tel | CODE |
| LOW⚡ | …testdata/TestCoderAgent/deepseek-v4/download_tool.yaml | 118 | The names "John Doe" for males, "Jane Doe" or "Jane Roe" for females, or "Jonnie Doe" and "Janie Doe" for children | CODE |
| LOW⚡ | …testdata/TestCoderAgent/deepseek-v4/download_tool.yaml | 118 | The names "John Doe" for males, "Jane Doe" or "Jane Roe" for females, or "Jonnie Doe" and "Janie Doe" for children | CODE |
| LOW⚡ | …testdata/TestCoderAgent/deepseek-v4/download_tool.yaml | 122 | Similarly, a child or baby whose identity is unknown may be referred to as Baby Doe. A notorious murder case in Ka | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 11 | body: "{\"messages\":[{\"content\":\"You are Crush, a powerful AI Assistant that runs in the CLI.\\n\\n<critical_rul | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 141 | The names "<b>John Doe</b>" for males, "<b>Jane Doe</b>" or "<b>Jane Roe</b>" for females, or "<b>Jonnie Doe</b>" | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 145 | <p>Similarly, a child or baby whose identity is unknown may be referred to as <b>Baby Doe</b>. A notorious murder | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 174 | body: '{"messages":[{"content":"You will generate a short title based on the first message a user begins a conversat | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 217 | data: {"id":"chatcmpl-1e002bd9edb812a509239e0a","object":"chat.completion.chunk","created":1793374738,"model":"dee | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 286 | body: "{\"messages\":[{\"content\":\"You are Crush, a powerful AI Assistant that runs in the CLI.\\n\\n<critical_rul | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 355 | data: {"id":"chatcmpl-2ad876bd452b01ec15e58685","object":"chat.completion.chunk","created":1793374741,"model":"dee | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 486 | The names "<b>John Doe</b>" for males, "<b>Jane Doe</b>" or "<b>Jane Roe</b>" for females, or "<b>Jonnie Doe</b>" | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 490 | <p>Similarly, a child or baby whose identity is unknown may be referred to as <b>Baby Doe</b>. A notorious murder | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 519 | body: "{\"messages\":[{\"content\":\"You are Crush, a powerful AI Assistant that runs in the CLI.\\n\\n<critical_rul | CODE |
| LOW | …nt/testdata/TestCoderAgent/deepseek-v4/fetch_tool.yaml | 546 | data: {"id":"chatcmpl-e1b0eb11bc56d35818b46ff0","object":"chat.completion.chunk","created":1793374745,"model":"dee | CODE |
| LOW | internal/agent/prompt/git_test.go | 34 | runGit(t, dir, "config", "user.email", "test@test.com") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | internal/ui/chat/assistant_thinking_window_test.go | 107 | // any future style-driven padding so the test is robust to | COMMENT |
| MEDIUM | internal/shell/dispatch_test.go | 309 | // if bash isn't available (rare in CI, but keep the test robust). | COMMENT |
| MEDIUM | internal/shell/dispatch.go | 85 | // on the raw string to stay robust if a future change ever bypasses that | COMMENT |