adhdev 0.1.53 → 0.2.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 +12020 -0
- package/dist/cli-entrypoint.js.map +1 -0
- package/dist/index.js +9789 -9864
- package/dist/index.js.map +1 -0
- package/package.json +19 -16
- package/providers/_builtin/CONTRIBUTING.md +141 -0
- package/providers/_builtin/README.md +51 -0
- package/providers/_builtin/acp/agentpool/provider.json +47 -0
- package/providers/_builtin/acp/amp/provider.json +45 -0
- package/providers/_builtin/acp/auggie/provider.json +50 -0
- package/providers/_builtin/acp/autodev/provider.json +47 -0
- package/providers/_builtin/acp/autohand/provider.json +45 -0
- package/providers/_builtin/acp/blackbox-ai/provider.json +47 -0
- package/providers/_builtin/acp/claude-agent/provider.json +50 -0
- package/providers/_builtin/acp/cline-acp/provider.json +47 -0
- package/providers/_builtin/acp/code-assistant/provider.json +47 -0
- package/providers/_builtin/acp/codebuddy/provider.json +47 -0
- package/providers/_builtin/acp/codex-cli/provider.json +50 -0
- package/providers/_builtin/acp/corust-agent/provider.json +45 -0
- package/providers/_builtin/acp/crow-cli/provider.json +47 -0
- package/providers/_builtin/acp/cursor-acp/provider.json +47 -0
- package/providers/_builtin/acp/deepagents/provider.json +45 -0
- package/providers/_builtin/acp/dimcode/provider.json +47 -0
- package/providers/_builtin/acp/docker-cagent/provider.json +50 -0
- package/providers/_builtin/acp/factory-droid/provider.json +53 -0
- package/providers/_builtin/acp/fast-agent/provider.json +45 -0
- package/providers/_builtin/acp/fount/provider.json +47 -0
- package/providers/_builtin/acp/gemini-cli/provider.json +107 -0
- package/providers/_builtin/acp/github-copilot/provider.json +47 -0
- package/providers/_builtin/acp/goose/provider.json +50 -0
- package/providers/_builtin/acp/junie/provider.json +45 -0
- package/providers/_builtin/acp/kilo/provider.json +45 -0
- package/providers/_builtin/acp/kimi-cli/provider.json +50 -0
- package/providers/_builtin/acp/kiro-cli/provider.json +47 -0
- package/providers/_builtin/acp/minion-code/provider.json +45 -0
- package/providers/_builtin/acp/mistral-vibe/provider.json +50 -0
- package/providers/_builtin/acp/nova/provider.json +47 -0
- package/providers/_builtin/acp/openclaw/provider.json +47 -0
- package/providers/_builtin/acp/opencode/provider.json +45 -0
- package/providers/_builtin/acp/openhands/provider.json +47 -0
- package/providers/_builtin/acp/pi-acp/provider.json +45 -0
- package/providers/_builtin/acp/qoder/provider.json +47 -0
- package/providers/_builtin/acp/qwen-code/provider.json +53 -0
- package/providers/_builtin/acp/stakpak/provider.json +47 -0
- package/providers/_builtin/acp/vtcode/provider.json +47 -0
- package/providers/_builtin/cli/claude-cli/provider.json +78 -0
- package/providers/_builtin/cli/codex-cli/provider.json +60 -0
- package/providers/_builtin/cli/gemini-cli/provider.json +64 -0
- package/providers/_builtin/extension/cline/provider.json +11 -0
- package/providers/_builtin/extension/cline/scripts/open_panel.js +1 -1
- package/providers/_builtin/extension/cline/{provider.js → scripts.js} +29 -55
- package/providers/_builtin/extension/roo-code/provider.json +11 -0
- package/providers/_builtin/extension/roo-code/{provider.js → scripts.js} +27 -97
- package/providers/_builtin/ide/antigravity/provider.json +32 -0
- package/providers/_builtin/ide/antigravity/scripts.js +73 -0
- package/providers/_builtin/ide/cursor/provider.json +35 -0
- package/providers/_builtin/ide/cursor/{provider.js → scripts.js} +31 -69
- package/providers/_builtin/ide/kiro/provider.json +36 -0
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/kiro/scripts.js +62 -0
- package/providers/_builtin/ide/pearai/provider.json +36 -0
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
- package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +32 -4
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
- package/providers/_builtin/ide/pearai/scripts.js +74 -0
- package/providers/_builtin/ide/trae/provider.json +35 -0
- package/providers/_builtin/ide/trae/scripts/send_message.js +53 -3
- package/providers/_builtin/ide/trae/scripts.js +57 -0
- package/providers/_builtin/ide/vscode/provider.json +33 -0
- package/providers/_builtin/ide/vscode-insiders/provider.json +31 -0
- package/providers/_builtin/ide/vscodium/provider.json +32 -0
- package/providers/_builtin/ide/windsurf/provider.json +22 -0
- package/providers/_builtin/ide/windsurf/scripts.js +57 -0
- package/providers/_builtin/validate.js +156 -0
- package/README.md +0 -43
- package/dist/dev-console-monaco.js +0 -176
- package/dist/dev-console.css +0 -326
- package/dist/dev-console.html +0 -148
- package/dist/dev-console.js +0 -1165
- package/dist/index.d.ts +0 -2
- package/dist/node_datachannel-LPY6EJH5.node +0 -0
- package/providers/_builtin/acp/codex-cli/provider.js +0 -54
- package/providers/_builtin/acp/goose/provider.js +0 -32
- package/providers/_builtin/acp/opencode/provider.js +0 -32
- package/providers/_builtin/cli/claude-cli/provider.js +0 -125
- package/providers/_builtin/cli/codex-cli/provider.js +0 -77
- package/providers/_builtin/cli/gemini-cli/provider.js +0 -121
- package/providers/_builtin/ide/antigravity/provider.js +0 -114
- package/providers/_builtin/ide/cursor/provider.js.backup +0 -116
- package/providers/_builtin/ide/cursor/provider.js.bak +0 -127
- package/providers/_builtin/ide/cursor/scripts_backup/focus_editor.js +0 -20
- package/providers/_builtin/ide/cursor/scripts_backup/list_chats.js +0 -111
- package/providers/_builtin/ide/cursor/scripts_backup/new_session.js +0 -62
- package/providers/_builtin/ide/cursor/scripts_backup/open_panel.js +0 -31
- package/providers/_builtin/ide/cursor/scripts_backup/read_chat.js +0 -433
- package/providers/_builtin/ide/cursor/scripts_backup/resolve_action.js +0 -90
- package/providers/_builtin/ide/cursor/scripts_backup/send_message.js +0 -86
- package/providers/_builtin/ide/cursor/scripts_backup/switch_session.js +0 -63
- package/providers/_builtin/ide/kiro/provider.js +0 -86
- package/providers/_builtin/ide/pearai/provider.js +0 -88
- package/providers/_builtin/ide/trae/provider.js +0 -83
- package/providers/_builtin/ide/vscode/provider.js +0 -36
- package/providers/_builtin/ide/vscode-insiders/provider.js +0 -27
- package/providers/_builtin/ide/vscodium/provider.js +0 -27
- package/providers/_builtin/ide/windsurf/provider.js +0 -76
- /package/providers/{_helpers → _builtin/_helpers}/index.js +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Scripts for Kiro
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
8
|
+
function loadScript(name) {
|
|
9
|
+
try { return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8'); }
|
|
10
|
+
catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module.exports.webviewReadChat = function webviewReadChat() { return loadScript('webview_read_chat.js'); };
|
|
15
|
+
|
|
16
|
+
module.exports.webviewSendMessage = function webviewSendMessage(text) {
|
|
17
|
+
const s = loadScript('webview_send_message.js');
|
|
18
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports.sendMessage = function sendMessage(text) {
|
|
22
|
+
const s = loadScript('send_message.js');
|
|
23
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports.resolveAction = function resolveAction(params) {
|
|
27
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
28
|
+
const buttonText = params?.button || params?.buttonText
|
|
29
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
30
|
+
const s = loadScript('resolve_action.js');
|
|
31
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports.openPanel = function openPanel() { return loadScript('open_panel.js'); };
|
|
35
|
+
|
|
36
|
+
module.exports.focusEditor = function focusEditor() { return loadScript('focus_editor.js'); };
|
|
37
|
+
|
|
38
|
+
module.exports.webviewListSessions = function webviewListSessions() { return loadScript('webview_list_sessions.js'); };
|
|
39
|
+
|
|
40
|
+
module.exports.webviewNewSession = function webviewNewSession() { return loadScript('webview_new_session.js'); };
|
|
41
|
+
|
|
42
|
+
module.exports.webviewSwitchSession = function webviewSwitchSession(sessionId) {
|
|
43
|
+
const s = loadScript('webview_switch_session.js');
|
|
44
|
+
return s ? s.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId)) : null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports.webviewListModels = function webviewListModels() { return loadScript('webview_list_models.js'); };
|
|
48
|
+
|
|
49
|
+
module.exports.webviewSetModel = function webviewSetModel(params) {
|
|
50
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
51
|
+
const s = loadScript('webview_set_model.js');
|
|
52
|
+
return s ? s.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model)) : null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports.webviewListModes = function webviewListModes() { return loadScript('webview_list_modes.js'); };
|
|
56
|
+
|
|
57
|
+
module.exports.webviewSetMode = function webviewSetMode(params) {
|
|
58
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
59
|
+
const s = loadScript('webview_set_mode.js');
|
|
60
|
+
return s ? s.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode)) : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "pearai",
|
|
3
|
+
"name": "PearAI",
|
|
4
|
+
"category": "ide",
|
|
5
|
+
"displayName": "PearAI",
|
|
6
|
+
"icon": "🍐",
|
|
7
|
+
"cli": "pearai",
|
|
8
|
+
"cdpPorts": [
|
|
9
|
+
9355,
|
|
10
|
+
9356
|
|
11
|
+
],
|
|
12
|
+
"processNames": {
|
|
13
|
+
"darwin": "PearAI",
|
|
14
|
+
"win32": [
|
|
15
|
+
"PearAI.exe"
|
|
16
|
+
],
|
|
17
|
+
"linux": [
|
|
18
|
+
"pearai"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"paths": {
|
|
22
|
+
"darwin": [
|
|
23
|
+
"/Applications/PearAI.app"
|
|
24
|
+
],
|
|
25
|
+
"win32": [
|
|
26
|
+
"C:\\Users\\*\\AppData\\Local\\Programs\\pearai\\PearAI.exe"
|
|
27
|
+
],
|
|
28
|
+
"linux": [
|
|
29
|
+
"/opt/pearai",
|
|
30
|
+
"/usr/share/pearai"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"inputMethod": "cdp-type-and-send",
|
|
34
|
+
"inputSelector": "[contenteditable=\"true\"][role=\"textbox\"]",
|
|
35
|
+
"webviewMatchText": "chat-text-area"
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PearAI — list_sessions (IDE 메인 프레임에서 실행)
|
|
3
|
+
*
|
|
4
|
+
* 히스토리 패널 토글 버튼을 클릭하여 히스토리 뷰를 오픈합니다.
|
|
5
|
+
* 히스토리 뷰가 열리면 webviewListSessions로 항목을 읽을 수 있습니다.
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
try {
|
|
9
|
+
// Panel title bar에서 히스토리 관련 버튼 찾기
|
|
10
|
+
const actionBtns = document.querySelectorAll('.action-item a.action-label, .action-item .action-label');
|
|
11
|
+
for (const btn of actionBtns) {
|
|
12
|
+
const title = (btn.getAttribute('title') || '').toLowerCase();
|
|
13
|
+
const ariaLabel = (btn.getAttribute('aria-label') || '').toLowerCase();
|
|
14
|
+
const cls = btn.className || '';
|
|
15
|
+
|
|
16
|
+
if (title.includes('history') || title.includes('히스토리') ||
|
|
17
|
+
ariaLabel.includes('history') || ariaLabel.includes('히스토리') ||
|
|
18
|
+
cls.includes('codicon-history')) {
|
|
19
|
+
btn.click();
|
|
20
|
+
return JSON.stringify({ toggled: true, method: 'panelAction', title: btn.getAttribute('title') });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Broader search
|
|
25
|
+
const allBtns = document.querySelectorAll('a[title], button[title]');
|
|
26
|
+
for (const btn of allBtns) {
|
|
27
|
+
const title = (btn.getAttribute('title') || '').toLowerCase();
|
|
28
|
+
if (title.includes('history') || title.includes('task history')) {
|
|
29
|
+
btn.click();
|
|
30
|
+
return JSON.stringify({ toggled: true, method: 'titleBtn', title: btn.getAttribute('title') });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return JSON.stringify({ toggled: false, error: 'History toggle button not found' });
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return JSON.stringify({ toggled: false, error: e.message });
|
|
37
|
+
}
|
|
38
|
+
})()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PearAI — new_session (IDE 메인 프레임에서 실행)
|
|
3
|
+
*
|
|
4
|
+
* Panel title bar의 "+" 버튼(New Task)을 찾아 클릭합니다.
|
|
5
|
+
* PearAI(Roo Code 기반)에서 새 태스크 버튼은 webview 바깥의 VS Code panel 헤더에 위치합니다.
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
try {
|
|
9
|
+
// Strategy 1: Find the codicon-add / plus button in panel title actions
|
|
10
|
+
const actionBtns = document.querySelectorAll('.panel .title .actions-container .action-item a, .pane-header .actions-container .action-item a, .title-actions .action-item a');
|
|
11
|
+
for (const btn of actionBtns) {
|
|
12
|
+
const title = btn.getAttribute('title') || '';
|
|
13
|
+
const ariaLabel = btn.getAttribute('aria-label') || '';
|
|
14
|
+
const cls = btn.className || '';
|
|
15
|
+
|
|
16
|
+
if (title.toLowerCase().includes('new task') ||
|
|
17
|
+
title.toLowerCase().includes('new chat') ||
|
|
18
|
+
ariaLabel.toLowerCase().includes('new task') ||
|
|
19
|
+
ariaLabel.toLowerCase().includes('new chat') ||
|
|
20
|
+
cls.includes('codicon-add') ||
|
|
21
|
+
cls.includes('codicon-plus')) {
|
|
22
|
+
btn.click();
|
|
23
|
+
return JSON.stringify({ created: true, method: 'panelAction', title: title || ariaLabel });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Strategy 2: Broader search for action items with "+" or "new"
|
|
28
|
+
const allActions = document.querySelectorAll('.action-item a.action-label');
|
|
29
|
+
for (const a of allActions) {
|
|
30
|
+
const title = (a.getAttribute('title') || '').toLowerCase();
|
|
31
|
+
const cls = a.className || '';
|
|
32
|
+
if ((title.includes('new') && title.includes('task')) ||
|
|
33
|
+
(title.includes('plus') || cls.includes('codicon-add'))) {
|
|
34
|
+
a.click();
|
|
35
|
+
return JSON.stringify({ created: true, method: 'actionLabel', title: a.getAttribute('title') });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Strategy 3: Use keybinding (Cmd+Shift+P -> "new task")
|
|
40
|
+
// Simulate keyboard shortcut for Roo Code: typically there's a command
|
|
41
|
+
// registered as roo-cline.plusButtonClicked or similar
|
|
42
|
+
const allBtns = document.querySelectorAll('a[title], button[title]');
|
|
43
|
+
const matches = [];
|
|
44
|
+
for (const btn of allBtns) {
|
|
45
|
+
const t = btn.getAttribute('title') || '';
|
|
46
|
+
if (t.toLowerCase().includes('new') || t.toLowerCase().includes('plus') || t === '+') {
|
|
47
|
+
matches.push({ tag: btn.tagName, title: t, cls: (btn.className || '').substring(0, 60) });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return JSON.stringify({ created: false, error: 'New Task button not found in panel', candidates: matches.slice(0, 5) });
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return JSON.stringify({ created: false, error: e.message });
|
|
54
|
+
}
|
|
55
|
+
})()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PearAI — webview_list_sessions (webview iframe 내부에서 실행)
|
|
3
|
+
*
|
|
4
|
+
* PearAI(Roo Code/Cline 기반) 히스토리 뷰에서 세션 목록을 추출.
|
|
5
|
+
* 각 항목은 data-testid="task-item-{UUID}" 로 식별됨.
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
try {
|
|
9
|
+
// ─── 히스토리 항목: data-testid="task-item-*" ───
|
|
10
|
+
const taskItems = document.querySelectorAll('[data-testid^="task-item-"]');
|
|
11
|
+
|
|
12
|
+
if (taskItems.length > 0) {
|
|
13
|
+
const sessions = [];
|
|
14
|
+
for (let i = 0; i < taskItems.length; i++) {
|
|
15
|
+
const item = taskItems[i];
|
|
16
|
+
if (!item) continue;
|
|
17
|
+
|
|
18
|
+
const testId = item.getAttribute('data-testid') || '';
|
|
19
|
+
const taskId = testId.replace('task-item-', '');
|
|
20
|
+
|
|
21
|
+
// 전체 텍스트에서 제목 추출
|
|
22
|
+
const fullText = (item.textContent || '').trim();
|
|
23
|
+
|
|
24
|
+
// 구조: "MARCH 16, 1:31 AM 173 B test123 Tokens:10.4k 229"
|
|
25
|
+
// 전략: Tokens: 앞의 마지막 의미있는 텍스트를 제목으로 사용
|
|
26
|
+
let title = '';
|
|
27
|
+
let date = '';
|
|
28
|
+
|
|
29
|
+
// 날짜 추출
|
|
30
|
+
const dateMatch = fullText.match(/^([A-Z]+\s+\d+,\s*\d+:\d+\s*[AP]M)/);
|
|
31
|
+
if (dateMatch) date = dateMatch[1];
|
|
32
|
+
|
|
33
|
+
// 제목 추출: 날짜와 크기(B/kB) 뒤, Tokens: 앞
|
|
34
|
+
const titleMatch = fullText.match(/[AP]M[\d.\s]*[kMG]?B\s*(.*?)(?:Tokens:|$)/s);
|
|
35
|
+
if (titleMatch && titleMatch[1]) {
|
|
36
|
+
title = titleMatch[1].trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// fallback: 첫 80자
|
|
40
|
+
if (!title) {
|
|
41
|
+
title = fullText.substring(0, 80);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
sessions.push({
|
|
45
|
+
id: taskId,
|
|
46
|
+
title: title.substring(0, 100),
|
|
47
|
+
date: date,
|
|
48
|
+
active: false
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return JSON.stringify({ sessions: sessions });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ─── 히스토리 뷰가 열려 있지 않음 ───
|
|
55
|
+
return JSON.stringify({
|
|
56
|
+
sessions: [],
|
|
57
|
+
note: 'History view not open. Toggle history first.'
|
|
58
|
+
});
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return JSON.stringify({ sessions: [], error: String(e.message || e) });
|
|
61
|
+
}
|
|
62
|
+
})()
|
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PearAI — webview_new_session (webview iframe 내부에서 실행)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Roo Code/Cline 기반 PearAI에서 새 세션(태스크)을 시작합니다.
|
|
5
|
+
* 1) "New Task" 텍스트를 가진 버튼 클릭 시도
|
|
6
|
+
* 2) 실패 시, vscode postMessage API로 newTask 명령 전송 시도
|
|
7
|
+
* 3) 실패 시, chat-text-area를 초기화하여 새 세션 상태로 진입
|
|
6
8
|
*/
|
|
7
9
|
(() => {
|
|
8
10
|
try {
|
|
11
|
+
// Strategy 1: Find "New Task" button text
|
|
9
12
|
const buttons = document.querySelectorAll('button, [role="button"]');
|
|
10
13
|
for (const btn of buttons) {
|
|
11
14
|
const text = (btn.textContent || '').trim();
|
|
12
|
-
if (text.includes('Start New Task') || text.includes('New Task')) {
|
|
15
|
+
if (text.includes('Start New Task') || text.includes('New Task') || text.includes('New Chat')) {
|
|
13
16
|
btn.click();
|
|
14
|
-
return JSON.stringify({ created: true, method: 'button' });
|
|
17
|
+
return JSON.stringify({ created: true, method: 'button', text });
|
|
15
18
|
}
|
|
16
19
|
}
|
|
20
|
+
|
|
21
|
+
// Strategy 2: Look for a "+" (plus/add) button that might create new tasks
|
|
22
|
+
for (const btn of buttons) {
|
|
23
|
+
const title = btn.getAttribute('title') || '';
|
|
24
|
+
const ariaLabel = btn.getAttribute('aria-label') || '';
|
|
25
|
+
if (title.includes('New') || ariaLabel.includes('New') ||
|
|
26
|
+
title.includes('new') || ariaLabel.includes('new')) {
|
|
27
|
+
btn.click();
|
|
28
|
+
return JSON.stringify({ created: true, method: 'title', title: title || ariaLabel });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Strategy 3: Use vscode API to post message for new task
|
|
33
|
+
if (typeof acquireVsCodeApi !== 'undefined') {
|
|
34
|
+
const vscode = acquireVsCodeApi();
|
|
35
|
+
vscode.postMessage({ type: 'newTask' });
|
|
36
|
+
return JSON.stringify({ created: true, method: 'vscodeApi' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Strategy 4: If already in empty state (no messages), report as already new
|
|
40
|
+
const messages = document.querySelectorAll('[class*="message"], [class*="chat-row"]');
|
|
41
|
+
if (messages.length === 0) {
|
|
42
|
+
return JSON.stringify({ created: true, method: 'alreadyNew', note: 'No messages - already in new session state' });
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
return JSON.stringify({ created: false, error: 'New Task button not found' });
|
|
18
46
|
} catch (e) {
|
|
19
47
|
return JSON.stringify({ created: false, error: e.message });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kiro — webview_send_message (webview iframe 내부에서 실행)
|
|
3
|
+
*
|
|
4
|
+
* Kiro의 채팅 입력은 webview iframe 안의 ProseMirror/tiptap 에디터.
|
|
5
|
+
* execCommand('insertText') + Enter 키 이벤트로 메시지 전송.
|
|
6
|
+
*
|
|
7
|
+
* 파라미터: ${ MESSAGE }
|
|
8
|
+
*/
|
|
9
|
+
(async () => {
|
|
10
|
+
try {
|
|
11
|
+
const msg = ${ MESSAGE };
|
|
12
|
+
|
|
13
|
+
// ─── 1. 입력 필드 찾기 ───
|
|
14
|
+
const editor =
|
|
15
|
+
document.querySelector('.tiptap.ProseMirror') ||
|
|
16
|
+
document.querySelector('[contenteditable="true"]') ||
|
|
17
|
+
document.querySelector('textarea');
|
|
18
|
+
|
|
19
|
+
if (!editor) return JSON.stringify({ sent: false, error: 'no input found in webview' });
|
|
20
|
+
|
|
21
|
+
const isTextarea = editor.tagName === 'TEXTAREA';
|
|
22
|
+
|
|
23
|
+
if (isTextarea) {
|
|
24
|
+
editor.focus();
|
|
25
|
+
const nativeSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
|
26
|
+
if (nativeSetter) nativeSetter.call(editor, msg);
|
|
27
|
+
else editor.value = msg;
|
|
28
|
+
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
29
|
+
await new Promise(r => setTimeout(r, 300));
|
|
30
|
+
|
|
31
|
+
const enterOpts = { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true, composed: true };
|
|
32
|
+
editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
|
|
33
|
+
editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
|
|
34
|
+
editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
|
|
35
|
+
return JSON.stringify({ sent: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── 2. contenteditable (ProseMirror / tiptap) ───
|
|
39
|
+
editor.focus();
|
|
40
|
+
await new Promise(r => setTimeout(r, 100));
|
|
41
|
+
|
|
42
|
+
// 전체 선택 + 삭제 + 삽입
|
|
43
|
+
const sel = window.getSelection();
|
|
44
|
+
const range = document.createRange();
|
|
45
|
+
range.selectNodeContents(editor);
|
|
46
|
+
sel.removeAllRanges();
|
|
47
|
+
sel.addRange(range);
|
|
48
|
+
await new Promise(r => setTimeout(r, 50));
|
|
49
|
+
|
|
50
|
+
document.execCommand('delete', false, null);
|
|
51
|
+
await new Promise(r => setTimeout(r, 50));
|
|
52
|
+
document.execCommand('insertText', false, msg);
|
|
53
|
+
|
|
54
|
+
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
55
|
+
await new Promise(r => setTimeout(r, 400));
|
|
56
|
+
|
|
57
|
+
// ─── 3. Enter 키 전송 ───
|
|
58
|
+
const enterOpts = {
|
|
59
|
+
key: 'Enter', code: 'Enter',
|
|
60
|
+
keyCode: 13, which: 13,
|
|
61
|
+
bubbles: true, cancelable: true, composed: true,
|
|
62
|
+
};
|
|
63
|
+
editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
|
|
64
|
+
await new Promise(r => setTimeout(r, 50));
|
|
65
|
+
editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
|
|
66
|
+
editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
|
|
67
|
+
|
|
68
|
+
return JSON.stringify({ sent: true });
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return JSON.stringify({ sent: false, error: e.message });
|
|
71
|
+
}
|
|
72
|
+
})()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PearAI — webview_switch_session (webview iframe 내부에서 실행)
|
|
3
|
+
*
|
|
4
|
+
* data-testid="task-item-{UUID}" 항목을 클릭하여 세션 전환.
|
|
5
|
+
* SESSION_ID는 task UUID 형태 (예: "51a08aba-1078-410c-a601-0e859205b12c")
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
try {
|
|
9
|
+
const targetId = ${SESSION_ID};
|
|
10
|
+
|
|
11
|
+
// ─── UUID 기반 매칭 ───
|
|
12
|
+
const selector = '[data-testid="task-item-' + targetId + '"]';
|
|
13
|
+
const targetItem = document.querySelector(selector);
|
|
14
|
+
if (targetItem) {
|
|
15
|
+
targetItem.click();
|
|
16
|
+
return JSON.stringify({ switched: true, id: targetId });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── 인덱스 기반 fallback (숫자로 전달된 경우) ───
|
|
20
|
+
const index = parseInt(targetId, 10);
|
|
21
|
+
if (!isNaN(index)) {
|
|
22
|
+
const allItems = document.querySelectorAll('[data-testid^="task-item-"]');
|
|
23
|
+
if (allItems[index]) {
|
|
24
|
+
allItems[index].click();
|
|
25
|
+
const taskId = (allItems[index].getAttribute('data-testid') || '').replace('task-item-', '');
|
|
26
|
+
return JSON.stringify({ switched: true, id: taskId, method: 'index' });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return JSON.stringify({ switched: false, error: 'Task item not found: ' + targetId });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return JSON.stringify({ switched: false, error: e.message });
|
|
33
|
+
}
|
|
34
|
+
})()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Scripts for PearAI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
8
|
+
function loadScript(name) {
|
|
9
|
+
try { return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8'); }
|
|
10
|
+
catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module.exports.webviewReadChat = function webviewReadChat() { return loadScript('webview_read_chat.js'); };
|
|
15
|
+
|
|
16
|
+
module.exports.webviewSendMessage = function webviewSendMessage(text) {
|
|
17
|
+
const s = loadScript('webview_send_message.js');
|
|
18
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports.sendMessage = function sendMessage(text) {
|
|
22
|
+
const s = loadScript('send_message.js');
|
|
23
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports.resolveAction = function resolveAction(params) {
|
|
27
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
28
|
+
const buttonText = params?.button || params?.buttonText
|
|
29
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
30
|
+
const s = loadScript('resolve_action.js');
|
|
31
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports.webviewResolveAction = function webviewResolveAction(params) {
|
|
35
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
36
|
+
const buttonText = params?.button || params?.buttonText
|
|
37
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
38
|
+
const s = loadScript('webview_resolve_action.js');
|
|
39
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports.openPanel = function openPanel() { return loadScript('open_panel.js'); };
|
|
43
|
+
|
|
44
|
+
module.exports.focusEditor = function focusEditor() { return loadScript('focus_editor.js'); };
|
|
45
|
+
|
|
46
|
+
module.exports.newSession = function newSession() { return loadScript('new_session.js'); };
|
|
47
|
+
|
|
48
|
+
module.exports.listSessions = function listSessions() { return loadScript('list_sessions.js'); };
|
|
49
|
+
|
|
50
|
+
module.exports.webviewNewSession = function webviewNewSession() { return loadScript('webview_new_session.js'); };
|
|
51
|
+
|
|
52
|
+
module.exports.webviewListSessions = function webviewListSessions() { return loadScript('webview_list_sessions.js'); };
|
|
53
|
+
|
|
54
|
+
module.exports.webviewSwitchSession = function webviewSwitchSession(sessionId) {
|
|
55
|
+
const s = loadScript('webview_switch_session.js');
|
|
56
|
+
return s ? s.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId)) : null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports.webviewListModels = function webviewListModels() { return loadScript('webview_list_models.js'); };
|
|
60
|
+
|
|
61
|
+
module.exports.webviewSetModel = function webviewSetModel(params) {
|
|
62
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
63
|
+
const s = loadScript('webview_set_model.js');
|
|
64
|
+
return s ? s.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model)) : null;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
module.exports.webviewListModes = function webviewListModes() { return loadScript('webview_list_modes.js'); };
|
|
68
|
+
|
|
69
|
+
module.exports.webviewSetMode = function webviewSetMode(params) {
|
|
70
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
71
|
+
const s = loadScript('webview_set_mode.js');
|
|
72
|
+
return s ? s.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode)) : null;
|
|
73
|
+
};
|
|
74
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "trae",
|
|
3
|
+
"name": "Trae",
|
|
4
|
+
"category": "ide",
|
|
5
|
+
"displayName": "Trae",
|
|
6
|
+
"icon": "🔮",
|
|
7
|
+
"cli": "trae",
|
|
8
|
+
"cdpPorts": [
|
|
9
|
+
9353,
|
|
10
|
+
9354
|
|
11
|
+
],
|
|
12
|
+
"processNames": {
|
|
13
|
+
"darwin": "Trae",
|
|
14
|
+
"win32": [
|
|
15
|
+
"Trae.exe"
|
|
16
|
+
],
|
|
17
|
+
"linux": [
|
|
18
|
+
"trae"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"paths": {
|
|
22
|
+
"darwin": [
|
|
23
|
+
"/Applications/Trae.app"
|
|
24
|
+
],
|
|
25
|
+
"win32": [
|
|
26
|
+
"C:\\Users\\*\\AppData\\Local\\Programs\\trae\\Trae.exe"
|
|
27
|
+
],
|
|
28
|
+
"linux": [
|
|
29
|
+
"/opt/trae",
|
|
30
|
+
"/usr/share/trae"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"inputMethod": "cdp-type-and-send",
|
|
34
|
+
"inputSelector": ".chat-input-v2-input-box-editable, [contenteditable=\"true\"][role=\"textbox\"]"
|
|
35
|
+
}
|
|
@@ -1,13 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trae — send_message
|
|
3
3
|
*
|
|
4
|
-
* Trae는 .chat-input-v2-input-box-editable (contenteditable) 사용.
|
|
5
|
-
*
|
|
4
|
+
* Trae는 .chat-input-v2-input-box-editable (contenteditable / Lexical) 사용.
|
|
5
|
+
* Lexical의 내부 state를 올바르게 업데이트하기 위해 Selection API + execCommand 사용.
|
|
6
6
|
*
|
|
7
7
|
* 파라미터: ${ MESSAGE }
|
|
8
8
|
*/
|
|
9
|
-
(() => {
|
|
9
|
+
(async () => {
|
|
10
10
|
try {
|
|
11
|
+
const msg = ${ MESSAGE };
|
|
12
|
+
|
|
13
|
+
// ─── 1. 입력 필드 찾기 ───
|
|
14
|
+
const editor =
|
|
15
|
+
document.querySelector('.chat-input-v2-input-box-editable') ||
|
|
16
|
+
document.querySelector('[contenteditable="true"][role="textbox"]') ||
|
|
17
|
+
document.querySelector('[data-lexical-editor="true"]');
|
|
18
|
+
|
|
19
|
+
if (!editor) return JSON.stringify({ sent: false, error: 'no input found' });
|
|
20
|
+
|
|
21
|
+
// ─── 2. 포커스 + 전체 선택 + 삭제 + 삽입 ───
|
|
22
|
+
editor.focus();
|
|
23
|
+
await new Promise(r => setTimeout(r, 100));
|
|
24
|
+
|
|
25
|
+
// Selection API로 전체 선택
|
|
26
|
+
const sel = window.getSelection();
|
|
27
|
+
const range = document.createRange();
|
|
28
|
+
range.selectNodeContents(editor);
|
|
29
|
+
sel.removeAllRanges();
|
|
30
|
+
sel.addRange(range);
|
|
31
|
+
await new Promise(r => setTimeout(r, 50));
|
|
32
|
+
|
|
33
|
+
// 삭제 후 삽입 (Lexical state 동기화)
|
|
34
|
+
document.execCommand('delete', false, null);
|
|
35
|
+
await new Promise(r => setTimeout(r, 50));
|
|
36
|
+
document.execCommand('insertText', false, msg);
|
|
37
|
+
|
|
38
|
+
// Input 이벤트
|
|
39
|
+
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
40
|
+
await new Promise(r => setTimeout(r, 500));
|
|
41
|
+
|
|
42
|
+
// ─── 3. send 버튼 클릭 ───
|
|
43
|
+
const sendBtn = document.querySelector('.chat-input-v2-send-button');
|
|
44
|
+
if (sendBtn && !sendBtn.disabled) {
|
|
45
|
+
sendBtn.click();
|
|
46
|
+
return JSON.stringify({ sent: true, method: 'button' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 버튼이 아직 disabled이면 Enter 키 시도
|
|
50
|
+
const enterOpts = {
|
|
51
|
+
key: 'Enter', code: 'Enter',
|
|
52
|
+
keyCode: 13, which: 13,
|
|
53
|
+
bubbles: true, cancelable: true, composed: true,
|
|
54
|
+
};
|
|
55
|
+
editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
|
|
56
|
+
await new Promise(r => setTimeout(r, 50));
|
|
57
|
+
editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
|
|
58
|
+
editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
|
|
59
|
+
|
|
60
|
+
// 여전히 안 되면 needsTypeAndSend 폴백
|
|
11
61
|
return JSON.stringify({
|
|
12
62
|
sent: false,
|
|
13
63
|
needsTypeAndSend: true,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Scripts for Trae
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
8
|
+
function loadScript(name) {
|
|
9
|
+
try { return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8'); }
|
|
10
|
+
catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module.exports.readChat = function readChat() { return loadScript('read_chat.js'); };
|
|
15
|
+
|
|
16
|
+
module.exports.sendMessage = function sendMessage(text) {
|
|
17
|
+
const s = loadScript('send_message.js');
|
|
18
|
+
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports.resolveAction = function resolveAction(params) {
|
|
22
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
23
|
+
const buttonText = params?.button || params?.buttonText
|
|
24
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
25
|
+
const s = loadScript('resolve_action.js');
|
|
26
|
+
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports.openPanel = function openPanel() { return loadScript('open_panel.js'); };
|
|
30
|
+
|
|
31
|
+
module.exports.focusEditor = function focusEditor() { return loadScript('focus_editor.js'); };
|
|
32
|
+
|
|
33
|
+
module.exports.newSession = function newSession() { return loadScript('new_session.js'); };
|
|
34
|
+
|
|
35
|
+
module.exports.listSessions = function listSessions() { return loadScript('list_chats.js'); };
|
|
36
|
+
|
|
37
|
+
module.exports.switchSession = function switchSession(sessionId) {
|
|
38
|
+
const s = loadScript('switch_session.js');
|
|
39
|
+
return s ? s.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId)) : null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports.listModels = function listModels() { return loadScript('list_models.js'); };
|
|
43
|
+
|
|
44
|
+
module.exports.setModel = function setModel(params) {
|
|
45
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
46
|
+
const s = loadScript('set_model.js');
|
|
47
|
+
return s ? s.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model)) : null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports.listModes = function listModes() { return loadScript('list_modes.js'); };
|
|
51
|
+
|
|
52
|
+
module.exports.setMode = function setMode(params) {
|
|
53
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
54
|
+
const s = loadScript('set_mode.js');
|
|
55
|
+
return s ? s.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode)) : null;
|
|
56
|
+
};
|
|
57
|
+
|