| name | launch-app |
| description | Use when the user asks to start, run, boot, restart, or verify the CodingX Electron development app in this repository. Trigger for phrases like "启动 app", "本地启动一下", "run the app", "open CodingX", or when debugging whether the renderer, Electron window, or automation server is up. This skill covers dependency checks, clean install choices, dev-server startup, port discovery, process verification, health checks, and common startup failures. |
CodingX Launch
Use this skill from the CodingX repository root. The app is an Electron desktop client with a Vite renderer and an optional automation HTTP server.
Preconditions
Before non-trivial development or startup diagnosis, read docs/development-context.md. It documents the dev script behavior, local data stores, IPC boundaries, and UI fidelity rules.
Use the project-local scripts instead of hand-running Vite or Electron separately unless you are isolating a failure:
npm run dev
Dependency Check
First check whether Node, npm, and dependencies are available:
node -v
npm -v
test -d node_modules && echo node_modules_present || echo node_modules_missing
If node_modules is missing, prefer the lockfile-preserving install:
npm ci
If npm ci fails for a local registry or lockfile reason, use:
npm install
After any install, check git status --short. If the only change is lockfile metadata churn caused by installation and the user's request was only to start the app, do not commit that churn. Restore only changes you made and do not discard unrelated user work.
If node or npm is not visible in the Codex shell, try a login shell or ensure Node is available via nvm or your system PATH:
npm run dev
If node is not found, source your nvm setup or use the full path to the installed Node binary.
Start Workflow
Start the app with a long-running terminal session:
npm run dev
The script performs these steps:
- Picks an available renderer port starting at
5173, unless CODINGX_DEV_PORT is set.
- Enables automation by default with
CODINGX_AUTOMATION=1.
- Picks an available automation port starting at
47321, unless CODINGX_AUTOMATION_PORT is set.
- Typechecks/builds the Electron main process with
tsc -p tsconfig.electron.json.
- Starts Vite on
127.0.0.1.
- Waits for the renderer TCP port.
- Starts Electron with
VITE_DEV_SERVER_URL pointing at the chosen renderer URL.
Expected startup output includes lines like:
[dev] instance dev-xxxxxxxx
[dev] renderer http://127.0.0.1:5173
[dev] automation http://127.0.0.1:47321
VITE v... ready
On a fresh install, Electron may print Downloading Electron binary...; wait for it to finish. Do not report failure only because the download takes time.
Verification
Confirm the renderer responds:
curl -sS -I http://127.0.0.1:5173/ | head
Use the actual renderer port from startup output if it is not 5173.
Confirm automation if enabled:
curl -sS http://127.0.0.1:47321/automation/health
Use the actual automation port from startup output if it is not 47321. A healthy response includes "status":"ok" and a list of automation commands.
Confirm Electron is running:
ps aux | rg 'node_modules/electron|scripts/dev.mjs|vite' | rg -v rg
Report the renderer URL, automation URL, and whether Electron processes are present.
Interacting With the App (UI Automation Protocol)
IMPORTANT: Do NOT use chrome-devtools MCP or any browser-based tools (navigate_page, take_snapshot, click, etc.) for this app. Always use the project's built-in automation HTTP protocol at POST /automation.
The automation server exposes a JSON-RPC-style HTTP API. All commands are sent as POST requests to http://127.0.0.1:<automation-port>/automation with this body shape:
{
"id": "<unique-id>",
"command": "<command-name>",
"params": { ... }
}
Taking Screenshots
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"screenshot","params":{}}' | jq -r '.data.base64' | base64 -d > /tmp/app-screenshot.png
Then use Read on the resulting PNG to see the app window.
For element screenshots:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"screenshot.element","params":{"selector":".some-class"}}' | jq -r '.data.base64' | base64 -d > /tmp/element.png
Querying the DOM
Get a DOM snapshot (tree structure with roles, classes, text):
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"query.snapshot","params":{"root":"body","depth":4}}'
Query a specific element:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"query.element","params":{"selector":".my-element"}}'
Query multiple elements:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"query.elements","params":{"selector":"button","limit":20}}'
Check if an element exists:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"query.exists","params":{"selector":"role=dialog"}}'
Get element text:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"query.text","params":{"selector":".message-content"}}'
Selectors
Selectors support these prefixes:
- Plain CSS:
".class", "#id", "button[type=submit]"
aria=<label>: matches [aria-label="<label>"]
role=<role>: matches [role="<role>"]
text=<exact>: matches elements whose textContent.trim() equals the text
Performing Actions
Click an element:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.click","params":{"selector":"text=New Chat"}}'
Type text (character by character, simulates keyboard):
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.type","params":{"selector":"textarea","text":"hello","clear":true}}'
Fill a field (sets value directly, faster):
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.fill","params":{"selector":"input.search","text":"query"}}'
Press a key:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.press","params":{"key":"Enter"}}'
With modifiers:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.press","params":{"key":"a","modifiers":["meta"]}}'
Scroll:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.scroll","params":{"selector":".scrollable","deltaY":300}}'
Hover:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"action.hover","params":{"selector":".menu-item"}}'
Waiting
Wait for an element to appear:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"wait.element","params":{"selector":".loaded","timeout":5000}}'
Wait for text content:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"wait.text","params":{"text":"Ready","timeout":5000}}'
Wait for app idle (no spinners):
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"wait.idle","params":{"timeout":10000}}'
App State
Get window info:
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"app.window","params":{}}'
Get app UI state (sidebar collapsed, settings open, etc.):
curl -sS http://127.0.0.1:47321/automation -d '{"id":"1","command":"app.state","params":{}}'
Full Command List
| Command | Description |
|---|
screenshot | Full window screenshot (returns base64) |
screenshot.element | Element screenshot by selector |
query.element | Single element info |
query.elements | Multiple elements info |
query.bounds | Element bounding rect |
query.styles | Computed style properties |
query.text | Element text content |
query.exists | Check element existence |
query.snapshot | DOM tree snapshot |
action.click | Click element |
action.dblclick | Double-click element |
action.type | Type text character by character |
action.fill | Set input value directly |
action.press | Press key/combo |
action.scroll | Scroll element or window |
action.drag | Drag from one point/element to another |
action.hover | Hover over element |
action.focus | Focus element |
wait.element | Wait for element visible/hidden |
wait.text | Wait for text to appear |
wait.idle | Wait for no active spinners |
app.window | Window bounds and title |
app.state | App UI state summary |
Restarting Or Pinning Ports
To force a renderer port:
CODINGX_DEV_PORT=5173 npm run dev
To force an automation port:
CODINGX_AUTOMATION_PORT=47321 npm run dev
To disable automation:
CODINGX_AUTOMATION=0 npm run dev
If a previous dev session is still running, either keep it and report its URLs, or stop only the matching npm run dev session you started. Do not kill unrelated Electron apps such as Codex or other user apps.
Common Failures
node_modules_missing: run npm ci first, then retry npm run dev.
Invalid port: check CODINGX_DEV_PORT or CODINGX_AUTOMATION_PORT; they must be integers from 1 to 65535.
- Renderer timeout: inspect the Vite output and retry after freeing the selected port or unsetting the pinned port.
- Electron starts but renderer is blank: confirm
VITE_DEV_SERVER_URL was set by scripts/dev.mjs; main-process, preload, IPC, and Electron window changes require restarting Electron.
- Automation root returns
Not found: this is normal. Use /automation/health.
- Claude runtime unavailable: the app can still start, but Claude chat needs
ANTHROPIC_API_KEY or another supported Claude Agent SDK auth mode.
- Codex runtime unavailable: the app can still start, but Codex chat needs the bundled
@openai/codex package installed and normal Codex CLI authentication.
Response Template
When startup succeeds, answer concisely:
App 已启动。
- Renderer: http://127.0.0.1:<port>/
- Automation: http://127.0.0.1:<port>/automation/health
- Electron 进程已确认在运行
- 工作区状态: <clean 或列出相关变更>
When startup fails, include the failing command, the exact error line, and the next concrete command to try.
Important: Tool Choice
- Use:
curl + automation protocol for all UI interaction (screenshots, clicks, DOM queries)
- Do NOT use: chrome-devtools MCP tools (
navigate_page, take_snapshot, click, fill, etc.) — they connect to a separate browser, not the Electron app window