🍒 This is the mobile version of Cherry Studio.
This report summarizes scanner evidence for CherryHQ/cherry-studio-app, a TypeScript project with 4,049 GitHub stars. SynthScan examined 530,696 lines of code across 3836 source files, recording 642 pattern matches distributed across 10 syntactic categories. The legacy adjusted pattern signal is 1.4 (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? 621 finding(s).
Are imports declared and package/API names verified? 6 finding(s).
Are wrappers, guards, and repeated structures necessary? 597 finding(s).
Which changes need focused human review? 642 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 642 distinct pattern matches across 10 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 | …ve-best-practices/references/native-memory-patterns.md | 145 | class DataManager { | CODE |
| LOW | …ive-best-practices/references/bundle-code-splitting.md | 126 | function getFirstPartyChunkBaseURL(scriptId, release) { | CODE |
| LOW | scripts/skillsCommon.ts | 57 | export function buildAgentsSkillsGitignore(skillNames: string[]): string { | CODE |
| LOW | scripts/skillsCommon.ts | 75 | export function buildClaudeSkillsGitignore(skillNames: string[]): string { | CODE |
| LOW | scripts/skillsCheck.ts | 67 | function checkTrackedFilesAgainstWhitelist(skillNames: string[], errors: string[]) { | CODE |
| LOW | scripts/generateBackgroundActivityLogos.ts | 11 | async function generateBackgroundActivityLogos() { | CODE |
| LOW | scripts/generateHeaderToolbarIcons.ts | 36 | async function generateHeaderToolbarIcons() { | CODE |
| LOW | packages/ui/src/utils/typography-scale.ts | 60 | export function createTypographyCSSVariables(value: unknown): Record<string, number> { | CODE |
| LOW | …/components/markdown-text/components/markdown-text.tsx | 75 | function createMarkdownTypographyStyle( | CODE |
| LOW | …ckages/ui/src/components/portal/overlay-navigation.tsx | 17 | export function OverlayNavigationProvider({ children }: PropsWithChildren) { | CODE |
| LOW | …ages/ui/src/components/portal/portal-accessibility.tsx | 14 | export function PortalAccessibilityBoundary({ children }: PropsWithChildren) { | CODE |
| LOW | …ages/ui/src/components/portal/portal-accessibility.tsx | 39 | export function usePortalBackgroundIsolation(active: boolean) { | CODE |
| LOW | …c/components/loading/image-generation-loader-effect.ts | 70 | export function getImageGenerationLoaderEffect(): SkRuntimeEffect { | CODE |
| LOW | …nts/scroll-interaction/scroll-interaction-boundary.tsx | 11 | export function ScrollInteractionBoundary({ | CODE |
| LOW | …/src/components/composer/utils/composer-dock-layout.ts | 17 | export function getComposerActionCenterOffset(bottomInset: number) { | CODE |
| LOW | …/src/components/composer/utils/composer-dock-layout.ts | 27 | export function getComposerKeyboardStickyOffset(bottomInset: number) { | CODE |
| LOW | …src/components/composer/components/composer-editor.tsx | 257 | export function ComposerEditorAccessories({ | CODE |
| LOW | …/components/composer/components/composer-expansion.tsx | 166 | export function ComposerExpansionBackground() { | CODE |
| LOW | …components/file-preview/utils/file-preview-registry.ts | 16 | export function createFilePreviewRegistry( | CODE |
| LOW | …components/file-preview/utils/file-preview-registry.ts | 31 | export function extendFilePreviewRegistry( | CODE |
| LOW | …look-thumbnail-cache/quick-look-thumbnail-cache.ios.ts | 17 | export function quickLookThumbnailCacheKey(input: QuickLookThumbnailInput): string { | CODE |
| LOW | …le-preview/components/file-preview-plugin-provider.tsx | 17 | export function FilePreviewPluginProvider({ | CODE |
| LOW | …/components/menu/context-menu/context-menu.android.tsx | 125 | function withMenuAccessibilityActions( | CODE |
| LOW | …rc/components/message-part/utils/message-part-value.ts | 24 | export function getMessagePartValueEntries(value: unknown): [string, unknown][] { | CODE |
| LOW | …onents/message-part/components/message-part-status.tsx | 18 | export function MessagePartStatusDensityScope({ | CODE |
| LOW | …onents/message-part/components/message-part-status.tsx | 69 | export function MessagePartStatusTextFloor() { | CODE |
| LOW | …ts/message-part/components/message-part-disclosure.tsx | 193 | function MessagePartDisclosureIcon({ isOpen }: { isOpen: boolean }) { | CODE |
| LOW | …onents/image-preview-toolbar/image-preview-toolbar.tsx | 13 | function ImagePreviewToolbarAction({ | CODE |
| LOW | packages/ui/src/icons/registry.ts | 25 | export function resolveModelToProviderIcon(modelId: string): IconSource | undefined { | CODE |
| LOW | …rc/background-activity/background-activity.preview.tsx | 47 | export function BackgroundActivityPreview(props: BackgroundActivityPreviewProps) { | CODE |
| LOW⚡ | packages/universal/src/utils/conversationTitle.ts | 5 | export function sanitizeConversationTitle(title: string): string { | CODE |
| LOW⚡ | packages/universal/src/utils/conversationTitle.ts | 9 | export function truncateFirstUserMessageTitleSource(text: string): string { | CODE |
| LOW⚡ | packages/universal/src/utils/conversationTitle.ts | 17 | export function buildFirstUserMessageTitle(userText: string): string { | CODE |
| LOW⚡ | packages/universal/src/utils/conversationTitle.ts | 21 | export function normalizeConversationTitle(title: string | null | undefined): string { | CODE |
| LOW | packages/universal/src/utils/url.ts | 103 | export function withoutTrailingApiVersion(baseURL = ''): string { | CODE |
| LOW | packages/universal/src/utils/url.ts | 167 | export function shouldAppendProviderApiVersion(provider?: { | CODE |
| LOW | packages/universal/src/ai/reasoning.ts | 65 | export function resolveReasoningEffortForModel( | CODE |
| LOW | packages/universal/src/ai/paintingGenerateError.ts | 51 | export function createPaintingGenerateError( | CODE |
| LOW | packages/universal/src/ai/paintingGenerateError.ts | 58 | export function normalizePaintingGenerateError(error: unknown): Error { | CODE |
| LOW | packages/universal/src/ai/tools/mcpToolName.ts | 74 | export function generateMcpToolFunctionName( | CODE |
| LOW | packages/universal/src/ai/tools/mcpToolName.ts | 114 | export function buildFunctionCallToolName(serverName: string, toolName: string): string { | CODE |
| LOW | packages/universal/src/ai/tools/mcpToolName.ts | 132 | export function parseFunctionCallToolName(toolName: string): McpFunctionCallToolNameParts | null { | CODE |
| LOW | packages/universal/src/ai/tools/mcpToolName.ts | 146 | export function isFunctionCallToolNameForServer(serverName: string, toolId: string): boolean { | CODE |
| LOW | packages/universal/src/data/types/aiUsageRecord.ts | 147 | export function getAiUsageRecordTotalTokens(record: AiUsageRecordTokenFields): number | null { | CODE |
| LOW | packages/design-tokens/scripts/sync-desktop.ts | 208 | function assertRadeonCloudVisualContract(source: string, sourcePath: string): void { | CODE |
| LOW | packages/design-tokens/scripts/sync-desktop.ts | 261 | async function buildCatalogOnlyProviderSource( | CODE |
| LOW | packages/ai-runtime/scripts/check-boundaries.ts | 44 | function referencedForbiddenGlobals(source: string, file: string): string[] { | CODE |
| LOW | packages/ai-runtime/scripts/check-boundaries.ts | 70 | export async function collectBoundaryViolations( | CODE |
| LOW | packages/ai-runtime/src/provider/zhipuWebSearch.ts | 10 | export function transformZhipuRequestBody(args: Record<string, any>): Record<string, any> { | CODE |
| LOW | packages/ai-runtime/src/provider/listModels.ts | 208 | function getOpenAIVoiceModelMetadata(modelId: string): Partial<Model> | undefined { | CODE |
| LOW | packages/ai-runtime/src/provider/factory.ts | 9 | export function registerProviderExtensions(): void { | CODE |
| LOW | packages/ai-runtime/src/provider/vertex.ts | 15 | export function normalizeVertexCredentials(credentials: Record<string, unknown> | undefined): { | CODE |
| LOW | packages/ai-runtime/src/provider/diagnostics.ts | 16 | export function emitProviderRuntimeDiagnostic( | CODE |
| LOW | packages/ai-runtime/src/provider/ark.ts | 9 | export function stripArkUnsupportedIncludes( | CODE |
| LOW | packages/ai-runtime/src/provider/listModels/vertex.ts | 50 | export async function createVertexModelListRequest( | CODE |
| LOW | packages/ai-runtime/src/provider/listModels/vertex.ts | 159 | export function isSupportedVertexPublisherModel(modelId: string): boolean { | CODE |
| LOW | …ai-runtime/src/provider/custom/imageGenerationModel.ts | 97 | export function createImageGenerationModel( | CODE |
| LOW | …-runtime/src/provider/custom/imageTransportRegistry.ts | 89 | export function isImageTransportDescriptorSupported( | CODE |
| LOW | …-runtime/src/provider/custom/wire/buildImageRequest.ts | 117 | export function buildVendorProviderOptions( | CODE |
| LOW | …ai-runtime/src/provider/custom/dmxapi/dmxapiRouting.ts | 59 | export function resolveDmxapiEndpointType(modelId: string): EndpointType { | CODE |
| 522 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | packages/universal/src/ai/builtinTools.ts | 12 | // ── web_search ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/universal/src/ai/builtinTools.ts | 77 | // ── report_artifacts ───────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | packages/universal/src/ai/builtinTools.ts | 114 | // ── generate_image ─────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | packages/universal/src/ai/builtinTools.ts | 123 | // ── agent autonomy tools (cron / notify / config) ──────────────── | COMMENT |
| MEDIUM⚡ | packages/universal/src/ai/builtinTools.ts | 132 | // ── read_file ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/universal/src/data/types/model.ts | 214 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | packages/universal/src/data/types/model.ts | 216 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | …-runtime/src/provider/custom/dmxapi/dmxapiTransport.ts | 239 | // ────────────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …-runtime/src/provider/custom/dmxapi/dmxapiTransport.ts | 241 | // ────────────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …ime/src/provider/custom/aihubmix/aihubmixImageModel.ts | 231 | // ── Doubao Seedream (OpenAI-compat custom family) ───────────────────────────── | COMMENT |
| MEDIUM | src/backend/ai/AiService.ts | 42 | // ── Request types ────────────────────────────────────────────────── | COMMENT |
| MEDIUM | src/backend/data/db/schemas/userModel.ts | 47 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/backend/data/db/schemas/userModel.ts | 49 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/backend/data/db/schemas/userModel.ts | 147 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/backend/data/db/schemas/userModel.ts | 149 | // ═══════════════════════════════════════════════════════════════════════════════ | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .agents/skills/react-native-best-practices/POWER.md | 11 | ## Step 1: Validate React Native Setup | COMMENT |
| LOW | …ive-best-practices/references/bundle-barrel-exports.md | 191 | ### Step 1: Identify Barrel Files | COMMENT |
| LOW | …ive-best-practices/references/bundle-barrel-exports.md | 200 | ### Step 2: Update Imports | COMMENT |
| LOW | …-native-best-practices/references/js-react-compiler.md | 43 | ### Step 1: Check Compatibility | COMMENT |
| LOW | …-native-best-practices/references/js-react-compiler.md | 53 | ### Step 2: Install React Compiler | COMMENT |
| LOW | …-native-best-practices/references/js-react-compiler.md | 93 | ### Step 3: Configure Babel (React Native without Expo) | COMMENT |
| LOW | …-native-best-practices/references/js-react-compiler.md | 115 | ### Step 4: Set Up ESLint (Recommended) | COMMENT |
| LOW | …-native-best-practices/references/js-react-compiler.md | 119 | ### Step 5: Verify Optimizations | COMMENT |
| LOW⚡ | …tive-best-practices/references/bundle-native-assets.md | 76 | ### Step 1: Create Asset Catalog | COMMENT |
| LOW⚡ | …tive-best-practices/references/bundle-native-assets.md | 86 | ### Step 2: Configure Build Phase | COMMENT |
| LOW⚡ | …tive-best-practices/references/bundle-native-assets.md | 96 | ### Step 3: Build | COMMENT |
| LOW | …tive-best-practices/references/bundle-native-assets.md | 116 | ### Step 4: Verify | COMMENT |
| LOW⚡ | src/backend/services/jobs/runtime/recovery.ts | 63 | // Step 1: a persisted cancel intent overrides every strategy. | COMMENT |
| LOW⚡ | src/backend/services/jobs/runtime/recovery.ts | 72 | // Step 2: per-handler strategy. | COMMENT |
| LOW | src/backend/services/jobs/runtime/recovery.ts | 104 | // Step 3: orphan sweep — types with no registered handler (e.g. removed in | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | …ckend/ai/agent/host/__tests__/DurableAgentHost.test.ts | 2014 | const interaction = state.reply.setInteraction.mock.calls.at(-1)?.[0]; | CODE |
| CRITICAL | …ckend/ai/agent/host/__tests__/DurableAgentHost.test.ts | 2129 | const interaction = state.reply.setInteraction.mock.calls.at(-1)?.[0]; | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | jest.config.js | 41 | // Local build/export artifacts can contain copied workspace packages. Keep | COMMENT |
| LOW | .agents/skills/diagnose/scripts/hitl-loop.template.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | .agents/skills/expo-tailwind-setup/SKILL.md | 101 | --font-rounded: ui-rounded; | COMMENT |
| LOW | …es/ui/src/components/composer/utils/composer-layout.ts | 21 | // circle already sits 4pt in from the edge, and matching that on a label reads as | COMMENT |
| LOW | …r/utils/composer-text-style/composer-text-style.ios.ts | 1 | import type { TextStyle } from 'react-native'; | COMMENT |
| LOW | …r/utils/composer-text-style/composer-text-style.ios.ts | 21 | // nothing here (verified by measurement, not by reading the docs). | COMMENT |
| LOW | packages/universal/src/ai/builtinTools.ts | 41 | COMMENT | |
| LOW | …ime/src/provider/__tests__/openaiImageUrlPatch.test.ts | 1 | import { OpenAIImageModel } from '@ai-sdk/openai/internal'; | COMMENT |
| LOW | …-runtime/src/provider/custom/wire/buildImageRequest.ts | 121 | vendorBag: Record<string, unknown> = {}, | COMMENT |
| LOW | modules/js-sandbox/cpp/JsSandbox.cpp | 1 | #include "JsSandbox.h" | COMMENT |
| LOW | modules/js-sandbox/cpp/QuickJs.c | 1 | // Compiles the vendored QuickJS-NG amalgamation (vendor/quickjs-ng) as one | COMMENT |
| LOW | modules/js-sandbox/cpp/QuickJs.c | 41 | } | COMMENT |
| LOW | modules/js-sandbox/cpp/JsSandbox.h | 1 | #pragma once | COMMENT |
| LOW | src/frontend/utils/constants.ts | 1 | import { | COMMENT |
| LOW | src/frontend/utils/constants.ts | 21 | // Duration is the only knob: the curve is compiled into the library, which is | COMMENT |
| LOW | src/backend/data/db/schemas/agentSessionMessage.ts | 61 | // Message role: user, assistant, system — no virtual root row | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/backend/services/jobs/runtime/recovery.ts | 63 | // Step 1: a persisted cancel intent overrides every strategy. | COMMENT |
| LOW⚡ | src/backend/services/jobs/runtime/recovery.ts | 72 | // Step 2: per-handler strategy. | COMMENT |
| LOW | src/backend/services/jobs/runtime/recovery.ts | 104 | // Step 3: orphan sweep — types with no registered handler (e.g. removed in | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/backend/services/builtInMcp/plugins/feishu/guide.ts | 52 | content: `## Create a Base record | CODE |
| MEDIUM | src/backend/services/builtInMcp/plugins/github/guide.ts | 107 | content: `## Create a remote branch | CODE |
| MEDIUM | src/backend/services/builtInMcp/plugins/github/guide.ts | 114 | content: `## Create a pull request | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …nts/skills/expo-deployment/references/ios-app-store.md | 167 | | Placeholder content | Remove "lorem ipsum" and test data | | CODE |
| LOW | …/expo/modules/systemintegration/BackgroundExecution.kt | 107 | val input = RemoteInput.Builder("reply").setLabel(reply["placeholder"] as? String) | CODE |
| LOW | …d/appShell/observability/__tests__/sentryEvent.test.ts | 84 | tags: { operation: 'request https://private.example.com', module: 'user@example.com' }, | CODE |
| LOW | …ntend/components/Composer/components/ComposerField.tsx | 27 | 'layout' | 'onBlur' | 'onFocus' | 'onLayout' | 'placeholder' | 'style' | 'testID' | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | …/frontend/appShell/sidebar/components/SidebarTitle.tsx | 63 | // The second copy sits exactly one lap behind, so snapping back to 0 is seamless. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .agents/skills/diagnose/scripts/hitl-loop.template.sh | 6 | # Usage: | COMMENT |