cherry-electron-dev
Develop, fix, and profile Cherry Studio in a tracked Electron instance. Use for everyday implementation, UI and interaction work, bug fixing, runtime debugging, DevTools inspection, lag or jank investigation, CPU and memory monitoring, leak checks, and startup-performance analysi
Install
npx skills add https://github.com/CherryHQ/cherry-studio/tree/main/.agents/skills/cherry-electron-dev
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install cherryhq-cherry-studio@llmmart
git clone https://github.com/CherryHQ/cherry-studio.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole cherryhq/cherry-studio collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Cherry Studio Development
Use this skill for ongoing work in the current checkout. Do not use it to check
out or report on PRs; use cherry-pr-test for that workflow.
Required runtime workflow
Before reading or controlling Electron UI, read
Electron Instance Management and use its
persistent policy.
That reference is the only authority for instance discovery, instance.json,
CDP target selection, launching, replacement, shutdown, and troubleshooting.
Do not reproduce those procedures here or substitute generic Electron app
control.
Development loop
- State the requested behavior and the evidence that will prove it.
- Read the relevant code and nearby README files.
- Verify and reuse the tracked instance through the runtime reference.
- Reproduce or inspect the current behavior before editing when practical.
- Capture the smallest useful evidence: UI state, DOM, console/network output, main-process logs, persisted state, or performance metrics.
- Trace the responsible code path and make only the requested change.
- Keep Electron running. Use HMR for renderer changes and verify in the same window.
- Repeat the same scenario and compare before/after evidence.
Inspect the real window at the relevant size and theme for UI work. Check both renderer and main-process evidence for renderer failures.
Restart only for a non-reloadable layer, crash, unreliable runtime state, or
startup profiling. Use the reference's exact-instance replacement procedure,
refresh instance.json, and keep the replacement running.
Run the narrowest relevant validation and follow current user and repository instructions for lint, formatting, and tests.
Performance and DevTools
For lag, jank, high CPU, memory growth, leaks, slow startup, or explicit DevTools use, read Performance Debugging.
Store temporary logs, screenshots, and profiles under
.context/cherry-electron-dev/. Compare a quiet baseline with the same bounded
scenario before and after a fix. Detach profiling sessions afterward; do not
close the CDP browser, page, or Electron process.
Handoff
Leave reused user-owned and healthy agent-launched instances running after the instruction. Stop one only when the user asks, a required restart is part of the task, or the instance is unhealthy and blocks progress.
Report the verified PID, whether it remains running, CDP port, tracking file, evidence paths, reproduction, and verification.
Files (cherry-studio)
-
references
-
electron-instance.md 7.9 KB
# Electron Instance Management Use this as the single runtime procedure for both `cherry-electron-dev` and `cherry-pr-test`. ## Contents - Session policy and scratch data - Verify or discover an instance - Bind CDP to the exact target - Gracefully replace an instance - Start and track a debug instance - Finish or recover - Troubleshooting ## Session policy and scratch data Choose the caller's policy before touching Electron: | Policy | Use | Finish | | --- | --- | --- | | `persistent` | Ongoing development | Leave a healthy instance running | | `ephemeral` | A bounded PR test | Stop only the instance started by this test | Treat pre-existing processes as user-owned. Preserve `userData`, databases, caches, and preferences unless the user explicitly requests a reset. Use `.context/cherry-electron-dev/` for runtime state and artifacts. The repository ignores this path, and `mkdir -p` creates it in Conductor and ordinary clones: ```bash mkdir -p .context/cherry-electron-dev ``` Track the active instance in `.context/cherry-electron-dev/instance.json` with: - workspace path and Git HEAD at launch - `persistent` or `ephemeral` policy and `user` or `agent` ownership - `launch_purpose` (`development`, `pr-test:<number>`, or `external`) - `workflow_relation` (`started` or `borrowed`) - Electron PID, runner PID/session, and process group - CDP and main-process inspector ports - exact main-window target URL - development profile suffix - launch command, log path, and start time The file is a hint, not proof. Revalidate it before every UI operation. At the start of a new workflow, an already-running instance is `borrowed` even when the same agent launched it during an earlier instruction. ## Verify or discover an instance Reuse the record only when all checks pass: 1. The Electron PID is alive. 2. Its cwd is the exact current workspace. 3. The recorded CDP listener belongs to that PID. 4. `/json/list` contains the recorded Cherry Studio target. 5. When exact checked-out code matters, the launch Git HEAD matches current HEAD; otherwise restart instead of relying on HMR for main-process changes. ```bash lsof -a -p <ELECTRON_PID> -d cwd -Fn lsof -nP -a -p <ELECTRON_PID> -iTCP:<CDP_PORT> -sTCP:LISTEN curl -fsS http://127.0.0.1:<CDP_PORT>/json/version | jq . curl -fsS http://127.0.0.1:<CDP_PORT>/json/list | \ jq '.[] | {id, type, title, url}' ``` If the record is missing or stale, discover before launching: ```bash ps -axo pid=,ppid=,pgid=,command= | \ rg -i 'Cherry Studio|CherryStudio|electron-vite|remote-debugging-port' lsof -nP -iTCP -sTCP:LISTEN | rg 'Electron|Cherry|:9222|:5173' lsof -a -p <ELECTRON_PID> -d cwd -Fn ps -o pid=,ppid=,pgid=,command= -p <PID>,<PARENT_PID> ``` Ignore candidates that cannot be tied to this workspace. A packaged app and another checkout are not interchangeable. Never infer ownership from an open port alone. If a candidate passes every applicable check, adopt it immediately instead of launching: 1. Attach to its verified CDP endpoint. 2. Write or refresh `instance.json` with its live identity and `workflow_relation: borrowed`. 3. Preserve a trustworthy existing policy and ownership. Without a trustworthy record, treat a pre-existing process as `user` owned and `persistent`. 4. End the launch path. Start another instance only when no suitable candidate exists or replacement is required. ## Bind CDP to the exact target Use the verified CDP endpoint exclusively. List targets and match URL and title; never assume target index `0`. The normal main target is titled `Cherry Studio` and uses: ```text http://localhost:5173/windows/main/index.html ``` If the dev server selects another port, require the same `/windows/main/index.html` path and record the exact URL. Re-list after windows open or close. Do not navigate a target unless navigation is part of the test. Use Playwright/CDP or optional `agent-browser`. Do not install a global CLI just for a task, and do not launch another instance because one controller is unavailable. Never pass `Electron`, `com.github.Electron`, or a `node_modules` Electron.app path to Computer Use or another app-control API. Shared Electron identifiers can launch or select the wrong checkout. ## Gracefully replace an instance Replace only when current-workspace code or required CDP access is unavailable. Before replacing a user-owned process, explain why and record its PID, command, cwd, parent/runner, PGID, and ports. Send `SIGTERM` to the exact Electron main PID and wait up to eight seconds: ```bash kill -TERM <ELECTRON_PID> for _ in $(seq 1 16); do kill -0 <ELECTRON_PID> 2>/dev/null || break sleep 0.5 done kill -0 <ELECTRON_PID> 2>/dev/null && echo "still running" ``` If Electron exits but its verified same-workspace runner remains, terminate that PID separately. Never use broad `pkill`, kill arbitrary port owners, or signal a process group before inspecting every member. Do not escalate automatically to `SIGKILL`. Report the remaining PID and logs and ask before forcing a process that may be migrating, backing up, or preventing quit. Verify the old PID and ports are gone before replacement. ## Start and track a debug instance Read `package.json` for the current debug command. If its default CDP or inspector port belongs to an unrelated process, choose free ports and change only those arguments; do not kill the owner. Prefer a managed terminal session. With a terminal tool such as `exec_command`, request a PTY and short initial yield, retain its returned session ID, and let this command continue: ```bash pnpm debug 2>&1 | tee .context/cherry-electron-dev/electron.log ``` Do not run that command as an ordinary blocking call. If no managed-session tool exists, use a recorded background runner: ```bash nohup pnpm debug >.context/cherry-electron-dev/electron.log 2>&1 & echo $! ``` Immediately record the returned runner PID. Resolve and record the real Electron PID and PGID after launch. Keep the existing development profile for `persistent` work. For an isolated PR test, set a distinct `CS_DEV_USER_DATA_SUFFIX` and record it. Wait for the selected CDP endpoint, then identify and record the exact target: ```bash for _ in $(seq 1 60); do curl -fsS http://127.0.0.1:<CDP_PORT>/json/version >/dev/null && break sleep 0.5 done curl -fsS http://127.0.0.1:<CDP_PORT>/json/list | \ jq '.[] | {id, type, title, url}' ``` Write `instance.json` only after PID, cwd, listener ownership, and target checks pass. Give a new instance `workflow_relation: started`; never reclassify a borrowed instance as newly started. ## Finish or recover For `persistent`, leave healthy user-owned and agent-owned instances running. For `ephemeral`, stop an instance only when all of these are true: 1. Its verified record has `workflow_relation: started`. 2. Its verified record still says `ephemeral` and `agent` owned. 3. Its PID, cwd, and launch purpose still match the current test. Leave every borrowed instance running, regardless of whether its existing ownership is `user` or `agent`. If an unexpected window or PID appears: 1. Stop UI actions. 2. Identify only the new PID by command and cwd. 3. Gracefully stop that verified unexpected PID. 4. Revalidate the tracked PID, cwd, CDP listener, and target. 5. Resume only through the tracked CDP endpoint. Never close all Electron processes to recover from a targeting mistake. ## Troubleshooting | Symptom | Action | | --- | --- | | CDP works but the page is missing | Re-list targets and match URL/title; splash, migration, settings, detached tabs, and mini-apps are separate targets. | | Debug launch exits | Inspect the recorded log for a profile lock, native rebuild, database/startup failure, or port collision; confirm the old PID exited. | | Splash or migration is stuck | Read startup logs and wait; do not bypass, reset, or force-close without understanding its phase. | | CDP automation is unavailable | Use logs/source when sufficient. If UI evidence is essential, explain why and use the replacement procedure; never fall back to generic Electron control. | -
performance-debugging.md 6.3 KB
# Performance Debugging Use this reference for Cherry Studio lag, jank, CPU, memory, leak, startup, and DevTools investigations. ## Contents - Bind safely and choose a profile - Quick renderer metrics - Interaction trace - Memory and allocation - Process and main-process checks - Startup analysis - Interpretation and reporting ## Bind safely and choose a profile First read [Electron Instance Management](electron-instance.md) and complete its PID, workspace, CDP, and target checks. Never find or open a development instance through the macOS application name `Electron`. Connect Playwright to the recorded CDP port and exact main target: ```js var { chromium } = await import("playwright") var browser = await chromium.connectOverCDP("http://127.0.0.1:<CDP_PORT>") var page = browser .contexts() .flatMap((context) => context.pages()) .find((candidate) => candidate.url() === "<MAIN_TARGET_URL>") if (!page || (await page.title()) !== "Cherry Studio") { throw new Error("Bound Cherry Studio main target not found") } var cdp = await page.context().newCDPSession(page) ``` Never call `browser.close()`, `page.close()`, or an Electron quit action. Detach only the profiling `CDPSession`. For visible DevTools, open the selected target's frontend without replacing the bound session: ```text http://127.0.0.1:<CDP_PORT><devtoolsFrontendUrl> ``` Choose the smallest profile: | Question | Profile | | --- | --- | | Renderer load or memory level? | Quick metrics | | Cause of a visible stall? | 5-30 second trace | | Memory growth after repetition? | Repeated checkpoints | | Allocation source? | Allocation sampling | | Main/helper resource use? | Process sampling | | Slow startup? | Restart-aware startup analysis | Collect a quiet idle baseline. Keep action, duration, window size, route, data, and visible-DevTools state identical between comparisons. ## Quick renderer metrics ```js await cdp.send("Performance.enable") var beforeRaw = await cdp.send("Performance.getMetrics") var before = Object.fromEntries( beforeRaw.metrics.map(({ name, value }) => [name, value]) ) // Run one bounded scenario. var afterRaw = await cdp.send("Performance.getMetrics") var after = Object.fromEntries( afterRaw.metrics.map(({ name, value }) => [name, value]) ) ``` Compare deltas for cumulative counters: `TaskDuration`, `ScriptDuration`, `LayoutDuration`, `LayoutCount`, `RecalcStyleDuration`, `RecalcStyleCount`, and `V8CompileDuration`. Treat `JSHeapUsedSize`, `JSHeapTotalSize`, `Nodes`, `Documents`, `Frames`, and `JSEventListeners` as point-in-time gauges. Approximate renderer main-thread utilization for a window of `elapsedSeconds`: ```text 100 * delta(TaskDuration) / elapsedSeconds ``` This is orientation, not complete CPU usage. Repeat identical scenarios and compare medians when differences are small. ## Interaction trace Trace one reproducible action for 5-30 seconds: ```js var traceDone = new Promise((resolve) => cdp.once("Tracing.tracingComplete", resolve) ) await cdp.send("Tracing.start", { categories: [ "devtools.timeline", "v8", "blink.user_timing", "disabled-by-default-devtools.timeline" ].join(","), transferMode: "ReturnAsStream" }) // Perform the action. await cdp.send("Tracing.end") var { stream } = await traceDone var chunks = [] while (true) { var part = await cdp.send("IO.read", { handle: stream }) chunks.push(Buffer.from(part.data, part.base64Encoded ? "base64" : "utf8")) if (part.eof) break } await cdp.send("IO.close", { handle: stream }) var fs = await import("node:fs/promises") await fs.writeFile("<TRACE_PATH>.json", Buffer.concat(chunks)) ``` Correlate long tasks with script stacks, layout, paint, GC, and user timing. Keep raw traces under `.context`; they may contain private UI content or URLs. ## Memory and allocation For leak suspicion, record heap/nodes/documents/listeners at idle, repeat the same action a fixed number of times, return to the same idle state, and record again across multiple cycles. One larger heap value is not proof; V8 may defer GC. Do not force GC unless a post-GC comparison is explicitly needed. For bounded allocation attribution: ```js await cdp.send("HeapProfiler.enable") await cdp.send("HeapProfiler.startSampling", { samplingInterval: 32768 }) // Perform one bounded scenario. var { profile } = await cdp.send("HeapProfiler.stopSampling") var fs = await import("node:fs/promises") await fs.writeFile("<PROFILE_PATH>.json", JSON.stringify(profile)) ``` Use a full heap snapshot only when sampling and gauges are insufficient. Warn first: it can pause the renderer, be large, and contain private data. ## Process and main-process checks Sample the tracked process group several times before, during, and after: ```bash ps -axo pid=,ppid=,pgid=,%cpu=,rss=,command= | \ awk '$3 == <TRACKED_PGID>' ``` Separate Electron main, renderer/helper, Vite, and runner costs. If renderer metrics are quiet while main is busy, verify and attach to the recorded Node inspector target, normally port `9229`. Use a bounded CPU profile and correlate it with application logs. Do not confuse it with renderer CDP `9222`. ## Startup analysis Startup profiling requires a restart: 1. Explain and record the current instance/scenario. 2. Gracefully stop only the tracked instance. 3. Start the same debug command and profile. 4. Preserve startup logs and timestamps. 5. Measure navigation milestones such as `NavigationStart`, `DomContentLoaded`, and `FirstMeaningfulPaint`. 6. Update `instance.json` and keep the replacement running. Separate native rebuild, main bootstrap, database migration, service startup, renderer load, and first interactive UI. Do not treat the whole `pnpm debug` duration as app startup. ## Interpretation and reporting - Script-heavy `TaskDuration` suggests JavaScript/React work. - Layout/style deltas suggest DOM measurement or CSS invalidation. - Repeated long trace tasks identify likely jank sources. - Heap plus node/listener growth after identical idle cycles suggests a leak. - Quiet renderer plus high main CPU points to services or IPC. - High helper/GPU CPU without renderer task growth points to media, canvas, GPU, or embedded web content. Report exact PID/target/route/scenario/duration, baseline and scenario deltas, artifact paths, strongest evidence, uncertainty, and before/after comparison for a fix. Keep Electron running after collection unless restart was explicitly part of the profile.
-
-
SKILL.md 2.9 KB
--- name: cherry-electron-dev description: Develop, fix, and profile Cherry Studio in a tracked Electron instance. Use for everyday implementation, UI and interaction work, bug fixing, runtime debugging, DevTools inspection, lag or jank investigation, CPU and memory monitoring, leak checks, and startup-performance analysis; reuse a verified workspace instance across instructions and launch or replace one only when required. --- # Cherry Studio Development Use this skill for ongoing work in the current checkout. Do not use it to check out or report on PRs; use `cherry-pr-test` for that workflow. ## Required runtime workflow Before reading or controlling Electron UI, read [Electron Instance Management](references/electron-instance.md) and use its `persistent` policy. That reference is the only authority for instance discovery, `instance.json`, CDP target selection, launching, replacement, shutdown, and troubleshooting. Do not reproduce those procedures here or substitute generic Electron app control. ## Development loop 1. State the requested behavior and the evidence that will prove it. 2. Read the relevant code and nearby README files. 3. Verify and reuse the tracked instance through the runtime reference. 4. Reproduce or inspect the current behavior before editing when practical. 5. Capture the smallest useful evidence: UI state, DOM, console/network output, main-process logs, persisted state, or performance metrics. 6. Trace the responsible code path and make only the requested change. 7. Keep Electron running. Use HMR for renderer changes and verify in the same window. 8. Repeat the same scenario and compare before/after evidence. Inspect the real window at the relevant size and theme for UI work. Check both renderer and main-process evidence for renderer failures. Restart only for a non-reloadable layer, crash, unreliable runtime state, or startup profiling. Use the reference's exact-instance replacement procedure, refresh `instance.json`, and keep the replacement running. Run the narrowest relevant validation and follow current user and repository instructions for lint, formatting, and tests. ## Performance and DevTools For lag, jank, high CPU, memory growth, leaks, slow startup, or explicit DevTools use, read [Performance Debugging](references/performance-debugging.md). Store temporary logs, screenshots, and profiles under `.context/cherry-electron-dev/`. Compare a quiet baseline with the same bounded scenario before and after a fix. Detach profiling sessions afterward; do not close the CDP browser, page, or Electron process. ## Handoff Leave reused user-owned and healthy agent-launched instances running after the instruction. Stop one only when the user asks, a required restart is part of the task, or the instance is unhealthy and blocks progress. Report the verified PID, whether it remains running, CDP port, tracking file, evidence paths, reproduction, and verification.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.