adhdev 0.2.0 → 0.4.0
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 +18164 -1101
- package/dist/cli-entrypoint.js.map +1 -1
- package/dist/index.js +18304 -1262
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/COMPATIBILITY.md +217 -0
- package/providers/_builtin/acp/agentpool/provider.json +7 -0
- package/providers/_builtin/acp/amp/provider.json +7 -0
- package/providers/_builtin/acp/auggie/provider.json +7 -0
- package/providers/_builtin/acp/autodev/provider.json +7 -0
- package/providers/_builtin/acp/autohand/provider.json +7 -0
- package/providers/_builtin/acp/blackbox-ai/provider.json +7 -0
- package/providers/_builtin/acp/claude-agent/provider.json +7 -0
- package/providers/_builtin/acp/cline-acp/provider.json +7 -0
- package/providers/_builtin/acp/codebuddy/provider.json +7 -0
- package/providers/_builtin/acp/codex-cli/provider.json +7 -0
- package/providers/_builtin/acp/corust-agent/provider.json +7 -0
- package/providers/_builtin/acp/crow-cli/provider.json +7 -0
- package/providers/_builtin/acp/cursor-acp/provider.json +7 -0
- package/providers/_builtin/acp/deepagents/provider.json +7 -0
- package/providers/_builtin/acp/dimcode/provider.json +7 -0
- package/providers/_builtin/acp/docker-cagent/provider.json +7 -0
- package/providers/_builtin/acp/factory-droid/provider.json +7 -0
- package/providers/_builtin/acp/fast-agent/provider.json +7 -0
- package/providers/_builtin/acp/gemini-cli/provider.json +7 -0
- package/providers/_builtin/acp/github-copilot/provider.json +7 -0
- package/providers/_builtin/acp/goose/provider.json +7 -0
- package/providers/_builtin/acp/junie/provider.json +7 -0
- package/providers/_builtin/acp/kilo/provider.json +10 -1
- package/providers/_builtin/acp/kimi-cli/provider.json +7 -0
- package/providers/_builtin/acp/minion-code/provider.json +7 -0
- package/providers/_builtin/acp/mistral-vibe/provider.json +7 -0
- package/providers/_builtin/acp/nova/provider.json +7 -0
- package/providers/_builtin/acp/openclaw/provider.json +7 -0
- package/providers/_builtin/acp/opencode/provider.json +7 -0
- package/providers/_builtin/acp/openhands/provider.json +7 -0
- package/providers/_builtin/acp/pi-acp/provider.json +7 -0
- package/providers/_builtin/acp/qoder/provider.json +7 -0
- package/providers/_builtin/acp/qwen-code/provider.json +7 -0
- package/providers/_builtin/acp/stakpak/provider.json +7 -0
- package/providers/_builtin/acp/vtcode/provider.json +7 -0
- package/providers/_builtin/cli/claude-cli/provider.json +22 -0
- package/providers/_builtin/cli/codex-cli/provider.json +29 -0
- package/providers/_builtin/cli/gemini-cli/provider.json +29 -0
- package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
- package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
- package/providers/_builtin/extension/cline/provider.json +24 -0
- package/providers/_builtin/extension/roo-code/provider.json +24 -0
- package/providers/_builtin/ide/antigravity/provider.json +32 -1
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_models.js +38 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_modes.js +48 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/scripts.js +64 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_mode.js +34 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_model.js +47 -0
- package/providers/_builtin/ide/antigravity/scripts/list_models.js +31 -8
- package/providers/_builtin/ide/antigravity/scripts/list_modes.js +37 -13
- package/providers/_builtin/ide/antigravity/scripts/set_mode.js +49 -16
- package/providers/_builtin/ide/antigravity/scripts/set_model.js +47 -22
- package/providers/_builtin/ide/antigravity/scripts.js +54 -60
- package/providers/_builtin/ide/cursor/provider.json +24 -0
- package/providers/_builtin/ide/cursor/scripts.js +16 -10
- package/providers/_builtin/ide/kiro/provider.json +25 -1
- package/providers/_builtin/ide/pearai/provider.json +25 -1
- package/providers/_builtin/ide/trae/provider.json +25 -1
- package/providers/_builtin/ide/vscode/provider.json +25 -1
- package/providers/_builtin/ide/vscode-insiders/provider.json +25 -1
- package/providers/_builtin/ide/vscodium/provider.json +25 -1
- package/providers/_builtin/ide/windsurf/provider.json +25 -1
- package/providers/_builtin/acp/code-assistant/provider.json +0 -47
- package/providers/_builtin/acp/fount/provider.json +0 -47
- package/providers/_builtin/acp/kiro-cli/provider.json +0 -47
|
@@ -7,5 +7,29 @@
|
|
|
7
7
|
"extensionIdPattern_flags": "i",
|
|
8
8
|
"vscodeCommands": {
|
|
9
9
|
"focusPanel": "claude-dev.SidebarProvider.focus"
|
|
10
|
+
},
|
|
11
|
+
"settings": {
|
|
12
|
+
"approvalAlert": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"default": true,
|
|
15
|
+
"public": true,
|
|
16
|
+
"label": "Approval Notifications",
|
|
17
|
+
"description": "Show notification when approval is needed"
|
|
18
|
+
},
|
|
19
|
+
"longGeneratingAlert": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": true,
|
|
22
|
+
"public": true,
|
|
23
|
+
"label": "Long Generation Alert",
|
|
24
|
+
"description": "Alert when generation takes too long"
|
|
25
|
+
},
|
|
26
|
+
"longGeneratingThresholdSec": {
|
|
27
|
+
"type": "number",
|
|
28
|
+
"default": 180,
|
|
29
|
+
"public": true,
|
|
30
|
+
"label": "Long Generation Threshold (sec)",
|
|
31
|
+
"min": 30,
|
|
32
|
+
"max": 600
|
|
33
|
+
}
|
|
10
34
|
}
|
|
11
35
|
}
|
|
@@ -7,5 +7,29 @@
|
|
|
7
7
|
"extensionIdPattern_flags": "i",
|
|
8
8
|
"vscodeCommands": {
|
|
9
9
|
"focusPanel": "roo-cline.SidebarProvider.focus"
|
|
10
|
+
},
|
|
11
|
+
"settings": {
|
|
12
|
+
"approvalAlert": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"default": true,
|
|
15
|
+
"public": true,
|
|
16
|
+
"label": "Approval Notifications",
|
|
17
|
+
"description": "Show notification when approval is needed"
|
|
18
|
+
},
|
|
19
|
+
"longGeneratingAlert": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": true,
|
|
22
|
+
"public": true,
|
|
23
|
+
"label": "Long Generation Alert",
|
|
24
|
+
"description": "Alert when generation takes too long"
|
|
25
|
+
},
|
|
26
|
+
"longGeneratingThresholdSec": {
|
|
27
|
+
"type": "number",
|
|
28
|
+
"default": 180,
|
|
29
|
+
"public": true,
|
|
30
|
+
"label": "Long Generation Threshold (sec)",
|
|
31
|
+
"min": 30,
|
|
32
|
+
"max": 600
|
|
33
|
+
}
|
|
10
34
|
}
|
|
11
35
|
}
|
|
@@ -28,5 +28,36 @@
|
|
|
28
28
|
]
|
|
29
29
|
},
|
|
30
30
|
"inputMethod": "cdp-type-and-send",
|
|
31
|
-
"inputSelector": "[contenteditable=\"true\"][role=\"textbox\"]"
|
|
31
|
+
"inputSelector": "[contenteditable=\"true\"][role=\"textbox\"]",
|
|
32
|
+
"versionCommand": "antigravity --version",
|
|
33
|
+
"testedVersions": ["1.107.0"],
|
|
34
|
+
"versions": {
|
|
35
|
+
"< 1.107.0": {
|
|
36
|
+
"__dir": "scripts/legacy"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"settings": {
|
|
40
|
+
"approvalAlert": {
|
|
41
|
+
"type": "boolean",
|
|
42
|
+
"default": true,
|
|
43
|
+
"public": true,
|
|
44
|
+
"label": "Approval Notifications",
|
|
45
|
+
"description": "Show notification when approval is needed"
|
|
46
|
+
},
|
|
47
|
+
"longGeneratingAlert": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"default": true,
|
|
50
|
+
"public": true,
|
|
51
|
+
"label": "Long Generation Alert",
|
|
52
|
+
"description": "Alert when generation takes too long"
|
|
53
|
+
},
|
|
54
|
+
"longGeneratingThresholdSec": {
|
|
55
|
+
"type": "number",
|
|
56
|
+
"default": 180,
|
|
57
|
+
"public": true,
|
|
58
|
+
"label": "Long Generation Threshold (sec)",
|
|
59
|
+
"min": 30,
|
|
60
|
+
"max": 600
|
|
61
|
+
}
|
|
62
|
+
}
|
|
32
63
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — list_models
|
|
3
|
+
* antigravity-agent-side-panel 내부 모델 드롭다운에서 목록 + 현재 모델 추출
|
|
4
|
+
* → { models: string[], current: string }
|
|
5
|
+
*/
|
|
6
|
+
(() => {
|
|
7
|
+
try {
|
|
8
|
+
const models = [];
|
|
9
|
+
let current = '';
|
|
10
|
+
|
|
11
|
+
// 1. 모델 항목에서 목록 추출
|
|
12
|
+
// 셀렉터: .px-2.py-1.flex.items-center.justify-between.cursor-pointer
|
|
13
|
+
const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
14
|
+
for (const item of items) {
|
|
15
|
+
const label = item.querySelector('.text-xs.font-medium');
|
|
16
|
+
const text = (label || item).textContent?.trim();
|
|
17
|
+
if (!text || text.length > 60) continue;
|
|
18
|
+
// 모델명 검증 (Claude, Gemini, GPT, etc.)
|
|
19
|
+
models.push(text);
|
|
20
|
+
// 선택된 항목: bg-gray-500/20
|
|
21
|
+
if ((item.className || '').includes('bg-gray-500/20')) {
|
|
22
|
+
current = text;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. 모델 목록이 없으면 (드롭다운 닫힘) → 트리거 버튼에서 현재 모델만
|
|
27
|
+
if (models.length === 0) {
|
|
28
|
+
const trigger = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
|
|
29
|
+
if (trigger) {
|
|
30
|
+
current = trigger.textContent?.trim() || '';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return JSON.stringify({ models, current });
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return JSON.stringify({ models: [], current: '', error: e.message });
|
|
37
|
+
}
|
|
38
|
+
})()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — list_modes
|
|
3
|
+
* Conversation mode: Planning / Fast 세그먼트 컨트롤
|
|
4
|
+
* inputBox 근처의 "Conversation mode" 패널에서 읽기
|
|
5
|
+
* → { modes: string[], current: string }
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
try {
|
|
9
|
+
const modes = [];
|
|
10
|
+
let current = '';
|
|
11
|
+
|
|
12
|
+
// "Conversation mode" 헤더를 포함하는 패널 찾기
|
|
13
|
+
const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
|
|
14
|
+
for (const header of headers) {
|
|
15
|
+
if (header.textContent?.trim() === 'Conversation mode') {
|
|
16
|
+
// 형제 요소들에서 모드 항목 추출
|
|
17
|
+
const parent = header.parentElement;
|
|
18
|
+
if (!parent) continue;
|
|
19
|
+
const items = parent.querySelectorAll('.font-medium');
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const text = item.textContent?.trim();
|
|
22
|
+
if (text && text.length < 20) {
|
|
23
|
+
modes.push(text);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 현재 모드: Fast 버튼의 텍스트 (현재 활성 모드 표시)
|
|
31
|
+
const modeBtn = [...document.querySelectorAll('button')].find(b => {
|
|
32
|
+
const cls = b.className || '';
|
|
33
|
+
return cls.includes('py-1') && cls.includes('pl-1') && cls.includes('pr-2') && b.offsetWidth > 0;
|
|
34
|
+
});
|
|
35
|
+
if (modeBtn) {
|
|
36
|
+
current = modeBtn.textContent?.trim() || '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// modes가 비어있으면 기본값
|
|
40
|
+
if (modes.length === 0) {
|
|
41
|
+
modes.push('Planning', 'Fast');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return JSON.stringify({ modes, current });
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return JSON.stringify({ modes: [], current: '', error: e.message });
|
|
47
|
+
}
|
|
48
|
+
})()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity CDP Scripts — legacy (< 1.107.0)
|
|
3
|
+
* DOM uses exact CSS class selectors (original Tailwind classes without arbitrary values)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const DIR = __dirname; // scripts/legacy/
|
|
11
|
+
|
|
12
|
+
function load(name) {
|
|
13
|
+
try { return fs.readFileSync(path.join(DIR, name), 'utf-8'); }
|
|
14
|
+
catch { return null; }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Non-model/mode scripts fall back to parent scripts/
|
|
18
|
+
const PARENT_DIR = path.join(DIR, '..');
|
|
19
|
+
function loadParent(name) {
|
|
20
|
+
try { return fs.readFileSync(path.join(PARENT_DIR, name), 'utf-8'); }
|
|
21
|
+
catch { return null; }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports.readChat = () => loadParent('read_chat.js');
|
|
25
|
+
module.exports.focusEditor = () => loadParent('focus_editor.js');
|
|
26
|
+
module.exports.listSessions = () => loadParent('list_chats.js');
|
|
27
|
+
module.exports.newSession = () => loadParent('new_session.js');
|
|
28
|
+
module.exports.listModels = () => load('list_models.js');
|
|
29
|
+
module.exports.listModes = () => load('list_modes.js');
|
|
30
|
+
|
|
31
|
+
module.exports.sendMessage = (text) => {
|
|
32
|
+
const script = loadParent('send_message.js');
|
|
33
|
+
if (!script) return null;
|
|
34
|
+
return script.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports.switchSession = (sessionId) => {
|
|
38
|
+
const script = loadParent('switch_session.js');
|
|
39
|
+
if (!script) return null;
|
|
40
|
+
return script.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
module.exports.resolveAction = (params) => {
|
|
44
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
45
|
+
const buttonText = params?.button || params?.buttonText
|
|
46
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
47
|
+
const script = loadParent('resolve_action.js');
|
|
48
|
+
if (!script) return null;
|
|
49
|
+
return script.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports.setModel = (params) => {
|
|
53
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
54
|
+
const script = load('set_model.js');
|
|
55
|
+
if (!script) return null;
|
|
56
|
+
return script.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports.setMode = (params) => {
|
|
60
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
61
|
+
const script = load('set_mode.js');
|
|
62
|
+
if (!script) return null;
|
|
63
|
+
return script.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode));
|
|
64
|
+
};
|
|
@@ -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
|
+
})()
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Antigravity — list_models
|
|
3
|
-
*
|
|
3
|
+
* 모델 드롭다운 트리거 버튼 + 항목 목록 추출
|
|
4
|
+
*
|
|
5
|
+
* Version compatibility:
|
|
6
|
+
* v0 (old): .flex.min-w-0.max-w-full.cursor-pointer.items-center (exact CSS selector works)
|
|
7
|
+
* v1 (new): Tailwind arbitrary values (pl-[0.125rem]) — must use partial class matching
|
|
8
|
+
* Both: dropdown items use .px-2.py-1.flex.items-center.justify-between.cursor-pointer
|
|
9
|
+
*
|
|
4
10
|
* → { models: string[], current: string }
|
|
5
11
|
*/
|
|
6
12
|
(() => {
|
|
@@ -8,26 +14,43 @@
|
|
|
8
14
|
const models = [];
|
|
9
15
|
let current = '';
|
|
10
16
|
|
|
11
|
-
// 1
|
|
12
|
-
//
|
|
17
|
+
// ── Step 1: Dropdown open state — extract item list ──────────────────
|
|
18
|
+
// Selector works for both v0 and v1 (hover class changed but cursor-pointer persists)
|
|
13
19
|
const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
14
20
|
for (const item of items) {
|
|
15
21
|
const label = item.querySelector('.text-xs.font-medium');
|
|
16
22
|
const text = (label || item).textContent?.trim();
|
|
17
23
|
if (!text || text.length > 60) continue;
|
|
18
|
-
// 모델명 검증 (Claude, Gemini, GPT, etc.)
|
|
19
24
|
models.push(text);
|
|
20
|
-
//
|
|
25
|
+
// Selected item: bg-gray-500/20 (both v0 & v1)
|
|
21
26
|
if ((item.className || '').includes('bg-gray-500/20')) {
|
|
22
27
|
current = text;
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
// 2
|
|
31
|
+
// ── Step 2: Dropdown closed — extract current model from trigger ──────
|
|
27
32
|
if (models.length === 0) {
|
|
28
|
-
|
|
33
|
+
// v0: exact class match works
|
|
34
|
+
let trigger = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
|
|
35
|
+
|
|
36
|
+
// v1: arbitrary Tailwind classes → partial matching
|
|
37
|
+
if (!trigger || trigger.offsetWidth === 0) {
|
|
38
|
+
trigger = [...document.querySelectorAll('div, button')].find(e => {
|
|
39
|
+
const cls = e.className || '';
|
|
40
|
+
return cls.includes('min-w-0') &&
|
|
41
|
+
cls.includes('max-w-full') &&
|
|
42
|
+
cls.includes('cursor-pointer') &&
|
|
43
|
+
cls.includes('items-center') &&
|
|
44
|
+
e.offsetWidth > 0;
|
|
45
|
+
}) || null;
|
|
46
|
+
}
|
|
47
|
+
|
|
29
48
|
if (trigger) {
|
|
30
|
-
|
|
49
|
+
// v0: .text-xs.font-medium span / v1: .text-xs span with opacity-70
|
|
50
|
+
const span = trigger.querySelector('.text-xs.font-medium') ||
|
|
51
|
+
trigger.querySelector('span.text-xs') ||
|
|
52
|
+
trigger;
|
|
53
|
+
current = span.textContent?.trim() || '';
|
|
31
54
|
}
|
|
32
55
|
}
|
|
33
56
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Antigravity — list_modes
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* 현재 모드 + 사용 가능한 모드 목록 추출
|
|
4
|
+
*
|
|
5
|
+
* Version compatibility:
|
|
6
|
+
* v0 (old): span-based mode button (plain text, no opacity class)
|
|
7
|
+
* v1 (new): BUTTON element with py-1 pl-1 pr-2 opacity-70 classes
|
|
8
|
+
* Both: "Conversation mode" panel with .font-medium items (when dropdown open)
|
|
9
|
+
*
|
|
5
10
|
* → { modes: string[], current: string }
|
|
6
11
|
*/
|
|
7
12
|
(() => {
|
|
@@ -9,34 +14,53 @@
|
|
|
9
14
|
const modes = [];
|
|
10
15
|
let current = '';
|
|
11
16
|
|
|
12
|
-
//
|
|
17
|
+
// ── Helper: find mode trigger button (v0 + v1 compat) ────────────────
|
|
18
|
+
function findModeTrigger() {
|
|
19
|
+
// v1: BUTTON with opacity-70 + py-1 pl-1 pr-2
|
|
20
|
+
const v1 = [...document.querySelectorAll('button')].find(b => {
|
|
21
|
+
const cls = b.className || '';
|
|
22
|
+
return cls.includes('py-1') &&
|
|
23
|
+
cls.includes('pl-1') &&
|
|
24
|
+
cls.includes('pr-2') &&
|
|
25
|
+
cls.includes('opacity-70') &&
|
|
26
|
+
b.offsetWidth > 0;
|
|
27
|
+
});
|
|
28
|
+
if (v1) return v1;
|
|
29
|
+
|
|
30
|
+
// v0: span/button with py-1 pl-1 pr-2 (without opacity-70)
|
|
31
|
+
return [...document.querySelectorAll('button, span')].find(b => {
|
|
32
|
+
const cls = b.className || '';
|
|
33
|
+
return cls.includes('py-1') &&
|
|
34
|
+
cls.includes('pl-1') &&
|
|
35
|
+
cls.includes('pr-2') &&
|
|
36
|
+
b.offsetWidth > 0 &&
|
|
37
|
+
// ensure it's in the model/mode area (not some other button)
|
|
38
|
+
(b.textContent?.trim() === 'Fast' || b.textContent?.trim() === 'Planning' || b.textContent?.trim() === 'Normal');
|
|
39
|
+
}) || null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Step 1: "Conversation mode" panel open — extract items ───────────
|
|
13
43
|
const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
|
|
14
44
|
for (const header of headers) {
|
|
15
45
|
if (header.textContent?.trim() === 'Conversation mode') {
|
|
16
|
-
// 형제 요소들에서 모드 항목 추출
|
|
17
46
|
const parent = header.parentElement;
|
|
18
47
|
if (!parent) continue;
|
|
19
48
|
const items = parent.querySelectorAll('.font-medium');
|
|
20
49
|
for (const item of items) {
|
|
21
50
|
const text = item.textContent?.trim();
|
|
22
|
-
if (text && text.length < 20)
|
|
23
|
-
modes.push(text);
|
|
24
|
-
}
|
|
51
|
+
if (text && text.length < 20) modes.push(text);
|
|
25
52
|
}
|
|
26
53
|
break;
|
|
27
54
|
}
|
|
28
55
|
}
|
|
29
56
|
|
|
30
|
-
//
|
|
31
|
-
const modeBtn =
|
|
32
|
-
const cls = b.className || '';
|
|
33
|
-
return cls.includes('py-1') && cls.includes('pl-1') && cls.includes('pr-2') && b.offsetWidth > 0;
|
|
34
|
-
});
|
|
57
|
+
// ── Step 2: Read current mode from trigger button ─────────────────────
|
|
58
|
+
const modeBtn = findModeTrigger();
|
|
35
59
|
if (modeBtn) {
|
|
36
60
|
current = modeBtn.textContent?.trim() || '';
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
// modes
|
|
63
|
+
// ── Fallback: default modes ───────────────────────────────────────────
|
|
40
64
|
if (modes.length === 0) {
|
|
41
65
|
modes.push('Planning', 'Fast');
|
|
42
66
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Antigravity — set_mode
|
|
3
|
-
*
|
|
2
|
+
* Antigravity — set_mode [>= 1.107.0]
|
|
3
|
+
* mode trigger: BUTTON with py-1 pl-1 pr-2 opacity-70
|
|
4
4
|
* ${MODE} → JSON.stringify(modeName)
|
|
5
5
|
* → { success: boolean, mode?: string }
|
|
6
6
|
*/
|
|
@@ -8,23 +8,56 @@
|
|
|
8
8
|
try {
|
|
9
9
|
const target = ${MODE};
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
// ── Helper: find mode button ──────────────────────────────────────────
|
|
12
|
+
function findModeBtn() {
|
|
13
|
+
return [...document.querySelectorAll('button')].find(b => {
|
|
14
|
+
const cls = b.className || '';
|
|
15
|
+
return cls.includes('py-1') &&
|
|
16
|
+
cls.includes('pl-1') &&
|
|
17
|
+
cls.includes('pr-2') &&
|
|
18
|
+
cls.includes('opacity-70') &&
|
|
19
|
+
b.offsetWidth > 0;
|
|
20
|
+
}) || null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ── Helper: click item in open "Conversation mode" panel ─────────────
|
|
24
|
+
function clickModeItem(targetName) {
|
|
25
|
+
const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
|
|
26
|
+
for (const header of headers) {
|
|
27
|
+
if (header.textContent?.trim() === 'Conversation mode') {
|
|
28
|
+
const parent = header.parentElement;
|
|
29
|
+
if (!parent) continue;
|
|
30
|
+
for (const item of parent.querySelectorAll('.font-medium')) {
|
|
31
|
+
const text = item.textContent?.trim();
|
|
32
|
+
if (text && text.toLowerCase() === targetName.toLowerCase()) {
|
|
33
|
+
item.click();
|
|
34
|
+
return text;
|
|
35
|
+
}
|
|
24
36
|
}
|
|
37
|
+
break;
|
|
25
38
|
}
|
|
26
|
-
break;
|
|
27
39
|
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Step 1: Panel already open — direct click ─────────────────────────
|
|
44
|
+
const direct = clickModeItem(target);
|
|
45
|
+
if (direct) {
|
|
46
|
+
await new Promise(r => setTimeout(r, 300));
|
|
47
|
+
return JSON.stringify({ success: true, mode: direct });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Step 2: Open panel via mode button, then click ───────────────────
|
|
51
|
+
const modeBtn = findModeBtn();
|
|
52
|
+
if (modeBtn) {
|
|
53
|
+
modeBtn.click();
|
|
54
|
+
await new Promise(r => setTimeout(r, 400));
|
|
55
|
+
|
|
56
|
+
const hit = clickModeItem(target);
|
|
57
|
+
if (hit) {
|
|
58
|
+
return JSON.stringify({ success: true, mode: hit });
|
|
59
|
+
}
|
|
60
|
+
modeBtn.click(); // Close if not found
|
|
28
61
|
}
|
|
29
62
|
|
|
30
63
|
return JSON.stringify({ success: false, error: 'mode not found: ' + target });
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Antigravity — set_model
|
|
3
|
-
*
|
|
3
|
+
* 모델 드롭다운에서 모델 선택
|
|
4
|
+
*
|
|
5
|
+
* Version compatibility:
|
|
6
|
+
* v0 (old): .flex.min-w-0.max-w-full.cursor-pointer.items-center trigger
|
|
7
|
+
* v1 (new): Tailwind arbitrary values → partial matching required
|
|
8
|
+
* Both: dropdown items .px-2.py-1.flex.items-center.justify-between.cursor-pointer
|
|
9
|
+
*
|
|
4
10
|
* ${MODEL} → JSON.stringify(modelName)
|
|
5
11
|
* → { success: boolean, model?: string }
|
|
6
12
|
*/
|
|
@@ -8,35 +14,54 @@
|
|
|
8
14
|
try {
|
|
9
15
|
const target = ${MODEL};
|
|
10
16
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return
|
|
17
|
+
// ── Helper: find model trigger button (v0 + v1 compat) ───────────────
|
|
18
|
+
function findModelTrigger() {
|
|
19
|
+
// v0: exact selector
|
|
20
|
+
const v0 = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
|
|
21
|
+
if (v0 && v0.offsetWidth > 0) return v0;
|
|
22
|
+
// v1: partial matching
|
|
23
|
+
return [...document.querySelectorAll('div, button')].find(e => {
|
|
24
|
+
const cls = e.className || '';
|
|
25
|
+
return cls.includes('min-w-0') &&
|
|
26
|
+
cls.includes('max-w-full') &&
|
|
27
|
+
cls.includes('cursor-pointer') &&
|
|
28
|
+
cls.includes('items-center') &&
|
|
29
|
+
e.offsetWidth > 0;
|
|
30
|
+
}) || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Helper: find and click a model from open dropdown ────────────────
|
|
34
|
+
function clickModelItem(targetName) {
|
|
35
|
+
const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
const label = item.querySelector('.text-xs.font-medium');
|
|
38
|
+
const text = (label || item).textContent?.trim();
|
|
39
|
+
if (text && (text === targetName || text.toLowerCase().includes(targetName.toLowerCase()))) {
|
|
40
|
+
item.click();
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
20
43
|
}
|
|
44
|
+
return null;
|
|
21
45
|
}
|
|
22
46
|
|
|
23
|
-
//
|
|
24
|
-
const
|
|
47
|
+
// ── Step 1: Dropdown already open — try direct click ─────────────────
|
|
48
|
+
const directHit = clickModelItem(target);
|
|
49
|
+
if (directHit) {
|
|
50
|
+
await new Promise(r => setTimeout(r, 200));
|
|
51
|
+
return JSON.stringify({ success: true, model: directHit });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Step 2: Dropdown closed — open trigger, then select ──────────────
|
|
55
|
+
const trigger = findModelTrigger();
|
|
25
56
|
if (trigger) {
|
|
26
57
|
trigger.click();
|
|
27
58
|
await new Promise(r => setTimeout(r, 400));
|
|
28
59
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}
|
|
60
|
+
const hit = clickModelItem(target);
|
|
61
|
+
if (hit) {
|
|
62
|
+
return JSON.stringify({ success: true, model: hit });
|
|
38
63
|
}
|
|
39
|
-
//
|
|
64
|
+
// Close dropdown if target not found
|
|
40
65
|
trigger.click();
|
|
41
66
|
}
|
|
42
67
|
|