@ahmednawaz/crank 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +53 -0
- package/README.md +266 -0
- package/bin/crank.js +737 -0
- package/bookmarklet/crank-bookmarklet.js +2295 -0
- package/bookmarklet/crank-ui-helpers.js +355 -0
- package/bookmarklet/install-snippet.txt +5 -0
- package/bookmarklet/text-source.js +239 -0
- package/companion/agent-queue.js +485 -0
- package/companion/agent-runner.js +334 -0
- package/companion/bin/crank.js +69 -0
- package/companion/dev-loader.js +39 -0
- package/companion/glean-client.js +991 -0
- package/companion/package.json +18 -0
- package/companion/persist-apply.js +189 -0
- package/companion/selection-store.js +175 -0
- package/companion/server.js +1147 -0
- package/companion/text-dispatch.js +419 -0
- package/companion/vizpatch-bridge.js +49 -0
- package/lib/copy-labels.js +86 -0
- package/lib/detect.js +88 -0
- package/lib/env.js +23 -0
- package/lib/init.js +435 -0
- package/lib/load-team-env.js +43 -0
- package/lib/resolve-project.js +203 -0
- package/lib/team-auth.js +82 -0
- package/lib/urls.js +164 -0
- package/mcp-server/index.js +174 -0
- package/mcp-server/package.json +16 -0
- package/mcp-server/tools.js +480 -0
- package/package.json +57 -0
- package/panel/demo.html +62 -0
- package/skill/SKILL.md +60 -0
- package/skills/README.md +16 -0
- package/skills/copy-guidance.md +25 -0
- package/team.env +4 -0
- package/vite.js +75 -0
package/.env.example
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Companion (browser ↔ local bridge)
|
|
2
|
+
CRANK_PORT=3344
|
|
3
|
+
CRANK_HOST=127.0.0.1
|
|
4
|
+
# Shared team passphrase — required for panel + MCP when set (share internally, never commit real value)
|
|
5
|
+
# CRANK_TEAM_TOKEN=Crank-xxxxxxxxxxxxxxxxxxxxxxxx
|
|
6
|
+
# App repo to search/write when Apply persists copy (defaults to crank package root).
|
|
7
|
+
# For a real product app: CRANK_PROJECT_ROOT=/path/to/your-app
|
|
8
|
+
# Or: npx crank /path/to/your-app
|
|
9
|
+
# CRANK_PROJECT_ROOT=
|
|
10
|
+
|
|
11
|
+
# Motive Glean Client API
|
|
12
|
+
GLEAN_BASE_URL=https://motive-prod-be.glean.com
|
|
13
|
+
GLEAN_API_KEY=
|
|
14
|
+
# Default: Robot Content Designer (UX string options)
|
|
15
|
+
GLEAN_AGENT_ID=a866095fd53b419ab08338e3559fb7aa
|
|
16
|
+
# Required only when using a global token:
|
|
17
|
+
# GLEAN_ACT_AS=you@motive.com
|
|
18
|
+
# When true, skip network and return deterministic stub variants
|
|
19
|
+
GLEAN_STUB=true
|
|
20
|
+
# Max copy options to request/parse (default 3; was 4, was 6)
|
|
21
|
+
# CRANK_GLEAN_OPTION_COUNT=3
|
|
22
|
+
# Prefer /agents/runs/stream (live token progress). Set false to force /runs/wait.
|
|
23
|
+
# Stream does not reduce total latency — both block ~1–2 min for Robot Content Designer.
|
|
24
|
+
# GLEAN_USE_STREAM=true
|
|
25
|
+
# GLEAN_STREAM_FIRST_BYTE_MS=12000
|
|
26
|
+
# GLEAN_WAIT_HEARTBEAT_MS=3500
|
|
27
|
+
# Overall cap for stream + /runs/wait (default 5 min; agent often 90–140s)
|
|
28
|
+
# GLEAN_AGENT_TIMEOUT_MS=300000
|
|
29
|
+
|
|
30
|
+
# MCP server
|
|
31
|
+
CRANK_MCP_MODE=stdio
|
|
32
|
+
# For Replit / remote agents, run HTTP mode and expose HTTPS:
|
|
33
|
+
# CRANK_MCP_MODE=http
|
|
34
|
+
# CRANK_MCP_HTTP_PORT=3345
|
|
35
|
+
# Optional shared secret for remote MCP clients (Replit custom headers)
|
|
36
|
+
CRANK_MCP_API_KEY=
|
|
37
|
+
|
|
38
|
+
# Companion URL the MCP server uses to talk to the selection store
|
|
39
|
+
CRANK_COMPANION_URL=http://127.0.0.1:3344
|
|
40
|
+
VIZPATCH_ROOT=/Users/ahmed.nawaz/Downloads/vizpatch
|
|
41
|
+
|
|
42
|
+
# Optional: one-click "Run fully automatic" (bookmarklet Agent → SDK/CLI)
|
|
43
|
+
# User key: Cursor Dashboard → Integrations / API Keys
|
|
44
|
+
# OR team service-account key: Team Settings → Service accounts
|
|
45
|
+
# Team Admin API keys are NOT supported.
|
|
46
|
+
# CURSOR_API_KEY=
|
|
47
|
+
# Runtime: local (default, edits files on this machine) or cloud (Cursor VM + GitHub clone)
|
|
48
|
+
# CURSOR_AGENT_RUNTIME=local
|
|
49
|
+
# Required for cloud when git has no origin remote:
|
|
50
|
+
# CURSOR_AGENT_REPO_URL=https://github.com/org/repo
|
|
51
|
+
# CURSOR_AGENT_STARTING_REF=main
|
|
52
|
+
# CURSOR_AGENT_MODEL=composer-2.5
|
|
53
|
+
# CURSOR_AGENT_AUTO_PR=false
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Crank
|
|
2
|
+
|
|
3
|
+
Internal Motive tool for **copy iteration on live UI panels**.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
This repo is a **sibling extract** of patterns from [Vizpatch](../vizpatch) (inspect/select, companion bridge, text apply). It is intentionally separate so Vizpatch stays CSS-focused and Crank stays copy-focused.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
flowchart LR
|
|
15
|
+
subgraph Browser
|
|
16
|
+
Page[Product page / panel]
|
|
17
|
+
BM[Crank bookmarklet]
|
|
18
|
+
Page --> BM
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
subgraph Local_or_Hosted["Local or hosted bridge"]
|
|
22
|
+
Comp[Companion :3344]
|
|
23
|
+
Store[(Selection store)]
|
|
24
|
+
GleanClient[Glean client]
|
|
25
|
+
Comp --> Store
|
|
26
|
+
Comp --> GleanClient
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
subgraph Agent_backends["Agent backends"]
|
|
30
|
+
Glean[Glean agent API]
|
|
31
|
+
Skills[skills/ rules]
|
|
32
|
+
GleanClient --> Glean
|
|
33
|
+
Skills -.-> Glean
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
subgraph MCP_clients["MCP clients"]
|
|
37
|
+
Cursor[Cursor / Claude Desktop]
|
|
38
|
+
Replit[Replit Agent]
|
|
39
|
+
MCP[Crank MCP server]
|
|
40
|
+
Cursor -->|stdio| MCP
|
|
41
|
+
Replit -->|HTTPS custom MCP| MCP
|
|
42
|
+
MCP -->|HTTP| Comp
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
BM -->|WS / HTTP selection| Comp
|
|
46
|
+
Comp -->|APPLY_VARIANT| BM
|
|
47
|
+
Glean -->|3–4 variants| Comp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Flow
|
|
51
|
+
|
|
52
|
+
1. **Select** — Bookmarklet hover/click picks a `div`, extracts text nodes (`path`, `role`, `text`), posts to companion `/api/selection`.
|
|
53
|
+
2. **Propose** — Companion calls Glean (`glean-client.js`) with selection context. Stub returns deterministic variants when `GLEAN_STUB=true` (count = `CRANK_GLEAN_OPTION_COUNT`, default 3).
|
|
54
|
+
3. **Review** — Variants appear in the bookmarklet panel and/or via MCP tool `propose_copy_variants`.
|
|
55
|
+
4. **Apply** — Panel **Apply** (or MCP `apply_copy_variant`) updates live DOM **and** persists editable hardcoded literals via Vizpatch `text-writer`. **Preview** is DOM-only.
|
|
56
|
+
5. **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 the `crank-apply-changes` skill). The agent calls `crank_get_formatted_changes` → edits source → `crank_clear_changes`. Optional secondary: **Open new chat instead** uses a `cursor://…/prompt` deeplink (opens a *new* prefilled chat — not the same Composer).
|
|
57
|
+
|
|
58
|
+
**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](https://cursor.com/docs/reference/deeplinks): “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`.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Quick start (one command — Retune-style)
|
|
63
|
+
|
|
64
|
+
From your **app repo root** (Replit, Cursor terminal, or Codespaces — no paths):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx crank setup # once: MCP config, skill, devDependency, Vite inject
|
|
68
|
+
npx crank # starts companion + dev + panel
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
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.
|
|
72
|
+
|
|
73
|
+
**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.
|
|
74
|
+
|
|
75
|
+
That automatically:
|
|
76
|
+
|
|
77
|
+
1. **Detects** your app directory (`app/`, `client/`, `web/`, or repo root — same heuristics as Retune)
|
|
78
|
+
2. **Inits** MCP for Cursor / VS Code / Claude + installs the skill
|
|
79
|
+
3. **Starts** companion + `npm run dev` when a dev script exists
|
|
80
|
+
4. **Writes** `.crank/runtime.json` with public URLs for the panel loader
|
|
81
|
+
|
|
82
|
+
| Environment | Agent MCP | Panel |
|
|
83
|
+
|-------------|-----------|-------|
|
|
84
|
+
| **Cursor / Claude / VS Code** | `npx crank mcp` (stdio) | Vite auto-inject or bookmarklet |
|
|
85
|
+
| **Replit Agent** | HTTPS `/mcp` from `crank_get_environment` | Webview + bookmarklet |
|
|
86
|
+
|
|
87
|
+
Stop local daemon: `npx crank stop`
|
|
88
|
+
|
|
89
|
+
### Vizpatch reuse (UI + text dispatch)
|
|
90
|
+
|
|
91
|
+
Set `VIZPATCH_ROOT` (defaults to sibling `../vizpatch`). Crank reuses:
|
|
92
|
+
|
|
93
|
+
- **tool-ui CSS** — `/ui/vizpatch-ui.css`
|
|
94
|
+
- **control helpers** — `/ui/crank-ui-helpers.js` (buttons, fields, textareas)
|
|
95
|
+
- **hardcoded vs dynamic/bound** — `/ui/text-source.js` (`classifyTextSource`)
|
|
96
|
+
- **DOM preview** — Preview / Discard (`PREVIEW_TEXT_CHANGE`)
|
|
97
|
+
- **source save** — Save source (`SAVE_TEXT_CHANGE` → Vizpatch `text-writer.js`)
|
|
98
|
+
- Locked nodes show a badge + reason and cannot be edited in-panel
|
|
99
|
+
|
|
100
|
+
Useful variants:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx crank init # write configs only
|
|
104
|
+
npx crank start --daemon --no-open # durable companion
|
|
105
|
+
npx crank --dev # also runs npm run dev
|
|
106
|
+
npx crank --http-mcp # force remote MCP (Replit-style)
|
|
107
|
+
npx crank doctor # print detected environment
|
|
108
|
+
npx crank stop # stop daemon
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Manual companion-only start
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
cd ~/Downloads/crank
|
|
117
|
+
cp .env.example .env # if needed
|
|
118
|
+
npm install
|
|
119
|
+
node bin/crank.js start --daemon --no-open
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Open `http://127.0.0.1:3344`, drag the **Crank** bookmarklet to the bookmarks bar, open any page, click the bookmarklet → **Select** → **Ask Glean**.
|
|
123
|
+
|
|
124
|
+
Stub demo without a browser (companion must be running):
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm run demo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### MCP (Cursor / Claude Desktop)
|
|
131
|
+
|
|
132
|
+
Companion must be running. Add to MCP config:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"mcpServers": {
|
|
137
|
+
"crank": {
|
|
138
|
+
"command": "node",
|
|
139
|
+
"args": ["~/Downloads/crank/mcp-server/index.js"],
|
|
140
|
+
"env": {
|
|
141
|
+
"CRANK_COMPANION_URL": "http://127.0.0.1:3344",
|
|
142
|
+
"CRANK_MCP_MODE": "stdio"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Tools:
|
|
150
|
+
|
|
151
|
+
| Tool | Purpose |
|
|
152
|
+
|------|---------|
|
|
153
|
+
| `get_selected_copy` | Read active selection + text nodes |
|
|
154
|
+
| `propose_copy_variants` | Call Glean for 3–4 iterations |
|
|
155
|
+
| `apply_copy_variant` | DOM apply + source persist (set `persist:false` for preview) |
|
|
156
|
+
| `apply_custom_copy` | Apply agent-edited `nodes[]` (DOM) |
|
|
157
|
+
| `crank_get_formatted_changes` | Agent-ready markdown for pending Agent-button changes (Retune-style) |
|
|
158
|
+
| `crank_get_pending_changes` | Structured JSON diffs for pending changes |
|
|
159
|
+
| `crank_watch_changes` | Long-poll until pending changes appear |
|
|
160
|
+
| `crank_clear_changes` | Clear pending after apply |
|
|
161
|
+
| `crank_status` | Whether anything is queued |
|
|
162
|
+
| `get_pending_agent_prompt` | Legacy: read full queued prompt |
|
|
163
|
+
| `ack_pending_agent_prompt` | Legacy: clear pending after applying |
|
|
164
|
+
| `queue_agent_prompt` | Queue a variant for Cursor (same as **Agent**) |
|
|
165
|
+
|
|
166
|
+
### MCP (HTTP / Replit)
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
CRANK_MCP_MODE=http CRANK_MCP_HTTP_PORT=3345 npm run mcp:http
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Expose `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.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Glean integration
|
|
177
|
+
|
|
178
|
+
Uses Motive Client API against `https://motive-prod-be.glean.com`.
|
|
179
|
+
|
|
180
|
+
Default agent: **Robot Content Designer** (`a866095fd53b419ab08338e3559fb7aa`) — generates + scores UX string options.
|
|
181
|
+
|
|
182
|
+
Env vars (see `.env.example`; secrets live in `.env`, gitignored):
|
|
183
|
+
|
|
184
|
+
| Variable | Role |
|
|
185
|
+
|----------|------|
|
|
186
|
+
| `GLEAN_BASE_URL` | Instance backend, e.g. `https://motive-prod-be.glean.com` |
|
|
187
|
+
| `GLEAN_API_KEY` | Client API bearer token |
|
|
188
|
+
| `GLEAN_AGENT_ID` | Agent Builder id (default = Robot Content Designer) |
|
|
189
|
+
| `GLEAN_ACT_AS` | Optional; required for global tokens |
|
|
190
|
+
| `GLEAN_STUB` | `true` = local stub variants; `false` = live agent |
|
|
191
|
+
| `CRANK_GLEAN_OPTION_COUNT` | Max options to request/parse (default **3**; was 4, was 6) |
|
|
192
|
+
| `GLEAN_USE_STREAM` | `true` = live SSE progress (same latency as `/runs/wait`) |
|
|
193
|
+
| `GLEAN_AGENT_TIMEOUT_MS` | Overall cap (default 5 min) |
|
|
194
|
+
|
|
195
|
+
**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.
|
|
196
|
+
|
|
197
|
+
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.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Replit MCP conclusion
|
|
202
|
+
|
|
203
|
+
**Replit Agent can consume MCP natively** — no browser extension is required for MCP itself.
|
|
204
|
+
|
|
205
|
+
Findings (as of Dec 2025 / docs current through 2026):
|
|
206
|
+
|
|
207
|
+
- 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](https://docs.replit.com/build/connect-via-mcp), [changelog 2025-12-12](https://docs.replit.com/updates/2025/12/12/changelog)).
|
|
208
|
+
- 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](https://docs.replit.com/references/mcp/overview)).
|
|
209
|
+
- Traffic is scanned by Replit’s security layer before tools run.
|
|
210
|
+
- Replit needs a **network-reachable HTTPS** MCP endpoint. Local `stdio` MCP (Cursor-style) does **not** work for Replit. Host Crank MCP with `CRANK_MCP_MODE=http` behind HTTPS (or a tunnel).
|
|
211
|
+
|
|
212
|
+
**What still needs a browser-side hook in Replit:**
|
|
213
|
+
|
|
214
|
+
| Concern | Native MCP? | What you need |
|
|
215
|
+
|---------|-------------|----------------|
|
|
216
|
+
| Agent calling `get_selected_copy` / `apply_copy_variant` | Yes (remote HTTPS MCP) | Hosted `mcp-server` |
|
|
217
|
+
| User selecting a `div` inside a Replit webview/preview | No | Bookmarklet (or a small content-script extension) injected into that page, talking to companion |
|
|
218
|
+
| Companion selection store on laptop localhost | Not reachable from Replit cloud | Host companion + MCP together, or tunnel both |
|
|
219
|
+
|
|
220
|
+
**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.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Repo layout
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
crank/
|
|
228
|
+
├── bin/crank.js # CLI: init, start --daemon, stop, status
|
|
229
|
+
├── bookmarklet/ # Div picker + text extract + panel
|
|
230
|
+
├── companion/ # Express + WebSocket bridge + Glean client
|
|
231
|
+
├── mcp-server/ # MCP tools (stdio + HTTP)
|
|
232
|
+
├── skills/ # Copy rules sent to / used by Glean
|
|
233
|
+
├── scripts/demo-flow.js # Curl-less E2E stub demo
|
|
234
|
+
├── .env.example
|
|
235
|
+
└── README.md
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## What was reused from Vizpatch vs new
|
|
241
|
+
|
|
242
|
+
| Area | Approach |
|
|
243
|
+
|------|----------|
|
|
244
|
+
| Element picker (highlight, click, Esc) | **Forked pattern** from `extension/content.js` / bookmarklet inspect |
|
|
245
|
+
| Companion Express + WS on fixed port | **Forked pattern** from `companion/server.js` (stripped CSS/sourcemap/VFS) |
|
|
246
|
+
| Text node write-back | **Simplified invent** — live DOM by path index; source-file `text-writer.js` not copied yet |
|
|
247
|
+
| Glean client + skills/ | **New** |
|
|
248
|
+
| MCP tool surface | **New** |
|
|
249
|
+
| tool-ui / CSS inspector chrome | **Not reused** (out of scope) |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Key next steps (real Glean + product wiring)
|
|
254
|
+
|
|
255
|
+
1. Drop real `GLEAN_API_KEY` / `GLEAN_AGENT_ID`; set `GLEAN_STUB=false`.
|
|
256
|
+
2. Align request/response schema in `companion/glean-client.js` with the Motive Glean agent contract.
|
|
257
|
+
3. Point Glean at `skills/` (or sync rules into Glean’s skill store).
|
|
258
|
+
4. Optionally port Vizpatch `text-writer.js` for source-file persistence when the panel maps to a known project root.
|
|
259
|
+
5. For Replit: deploy companion + MCP HTTP behind HTTPS; document the Integrations URL + `X-API-Key`.
|
|
260
|
+
6. Harden MCP HTTP to the official Streamable HTTP transport if Replit’s client requires it beyond JSON-RPC `/mcp`.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Status
|
|
265
|
+
|
|
266
|
+
Internal scaffold — runnable stubs. Not a production Motive service yet.
|