cobro-mcp 0.10.0 → 0.10.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@ All notable changes to this project are documented here. The format follows [Kee
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.10.1] — 2026-09-24
8
+ ### Changed
9
+ - The Send button is always visible; it is disabled until a draft has a note (it was hidden before).
10
+ - The toolbar's "view /path" link no longer repeats "Done" next to the Done chip.
11
+
7
12
  ## [0.10.0] — 2026-09-24
8
13
  ### Added
9
14
  - Batches carry the page they were picked on (batches[].page), and status/done accept an optional batchId so an agent can work through several batches in order.
package/README.ko.md CHANGED
@@ -54,7 +54,7 @@ claude mcp add -s user cobro -- npx -y cobro-mcp@latest
54
54
 
55
55
  ## 사용법
56
56
 
57
- - **사람**: 페이지에서 `Ctrl+Shift+F`로 선택 모드(`Esc`로 해제) → 요소 클릭(드래그는 밴드 안에 완전히 든 최상위 요소들) → 메모 → **Send**. Select를 누르면 패널이 열린다(요소를 고르거나 메모만 써도 된다); Select를 다시 누르거나 Esc, 패널 헤더의 ✕를 누르면 닫히고 초안은 그대로 남는다. 초안은 고른 페이지에 속한다 — 페이지를 이동하면 이전 페이지의 요소는 없음으로 표시된다. 고른 요소마다 번호 마커가 붙는다. 여러 개면 "1: …, 2: …"로 쓴다. 요소 둘 이상 위에서 드래그하면 하나의 그룹으로 잡힌다 — 밴드가 영역(1)이 되고 안의 요소들은 1a, 1b…를 받는다. 행을 펼치면 보이거나 뺄 수 있다. 요소 하나 위 밴드는 그 요소 하나만, 빈 곳 위 밴드는 영역이 된다. 툴바(왼쪽 손잡이)와 패널(헤더)은 끌어서 옮길 수 있고, 새로고침되면 기본 위치로 돌아온다. 툴바 상태 줄이 다음에 할 일을 알려준다. 에이전트가 작업 중(전송됨·수정 중)이면 Send가 잠기고 `done` 뒤 풀린다. 그동안 보낸 요소·영역에는 파란 스캔 테두리가, 툴바 아래에는 흐르는 선이 표시된다(`prefers-reduced-motion`이면 정지 상태). ⚙ 버튼에서 설정을 연다(테마: auto / dark / light / frost, frost는 반투명).
57
+ - **사람**: 페이지에서 `Ctrl+Shift+F`로 선택 모드(`Esc`로 해제) → 요소 클릭(드래그는 밴드 안에 완전히 든 최상위 요소들) → 메모 → **Send**. Select를 누르면 패널이 열린다(요소를 고르거나 메모만 써도 된다); Select를 다시 누르거나 Esc, 패널 헤더의 ✕를 누르면 닫히고 초안은 그대로 남는다. 고른 요소마다 번호 마커가 붙는다. 여러 개면 "1: …, 2: …"로 쓴다. 요소 둘 이상 위에서 드래그하면 하나의 그룹으로 잡힌다 — 밴드가 영역(1)이 되고 안의 요소들은 1a, 1b…를 받는다. 행을 펼치면 보이거나 뺄 수 있다. 요소 하나 위 밴드는 그 요소 하나만, 빈 곳 위 밴드는 영역이 된다. 툴바(왼쪽 손잡이)와 패널(헤더)은 끌어서 옮길 수 있고, 새로고침되면 기본 위치로 돌아온다. 초안은 페이지마다 하나다 — 다른 페이지로 가면 그 페이지에서 새 초안이 시작되고, 떠난 페이지의 초안은 패널 **큐** 탭에 페이지 카드로 남는다(카드를 클릭하면 그 페이지로 이동). **Send**는 메모가 있는 모든 페이지 초안을 순서대로 보낸다(`Send · 2페이지`); 메모가 하나도 없으면 비활성이다. 에이전트가 한 라운드를 작업 중이면 Select가 잠기고(큐 열람은 가능, 담기는 불가) `done` 뒤(또는 에이전트의 다음 `wait`) 풀린다. 작업 중인 요소·영역에는 파란 스캔 테두리가, 툴바에는 흐르는 선이 표시된다(`prefers-reduced-motion`이면 정지). 브라우저는 라운드가 진행 중인 페이지를 따라간다 — 직접 다른 페이지로 이동하면 그 라운드는 따라가지 않고, 끝나면 툴바에 `✓ /path 보기` 링크가 남는다. 완료된 페이지는 큐 탭에 접힌 한 줄로 남고, 다시 담으면 정리된다. 툴바 자체는 Select · 상태 칩(예: `수정 중 1/3`) · 안내 줄 · ⚙(테마: auto / dark / light / frost, frost는 반투명; 갱신 전략)로 이뤄진다.
58
58
  - **에이전트**: `open(url)` → `wait()` → payload의 `batches[].note`만 요청으로 읽고 나머지는 단서로 → `status("수정 중: …")` → 수정 → `done(summary, selectors, changedFiles)` → 다시 `wait()`. 끝내면 `close()`.
59
59
 
60
60
  도구는 여섯 개로 고정이다. 관찰·조작이 더 필요하면 다른 MCP를 함께 쓴다.
@@ -165,7 +165,8 @@ window.addEventListener('cobro:done', (e) => { const { summary, changedFiles, se
165
165
 
166
166
  - React 컴포넌트 이름은 dev 빌드에서만 온다. `source`(파일:행)는 dev 서버의 소스맵(React 19) 또는 `_debugSource`(React 18까지)로 복원한다 — 소스맵 없는 프로덕션 빌드는 컴포넌트 이름만 온다. 컴포넌트 라이브러리 안 요소는 라이브러리 내부가 아니라 사용자가 쓴 JSX 위치가 온다.
167
167
  - iframe 미지원(최상위 문서만). 네이티브 modal `<dialog>`가 열려 있는 동안은 오버레이가 가려진다(라이브러리 모달은 무관).
168
- - `done`의 요소 강조는 best-effort이고, `reload` 전략에서는 새로 로드되느라 보이지 않는다.
168
+ - `done`의 요소 강조는 best-effort다(리로드 뒤 재생된다).
169
+ - 비활성화된 컨트롤(예: disabled 버튼)은 클릭을 받지 않아 클릭 픽이 부모 요소로 잡힌다 — 대신 드래그로 밴드를 씌운다.
169
170
  - 선택 모드 단축키 `Ctrl+Shift+F`는 바꿀 수 없다. WebKit 빌드는 실제 Safari와 폰트·스크롤바가 다르다.
170
171
  - 브라우저 프로필은 한 번에 한 세션만 쓴다 — 다른 세션이 쓰는 중이면 `open`이 "프로필 사용 중"으로 실패한다(`COBRO_PROFILE_DIR`로 따로 지정 가능).
171
172
 
package/README.md CHANGED
@@ -54,7 +54,7 @@ The whole loop is the GIF at the top. No copy-paste, no "check my annotations"
54
54
 
55
55
  ## Usage
56
56
 
57
- - **Human**: on the page, press `Ctrl+Shift+F` to toggle pick mode (`Esc` to exit) → click an element (drag picks the topmost elements fully inside the band) → write a note → **Send**. Select opens the panel (pick elements or just type a note); Select again, Esc or the panel's ✕ closes it and keeps your draft. Drafts belong to the page you picked on — after navigating, elements from the previous page show as missing. Each picked element gets a numbered marker; with several, write "1: …, 2: …". Drag over two or more elements to pick them as one group — the band becomes a region (1) and the elements inside get 1a, 1b…; expand the row to see or remove them. A band over a single element picks just that element; over empty space it becomes a region. Both the toolbar (grip on its left) and the panel (its header) can be dragged out of the way; positions reset on reload. The toolbar status line tells you what to do next. Send is locked while the agent is working (sent, editing) and unlocks after `done`; until then the elements and regions you sent keep a blue scanning outline and the toolbar shows a flowing line (static under `prefers-reduced-motion`). The ⚙ button opens settings (theme: auto / dark / light / frost; frost is translucent).
57
+ - **Human**: on the page, press `Ctrl+Shift+F` to toggle pick mode (`Esc` to exit) → click an element (drag picks the topmost elements fully inside the band) → write a note → **Send**. Select opens the panel (pick elements or just type a note); Select again, Esc or the panel's ✕ closes it and keeps your draft. Each picked element gets a numbered marker; with several, write "1: …, 2: …". Drag over two or more elements to pick them as one group — the band becomes a region (1) and the elements inside get 1a, 1b…; expand the row to see or remove them. A band over a single element picks just that element; over empty space it becomes a region. Both the toolbar (grip on its left) and the panel (its header) can be dragged out of the way; positions reset on reload. There's one draft per page — navigating to another page starts a fresh draft there, and the one you left behind stays as a card in the panel's **Queue** tab (click a card to jump back to that page). **Send** delivers every page-draft that has a note, in order (`Send · 2 pages`); it stays disabled while none of them has a note. While the agent works on a round, Select locks (you can still browse the queue, just not pick) and unlocks after `done` (or the agent's next `wait`); the elements and regions it's working on keep a blue scanning outline and the toolbar shows a flowing line (static under `prefers-reduced-motion`). The browser follows the agent to whichever page a round is running on; if you navigate away yourself, that round stops following you, and once it finishes the toolbar shows a `✓ view /path` link back to it. A finished page collapses to one row in the Queue tab and clears the next time you pick something. The toolbar itself is Select, a status chip (e.g. `Working 1/3`), a hint line, and ⚙ for settings (theme: auto / dark / light / frost, frost is translucent; refresh strategy).
58
58
  - **Agent**: `open(url)` → `wait()` → read only `batches[].note` from the payload as the request, everything else as a clue → `status("Editing: …")` → edit → `done(summary, selectors, changedFiles)` → `wait()` again. Call `close()` to end the session.
59
59
 
60
60
  There are exactly six fixed tools. If you need more observation or control, pair Cobro with another MCP.
@@ -165,7 +165,8 @@ window.addEventListener('cobro:done', (e) => { const { summary, changedFiles, se
165
165
 
166
166
  - React component name comes from dev builds. `source` (file:line) is recovered from the dev server's source maps (React 19) or from `_debugSource` (React ≤ 18); production builds without maps report the component only. For component libraries: the location is the JSX call site in your code, not the library's internals.
167
167
  - No iframe support (top-level document only). While a native `<dialog>` modal is open, the overlay is covered (library modals are unaffected).
168
- - Element highlighting on `done` is best-effort, and is not visible under the `reload` strategy since the page reloads.
168
+ - Element highlighting on `done` is best-effort (it replays after a reload).
169
+ - A disabled control (e.g. a disabled button) does not receive clicks, so click-picking lands on its parent; drag a band over it instead.
169
170
  - The pick-mode shortcut `Ctrl+Shift+F` cannot be changed. The WebKit build differs from real Safari in fonts and scrollbars.
170
171
  - The browser profile is used by one session at a time — if another session is using it, `open` fails with "profile in use" (point `COBRO_PROFILE_DIR` elsewhere to work around it).
171
172
 
package/dist/overlay.js CHANGED
@@ -240,7 +240,7 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
240
240
  .row .sends{color:var(--fg-5);font-size:11px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
241
241
  .row .send{color:var(--fg-on-accent);background:var(--accent);border-color:var(--accent);font-weight:700;flex:none;white-space:nowrap}
242
242
  .row .send:disabled{opacity:.4;cursor:not-allowed}
243
- .row .foot{font-size:11px;color:var(--fg-4)}
243
+ .row .foot{font-size:11px;color:var(--fg-4);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
244
244
  .row .foot.ok{color:var(--ok)}
245
245
  .marker{position:fixed;border:2px solid var(--accent);border-radius:2px;pointer-events:none;box-sizing:border-box}
246
246
  .marker.region{border-style:dashed;background:color-mix(in srgb, var(--accent) 6%, transparent)}
@@ -325,9 +325,11 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
325
325
  tipDrag: "\uD234\uBC14 \uC774\uB3D9",
326
326
  sends: "\uC120\uD0DD\uC790 \xB7 \uC2A4\uD0C0\uC77C \xB7 \uC2A4\uD06C\uB9B0\uC0F7 \xB7 \uCF58\uC194",
327
327
  tipSend: "\uC120\uD0DD\uD55C \uC694\uC18C\uC640 \uBA54\uBAA8\uB97C \uC5D0\uC774\uC804\uD2B8\uC5D0 \uC804\uC1A1",
328
+ tipSendLocked: "\uC5D0\uC774\uC804\uD2B8\uAC00 \uC218\uC815 \uC911 \u2014 \uC644\uB8CC \uD6C4 \uBCF4\uB0BC \uC218 \uC788\uC5B4\uC694",
329
+ tipSendNote: "\uBA54\uBAA8\uB97C \uC801\uC73C\uBA74 \uBCF4\uB0BC \uC218 \uC788\uC5B4\uC694",
328
330
  tipRemove: "\uC774 \uC694\uC18C \uBE7C\uAE30",
329
331
  cartNoNote: "\uBA54\uBAA8 \uC5C6\uC74C",
330
- doneElsewhere: (path) => `\u2713 \uC644\uB8CC \xB7 ${path} \uBCF4\uAE30`,
332
+ doneElsewhere: (path) => `\u2713 ${path} \uBCF4\uAE30`,
331
333
  tabHere: "\uC774 \uD398\uC774\uC9C0",
332
334
  tabQueue: "\uD050",
333
335
  herePage: "\uC9C0\uAE08 \uBCF4\uACE0 \uC788\uB294 \uD398\uC774\uC9C0",
@@ -387,9 +389,11 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
387
389
  tipDrag: "Move toolbar",
388
390
  sends: "Sends selector \xB7 styles \xB7 shot \xB7 console",
389
391
  tipSend: "Send the selected elements and note to the agent",
392
+ tipSendLocked: "Agent is working \u2014 you can send after done",
393
+ tipSendNote: "Write a note to send",
390
394
  tipRemove: "Remove this element",
391
395
  cartNoNote: "No note",
392
- doneElsewhere: (path) => `\u2713 Done \xB7 view ${path}`,
396
+ doneElsewhere: (path) => `\u2713 view ${path}`,
393
397
  tabHere: "This page",
394
398
  tabQueue: "Queue",
395
399
  herePage: "The page you're on",
@@ -870,8 +874,8 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
870
874
  }
871
875
  const readyDrafts = vm.drafts.filter((b) => b.note.trim());
872
876
  const row = el("div", "row");
877
+ const send = el("button", "send", readyDrafts.length >= 2 ? T.sendPages(readyDrafts.length) : "Send \u2192");
873
878
  if (readyDrafts.length > 0) {
874
- const send = el("button", "send", readyDrafts.length >= 2 ? T.sendPages(readyDrafts.length) : "Send \u2192");
875
879
  send.title = T.tipSend;
876
880
  send.onclick = () => {
877
881
  closePop();
@@ -879,6 +883,8 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
879
883
  };
880
884
  row.append(el("span", "sends", T.sends), send);
881
885
  } else {
886
+ send.disabled = true;
887
+ send.title = vm.locked ? T.tipSendLocked : T.tipSendNote;
882
888
  let footText;
883
889
  let footOk = false;
884
890
  if (vm.busy) footText = vm.followPaused ? T.footPaused(round.done, round.total) : T.footBusy(round.done, round.total);
@@ -886,7 +892,7 @@ div.queue{display:flex;flex-direction:column;gap:8px;max-height:min(45vh,320px);
886
892
  footText = T.footAllDone(doneList.length);
887
893
  footOk = true;
888
894
  } else footText = T.footHint;
889
- row.append(el("span", `foot${footOk ? " ok" : ""}`, footText));
895
+ row.append(el("span", `foot${footOk ? " ok" : ""}`, footText), send);
890
896
  }
891
897
  panel.append(row);
892
898
  for (const id of [...textareas.keys()]) if (!vm.drafts.some((b) => b.id === id)) textareas.delete(id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cobro-mcp",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Co-browse for coding agents: pick elements in the browser, write a note, Send — the context lands in the agent's chat. MCP server with 6 tools.",
5
5
  "keywords": [
6
6
  "mcp",