@ahmednawaz/crank
v0.3.10
Published
Internal Motive UX copy iteration: select UI text → Glean variants → MCP apply. Cursor, Replit, Claude.
Maintainers
Readme
Crank
Internal Motive tool for copy iteration on live UI panels.
Select a div → send its text nodes to a Glean agent (skills/rules aware) → review 3–4 copy options → apply the chosen variant via MCP into whatever product the panel is running in.
This repo owns the copy iteration loop (select → Glean variants → MCP apply). CSS inspection lives in a separate tooling track if needed.
Architecture
flowchart LR
subgraph Browser
Page[Product page / panel]
BM[Crank bookmarklet]
Page --> BM
end
subgraph Local_or_Hosted["Local or hosted bridge"]
Comp[Companion :3344]
Store[(Selection store)]
GleanClient[Glean client]
Comp --> Store
Comp --> GleanClient
end
subgraph Agent_backends["Agent backends"]
Glean[Glean agent API]
Skills[skills/ rules]
GleanClient --> Glean
Skills -.-> Glean
end
subgraph MCP_clients["MCP clients"]
Cursor[Cursor / Claude Desktop]
Replit[Replit Agent]
MCP[Crank MCP server]
Cursor -->|stdio| MCP
Replit -->|HTTPS custom MCP| MCP
MCP -->|HTTP| Comp
end
BM -->|WS / HTTP selection| Comp
Comp -->|APPLY_VARIANT| BM
Glean -->|3–4 variants| CompFlow
- Select — Bookmarklet hover/click picks a
div, extracts text nodes (path,role,text), posts to companion/api/selection. - Propose — Companion calls Glean (
glean-client.js) with selection context. Stub returns deterministic variants whenGLEAN_STUB=true(count =CRANK_GLEAN_OPTION_COUNT, default 3). - Review — Variants appear in the bookmarklet panel and/or via MCP tool
propose_copy_variants. - Apply — Panel Apply (or MCP
apply_copy_variant) updates live DOM and persists editable hardcoded literals via the bundled Cranktext-writer. Preview is DOM-only. - Agent handoff (same chat) — Panel Agent queues structured pending changes for MCP pull and copies a short phrase (
Apply the pending Crank change) to the clipboard. Paste that phrase in your current Cursor chat (or keep an agent session that already has thecrank-apply-changesskill). The agent callscrank_get_formatted_changes→ edits source →crank_clear_changes. Optional secondary: Open new chat instead uses acursor://…/promptdeeplink (opens a new prefilled chat — not the same Composer).
There is no silent push into the IDE chat panel. Cursor has no public “append to current Composer” API. MCP is pull-only (the agent calls tools). Prompt deeplinks always require user confirm (docs: “never trigger automatic execution”) and open a new chat. Community workarounds (composer.newAgentChat + clipboard paste) also start a new chat — Crank does not ship those as primary. A background run via Cursor SDK / CLI (Agent.prompt, agent -p) is a separate agent — not the Composer chat — and needs CURSOR_API_KEY.
Quick start (one command — Retune-style)
From your app repo root (Replit, Cursor terminal, or Codespaces — no paths):
npx @ahmednawaz/crank setup # once: MCP + skill (Retune-style, global ~/.cursor + ~/.claude)
npx @ahmednawaz/crank # local: companion + dev when present
npx @ahmednawaz/crank start # Replit Workflow: companion + HTTP MCP onlySetup auto-installs MCP config and crank-apply-changes skill — no manual skill copy or MCP JSON editing (same model as Retune). Reload MCP in your IDE once after first setup.
MCP config uses npx -y crank mcp (like Retune) — no absolute paths. After setup, crank is added to devDependencies so virtual environments resolve it automatically.
Agent chat: paste Apply the pending Crank change or ask the agent to run npx crank. The agent should call crank_get_environment first to learn which repo and URLs are active.
That automatically:
- Detects your app directory (
app/,client/,web/, or repo root — same heuristics as Retune) - Inits MCP for Cursor / VS Code / Claude + installs the skill
- Starts companion +
npm run devwhen a dev script exists - Writes
.crank/runtime.jsonwith public URLs for the panel loader
| Environment | Agent MCP | Panel |
|-------------|-----------|-------|
| Cursor / Claude / VS Code | npx crank mcp (stdio) | Vite auto-inject or bookmarklet |
| Replit Agent | HTTPS /mcp from crank_get_environment | Webview + bookmarklet |
Stop local daemon: npx crank stop
UI + text dispatch (bundled)
No external sibling tool required. Crank ships:
- tool-ui CSS —
/ui/crank-ui.css - control helpers —
/ui/crank-ui-helpers.js(buttons, fields, textareas) - hardcoded vs dynamic/bound —
/ui/text-source.js(classifyTextSource) - DOM preview — Preview / Discard (
PREVIEW_TEXT_CHANGE) - source save — Save source (
SAVE_TEXT_CHANGE→ bundledcompanion/text-writer.js) - Locked nodes show a badge + reason and cannot be edited in-panel
Optional: CRANK_ASSET_ROOT overrides where packaged UI assets are loaded from (defaults to this install).
Panel injection (Retune-style)
npx @ahmednawaz/crank@latest setup
npx @ahmednawaz/crank inject # patches main.tsx — no manual edits
npx @ahmednawaz/crank startinject installs the package and adds import { Crank } + <Crank /> (Sentry/Clerk-style).
Replit public URL: window.__CRANK_URL__ or <Crank companionUrl="https://3344-….replit.dev" /> or VITE_CRANK_URL.
Useful variants:
npx crank init # write configs only
npx crank start --daemon --no-open # durable companion
npx crank --dev # also runs npm run dev
npx crank --http-mcp # force remote MCP (Replit-style)
npx crank doctor # print detected environment
npx crank stop # stop daemonManual companion-only start
cd ~/Downloads/crank
cp .env.example .env # if needed
npm install
node bin/crank.js start --daemon --no-openOpen http://127.0.0.1:3344, drag the Crank bookmarklet to the bookmarks bar, open any page, click the bookmarklet → Select → Ask Glean.
Stub demo without a browser (companion must be running):
npm run demoMCP (Cursor / Claude Desktop)
Companion must be running. Add to MCP config:
{
"mcpServers": {
"crank": {
"command": "node",
"args": ["~/Downloads/crank/mcp-server/index.js"],
"env": {
"CRANK_COMPANION_URL": "http://127.0.0.1:3344",
"CRANK_MCP_MODE": "stdio"
}
}
}
}Tools:
| Tool | Purpose |
|------|---------|
| get_selected_copy | Read active selection + text nodes |
| propose_copy_variants | Call Glean for 3–4 iterations |
| apply_copy_variant | DOM apply + source persist (set persist:false for preview) |
| apply_custom_copy | Apply agent-edited nodes[] (DOM) |
| crank_get_formatted_changes | Agent-ready markdown for pending Agent-button changes (Retune-style) |
| crank_get_pending_changes | Structured JSON diffs for pending changes |
| crank_watch_changes | Long-poll until pending changes appear |
| crank_clear_changes | Clear pending after apply |
| crank_status | Whether anything is queued |
| get_pending_agent_prompt | Legacy: read full queued prompt |
| ack_pending_agent_prompt | Legacy: clear pending after applying |
| queue_agent_prompt | Queue a variant for Cursor (same as Agent) |
MCP (HTTP / Replit)
CRANK_MCP_MODE=http CRANK_MCP_HTTP_PORT=3345 npm run mcp:httpExpose https://…/mcp (tunnel or hosted) and add as a custom MCP server in Replit Integrations. Optional auth: set CRANK_MCP_API_KEY and pass X-API-Key in Replit advanced headers.
Glean integration
Uses Motive Client API against https://motive-prod-be.glean.com.
Default agent: Robot Content Designer (a866095fd53b419ab08338e3559fb7aa) — generates + scores UX string options.
Env vars (see .env.example; secrets live in .env, gitignored):
| Variable | Role |
|----------|------|
| GLEAN_BASE_URL | Instance backend, e.g. https://motive-prod-be.glean.com |
| GLEAN_API_KEY | Client API bearer token |
| GLEAN_AGENT_ID | Agent Builder id (default = Robot Content Designer) |
| GLEAN_ACT_AS | Optional; required for global tokens |
| GLEAN_STUB | true = local stub variants; false = live agent |
| CRANK_GLEAN_OPTION_COUNT | Max options to request/parse (default 3; was 4, was 6) |
| GLEAN_USE_STREAM | true = live SSE progress (same latency as /runs/wait) |
| GLEAN_AGENT_TIMEOUT_MS | Overall cap (default 5 min) |
Latency: Robot Content Designer typically takes 1–2 minutes end-to-end. Most of that is inherent agent runtime (~55–90s silent phase before tokens). Stream vs wait does not materially change total time — stream only adds live progress UI. Set GLEAN_STUB=true for instant local dev.
Live path: POST /rest/api/v1/agents/runs/stream (preferred) or /runs/wait with the agent’s form fields. Companion parses Option N / Heading / Body / CTA from the agent markdown into variants[] for the panel + MCP.
Replit MCP conclusion
Replit Agent can consume MCP natively — no browser extension is required for MCP itself.
Findings (as of Dec 2025 / docs current through 2026):
- Replit Agent supports the Model Context Protocol as a client: curated one-click servers plus custom remote MCP servers by HTTPS URL (Connect via MCP, changelog 2025-12-12).
- Custom servers are added under Integrations → MCP Servers → Add MCP server with an HTTPS endpoint; optional custom headers (e.g.
X-API-Key) for static tokens (MCP list / auth). - Traffic is scanned by Replit’s security layer before tools run.
- Replit needs a network-reachable HTTPS MCP endpoint. Local
stdioMCP (Cursor-style) does not work for Replit. Host Crank MCP withCRANK_MCP_MODE=httpbehind HTTPS (or a tunnel).
What still needs a browser-side hook in Replit:
| Concern | Native MCP? | What you need |
|---------|-------------|----------------|
| Agent calling get_selected_copy / apply_copy_variant | Yes (remote HTTPS MCP) | Hosted mcp-server |
| User selecting a div inside a Replit webview/preview | No | Bookmarklet (or a small content-script extension) injected into that page, talking to companion |
| Companion selection store on laptop localhost | Not reachable from Replit cloud | Host companion + MCP together, or tunnel both |
Bottom line: Replit does not need an extension for MCP. It does need (1) a remote HTTPS Crank MCP, and (2) something in the page (bookmarklet/extension) to capture the selection — same as local Cursor workflows.
Repo layout
crank/
├── bin/crank.js # CLI: init, start --daemon, stop, status
├── bookmarklet/ # Div picker + text extract + panel
├── companion/ # Express + WebSocket bridge + Glean client
├── mcp-server/ # MCP tools (stdio + HTTP)
├── skills/ # Copy rules sent to / used by Glean
├── scripts/demo-flow.js # Curl-less E2E stub demo
├── .env.example
└── README.mdArchitecture notes
| Area | Approach |
|------|----------|
| Element picker (highlight, click, Esc) | Companion bookmarklet inspect |
| Companion Express + WS on fixed port | companion/server.js |
| Text node write-back | Bundled companion/text-writer.js (HTML + JS/TS literals) |
| Glean client + skills/ | Crank-owned |
| MCP tool surface | Crank-owned |
| Panel UI CSS / helpers | Bundled bookmarklet/crank-ui.css + helpers |
Key next steps (real Glean + product wiring)
- Drop real
GLEAN_API_KEY/GLEAN_AGENT_ID; setGLEAN_STUB=false. - Align request/response schema in
companion/glean-client.jswith the Motive Glean agent contract. - Point Glean at
skills/(or sync rules into Glean’s skill store). - Confirm Apply persists via bundled text-writer when the panel maps to a known project root (
CRANK_PROJECT_ROOT). - For Replit: deploy companion + MCP HTTP behind HTTPS; document the Integrations URL +
X-API-Key. - Harden MCP HTTP to the official Streamable HTTP transport if Replit’s client requires it beyond JSON-RPC
/mcp.
Status
Internal scaffold — runnable stubs. Not a production Motive service yet.
