adhdev 0.2.0 → 0.4.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/dist/cli-entrypoint.js +18710 -1198
- package/dist/cli-entrypoint.js.map +1 -1
- package/dist/index.js +18720 -1228
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/providers/_builtin/CONTRIBUTING.md +0 -141
- package/providers/_builtin/README.md +0 -51
- package/providers/_builtin/_helpers/index.js +0 -188
- package/providers/_builtin/acp/agentpool/provider.json +0 -47
- package/providers/_builtin/acp/amp/provider.json +0 -45
- package/providers/_builtin/acp/auggie/provider.json +0 -50
- package/providers/_builtin/acp/autodev/provider.json +0 -47
- package/providers/_builtin/acp/autohand/provider.json +0 -45
- package/providers/_builtin/acp/blackbox-ai/provider.json +0 -47
- package/providers/_builtin/acp/claude-agent/provider.json +0 -50
- package/providers/_builtin/acp/cline-acp/provider.json +0 -47
- package/providers/_builtin/acp/code-assistant/provider.json +0 -47
- package/providers/_builtin/acp/codebuddy/provider.json +0 -47
- package/providers/_builtin/acp/codex-cli/provider.json +0 -50
- package/providers/_builtin/acp/corust-agent/provider.json +0 -45
- package/providers/_builtin/acp/crow-cli/provider.json +0 -47
- package/providers/_builtin/acp/cursor-acp/provider.json +0 -47
- package/providers/_builtin/acp/deepagents/provider.json +0 -45
- package/providers/_builtin/acp/dimcode/provider.json +0 -47
- package/providers/_builtin/acp/docker-cagent/provider.json +0 -50
- package/providers/_builtin/acp/factory-droid/provider.json +0 -53
- package/providers/_builtin/acp/fast-agent/provider.json +0 -45
- package/providers/_builtin/acp/fount/provider.json +0 -47
- package/providers/_builtin/acp/gemini-cli/provider.json +0 -107
- package/providers/_builtin/acp/github-copilot/provider.json +0 -47
- package/providers/_builtin/acp/goose/provider.json +0 -50
- package/providers/_builtin/acp/junie/provider.json +0 -45
- package/providers/_builtin/acp/kilo/provider.json +0 -45
- package/providers/_builtin/acp/kimi-cli/provider.json +0 -50
- package/providers/_builtin/acp/kiro-cli/provider.json +0 -47
- package/providers/_builtin/acp/minion-code/provider.json +0 -45
- package/providers/_builtin/acp/mistral-vibe/provider.json +0 -50
- package/providers/_builtin/acp/nova/provider.json +0 -47
- package/providers/_builtin/acp/openclaw/provider.json +0 -47
- package/providers/_builtin/acp/opencode/provider.json +0 -45
- package/providers/_builtin/acp/openhands/provider.json +0 -47
- package/providers/_builtin/acp/pi-acp/provider.json +0 -45
- package/providers/_builtin/acp/qoder/provider.json +0 -47
- package/providers/_builtin/acp/qwen-code/provider.json +0 -53
- package/providers/_builtin/acp/stakpak/provider.json +0 -47
- package/providers/_builtin/acp/vtcode/provider.json +0 -47
- package/providers/_builtin/cli/claude-cli/provider.json +0 -78
- package/providers/_builtin/cli/codex-cli/provider.json +0 -60
- package/providers/_builtin/cli/gemini-cli/provider.json +0 -64
- package/providers/_builtin/extension/cline/provider.json +0 -11
- package/providers/_builtin/extension/cline/scripts/focus_editor.js +0 -48
- package/providers/_builtin/extension/cline/scripts/list_chats.js +0 -100
- package/providers/_builtin/extension/cline/scripts/list_models.js +0 -43
- package/providers/_builtin/extension/cline/scripts/list_modes.js +0 -35
- package/providers/_builtin/extension/cline/scripts/new_session.js +0 -85
- package/providers/_builtin/extension/cline/scripts/open_panel.js +0 -25
- package/providers/_builtin/extension/cline/scripts/read_chat.js +0 -257
- package/providers/_builtin/extension/cline/scripts/resolve_action.js +0 -83
- package/providers/_builtin/extension/cline/scripts/send_message.js +0 -95
- package/providers/_builtin/extension/cline/scripts/set_mode.js +0 -36
- package/providers/_builtin/extension/cline/scripts/set_model.js +0 -36
- package/providers/_builtin/extension/cline/scripts/switch_session.js +0 -206
- package/providers/_builtin/extension/cline/scripts.js +0 -73
- package/providers/_builtin/extension/roo-code/provider.json +0 -11
- package/providers/_builtin/extension/roo-code/scripts.js +0 -659
- package/providers/_builtin/ide/antigravity/provider.json +0 -32
- package/providers/_builtin/ide/antigravity/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/antigravity/scripts/list_chats.js +0 -137
- package/providers/_builtin/ide/antigravity/scripts/list_models.js +0 -38
- package/providers/_builtin/ide/antigravity/scripts/list_modes.js +0 -48
- package/providers/_builtin/ide/antigravity/scripts/new_session.js +0 -75
- package/providers/_builtin/ide/antigravity/scripts/read_chat.js +0 -262
- package/providers/_builtin/ide/antigravity/scripts/resolve_action.js +0 -68
- package/providers/_builtin/ide/antigravity/scripts/send_message.js +0 -56
- package/providers/_builtin/ide/antigravity/scripts/set_mode.js +0 -34
- package/providers/_builtin/ide/antigravity/scripts/set_model.js +0 -47
- package/providers/_builtin/ide/antigravity/scripts/switch_session.js +0 -114
- package/providers/_builtin/ide/antigravity/scripts.js +0 -73
- package/providers/_builtin/ide/cursor/provider.json +0 -35
- package/providers/_builtin/ide/cursor/scripts.js +0 -452
- package/providers/_builtin/ide/kiro/provider.json +0 -36
- package/providers/_builtin/ide/kiro/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/kiro/scripts/open_panel.js +0 -47
- package/providers/_builtin/ide/kiro/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/kiro/scripts/send_message.js +0 -29
- package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +0 -39
- package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +0 -39
- package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +0 -21
- package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +0 -34
- package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +0 -68
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +0 -72
- package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +0 -15
- package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +0 -15
- package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +0 -26
- package/providers/_builtin/ide/kiro/scripts.js +0 -62
- package/providers/_builtin/ide/pearai/provider.json +0 -36
- package/providers/_builtin/ide/pearai/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +0 -38
- package/providers/_builtin/ide/pearai/scripts/new_session.js +0 -55
- package/providers/_builtin/ide/pearai/scripts/open_panel.js +0 -46
- package/providers/_builtin/ide/pearai/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/pearai/scripts/send_message.js +0 -29
- package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +0 -43
- package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +0 -35
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +0 -62
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +0 -49
- package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +0 -92
- package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +0 -59
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +0 -72
- package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +0 -36
- package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +0 -36
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +0 -34
- package/providers/_builtin/ide/pearai/scripts.js +0 -74
- package/providers/_builtin/ide/trae/provider.json +0 -35
- package/providers/_builtin/ide/trae/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/trae/scripts/list_chats.js +0 -24
- package/providers/_builtin/ide/trae/scripts/list_models.js +0 -39
- package/providers/_builtin/ide/trae/scripts/list_modes.js +0 -39
- package/providers/_builtin/ide/trae/scripts/new_session.js +0 -30
- package/providers/_builtin/ide/trae/scripts/open_panel.js +0 -44
- package/providers/_builtin/ide/trae/scripts/read_chat.js +0 -113
- package/providers/_builtin/ide/trae/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/trae/scripts/send_message.js +0 -69
- package/providers/_builtin/ide/trae/scripts/set_mode.js +0 -15
- package/providers/_builtin/ide/trae/scripts/set_model.js +0 -15
- package/providers/_builtin/ide/trae/scripts/switch_session.js +0 -23
- package/providers/_builtin/ide/trae/scripts.js +0 -57
- package/providers/_builtin/ide/vscode/provider.json +0 -33
- package/providers/_builtin/ide/vscode-insiders/provider.json +0 -31
- package/providers/_builtin/ide/vscodium/provider.json +0 -32
- package/providers/_builtin/ide/windsurf/provider.json +0 -22
- package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +0 -30
- package/providers/_builtin/ide/windsurf/scripts/list_chats.js +0 -117
- package/providers/_builtin/ide/windsurf/scripts/list_models.js +0 -39
- package/providers/_builtin/ide/windsurf/scripts/list_modes.js +0 -39
- package/providers/_builtin/ide/windsurf/scripts/new_session.js +0 -69
- package/providers/_builtin/ide/windsurf/scripts/open_panel.js +0 -58
- package/providers/_builtin/ide/windsurf/scripts/read_chat.js +0 -297
- package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +0 -68
- package/providers/_builtin/ide/windsurf/scripts/send_message.js +0 -87
- package/providers/_builtin/ide/windsurf/scripts/set_mode.js +0 -15
- package/providers/_builtin/ide/windsurf/scripts/set_model.js +0 -15
- package/providers/_builtin/ide/windsurf/scripts/switch_session.js +0 -58
- package/providers/_builtin/ide/windsurf/scripts.js +0 -57
- package/providers/_builtin/validate.js +0 -156
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline v1 — new_session
|
|
3
|
-
*
|
|
4
|
-
* 구조:
|
|
5
|
-
* 1. "New Task" 버튼 또는 "+" 버튼 클릭
|
|
6
|
-
* 2. data-testid 우선 → aria-label → 텍스트 매칭
|
|
7
|
-
*
|
|
8
|
-
* 최종 확인: 2026-03-07
|
|
9
|
-
*/
|
|
10
|
-
(() => {
|
|
11
|
-
try {
|
|
12
|
-
const inner = document.querySelector('iframe');
|
|
13
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
14
|
-
if (!doc) return 'no doc';
|
|
15
|
-
|
|
16
|
-
const buttons = Array.from(doc.querySelectorAll('button'))
|
|
17
|
-
.filter(b => b.offsetWidth > 0 && b.offsetHeight > 0);
|
|
18
|
-
|
|
19
|
-
// ─── 1단계: data-testid 기반 ───
|
|
20
|
-
for (const btn of buttons) {
|
|
21
|
-
const testId = (btn.getAttribute('data-testid') || '').toLowerCase();
|
|
22
|
-
if (testId.includes('new-task') || testId.includes('new-chat') || testId.includes('new_task')) {
|
|
23
|
-
btn.click();
|
|
24
|
-
return 'clicked (testid)';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ─── 2단계: aria-label 기반 ───
|
|
29
|
-
for (const btn of buttons) {
|
|
30
|
-
const ariaLabel = (btn.getAttribute('aria-label') || '').toLowerCase();
|
|
31
|
-
if (ariaLabel.includes('new task') || ariaLabel.includes('new chat')
|
|
32
|
-
|| ariaLabel.includes('plus') || ariaLabel === 'new') {
|
|
33
|
-
btn.click();
|
|
34
|
-
return 'clicked (aria)';
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─── 3단계: 텍스트 매칭 ───
|
|
39
|
-
for (const btn of buttons) {
|
|
40
|
-
const text = (btn.textContent || '').trim();
|
|
41
|
-
if (text === '+' || text.includes('New Task') || text.includes('New Chat')) {
|
|
42
|
-
btn.click();
|
|
43
|
-
return 'clicked (text)';
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ─── 4단계: SVG plus 아이콘 버튼 ───
|
|
48
|
-
for (const btn of buttons) {
|
|
49
|
-
const svg = btn.querySelector('svg');
|
|
50
|
-
if (!svg) continue;
|
|
51
|
-
const path = svg.querySelector('path');
|
|
52
|
-
if (path) {
|
|
53
|
-
const d = path.getAttribute('d') || '';
|
|
54
|
-
// SVG plus icon의 일반적인 path pattern
|
|
55
|
-
if (d.includes('M12') && (d.includes('H5') || d.includes('h') || d.includes('v'))) {
|
|
56
|
-
// 다른 버튼 텍스트가 없는 아이콘 전용 버튼
|
|
57
|
-
const text = (btn.textContent || '').trim();
|
|
58
|
-
if (text.length < 3) {
|
|
59
|
-
btn.click();
|
|
60
|
-
return 'clicked (svg)';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─── 5단계: 웰컴 화면 감지 (이미 새 세션) ───
|
|
67
|
-
const bodyText = (doc.body.textContent || '').toLowerCase();
|
|
68
|
-
if (bodyText.includes('what can i do for you') || bodyText.includes('start a new task') || bodyText.includes('type your task')) {
|
|
69
|
-
return 'clicked (already new)';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ─── 6단계: History 뷰 감지 → Done 클릭으로 웰컴 복귀 ───
|
|
73
|
-
if (bodyText.includes('history') && bodyText.includes('done')) {
|
|
74
|
-
for (const btn of buttons) {
|
|
75
|
-
const text = (btn.textContent || '').trim();
|
|
76
|
-
if (text === 'Done') {
|
|
77
|
-
btn.click();
|
|
78
|
-
return 'clicked (history done)';
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return 'no button found';
|
|
84
|
-
} catch (e) { return 'error: ' + e.message; }
|
|
85
|
-
})()
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline v1 — open_panel
|
|
3
|
-
*
|
|
4
|
-
* 패널 상태 확인 및 열기 시도.
|
|
5
|
-
*
|
|
6
|
-
* iframe 컨텍스트에서는 VS Code API 접근이 제한적이므로,
|
|
7
|
-
* 패널이 숨겨져 있을 때는 'panel_hidden' 상태를 반환.
|
|
8
|
-
* → daemon의 AgentStreamManager 또는
|
|
9
|
-
* agent_stream_focus 메시지를 통해 열어야 함.
|
|
10
|
-
*
|
|
11
|
-
* 반환: 'visible' | 'panel_hidden'
|
|
12
|
-
* 최종 확인: 2026-03-07
|
|
13
|
-
*/
|
|
14
|
-
(() => {
|
|
15
|
-
try {
|
|
16
|
-
const inner = document.querySelector('iframe');
|
|
17
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
18
|
-
if (!doc) return 'panel_hidden';
|
|
19
|
-
|
|
20
|
-
const root = doc.getElementById('root');
|
|
21
|
-
if (root && root.offsetHeight > 0) return 'visible';
|
|
22
|
-
|
|
23
|
-
return 'panel_hidden';
|
|
24
|
-
} catch (e) { return 'error: ' + e.message; }
|
|
25
|
-
})()
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline v1 — read_chat (v2 — Fiber 기반 역할 판별)
|
|
3
|
-
*
|
|
4
|
-
* 구조 (Cline 3.x — saoudrizwan.claude-dev):
|
|
5
|
-
* 1. outer webview iframe → inner contentDocument
|
|
6
|
-
* 2. data-testid="virtuoso-item-list" (React Virtuoso) — 활성 메시지 블록
|
|
7
|
-
* 3. React Fiber → data 배열에서 메시지 타입 직접 추출
|
|
8
|
-
* - type: "say", say: "user_feedback" → user
|
|
9
|
-
* - type: "say", say: "text" → assistant
|
|
10
|
-
* - type: "say", say: "checkpoint_created" → system (skip)
|
|
11
|
-
* - type: "ask", ask: "followup" → assistant (질문)
|
|
12
|
-
* - type: "ask", ask: "tool" → assistant (tool 승인 대기)
|
|
13
|
-
* 4. DOM textContent에서 콘텐츠 추출 + 정제
|
|
14
|
-
*
|
|
15
|
-
* 최종 확인: 2026-03-07
|
|
16
|
-
*/
|
|
17
|
-
(() => {
|
|
18
|
-
try {
|
|
19
|
-
const inner = document.querySelector('iframe');
|
|
20
|
-
if (!inner) return JSON.stringify({ error: 'no inner iframe' });
|
|
21
|
-
const doc = inner.contentDocument || inner.contentWindow?.document;
|
|
22
|
-
if (!doc) return JSON.stringify({ error: 'cannot access contentDocument' });
|
|
23
|
-
|
|
24
|
-
const root = doc.getElementById('root');
|
|
25
|
-
if (!root) return JSON.stringify({ error: 'no root element' });
|
|
26
|
-
|
|
27
|
-
const isVisible = root.offsetHeight > 0;
|
|
28
|
-
|
|
29
|
-
// ─── 1. Fiber에서 data 배열 추출 ───
|
|
30
|
-
const virtuosoList = doc.querySelector('[data-testid="virtuoso-item-list"]');
|
|
31
|
-
let fiberData = null;
|
|
32
|
-
|
|
33
|
-
if (virtuosoList && virtuosoList.children.length > 0) {
|
|
34
|
-
const firstItem = virtuosoList.children[0];
|
|
35
|
-
const fiberKey = Object.keys(firstItem).find(k =>
|
|
36
|
-
k.startsWith('__reactFiber') || k.startsWith('__reactInternalInstance')
|
|
37
|
-
);
|
|
38
|
-
if (fiberKey) {
|
|
39
|
-
let fiber = firstItem[fiberKey];
|
|
40
|
-
for (let d = 0; d < 25 && fiber; d++) {
|
|
41
|
-
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
42
|
-
if (props && props.data && Array.isArray(props.data) && props.data.length > 0) {
|
|
43
|
-
fiberData = props.data;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
fiber = fiber.return;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ─── 2. 메시지 파싱 ───
|
|
52
|
-
const messages = [];
|
|
53
|
-
|
|
54
|
-
if (fiberData) {
|
|
55
|
-
// ★ Fiber 기반: 가장 정확한 역할 판별
|
|
56
|
-
for (let i = 0; i < fiberData.length; i++) {
|
|
57
|
-
const item = fiberData[i];
|
|
58
|
-
if (!item || typeof item !== 'object') continue;
|
|
59
|
-
|
|
60
|
-
const msgType = item.type; // "say" or "ask"
|
|
61
|
-
const saySub = item.say; // "user_feedback", "text", "checkpoint_created", etc.
|
|
62
|
-
const askSub = item.ask; // "followup", "tool", "command", etc.
|
|
63
|
-
const text = item.text || '';
|
|
64
|
-
|
|
65
|
-
// 시스템 이벤트 스킵
|
|
66
|
-
if (saySub === 'checkpoint_created') continue;
|
|
67
|
-
if (saySub === 'api_req_started' || saySub === 'api_req_finished') continue;
|
|
68
|
-
if (saySub === 'shell_integration_warning') continue;
|
|
69
|
-
|
|
70
|
-
// 역할 판별
|
|
71
|
-
let role = 'assistant';
|
|
72
|
-
if (saySub === 'user_feedback') role = 'user';
|
|
73
|
-
if (saySub === 'user_feedback_diff') role = 'user';
|
|
74
|
-
|
|
75
|
-
// 콘텐츠 추출
|
|
76
|
-
let content = '';
|
|
77
|
-
if (text) {
|
|
78
|
-
// ask.followup의 text는 JSON일 수 있음
|
|
79
|
-
if (askSub === 'followup' && text.startsWith('{')) {
|
|
80
|
-
try {
|
|
81
|
-
const parsed = JSON.parse(text);
|
|
82
|
-
content = parsed.question || parsed.text || text;
|
|
83
|
-
} catch { content = text; }
|
|
84
|
-
} else {
|
|
85
|
-
content = text;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// DOM 텍스트 fallback (Fiber text가 비어있을 때)
|
|
90
|
-
if (!content && virtuosoList && virtuosoList.children[i]) {
|
|
91
|
-
content = (virtuosoList.children[i].textContent || '').trim();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 너무 짧거나 빈 콘텐츠 스킵
|
|
95
|
-
if (!content || content.length < 2) continue;
|
|
96
|
-
|
|
97
|
-
// 노이즈 정리
|
|
98
|
-
content = content
|
|
99
|
-
.replace(/CheckpointCompareRestore(Save)?/gi, '')
|
|
100
|
-
.replace(/^\s*API Request.*$/gm, '')
|
|
101
|
-
.replace(/^\s*Cost:.*$/gm, '')
|
|
102
|
-
.replace(/\s{3,}/g, '\n')
|
|
103
|
-
.trim();
|
|
104
|
-
|
|
105
|
-
if (content.length < 2) continue;
|
|
106
|
-
|
|
107
|
-
// 코드 블록 보존 (DOM에서 구조 추출)
|
|
108
|
-
if (virtuosoList.children[i]) {
|
|
109
|
-
const domItem = virtuosoList.children[i];
|
|
110
|
-
const preBlocks = domItem.querySelectorAll('pre');
|
|
111
|
-
if (preBlocks.length > 0 && role === 'assistant') {
|
|
112
|
-
let structured = '';
|
|
113
|
-
const walk = (node) => {
|
|
114
|
-
if (node.nodeType === 3) {
|
|
115
|
-
structured += node.textContent;
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (node.nodeType !== 1) return;
|
|
119
|
-
const el = node;
|
|
120
|
-
if (el.tagName === 'PRE') {
|
|
121
|
-
const codeEl = el.querySelector('code');
|
|
122
|
-
const lang = codeEl ? (codeEl.className.match(/language-(\w+)/)?.[1] || '') : '';
|
|
123
|
-
const code = (codeEl || el).textContent || '';
|
|
124
|
-
structured += '\n```' + lang + '\n' + code.trim() + '\n```\n';
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
for (const child of el.childNodes) walk(child);
|
|
128
|
-
};
|
|
129
|
-
walk(domItem);
|
|
130
|
-
const cleaned = structured.replace(/CheckpointCompareRestore(Save)?/gi, '').trim();
|
|
131
|
-
if (cleaned.length > content.length * 0.5) {
|
|
132
|
-
content = cleaned;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 길이 제한
|
|
138
|
-
if (content.length > 2000) content = content.substring(0, 2000) + '…';
|
|
139
|
-
|
|
140
|
-
messages.push({
|
|
141
|
-
role,
|
|
142
|
-
content,
|
|
143
|
-
timestamp: item.ts || (Date.now() - (fiberData.length - i) * 1000),
|
|
144
|
-
// 디버그: 메시지 서브타입
|
|
145
|
-
_type: msgType,
|
|
146
|
-
_sub: saySub || askSub,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
} else if (virtuosoList && virtuosoList.children.length > 0) {
|
|
150
|
-
// Fallback: DOM 기반 파싱 (Fiber 접근 실패 시)
|
|
151
|
-
for (let i = 0; i < virtuosoList.children.length; i++) {
|
|
152
|
-
const item = virtuosoList.children[i];
|
|
153
|
-
const rawText = (item.textContent || '').trim();
|
|
154
|
-
if (!rawText || rawText.length < 2) continue;
|
|
155
|
-
if (/^Checkpoint(Compare|Restore|Save)/i.test(rawText)) continue;
|
|
156
|
-
if (/^(Thinking\.\.\.|Loading\.\.\.)$/i.test(rawText)) continue;
|
|
157
|
-
|
|
158
|
-
let role = 'assistant';
|
|
159
|
-
let content = rawText
|
|
160
|
-
.replace(/CheckpointCompareRestore(Save)?/gi, '')
|
|
161
|
-
.replace(/\s{3,}/g, '\n')
|
|
162
|
-
.trim();
|
|
163
|
-
if (content.length < 2) continue;
|
|
164
|
-
if (content.length > 2000) content = content.substring(0, 2000) + '…';
|
|
165
|
-
|
|
166
|
-
messages.push({ role, content, timestamp: Date.now() - (virtuosoList.children.length - i) * 1000 });
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ─── 3. 입력 필드 ───
|
|
171
|
-
let inputContent = '';
|
|
172
|
-
const chatInput = doc.querySelector('[data-testid="chat-input"]');
|
|
173
|
-
if (chatInput) {
|
|
174
|
-
inputContent = chatInput.value || chatInput.textContent || '';
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// ─── 4. 상태 판별 ───
|
|
178
|
-
let status = 'idle';
|
|
179
|
-
const buttons = Array.from(doc.querySelectorAll('button'))
|
|
180
|
-
.filter(b => b.offsetWidth > 0);
|
|
181
|
-
const buttonTexts = buttons.map(b => (b.textContent || '').trim().toLowerCase());
|
|
182
|
-
|
|
183
|
-
if (buttonTexts.includes('cancel')) status = 'generating';
|
|
184
|
-
|
|
185
|
-
// Fiber data에서 마지막 type=ask인지 확인
|
|
186
|
-
if (fiberData && fiberData.length > 0) {
|
|
187
|
-
const last = fiberData[fiberData.length - 1];
|
|
188
|
-
if (last.type === 'ask') {
|
|
189
|
-
if (last.ask === 'followup') status = 'waiting_approval';
|
|
190
|
-
if (last.ask === 'tool' || last.ask === 'command') status = 'waiting_approval';
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// 버튼 기반 보완
|
|
195
|
-
const approvalPatterns = /^(proceed|approve|allow|accept|save|run command|yes|confirm)/i;
|
|
196
|
-
if (buttonTexts.some(b => approvalPatterns.test(b))) status = 'waiting_approval';
|
|
197
|
-
|
|
198
|
-
if (!isVisible && messages.length === 0) status = 'panel_hidden';
|
|
199
|
-
|
|
200
|
-
// ─── 5. 모델/모드 ───
|
|
201
|
-
let model = '';
|
|
202
|
-
const modeSwitch = doc.querySelector('[data-testid="mode-switch"]');
|
|
203
|
-
if (modeSwitch) model = (modeSwitch.textContent || '').trim();
|
|
204
|
-
if (!model) {
|
|
205
|
-
const modelSel = doc.querySelector('[data-testid*="model"], [aria-label*="model" i]');
|
|
206
|
-
if (modelSel) model = (modelSel.textContent || '').trim();
|
|
207
|
-
}
|
|
208
|
-
const mode = modeSwitch ? (modeSwitch.textContent || '').trim() : '';
|
|
209
|
-
|
|
210
|
-
// ─── 6. 승인 모달 ───
|
|
211
|
-
let activeModal = null;
|
|
212
|
-
if (status === 'waiting_approval') {
|
|
213
|
-
const approvalBtns = buttons
|
|
214
|
-
.map(b => (b.textContent || '').trim())
|
|
215
|
-
.filter(t => t && t.length > 0 && t.length < 40 &&
|
|
216
|
-
/proceed|approve|allow|accept|run|yes|reject|deny|cancel|no|skip|save|confirm/i.test(t));
|
|
217
|
-
|
|
218
|
-
let modalMessage = 'Cline wants to perform an action';
|
|
219
|
-
if (fiberData && fiberData.length > 0) {
|
|
220
|
-
const last = fiberData[fiberData.length - 1];
|
|
221
|
-
if (last.ask === 'followup' && last.text) {
|
|
222
|
-
try {
|
|
223
|
-
const parsed = JSON.parse(last.text);
|
|
224
|
-
modalMessage = parsed.question || last.text.substring(0, 200);
|
|
225
|
-
} catch { modalMessage = last.text.substring(0, 200); }
|
|
226
|
-
} else if (last.ask === 'tool' || last.ask === 'command') {
|
|
227
|
-
modalMessage = `Cline wants to use ${last.ask}`;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (approvalBtns.length > 0) {
|
|
232
|
-
activeModal = { message: modalMessage, buttons: [...new Set(approvalBtns)] };
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// ─── 7. 토큰/비용 ───
|
|
237
|
-
let tokenInfo = '';
|
|
238
|
-
const costEl = doc.querySelector('[data-testid*="cost"], [data-testid*="token"]');
|
|
239
|
-
if (costEl) tokenInfo = (costEl.textContent || '').trim();
|
|
240
|
-
|
|
241
|
-
return JSON.stringify({
|
|
242
|
-
agentType: 'cline',
|
|
243
|
-
agentName: 'Cline',
|
|
244
|
-
extensionId: 'saoudrizwan.claude-dev',
|
|
245
|
-
status,
|
|
246
|
-
isVisible,
|
|
247
|
-
messages: messages.slice(-30),
|
|
248
|
-
inputContent,
|
|
249
|
-
model,
|
|
250
|
-
mode,
|
|
251
|
-
tokenInfo,
|
|
252
|
-
activeModal,
|
|
253
|
-
});
|
|
254
|
-
} catch (e) {
|
|
255
|
-
return JSON.stringify({ error: e.message || String(e) });
|
|
256
|
-
}
|
|
257
|
-
})()
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline v1 — resolve_action
|
|
3
|
-
*
|
|
4
|
-
* Cline의 승인/거부 처리.
|
|
5
|
-
* Cline은 button 태그 외에 vscode-button 웹 컴포넌트도 사용.
|
|
6
|
-
* chatState.primaryButtonText / secondaryButtonText로 정확한 매칭.
|
|
7
|
-
*
|
|
8
|
-
* 파라미터: ${ ACTION } — "approve" 또는 "reject"
|
|
9
|
-
*
|
|
10
|
-
* 전략:
|
|
11
|
-
* 1. chatState.primaryButtonText == Approve → primary vscode-button 클릭
|
|
12
|
-
* 2. data-testid 기반 탐색
|
|
13
|
-
* 3. 텍스트 매칭 (button + vscode-button)
|
|
14
|
-
* 4. Fiber onSendMessage로 직접 승인 전달
|
|
15
|
-
*
|
|
16
|
-
* 최종 확인: 2026-03-07
|
|
17
|
-
*/
|
|
18
|
-
(() => {
|
|
19
|
-
try {
|
|
20
|
-
const inner = document.querySelector('iframe');
|
|
21
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
22
|
-
if (!doc) return false;
|
|
23
|
-
|
|
24
|
-
const action = ${ ACTION };
|
|
25
|
-
const approvePatterns = ['proceed', 'approve', 'allow', 'accept', 'save', 'run', 'yes', 'confirm', 'resume'];
|
|
26
|
-
const rejectPatterns = ['reject', 'deny', 'cancel', 'no', 'skip'];
|
|
27
|
-
const patterns = action === 'approve' ? approvePatterns : rejectPatterns;
|
|
28
|
-
|
|
29
|
-
// ─── 모든 클릭 가능 요소 수집 (button + vscode-button) ───
|
|
30
|
-
const allBtns = [
|
|
31
|
-
...Array.from(doc.querySelectorAll('button')),
|
|
32
|
-
...Array.from(doc.querySelectorAll('vscode-button')),
|
|
33
|
-
].filter(b => b.offsetWidth > 0 && b.offsetHeight > 0);
|
|
34
|
-
|
|
35
|
-
// ─── 1단계: data-testid 기반 ───
|
|
36
|
-
for (const btn of allBtns) {
|
|
37
|
-
const testId = (btn.getAttribute('data-testid') || '').toLowerCase();
|
|
38
|
-
if (action === 'approve' && (testId.includes('approve') || testId.includes('proceed') || testId.includes('accept') || testId.includes('run') || testId.includes('primary'))) {
|
|
39
|
-
btn.click(); return true;
|
|
40
|
-
}
|
|
41
|
-
if (action === 'reject' && (testId.includes('reject') || testId.includes('deny') || testId.includes('cancel') || testId.includes('secondary'))) {
|
|
42
|
-
btn.click(); return true;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ─── 2단계: 텍스트 매칭 ───
|
|
47
|
-
for (const btn of allBtns) {
|
|
48
|
-
const text = (btn.textContent || '').trim().toLowerCase();
|
|
49
|
-
if (text.length === 0 || text.length > 40) continue;
|
|
50
|
-
if (patterns.some(p => text.startsWith(p) || text === p || text.includes(p))) {
|
|
51
|
-
btn.click(); return true;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ─── 3단계: aria-label ───
|
|
56
|
-
for (const btn of allBtns) {
|
|
57
|
-
const label = (btn.getAttribute('aria-label') || '').toLowerCase();
|
|
58
|
-
if (patterns.some(p => label.includes(p))) {
|
|
59
|
-
btn.click(); return true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ─── 4단계: chatState 기반 — primary/secondary 버튼 직접 매칭 ───
|
|
64
|
-
// chatState.primaryButtonText = "Approve", secondaryButtonText = "Reject"
|
|
65
|
-
// 가장 큰 vscode-button이 primary 버튼
|
|
66
|
-
const vscBtns = Array.from(doc.querySelectorAll('vscode-button'))
|
|
67
|
-
.filter(b => b.offsetWidth > 100); // 큰 버튼만
|
|
68
|
-
if (vscBtns.length > 0) {
|
|
69
|
-
if (action === 'approve') {
|
|
70
|
-
// 가장 큰 버튼이 primary
|
|
71
|
-
vscBtns.sort((a, b) => b.offsetWidth - a.offsetWidth);
|
|
72
|
-
vscBtns[0].click(); return true;
|
|
73
|
-
}
|
|
74
|
-
// reject: 가장 작은 큰 버튼
|
|
75
|
-
if (action === 'reject' && vscBtns.length > 1) {
|
|
76
|
-
vscBtns.sort((a, b) => a.offsetWidth - b.offsetWidth);
|
|
77
|
-
vscBtns[0].click(); return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return false;
|
|
82
|
-
} catch { return false; }
|
|
83
|
-
})()
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline v1 — send_message
|
|
3
|
-
*
|
|
4
|
-
* 구조:
|
|
5
|
-
* 1. outer webview → inner iframe의 contentDocument 접근
|
|
6
|
-
* 2. data-testid="chat-input" textarea에 값 설정 (React controlled)
|
|
7
|
-
* 3. React Fiber에서 onSend 함수 찾아 직접 호출 (가장 확실한 방법)
|
|
8
|
-
* 4. Fallback: data-testid="send-button" 클릭 or Enter 키
|
|
9
|
-
*
|
|
10
|
-
* ⚠️ Cline의 send-button은 DIV 태그이며, 일반 click 이벤트로는 React가
|
|
11
|
-
* 전송을 처리하지 않음. Fiber onSend()를 직접 호출해야 정확하게 동작.
|
|
12
|
-
*
|
|
13
|
-
* 최종 확인: 2026-03-07
|
|
14
|
-
*/
|
|
15
|
-
(async () => {
|
|
16
|
-
try {
|
|
17
|
-
const inner = document.querySelector('iframe');
|
|
18
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
19
|
-
if (!doc) return 'error: no doc';
|
|
20
|
-
|
|
21
|
-
// ─── 1. 입력 필드 찾기 ───
|
|
22
|
-
let target = doc.querySelector('[data-testid="chat-input"]');
|
|
23
|
-
if (!target) {
|
|
24
|
-
const textareas = doc.querySelectorAll('textarea');
|
|
25
|
-
for (const ta of textareas) {
|
|
26
|
-
if (ta.offsetParent !== null && ta.offsetHeight > 20) {
|
|
27
|
-
target = ta;
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (!target) return 'error: no chat-input';
|
|
33
|
-
|
|
34
|
-
// ─── 2. React controlled input 값 설정 ───
|
|
35
|
-
const proto = inner.contentWindow?.HTMLTextAreaElement?.prototype
|
|
36
|
-
|| HTMLTextAreaElement.prototype;
|
|
37
|
-
const nativeSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
38
|
-
|
|
39
|
-
if (nativeSetter) {
|
|
40
|
-
nativeSetter.call(target, ${ MESSAGE });
|
|
41
|
-
} else {
|
|
42
|
-
target.value = ${ MESSAGE };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// React 이벤트 트리거
|
|
46
|
-
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
47
|
-
target.dispatchEvent(new Event('change', { bubbles: true }));
|
|
48
|
-
|
|
49
|
-
// React setState 반영 대기
|
|
50
|
-
await new Promise(r => setTimeout(r, 300));
|
|
51
|
-
|
|
52
|
-
// ─── 3. Fiber onSend 직접 호출 (최우선) ───
|
|
53
|
-
const allEls = doc.querySelectorAll('*');
|
|
54
|
-
for (const el of allEls) {
|
|
55
|
-
const fk = Object.keys(el).find(k => k.startsWith('__reactFiber'));
|
|
56
|
-
if (!fk) continue;
|
|
57
|
-
let fiber = el[fk];
|
|
58
|
-
for (let d = 0; d < 15 && fiber; d++) {
|
|
59
|
-
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
60
|
-
if (props && typeof props.onSend === 'function') {
|
|
61
|
-
props.onSend();
|
|
62
|
-
return 'sent';
|
|
63
|
-
}
|
|
64
|
-
fiber = fiber.return;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─── 4. Fallback: send-button 클릭 ───
|
|
69
|
-
const sendBtn = doc.querySelector('[data-testid="send-button"]');
|
|
70
|
-
if (sendBtn) {
|
|
71
|
-
try {
|
|
72
|
-
const rect = sendBtn.getBoundingClientRect();
|
|
73
|
-
const opts = {
|
|
74
|
-
bubbles: true, cancelable: true, view: inner.contentWindow,
|
|
75
|
-
clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2
|
|
76
|
-
};
|
|
77
|
-
sendBtn.dispatchEvent(new MouseEvent('mousedown', opts));
|
|
78
|
-
sendBtn.dispatchEvent(new MouseEvent('mouseup', opts));
|
|
79
|
-
sendBtn.dispatchEvent(new MouseEvent('click', opts));
|
|
80
|
-
return 'sent';
|
|
81
|
-
} catch (e) { }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ─── 5. 최후 Fallback: Enter 키 ───
|
|
85
|
-
target.focus();
|
|
86
|
-
target.dispatchEvent(new KeyboardEvent('keydown', {
|
|
87
|
-
key: 'Enter', code: 'Enter', keyCode: 13,
|
|
88
|
-
bubbles: true, cancelable: true,
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
return 'sent';
|
|
92
|
-
} catch (e) {
|
|
93
|
-
return 'error: ' + e.message;
|
|
94
|
-
}
|
|
95
|
-
})()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline — set_mode
|
|
3
|
-
* 모드 selector에서 지정된 모드 선택
|
|
4
|
-
* ${MODE} → JSON.stringify(modeName)
|
|
5
|
-
* → { success: boolean }
|
|
6
|
-
*/
|
|
7
|
-
(async () => {
|
|
8
|
-
try {
|
|
9
|
-
const inner = document.querySelector('iframe');
|
|
10
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
11
|
-
if (!doc) return JSON.stringify({ success: false, error: 'no doc' });
|
|
12
|
-
|
|
13
|
-
const target = ${MODE};
|
|
14
|
-
const trigger = doc.querySelector('[data-testid="mode-selector-trigger"], [data-testid="mode-switch"]');
|
|
15
|
-
if (!trigger) return JSON.stringify({ success: false, error: 'no mode trigger' });
|
|
16
|
-
|
|
17
|
-
// 드롭다운 열기
|
|
18
|
-
trigger.click();
|
|
19
|
-
await new Promise(r => setTimeout(r, 300));
|
|
20
|
-
|
|
21
|
-
// 옵션에서 타겟 모드 찾기
|
|
22
|
-
const options = doc.querySelectorAll('[data-testid*="mode-option"], [role="option"], [class*="dropdown"] [class*="item"], [class*="listbox"] [class*="option"]');
|
|
23
|
-
for (const opt of options) {
|
|
24
|
-
const text = (opt.textContent || '').trim();
|
|
25
|
-
if (text === target || text.toLowerCase().includes(target.toLowerCase())) {
|
|
26
|
-
opt.click();
|
|
27
|
-
await new Promise(r => setTimeout(r, 200));
|
|
28
|
-
return JSON.stringify({ success: true, mode: text });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 닫기
|
|
33
|
-
doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
34
|
-
return JSON.stringify({ success: false, error: 'mode not found: ' + target });
|
|
35
|
-
} catch (e) { return JSON.stringify({ success: false, error: e.message }); }
|
|
36
|
-
})()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline — set_model
|
|
3
|
-
* 드롭다운에서 지정된 모델 선택
|
|
4
|
-
* ${MODEL} → JSON.stringify(modelName)
|
|
5
|
-
* → { success: boolean }
|
|
6
|
-
*/
|
|
7
|
-
(async () => {
|
|
8
|
-
try {
|
|
9
|
-
const inner = document.querySelector('iframe');
|
|
10
|
-
const doc = inner?.contentDocument || inner?.contentWindow?.document;
|
|
11
|
-
if (!doc) return JSON.stringify({ success: false, error: 'no doc' });
|
|
12
|
-
|
|
13
|
-
const target = ${MODEL};
|
|
14
|
-
const trigger = doc.querySelector('[data-testid="model-selector"], [data-testid*="model-dropdown"], [data-testid="dropdown-trigger"]');
|
|
15
|
-
if (!trigger) return JSON.stringify({ success: false, error: 'no model trigger' });
|
|
16
|
-
|
|
17
|
-
// 드롭다운 열기
|
|
18
|
-
trigger.click();
|
|
19
|
-
await new Promise(r => setTimeout(r, 300));
|
|
20
|
-
|
|
21
|
-
// 옵션에서 타겟 모델 찾기
|
|
22
|
-
const options = doc.querySelectorAll('[data-testid*="dropdown-option"], [role="option"], [class*="dropdown"] [class*="item"], [class*="listbox"] [class*="option"]');
|
|
23
|
-
for (const opt of options) {
|
|
24
|
-
const text = (opt.textContent || '').trim();
|
|
25
|
-
if (text === target || text.includes(target)) {
|
|
26
|
-
opt.click();
|
|
27
|
-
await new Promise(r => setTimeout(r, 200));
|
|
28
|
-
return JSON.stringify({ success: true, model: text });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 닫기
|
|
33
|
-
doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
34
|
-
return JSON.stringify({ success: false, error: 'model not found: ' + target });
|
|
35
|
-
} catch (e) { return JSON.stringify({ success: false, error: e.message }); }
|
|
36
|
-
})()
|