adhdev 0.1.47 → 0.1.49
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/index.js +1177 -454
- package/package.json +1 -1
- package/providers/_builtin/acp/codex-cli/provider.js +51 -0
- package/providers/_builtin/acp/goose/provider.js +32 -0
- package/providers/_builtin/acp/opencode/provider.js +32 -0
- package/providers/_builtin/extension/cline/provider.js +22 -0
- package/providers/_builtin/extension/cline/scripts/list_models.js +43 -0
- package/providers/_builtin/extension/cline/scripts/list_modes.js +35 -0
- package/providers/_builtin/extension/cline/scripts/set_mode.js +36 -0
- package/providers/_builtin/extension/cline/scripts/set_model.js +36 -0
- package/providers/_builtin/extension/roo-code/provider.js +151 -0
- package/providers/_builtin/ide/antigravity/provider.js +22 -0
- package/providers/_builtin/ide/antigravity/scripts/list_models.js +38 -0
- package/providers/_builtin/ide/antigravity/scripts/list_modes.js +48 -0
- package/providers/_builtin/ide/antigravity/scripts/resolve_action.js +55 -51
- package/providers/_builtin/ide/antigravity/scripts/set_mode.js +34 -0
- package/providers/_builtin/ide/antigravity/scripts/set_model.js +47 -0
- package/providers/_builtin/ide/cursor/provider.js +259 -11
- package/providers/_builtin/ide/kiro/provider.js +62 -0
- package/providers/_builtin/ide/pearai/provider.js +62 -0
- package/providers/_builtin/ide/trae/provider.js +62 -0
|
@@ -1,64 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Antigravity v1 — resolve_action
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 버튼 찾기 + 좌표 반환 (CDP Input.dispatchMouseEvent로 클릭)
|
|
5
|
+
* 파라미터: ${BUTTON_TEXT}
|
|
6
|
+
*
|
|
7
|
+
* 핵심: viewport 안에 보이는 버튼 중 마지막(최신) 매칭 우선
|
|
7
8
|
*/
|
|
8
9
|
(() => {
|
|
9
10
|
const want = ${ BUTTON_TEXT };
|
|
11
|
+
const wantNorm = (want || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
12
|
+
|
|
10
13
|
function norm(t) { return (t || '').replace(/\s+/g, ' ').trim().toLowerCase(); }
|
|
14
|
+
|
|
11
15
|
function matches(el) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
if (t
|
|
15
|
-
if (t
|
|
16
|
-
if (
|
|
17
|
-
if (
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
16
|
+
const raw = (el.textContent || '').trim();
|
|
17
|
+
const t = norm(raw);
|
|
18
|
+
if (!t || t.length > 80) return false;
|
|
19
|
+
if (t === wantNorm) return true;
|
|
20
|
+
if (t.indexOf(wantNorm) === 0) return true;
|
|
21
|
+
if (wantNorm.indexOf(t) >= 0 && t.length > 2) return true;
|
|
22
|
+
if (t.indexOf(wantNorm) >= 0) return true;
|
|
23
|
+
if (/^(run|approve|allow|accept|always|yes)\b/.test(wantNorm)) {
|
|
24
|
+
if (/^run\b/.test(t)) return true;
|
|
25
|
+
if (/^allow\b/.test(t)) return true;
|
|
26
|
+
if (/^accept\b/.test(t) && !t.includes('changes')) return true;
|
|
27
|
+
if (/^always\b/.test(t)) return true;
|
|
28
|
+
}
|
|
29
|
+
if (/^(reject|deny|no|abort)\b/.test(wantNorm)) {
|
|
30
|
+
if (/^reject\b/.test(t) && !t.includes('changes')) return true;
|
|
31
|
+
if (/^deny\b/.test(t)) return true;
|
|
32
|
+
}
|
|
33
|
+
if (/^(skip|cancel)\b/.test(wantNorm)) {
|
|
34
|
+
if (/^skip\b/.test(t) || /^cancel\b/.test(t)) return true;
|
|
35
|
+
}
|
|
20
36
|
return false;
|
|
21
37
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for (const b of btns) { if (matches(b)) { doClick(b); return true; } }
|
|
38
|
-
}
|
|
39
|
-
// 2. Try overlay containers
|
|
40
|
-
const overlay = document.querySelector('.quick-agent-overlay-container, [class*="overlay-container"]');
|
|
41
|
-
if (overlay) {
|
|
42
|
-
const btns = Array.from(overlay.querySelectorAll(sel)).filter(b => b.offsetWidth > 0);
|
|
43
|
-
for (const b of btns) { if (matches(b)) { doClick(b); return true; } }
|
|
44
|
-
}
|
|
45
|
-
// 3. Try dialog boxes
|
|
46
|
-
const dialog = document.querySelector('.monaco-dialog-box, .monaco-modal-block, [role="dialog"]');
|
|
47
|
-
if (dialog) {
|
|
48
|
-
const btns = Array.from(dialog.querySelectorAll(sel)).filter(b => b.offsetWidth > 0);
|
|
49
|
-
for (const b of btns) { if (matches(b)) { doClick(b); return true; } }
|
|
50
|
-
}
|
|
51
|
-
// 4. Global search — find any visible matching button
|
|
52
|
-
const allBtns = Array.from(document.querySelectorAll(sel)).filter(b => b.offsetWidth > 0 && b.getBoundingClientRect().height > 0);
|
|
53
|
-
for (const b of allBtns) { if (matches(b)) { doClick(b); return true; } }
|
|
54
|
-
// 5. If Run was requested, try Enter key on focused element
|
|
55
|
-
if (want === 'run') {
|
|
56
|
-
const focused = document.activeElement;
|
|
57
|
-
if (focused) {
|
|
58
|
-
focused.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }));
|
|
59
|
-
focused.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }));
|
|
60
|
-
return true;
|
|
38
|
+
|
|
39
|
+
const sel = 'button, [role="button"]';
|
|
40
|
+
const allBtns = [...document.querySelectorAll(sel)].filter(b => {
|
|
41
|
+
if (!b.offsetWidth || !b.getBoundingClientRect().height) return false;
|
|
42
|
+
const rect = b.getBoundingClientRect();
|
|
43
|
+
// viewport 안에 보이는 것만 (y > 0, y < window.innerHeight)
|
|
44
|
+
return rect.y > 0 && rect.y < window.innerHeight;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 마지막(최신) 매칭 우선 — 역순 검색
|
|
48
|
+
let found = null;
|
|
49
|
+
for (let i = allBtns.length - 1; i >= 0; i--) {
|
|
50
|
+
if (matches(allBtns[i])) {
|
|
51
|
+
found = allBtns[i];
|
|
52
|
+
break;
|
|
61
53
|
}
|
|
62
54
|
}
|
|
63
|
-
|
|
55
|
+
|
|
56
|
+
if (found) {
|
|
57
|
+
const rect = found.getBoundingClientRect();
|
|
58
|
+
return JSON.stringify({
|
|
59
|
+
found: true,
|
|
60
|
+
text: found.textContent?.trim()?.substring(0, 40),
|
|
61
|
+
x: Math.round(rect.x + rect.width / 2),
|
|
62
|
+
y: Math.round(rect.y + rect.height / 2),
|
|
63
|
+
w: Math.round(rect.width),
|
|
64
|
+
h: Math.round(rect.height)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return JSON.stringify({ found: false, want: wantNorm });
|
|
64
68
|
})()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — set_mode
|
|
3
|
+
* Conversation mode 패널에서 Planning/Fast 클릭
|
|
4
|
+
* ${MODE} → JSON.stringify(modeName)
|
|
5
|
+
* → { success: boolean, mode?: string }
|
|
6
|
+
*/
|
|
7
|
+
(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const target = ${MODE};
|
|
10
|
+
|
|
11
|
+
// "Conversation mode" 헤더의 부모에서 .font-medium 항목 찾기
|
|
12
|
+
const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
|
|
13
|
+
for (const header of headers) {
|
|
14
|
+
if (header.textContent?.trim() === 'Conversation mode') {
|
|
15
|
+
const parent = header.parentElement;
|
|
16
|
+
if (!parent) continue;
|
|
17
|
+
const items = parent.querySelectorAll('.font-medium');
|
|
18
|
+
for (const item of items) {
|
|
19
|
+
const text = item.textContent?.trim();
|
|
20
|
+
if (text && text.toLowerCase() === target.toLowerCase()) {
|
|
21
|
+
item.click();
|
|
22
|
+
await new Promise(r => setTimeout(r, 300));
|
|
23
|
+
return JSON.stringify({ success: true, mode: text });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return JSON.stringify({ success: false, error: 'mode not found: ' + target });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
33
|
+
}
|
|
34
|
+
})()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — set_model
|
|
3
|
+
* antigravity-agent-side-panel 모델 드롭다운에서 모델 선택
|
|
4
|
+
* ${MODEL} → JSON.stringify(modelName)
|
|
5
|
+
* → { success: boolean, model?: string }
|
|
6
|
+
*/
|
|
7
|
+
(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const target = ${MODEL};
|
|
10
|
+
|
|
11
|
+
// 1. 모델 드롭다운이 열려 있는 경우 → 직접 선택
|
|
12
|
+
const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
const label = item.querySelector('.text-xs.font-medium');
|
|
15
|
+
const text = (label || item).textContent?.trim();
|
|
16
|
+
if (text && (text === target || text.toLowerCase().includes(target.toLowerCase()))) {
|
|
17
|
+
item.click();
|
|
18
|
+
await new Promise(r => setTimeout(r, 200));
|
|
19
|
+
return JSON.stringify({ success: true, model: text });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. 드롭다운이 닫혀 있으면 → 트리거 버튼 클릭해서 열기
|
|
24
|
+
const trigger = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
|
|
25
|
+
if (trigger) {
|
|
26
|
+
trigger.click();
|
|
27
|
+
await new Promise(r => setTimeout(r, 400));
|
|
28
|
+
|
|
29
|
+
// 다시 항목 탐색
|
|
30
|
+
const newItems = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
31
|
+
for (const item of newItems) {
|
|
32
|
+
const label = item.querySelector('.text-xs.font-medium');
|
|
33
|
+
const text = (label || item).textContent?.trim();
|
|
34
|
+
if (text && (text === target || text.toLowerCase().includes(target.toLowerCase()))) {
|
|
35
|
+
item.click();
|
|
36
|
+
return JSON.stringify({ success: true, model: text });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// 못 찾으면 드롭다운 닫기
|
|
40
|
+
trigger.click();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return JSON.stringify({ success: false, error: 'model not found: ' + target });
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
46
|
+
}
|
|
47
|
+
})()
|
|
@@ -35,30 +35,48 @@ module.exports = {
|
|
|
35
35
|
|
|
36
36
|
// Detect approval dialogs
|
|
37
37
|
let activeModal = null;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
// Primary signal: Cursor uses .run-command-review-active on conversations container
|
|
40
|
+
const reviewActive = !!document.querySelector('.run-command-review-active');
|
|
41
|
+
|
|
42
|
+
// Also check clickable elements (Cursor uses divs with cursor-pointer, not buttons)
|
|
43
|
+
// Note: Cursor concatenates button text with shortcut key labels (e.g. "SkipEsc", "Run⏎")
|
|
44
|
+
const clickableEls = [...document.querySelectorAll('button, [role="button"], .cursor-pointer')].filter(b =>
|
|
45
|
+
b.offsetWidth > 0 && /^(accept|reject|approve|deny|run|skip|allow|cancel)/i.test((b.textContent || b.getAttribute('aria-label') || '').trim())
|
|
40
46
|
);
|
|
41
|
-
|
|
47
|
+
|
|
48
|
+
if (reviewActive || clickableEls.length > 0) {
|
|
42
49
|
status = 'waiting_approval';
|
|
50
|
+
const reviewContainer = document.querySelector('.run-command-review-active');
|
|
51
|
+
// Find the tool call context — last rendered message has the command being reviewed
|
|
52
|
+
const renderedMsgs = reviewContainer?.querySelectorAll('.composer-rendered-message');
|
|
53
|
+
const lastRendered = renderedMsgs?.length ? renderedMsgs[renderedMsgs.length - 1] : null;
|
|
54
|
+
const toolMsg = lastRendered || reviewContainer?.querySelector('.composer-tool-former-message:last-of-type');
|
|
43
55
|
activeModal = {
|
|
44
|
-
message:
|
|
45
|
-
buttons:
|
|
56
|
+
message: toolMsg?.textContent?.trim()?.substring(0, 200) || 'Command approval required',
|
|
57
|
+
buttons: clickableEls.map(b => b.textContent.trim().replace(/[⏎↵]/g, '').trim()).filter(Boolean),
|
|
46
58
|
};
|
|
47
59
|
}
|
|
48
60
|
|
|
49
61
|
const msgs = [];
|
|
50
62
|
document.querySelectorAll('.composer-human-ai-pair-container').forEach((p, i) => {
|
|
51
63
|
const h = p.querySelector('.composer-human-message');
|
|
52
|
-
|
|
64
|
+
const userText = h ? h.textContent.trim().substring(0, 6000) : '';
|
|
65
|
+
if (h) msgs.push({ role: 'user', content: userText, index: msgs.length });
|
|
66
|
+
// pair의 첫 번째 자식은 사용자 메시지 블록 — 그 안의 rendered는 건너뜀
|
|
67
|
+
const firstChild = p.children[0];
|
|
53
68
|
p.querySelectorAll('.composer-rendered-message').forEach(a => {
|
|
69
|
+
if (firstChild && firstChild.contains(a)) return;
|
|
54
70
|
if (a.closest('.composer-human-message')) return;
|
|
55
71
|
const t = a.textContent.trim();
|
|
56
|
-
if (t) msgs.push({ role: 'assistant', content: t.substring(0, 6000), index: msgs.length });
|
|
72
|
+
if (t && t !== userText) msgs.push({ role: 'assistant', content: t.substring(0, 6000), index: msgs.length });
|
|
57
73
|
});
|
|
58
74
|
});
|
|
59
75
|
const inputEl = document.querySelector('.aislash-editor-input[contenteditable="true"]');
|
|
60
76
|
const inputContent = inputEl?.textContent?.trim() || '';
|
|
61
|
-
|
|
77
|
+
const titleParts = document.title.split(' — ');
|
|
78
|
+
const projectTitle = (titleParts.length >= 2 ? titleParts[titleParts.length - 2] : titleParts[0] || '').trim();
|
|
79
|
+
return JSON.stringify({ id, status, title: projectTitle, messages: msgs, inputContent, activeModal });
|
|
62
80
|
} catch(e) {
|
|
63
81
|
return JSON.stringify({ id: '', status: 'error', messages: [] });
|
|
64
82
|
}
|
|
@@ -185,11 +203,12 @@ module.exports = {
|
|
|
185
203
|
resolveAction(params) {
|
|
186
204
|
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
187
205
|
const buttonText = params?.button || params?.buttonText
|
|
188
|
-
|| (action === 'approve' ? '
|
|
206
|
+
|| (action === 'approve' ? 'Run' : action === 'reject' ? 'Skip' : action);
|
|
189
207
|
return `(() => {
|
|
190
208
|
try {
|
|
191
|
-
const btns = [...document.querySelectorAll('button, [role="button"]')].filter(b => b.offsetWidth > 0);
|
|
192
|
-
const
|
|
209
|
+
const btns = [...document.querySelectorAll('button, [role="button"], .cursor-pointer')].filter(b => b.offsetWidth > 0);
|
|
210
|
+
const searchText = ${JSON.stringify((buttonText||'').toLowerCase())};
|
|
211
|
+
const target = btns.find(b => (b.textContent||'').trim().replace(/[⏎↵]/g, '').trim().toLowerCase().includes(searchText));
|
|
193
212
|
if (target) { target.click(); return JSON.stringify({ resolved: true, clicked: target.textContent.trim() }); }
|
|
194
213
|
return JSON.stringify({ resolved: false, available: btns.map(b => b.textContent.trim()).filter(Boolean).slice(0, 15) });
|
|
195
214
|
} catch(e) { return JSON.stringify({ resolved: false, error: e.message }); }
|
|
@@ -236,6 +255,235 @@ module.exports = {
|
|
|
236
255
|
if (closeBtn) { closeBtn.click(); return JSON.stringify({ dismissed: true }); }
|
|
237
256
|
return JSON.stringify({ dismissed: false, error: 'Close button not found' });
|
|
238
257
|
} catch(e) { return JSON.stringify({ dismissed: false, error: e.message }); }
|
|
258
|
+
})()`;
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* listModels → { models: string[], current: string }
|
|
263
|
+
* .composer-unified-dropdown-model 클릭 → Auto 토글 끄기 → 전체 모델 목록
|
|
264
|
+
*/
|
|
265
|
+
listModels(params) {
|
|
266
|
+
return `(async () => {
|
|
267
|
+
try {
|
|
268
|
+
let current = '';
|
|
269
|
+
const models = [];
|
|
270
|
+
|
|
271
|
+
// 현재 모델: .composer-unified-dropdown-model 텍스트
|
|
272
|
+
const modelBtn = document.querySelector('.composer-unified-dropdown-model');
|
|
273
|
+
if (modelBtn) {
|
|
274
|
+
current = modelBtn.textContent?.trim() || '';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 드롭다운 열기
|
|
278
|
+
if (modelBtn) {
|
|
279
|
+
modelBtn.click();
|
|
280
|
+
await new Promise(r => setTimeout(r, 500));
|
|
281
|
+
|
|
282
|
+
const menu = document.querySelector('[data-testid="model-picker-menu"]');
|
|
283
|
+
if (menu) {
|
|
284
|
+
// Auto 토글 확인 및 끄기
|
|
285
|
+
const autoItem = menu.querySelector('.composer-unified-context-menu-item[data-is-selected="true"]');
|
|
286
|
+
const autoToggle = autoItem ? [...autoItem.querySelectorAll('[class*="rounded-full"]')].find(el => el.offsetWidth === 24 && el.offsetHeight === 14) : null;
|
|
287
|
+
let wasAutoOn = false;
|
|
288
|
+
if (autoToggle) {
|
|
289
|
+
const bgStyle = autoToggle.getAttribute('style') || '';
|
|
290
|
+
wasAutoOn = bgStyle.includes('green');
|
|
291
|
+
if (wasAutoOn) {
|
|
292
|
+
autoToggle.click();
|
|
293
|
+
await new Promise(r => setTimeout(r, 500));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 모델 목록 수집 (Auto 끈 상태)
|
|
298
|
+
const refreshedMenu = document.querySelector('[data-testid="model-picker-menu"]');
|
|
299
|
+
if (refreshedMenu) {
|
|
300
|
+
const items = refreshedMenu.querySelectorAll('.composer-unified-context-menu-item');
|
|
301
|
+
for (const item of items) {
|
|
302
|
+
const nameEl = item.querySelector('.monaco-highlighted-label');
|
|
303
|
+
const name = nameEl?.textContent?.trim() || '';
|
|
304
|
+
if (name && name !== 'Add Models') {
|
|
305
|
+
// Think 모드 감지: codicon-br (brain) 아이콘
|
|
306
|
+
const hasBrain = !!item.querySelector('[class*="codicon-br"]');
|
|
307
|
+
const displayName = hasBrain ? name + ' 🧠' : name;
|
|
308
|
+
models.push(displayName);
|
|
309
|
+
if (item.getAttribute('data-is-selected') === 'true') current = displayName;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Auto 다시 켜기 (원래 상태 복원)
|
|
315
|
+
if (wasAutoOn) {
|
|
316
|
+
const newMenu = document.querySelector('[data-testid="model-picker-menu"]');
|
|
317
|
+
const newAutoItem = newMenu?.querySelector('.composer-unified-context-menu-item');
|
|
318
|
+
const newToggle = newAutoItem ? [...newAutoItem.querySelectorAll('[class*="rounded-full"]')].find(el => el.offsetWidth === 24) : null;
|
|
319
|
+
if (newToggle) {
|
|
320
|
+
newToggle.click();
|
|
321
|
+
await new Promise(r => setTimeout(r, 200));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 닫기 (Escape)
|
|
327
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return JSON.stringify({ models, current });
|
|
331
|
+
} catch(e) { return JSON.stringify({ models: [], current: '', error: e.message }); }
|
|
332
|
+
})()`;
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* setModel → { success: boolean }
|
|
337
|
+
* .composer-unified-dropdown-model 클릭 → 검색 → 선택
|
|
338
|
+
*/
|
|
339
|
+
setModel(params) {
|
|
340
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
341
|
+
const escaped = JSON.stringify(model);
|
|
342
|
+
return `(async () => {
|
|
343
|
+
try {
|
|
344
|
+
const target = ${escaped};
|
|
345
|
+
|
|
346
|
+
// 모델 드롭다운 열기
|
|
347
|
+
const modelBtn = document.querySelector('.composer-unified-dropdown-model');
|
|
348
|
+
if (!modelBtn) return JSON.stringify({ success: false, error: 'Model button not found' });
|
|
349
|
+
|
|
350
|
+
modelBtn.click();
|
|
351
|
+
await new Promise(r => setTimeout(r, 500));
|
|
352
|
+
|
|
353
|
+
const menu = document.querySelector('[data-testid="model-picker-menu"]');
|
|
354
|
+
if (!menu) return JSON.stringify({ success: false, error: 'Model picker menu not found' });
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
// 🧠 접미사 처리
|
|
358
|
+
const wantBrain = target.includes('🧠');
|
|
359
|
+
const searchName = target.replace(/\\s*🧠\\s*$/, '').trim();
|
|
360
|
+
|
|
361
|
+
// Auto 토글 끄기 (모델 목록 노출)
|
|
362
|
+
const autoItem = menu.querySelector('.composer-unified-context-menu-item[data-is-selected="true"]');
|
|
363
|
+
const autoToggle = autoItem ? [...autoItem.querySelectorAll('[class*="rounded-full"]')].find(el => el.offsetWidth === 24 && el.offsetHeight === 14) : null;
|
|
364
|
+
let wasAutoOn = false;
|
|
365
|
+
if (autoToggle) {
|
|
366
|
+
const bgStyle = autoToggle.getAttribute('style') || '';
|
|
367
|
+
wasAutoOn = bgStyle.includes('green');
|
|
368
|
+
if (wasAutoOn) {
|
|
369
|
+
autoToggle.click();
|
|
370
|
+
await new Promise(r => setTimeout(r, 500));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 검색 입력으로 필터링
|
|
375
|
+
const refreshedMenu = document.querySelector('[data-testid="model-picker-menu"]');
|
|
376
|
+
const searchInput = refreshedMenu?.querySelector('input[placeholder="Search models"]');
|
|
377
|
+
if (searchInput) {
|
|
378
|
+
searchInput.focus();
|
|
379
|
+
searchInput.value = searchName;
|
|
380
|
+
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
381
|
+
await new Promise(r => setTimeout(r, 300));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 아이템에서 찾기 (brain 아이콘 매칭)
|
|
385
|
+
const items = (refreshedMenu || menu).querySelectorAll('.composer-unified-context-menu-item');
|
|
386
|
+
for (const item of items) {
|
|
387
|
+
const nameEl = item.querySelector('.monaco-highlighted-label');
|
|
388
|
+
const name = nameEl?.textContent?.trim() || '';
|
|
389
|
+
if (!name || name === 'Add Models') continue;
|
|
390
|
+
const hasBrain = !!item.querySelector('[class*="codicon-br"]');
|
|
391
|
+
|
|
392
|
+
if (name.toLowerCase().includes(searchName.toLowerCase()) && hasBrain === wantBrain) {
|
|
393
|
+
item.click();
|
|
394
|
+
await new Promise(r => setTimeout(r, 200));
|
|
395
|
+
const displayName = hasBrain ? name + ' 🧠' : name;
|
|
396
|
+
return JSON.stringify({ success: true, model: displayName });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Auto 복원 + 닫기
|
|
401
|
+
if (wasAutoOn) {
|
|
402
|
+
const nm = document.querySelector('[data-testid="model-picker-menu"]');
|
|
403
|
+
const nai = nm?.querySelector('.composer-unified-context-menu-item');
|
|
404
|
+
const nt = nai ? [...nai.querySelectorAll('[class*="rounded-full"]')].find(el => el.offsetWidth === 24) : null;
|
|
405
|
+
if (nt) nt.click();
|
|
406
|
+
await new Promise(r => setTimeout(r, 200));
|
|
407
|
+
}
|
|
408
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
409
|
+
return JSON.stringify({ success: false, error: 'model not found: ' + target });
|
|
410
|
+
} catch(e) { return JSON.stringify({ success: false, error: e.message }); }
|
|
411
|
+
})()`;
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* listModes → { modes: string[], current: string }
|
|
416
|
+
* .composer-unified-dropdown (모드 아이콘) 클릭 → Agent/Plan/Debug/Ask
|
|
417
|
+
*/
|
|
418
|
+
listModes(params) {
|
|
419
|
+
return `(async () => {
|
|
420
|
+
try {
|
|
421
|
+
const modes = [];
|
|
422
|
+
let current = '';
|
|
423
|
+
|
|
424
|
+
// 모드 드롭다운 버튼 (아이콘, model이 아닌 unified-dropdown)
|
|
425
|
+
const modeBtn = document.querySelector('.composer-unified-dropdown:not(.composer-unified-dropdown-model)');
|
|
426
|
+
if (!modeBtn) return JSON.stringify({ modes: [], current: '', error: 'Mode button not found' });
|
|
427
|
+
|
|
428
|
+
modeBtn.click();
|
|
429
|
+
await new Promise(r => setTimeout(r, 500));
|
|
430
|
+
|
|
431
|
+
// 팝오버에서 아이템 수집
|
|
432
|
+
const menu = document.querySelector('[data-testid="model-picker-menu"]') || document.querySelector('.typeahead-popover');
|
|
433
|
+
if (menu) {
|
|
434
|
+
const items = menu.querySelectorAll('.composer-unified-context-menu-item');
|
|
435
|
+
for (const item of items) {
|
|
436
|
+
const nameEl = item.querySelector('.monaco-highlighted-label');
|
|
437
|
+
const name = nameEl?.textContent?.trim() || '';
|
|
438
|
+
if (name) {
|
|
439
|
+
modes.push(name);
|
|
440
|
+
if (item.getAttribute('data-is-selected') === 'true') current = name;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 닫기
|
|
446
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
447
|
+
|
|
448
|
+
return JSON.stringify({ modes, current });
|
|
449
|
+
} catch(e) { return JSON.stringify({ modes: [], current: '', error: e.message }); }
|
|
450
|
+
})()`;
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* setMode → { success: boolean }
|
|
455
|
+
* 모드 드롭다운 열기 → 항목 클릭
|
|
456
|
+
*/
|
|
457
|
+
setMode(params) {
|
|
458
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
459
|
+
const escaped = JSON.stringify(mode);
|
|
460
|
+
return `(async () => {
|
|
461
|
+
try {
|
|
462
|
+
const target = ${escaped};
|
|
463
|
+
|
|
464
|
+
const modeBtn = document.querySelector('.composer-unified-dropdown:not(.composer-unified-dropdown-model)');
|
|
465
|
+
if (!modeBtn) return JSON.stringify({ success: false, error: 'Mode button not found' });
|
|
466
|
+
|
|
467
|
+
modeBtn.click();
|
|
468
|
+
await new Promise(r => setTimeout(r, 500));
|
|
469
|
+
|
|
470
|
+
const menu = document.querySelector('[data-testid="model-picker-menu"]') || document.querySelector('.typeahead-popover');
|
|
471
|
+
if (!menu) return JSON.stringify({ success: false, error: 'Mode menu not found' });
|
|
472
|
+
|
|
473
|
+
const items = menu.querySelectorAll('.composer-unified-context-menu-item');
|
|
474
|
+
for (const item of items) {
|
|
475
|
+
const nameEl = item.querySelector('.monaco-highlighted-label');
|
|
476
|
+
const name = nameEl?.textContent?.trim() || '';
|
|
477
|
+
if (name && (name === target || name.toLowerCase() === target.toLowerCase())) {
|
|
478
|
+
item.click();
|
|
479
|
+
await new Promise(r => setTimeout(r, 200));
|
|
480
|
+
return JSON.stringify({ success: true, mode: name });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
485
|
+
return JSON.stringify({ success: false, error: 'mode not found: ' + target });
|
|
486
|
+
} catch(e) { return JSON.stringify({ success: false, error: e.message }); }
|
|
239
487
|
})()`;
|
|
240
488
|
},
|
|
241
489
|
},
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kiro (AWS) — IDE Provider
|
|
3
|
+
*
|
|
4
|
+
* Category: ide (VS Code fork, Code-OSS based)
|
|
5
|
+
* AWS의 AI-native spec-driven IDE
|
|
6
|
+
*
|
|
7
|
+
* @type {import('../../../src/providers/contracts').ProviderModule}
|
|
8
|
+
*/
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
13
|
+
|
|
14
|
+
function loadScript(name) {
|
|
15
|
+
try {
|
|
16
|
+
return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8');
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
type: 'kiro',
|
|
24
|
+
name: 'Kiro',
|
|
25
|
+
category: 'ide',
|
|
26
|
+
|
|
27
|
+
// ─── IDE 인프라 ───
|
|
28
|
+
displayName: 'Kiro',
|
|
29
|
+
icon: '🪁',
|
|
30
|
+
cli: 'kiro',
|
|
31
|
+
cdpPorts: [9351, 9352],
|
|
32
|
+
processNames: {
|
|
33
|
+
darwin: 'Kiro',
|
|
34
|
+
win32: ['Kiro.exe'],
|
|
35
|
+
linux: ['kiro'],
|
|
36
|
+
},
|
|
37
|
+
paths: {
|
|
38
|
+
darwin: ['/Applications/Kiro.app'],
|
|
39
|
+
win32: [
|
|
40
|
+
'C:\\Users\\*\\AppData\\Local\\Programs\\kiro\\Kiro.exe',
|
|
41
|
+
],
|
|
42
|
+
linux: ['/opt/kiro', '/usr/share/kiro'],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
inputMethod: 'cdp-type-and-send',
|
|
46
|
+
inputSelector: '[contenteditable="true"][role="textbox"]',
|
|
47
|
+
|
|
48
|
+
scripts: {
|
|
49
|
+
readChat() { return loadScript('read_chat.js'); },
|
|
50
|
+
sendMessage(text) {
|
|
51
|
+
const s = loadScript('send_message.js');
|
|
52
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
53
|
+
},
|
|
54
|
+
resolveAction(params) {
|
|
55
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
56
|
+
const buttonText = params?.button || params?.buttonText
|
|
57
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
58
|
+
const s = loadScript('resolve_action.js');
|
|
59
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PearAI — IDE Provider
|
|
3
|
+
*
|
|
4
|
+
* Category: ide (VS Code + Continue fork)
|
|
5
|
+
* AI-powered open-source code editor
|
|
6
|
+
*
|
|
7
|
+
* @type {import('../../../src/providers/contracts').ProviderModule}
|
|
8
|
+
*/
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
13
|
+
|
|
14
|
+
function loadScript(name) {
|
|
15
|
+
try {
|
|
16
|
+
return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8');
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
type: 'pearai',
|
|
24
|
+
name: 'PearAI',
|
|
25
|
+
category: 'ide',
|
|
26
|
+
|
|
27
|
+
// ─── IDE 인프라 ───
|
|
28
|
+
displayName: 'PearAI',
|
|
29
|
+
icon: '🍐',
|
|
30
|
+
cli: 'pearai',
|
|
31
|
+
cdpPorts: [9355, 9356],
|
|
32
|
+
processNames: {
|
|
33
|
+
darwin: 'PearAI',
|
|
34
|
+
win32: ['PearAI.exe'],
|
|
35
|
+
linux: ['pearai'],
|
|
36
|
+
},
|
|
37
|
+
paths: {
|
|
38
|
+
darwin: ['/Applications/PearAI.app'],
|
|
39
|
+
win32: [
|
|
40
|
+
'C:\\Users\\*\\AppData\\Local\\Programs\\pearai\\PearAI.exe',
|
|
41
|
+
],
|
|
42
|
+
linux: ['/opt/pearai', '/usr/share/pearai'],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
inputMethod: 'cdp-type-and-send',
|
|
46
|
+
inputSelector: '[contenteditable="true"][role="textbox"]',
|
|
47
|
+
|
|
48
|
+
scripts: {
|
|
49
|
+
readChat() { return loadScript('read_chat.js'); },
|
|
50
|
+
sendMessage(text) {
|
|
51
|
+
const s = loadScript('send_message.js');
|
|
52
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
53
|
+
},
|
|
54
|
+
resolveAction(params) {
|
|
55
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
56
|
+
const buttonText = params?.button || params?.buttonText
|
|
57
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
58
|
+
const s = loadScript('resolve_action.js');
|
|
59
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|