adhdev 0.1.51 → 0.1.54
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 +2248 -847
- package/package.json +1 -1
- package/providers/_builtin/CONTRIBUTING.md +141 -0
- package/providers/_builtin/README.md +51 -0
- package/providers/_builtin/_helpers/index.js +188 -0
- package/providers/_builtin/acp/agentpool/provider.js +59 -0
- package/providers/_builtin/acp/amp/provider.js +61 -0
- package/providers/_builtin/acp/auggie/provider.js +60 -0
- package/providers/_builtin/acp/autodev/provider.js +59 -0
- package/providers/_builtin/acp/autohand/provider.js +59 -0
- package/providers/_builtin/acp/blackbox-ai/provider.js +59 -0
- package/providers/_builtin/acp/claude-agent/provider.js +61 -0
- package/providers/_builtin/acp/cline-acp/provider.js +62 -0
- package/providers/_builtin/acp/code-assistant/provider.js +59 -0
- package/providers/_builtin/acp/codebuddy/provider.js +59 -0
- package/providers/_builtin/acp/codex-cli/provider.js +11 -1
- package/providers/_builtin/acp/corust-agent/provider.js +59 -0
- package/providers/_builtin/acp/crow-cli/provider.js +59 -0
- package/providers/_builtin/acp/cursor-acp/provider.js +59 -0
- package/providers/_builtin/acp/deepagents/provider.js +59 -0
- package/providers/_builtin/acp/dimcode/provider.js +58 -0
- package/providers/_builtin/acp/docker-cagent/provider.js +59 -0
- package/providers/_builtin/acp/factory-droid/provider.js +59 -0
- package/providers/_builtin/acp/fast-agent/provider.js +59 -0
- package/providers/_builtin/acp/fount/provider.js +59 -0
- package/providers/_builtin/acp/gemini-cli/provider.js +104 -0
- package/providers/_builtin/acp/github-copilot/provider.js +60 -0
- package/providers/_builtin/acp/goose/provider.js +37 -5
- package/providers/_builtin/acp/junie/provider.js +62 -0
- package/providers/_builtin/acp/kilo/provider.js +59 -0
- package/providers/_builtin/acp/kimi-cli/provider.js +63 -0
- package/providers/_builtin/acp/kiro-cli/provider.js +59 -0
- package/providers/_builtin/acp/minion-code/provider.js +59 -0
- package/providers/_builtin/acp/mistral-vibe/provider.js +63 -0
- package/providers/_builtin/acp/nova/provider.js +59 -0
- package/providers/_builtin/acp/openclaw/provider.js +59 -0
- package/providers/_builtin/acp/opencode/provider.js +34 -6
- package/providers/_builtin/acp/openhands/provider.js +59 -0
- package/providers/_builtin/acp/pi-acp/provider.js +59 -0
- package/providers/_builtin/acp/qoder/provider.js +58 -0
- package/providers/_builtin/acp/qwen-code/provider.js +61 -0
- package/providers/_builtin/acp/stakpak/provider.js +59 -0
- package/providers/_builtin/acp/vtcode/provider.js +59 -0
- package/providers/_builtin/cli/claude-cli/provider.js +3 -0
- package/providers/_builtin/cli/codex-cli/provider.js +3 -0
- package/providers/_builtin/cli/gemini-cli/provider.js +3 -0
- package/providers/_builtin/ide/antigravity/scripts/read_chat.js +22 -0
- package/providers/_builtin/ide/kiro/provider.js +29 -1
- package/providers/_builtin/ide/kiro/scripts/open_panel.js +47 -0
- package/providers/_builtin/ide/kiro/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/kiro/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +21 -0
- package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +34 -0
- package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +68 -0
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +26 -0
- package/providers/_builtin/ide/pearai/provider.js +39 -1
- package/providers/_builtin/ide/pearai/scripts/focus_editor.js +20 -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/open_panel.js +46 -0
- package/providers/_builtin/ide/pearai/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/pearai/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +43 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +35 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +49 -0
- package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +92 -0
- package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +59 -0
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
- package/providers/_builtin/ide/trae/provider.js +22 -1
- package/providers/_builtin/ide/trae/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/trae/scripts/list_chats.js +24 -0
- package/providers/_builtin/ide/trae/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/trae/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/trae/scripts/new_session.js +30 -0
- package/providers/_builtin/ide/trae/scripts/open_panel.js +44 -0
- package/providers/_builtin/ide/trae/scripts/read_chat.js +113 -0
- package/providers/_builtin/ide/trae/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/trae/scripts/send_message.js +69 -0
- package/providers/_builtin/ide/trae/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/trae/scripts/set_model.js +15 -0
- package/providers/_builtin/ide/trae/scripts/switch_session.js +23 -0
- package/providers/_builtin/ide/windsurf/provider.js +12 -0
- package/providers/_builtin/ide/windsurf/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/windsurf/scripts/set_model.js +15 -0
- package/providers/_builtin/validate.js +156 -0
- package/dist/node_datachannel-LPY6EJH5.node +0 -0
- 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/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/{cursor/scripts_backup → kiro/scripts}/focus_editor.js +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic fallback — list_models
|
|
3
|
+
*/
|
|
4
|
+
(() => {
|
|
5
|
+
try {
|
|
6
|
+
const models = [];
|
|
7
|
+
let current = '';
|
|
8
|
+
|
|
9
|
+
// Try generic Model string from select/button
|
|
10
|
+
const sel = document.querySelectorAll('select, [class*="model"], [id*="model"]');
|
|
11
|
+
for (const el of sel) {
|
|
12
|
+
const txt = (el.textContent || '').trim();
|
|
13
|
+
if (txt && /claude|gpt|gemini|sonnet|opus/i.test(txt)) {
|
|
14
|
+
if (txt.length < 50) {
|
|
15
|
+
models.push(txt);
|
|
16
|
+
if (!current) current = txt;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (models.length === 0) {
|
|
22
|
+
const btns = document.querySelectorAll('button');
|
|
23
|
+
for (const b of btns) {
|
|
24
|
+
const txt = (b.textContent || '').trim();
|
|
25
|
+
if (txt && /claude|gpt|gemini|sonnet/i.test(txt) && txt.length < 30) {
|
|
26
|
+
models.push(txt);
|
|
27
|
+
current = txt;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return JSON.stringify({
|
|
33
|
+
models: [...new Set(models)],
|
|
34
|
+
current: current || 'Default'
|
|
35
|
+
});
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return JSON.stringify({ models: [], current: '', error: e.message });
|
|
38
|
+
}
|
|
39
|
+
})()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic fallback — set_model
|
|
3
|
+
* ${ MODEL }
|
|
4
|
+
*/
|
|
5
|
+
(() => {
|
|
6
|
+
try {
|
|
7
|
+
const want = ${ MODEL } || '';
|
|
8
|
+
const norm = (t) => t.toLowerCase().trim();
|
|
9
|
+
|
|
10
|
+
// Very basic click attempt
|
|
11
|
+
return JSON.stringify({ success: false, error: 'Model selection requires UI interaction not supported by generic script' });
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
14
|
+
}
|
|
15
|
+
})()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic fallback — set_model
|
|
3
|
+
* ${ MODEL }
|
|
4
|
+
*/
|
|
5
|
+
(() => {
|
|
6
|
+
try {
|
|
7
|
+
const want = ${ MODEL } || '';
|
|
8
|
+
const norm = (t) => t.toLowerCase().trim();
|
|
9
|
+
|
|
10
|
+
// Very basic click attempt
|
|
11
|
+
return JSON.stringify({ success: false, error: 'Model selection requires UI interaction not supported by generic script' });
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
14
|
+
}
|
|
15
|
+
})()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ADHDev Provider Validator
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node validate.js # validate all providers
|
|
7
|
+
* node validate.js ide/my-ide/provider.js # validate a single file
|
|
8
|
+
*/
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const REQUIRED_FIELDS = ['type', 'name', 'category'];
|
|
13
|
+
const VALID_CATEGORIES = ['ide', 'extension', 'cli', 'acp'];
|
|
14
|
+
const USED_PORTS = new Map();
|
|
15
|
+
const USED_TYPES = new Map();
|
|
16
|
+
|
|
17
|
+
let errors = 0;
|
|
18
|
+
let warnings = 0;
|
|
19
|
+
let validated = 0;
|
|
20
|
+
|
|
21
|
+
function validate(filePath) {
|
|
22
|
+
const rel = path.relative(process.cwd(), filePath);
|
|
23
|
+
|
|
24
|
+
// 1. Syntax check
|
|
25
|
+
try {
|
|
26
|
+
delete require.cache[require.resolve(filePath)];
|
|
27
|
+
} catch {}
|
|
28
|
+
|
|
29
|
+
let mod;
|
|
30
|
+
try {
|
|
31
|
+
mod = require(filePath);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error(`❌ ${rel}: syntax error — ${e.message}`);
|
|
34
|
+
errors++;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Required fields
|
|
39
|
+
for (const field of REQUIRED_FIELDS) {
|
|
40
|
+
if (!mod[field]) {
|
|
41
|
+
console.error(`❌ ${rel}: missing required field '${field}'`);
|
|
42
|
+
errors++;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 3. Valid category
|
|
48
|
+
if (!VALID_CATEGORIES.includes(mod.category)) {
|
|
49
|
+
console.error(`❌ ${rel}: invalid category '${mod.category}' (must be: ${VALID_CATEGORIES.join(', ')})`);
|
|
50
|
+
errors++;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 4. Unique type
|
|
55
|
+
if (USED_TYPES.has(mod.type)) {
|
|
56
|
+
console.error(`❌ ${rel}: duplicate type '${mod.type}' (also in ${USED_TYPES.get(mod.type)})`);
|
|
57
|
+
errors++;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
USED_TYPES.set(mod.type, rel);
|
|
61
|
+
|
|
62
|
+
// 5. IDE-specific checks
|
|
63
|
+
if (mod.category === 'ide') {
|
|
64
|
+
if (!mod.cdpPorts || !Array.isArray(mod.cdpPorts) || mod.cdpPorts.length < 2) {
|
|
65
|
+
console.warn(`⚠ ${rel}: IDE missing cdpPorts [primary, fallback]`);
|
|
66
|
+
warnings++;
|
|
67
|
+
} else {
|
|
68
|
+
for (const port of mod.cdpPorts) {
|
|
69
|
+
if (USED_PORTS.has(port)) {
|
|
70
|
+
console.error(`❌ ${rel}: CDP port ${port} conflicts with ${USED_PORTS.get(port)}`);
|
|
71
|
+
errors++;
|
|
72
|
+
}
|
|
73
|
+
USED_PORTS.set(port, rel);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!mod.cli) {
|
|
78
|
+
console.warn(`⚠ ${rel}: IDE missing 'cli' field`);
|
|
79
|
+
warnings++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!mod.paths) {
|
|
83
|
+
console.warn(`⚠ ${rel}: IDE missing 'paths' (install detection won't work)`);
|
|
84
|
+
warnings++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 6. ACP-specific checks
|
|
89
|
+
if (mod.category === 'acp') {
|
|
90
|
+
if (!mod.spawn || !mod.spawn.command) {
|
|
91
|
+
console.warn(`⚠ ${rel}: ACP missing spawn.command`);
|
|
92
|
+
warnings++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 7. Scripts check
|
|
97
|
+
if (mod.category === 'ide' || mod.category === 'extension') {
|
|
98
|
+
const hasRead = mod.scripts?.readChat || mod.scripts?.webviewReadChat;
|
|
99
|
+
const hasSend = mod.scripts?.sendMessage || mod.scripts?.webviewSendMessage;
|
|
100
|
+
if (!hasRead) {
|
|
101
|
+
console.warn(`⚠ ${rel}: no readChat/webviewReadChat script`);
|
|
102
|
+
warnings++;
|
|
103
|
+
}
|
|
104
|
+
if (!hasSend) {
|
|
105
|
+
console.warn(`⚠ ${rel}: no sendMessage/webviewSendMessage script`);
|
|
106
|
+
warnings++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 8. Webview IDE consistency
|
|
111
|
+
if (mod.category === 'ide' && mod.webviewMatchText) {
|
|
112
|
+
if (!mod.scripts?.webviewReadChat) {
|
|
113
|
+
console.warn(`⚠ ${rel}: has webviewMatchText but no webviewReadChat script`);
|
|
114
|
+
warnings++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
validated++;
|
|
119
|
+
console.log(`✅ ${rel}: ${mod.type} (${mod.category}) — ${mod.name}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function scanDir(dir) {
|
|
123
|
+
if (!fs.existsSync(dir)) return;
|
|
124
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
125
|
+
const full = path.join(dir, entry.name);
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue;
|
|
128
|
+
scanDir(full);
|
|
129
|
+
} else if (entry.name === 'provider.js') {
|
|
130
|
+
validate(full);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Main ───
|
|
136
|
+
const args = process.argv.slice(2);
|
|
137
|
+
|
|
138
|
+
if (args.length > 0) {
|
|
139
|
+
// Validate specific file(s)
|
|
140
|
+
for (const arg of args) {
|
|
141
|
+
const filePath = path.resolve(arg);
|
|
142
|
+
if (!fs.existsSync(filePath)) {
|
|
143
|
+
console.error(`❌ File not found: ${arg}`);
|
|
144
|
+
errors++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
validate(filePath);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
// Validate all
|
|
151
|
+
console.log('🔍 Validating all providers...\n');
|
|
152
|
+
scanDir(process.cwd());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`\n━━━ Result: ${validated} passed, ${errors} errors, ${warnings} warnings ━━━`);
|
|
156
|
+
process.exit(errors > 0 ? 1 : 0);
|
|
Binary file
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor — IDE Provider
|
|
3
|
-
*
|
|
4
|
-
* Category: ide (workbench CDP session — iframe 없음)
|
|
5
|
-
*
|
|
6
|
-
* 특이 사항:
|
|
7
|
-
* - .composer-view 컨테이너 (data-composer-id, data-composer-status)
|
|
8
|
-
* - contenteditable[role="textbox"] 또는 textarea.native-input
|
|
9
|
-
* - Fiber summaries 기반 제목 추출
|
|
10
|
-
* - 6단계 모달 감지 계층 (overlay → run-command-review → ancestor → dialog → inline → quickInput → AI msg)
|
|
11
|
-
* - resolve_modal 사용
|
|
12
|
-
*
|
|
13
|
-
* @type {import('../../../src/providers/contracts').ProviderModule}
|
|
14
|
-
*/
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
19
|
-
|
|
20
|
-
function loadScript(name) {
|
|
21
|
-
try {
|
|
22
|
-
return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8');
|
|
23
|
-
} catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
module.exports = {
|
|
29
|
-
type: 'cursor',
|
|
30
|
-
name: 'Cursor',
|
|
31
|
-
category: 'ide',
|
|
32
|
-
|
|
33
|
-
// ─── IDE 인프라 ───
|
|
34
|
-
displayName: 'Cursor',
|
|
35
|
-
icon: '⚡',
|
|
36
|
-
cli: 'cursor',
|
|
37
|
-
cdpPorts: [9333, 9334],
|
|
38
|
-
processNames: {
|
|
39
|
-
darwin: 'Cursor',
|
|
40
|
-
win32: ['Cursor.exe'],
|
|
41
|
-
},
|
|
42
|
-
paths: {
|
|
43
|
-
darwin: ['/Applications/Cursor.app'],
|
|
44
|
-
win32: ['C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe'],
|
|
45
|
-
linux: ['/opt/Cursor', '/usr/share/cursor'],
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
inputMethod: 'cdp-type-and-send',
|
|
49
|
-
inputSelector: '[contenteditable="true"][role="textbox"]',
|
|
50
|
-
|
|
51
|
-
scripts: {
|
|
52
|
-
readChat() {
|
|
53
|
-
return loadScript('read_chat.js');
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
sendMessage(text) {
|
|
57
|
-
const script = loadScript('send_message.js');
|
|
58
|
-
if (!script) return null;
|
|
59
|
-
return script.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text));
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
listSessions() {
|
|
63
|
-
return loadScript('list_chats.js');
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
switchSession(sessionId) {
|
|
67
|
-
const script = loadScript('switch_session.js');
|
|
68
|
-
if (!script) return null;
|
|
69
|
-
return script.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId));
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
newSession() {
|
|
73
|
-
return loadScript('new_session.js');
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
resolveModal(buttonText) {
|
|
77
|
-
const script = loadScript('resolve_modal.js');
|
|
78
|
-
if (!script) return null;
|
|
79
|
-
return script.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText));
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
focusEditor() {
|
|
83
|
-
return loadScript('focus_editor.js');
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
openPanel() {
|
|
87
|
-
return loadScript('open_panel.js');
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
probe() {
|
|
91
|
-
return `(() => {
|
|
92
|
-
const checks = [];
|
|
93
|
-
try {
|
|
94
|
-
const composer = document.querySelector('.composer-view, .chat-view, [data-composer-id]');
|
|
95
|
-
checks.push({ name: 'composer_view', passed: !!composer });
|
|
96
|
-
|
|
97
|
-
const editor = document.querySelector('[contenteditable="true"][role="textbox"]')
|
|
98
|
-
|| document.querySelector('textarea.native-input');
|
|
99
|
-
checks.push({ name: 'editor', passed: !!editor });
|
|
100
|
-
|
|
101
|
-
const composerId = composer?.getAttribute('data-composer-id');
|
|
102
|
-
checks.push({ name: 'composer_id', passed: !!composerId });
|
|
103
|
-
|
|
104
|
-
const msgEls = document.querySelectorAll('[data-message-id]');
|
|
105
|
-
checks.push({ name: 'message_elements', passed: msgEls.length > 0, message: msgEls.length + ' messages' });
|
|
106
|
-
|
|
107
|
-
const healthy = checks.filter(c => c.passed).length >= 2;
|
|
108
|
-
return JSON.stringify({ healthy, checks });
|
|
109
|
-
} catch (e) {
|
|
110
|
-
checks.push({ name: 'error', passed: false, message: e.message });
|
|
111
|
-
return JSON.stringify({ healthy: false, checks });
|
|
112
|
-
}
|
|
113
|
-
})()`;
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor — IDE Provider
|
|
3
|
-
* @type {import('../../../src/providers/contracts').ProviderModule}
|
|
4
|
-
*/
|
|
5
|
-
module.exports = {
|
|
6
|
-
type: 'cursor',
|
|
7
|
-
name: 'Cursor',
|
|
8
|
-
category: 'ide',
|
|
9
|
-
displayName: 'Cursor',
|
|
10
|
-
icon: '⚡',
|
|
11
|
-
cli: 'cursor',
|
|
12
|
-
cdpPorts: [9333, 9334],
|
|
13
|
-
processNames: { darwin: 'Cursor', win32: ['Cursor.exe'] },
|
|
14
|
-
paths: {
|
|
15
|
-
darwin: ['/Applications/Cursor.app'],
|
|
16
|
-
win32: ['C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe'],
|
|
17
|
-
linux: ['/opt/Cursor', '/usr/share/cursor'],
|
|
18
|
-
},
|
|
19
|
-
inputMethod: 'cdp-type-and-send',
|
|
20
|
-
inputSelector: '.aislash-editor-input[contenteditable="true"]',
|
|
21
|
-
|
|
22
|
-
scripts: {
|
|
23
|
-
readChat() {
|
|
24
|
-
return `(() => {
|
|
25
|
-
try {
|
|
26
|
-
const c = document.querySelector('[data-composer-id]');
|
|
27
|
-
const id = c?.getAttribute('data-composer-id') || 'active';
|
|
28
|
-
const rawStatus = c?.getAttribute('data-composer-status') || 'idle';
|
|
29
|
-
const status = rawStatus === 'thinking' ? 'generating' : rawStatus;
|
|
30
|
-
const msgs = [];
|
|
31
|
-
document.querySelectorAll('.composer-human-ai-pair-container').forEach((p, i) => {
|
|
32
|
-
const h = p.querySelector('.composer-human-message');
|
|
33
|
-
if (h) msgs.push({ role: 'user', content: h.textContent.trim().substring(0, 6000), index: msgs.length });
|
|
34
|
-
p.querySelectorAll('.composer-rendered-message').forEach(a => {
|
|
35
|
-
if (a.closest('.composer-human-message')) return;
|
|
36
|
-
const t = a.textContent.trim();
|
|
37
|
-
if (t) msgs.push({ role: 'assistant', content: t.substring(0, 6000), index: msgs.length });
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
const inputEl = document.querySelector('.aislash-editor-input[contenteditable="true"]');
|
|
41
|
-
const inputContent = inputEl?.textContent?.trim() || '';
|
|
42
|
-
return JSON.stringify({ id, status, title: document.title.split(' — ')[0], messages: msgs, inputContent });
|
|
43
|
-
} catch(e) {
|
|
44
|
-
return JSON.stringify({ id: '', status: 'error', messages: [] });
|
|
45
|
-
}
|
|
46
|
-
})()`;
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
sendMessage(text) {
|
|
50
|
-
return `(() => {
|
|
51
|
-
try {
|
|
52
|
-
const input = document.querySelector('.aislash-editor-input[contenteditable="true"]');
|
|
53
|
-
if (!input) return JSON.stringify({ sent: false, error: 'Input box not found' });
|
|
54
|
-
return JSON.stringify({
|
|
55
|
-
sent: false,
|
|
56
|
-
needsTypeAndSend: true,
|
|
57
|
-
selector: '.aislash-editor-input[contenteditable="true"]',
|
|
58
|
-
});
|
|
59
|
-
} catch(e) {
|
|
60
|
-
return JSON.stringify({ sent: false, error: e.message });
|
|
61
|
-
}
|
|
62
|
-
})()`;
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
listSessions() {
|
|
66
|
-
return `(() => {
|
|
67
|
-
try {
|
|
68
|
-
const sessions = [];
|
|
69
|
-
const composer = document.querySelector('[data-composer-id]');
|
|
70
|
-
if (composer) {
|
|
71
|
-
sessions.push({
|
|
72
|
-
id: composer.getAttribute('data-composer-id'),
|
|
73
|
-
title: document.title.split(' — ')[0],
|
|
74
|
-
active: true,
|
|
75
|
-
status: composer.getAttribute('data-composer-status') || 'idle',
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
return JSON.stringify(sessions);
|
|
79
|
-
} catch(e) {
|
|
80
|
-
return JSON.stringify([]);
|
|
81
|
-
}
|
|
82
|
-
})()`;
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
newSession() {
|
|
86
|
-
return `(() => {
|
|
87
|
-
try {
|
|
88
|
-
const newBtn = [...document.querySelectorAll('a.action-label.codicon-add-two')]
|
|
89
|
-
.find(a => (a.getAttribute('aria-label') || '').startsWith('New Chat'));
|
|
90
|
-
if (newBtn) { newBtn.click(); return JSON.stringify({ created: true }); }
|
|
91
|
-
return JSON.stringify({ created: false, error: 'New Chat button not found' });
|
|
92
|
-
} catch(e) {
|
|
93
|
-
return JSON.stringify({ created: false, error: e.message });
|
|
94
|
-
}
|
|
95
|
-
})()`;
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
focusEditor() {
|
|
99
|
-
return `(() => {
|
|
100
|
-
try {
|
|
101
|
-
const input = document.querySelector('.aislash-editor-input[contenteditable="true"]');
|
|
102
|
-
if (input) { input.focus(); return 'focused'; }
|
|
103
|
-
return 'not_found';
|
|
104
|
-
} catch(e) { return 'error'; }
|
|
105
|
-
})()`;
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
openPanel() {
|
|
109
|
-
return `(() => {
|
|
110
|
-
try {
|
|
111
|
-
const sidebar = document.getElementById('workbench.parts.auxiliarybar');
|
|
112
|
-
if (sidebar && sidebar.offsetWidth > 0) {
|
|
113
|
-
const chatView = document.querySelector('[data-composer-id]');
|
|
114
|
-
if (chatView) return 'visible';
|
|
115
|
-
}
|
|
116
|
-
const btns = [...document.querySelectorAll('li.action-item a, button, [role="tab"]')];
|
|
117
|
-
const toggle = btns.find(b => {
|
|
118
|
-
const label = (b.textContent || b.getAttribute('aria-label') || '').toLowerCase();
|
|
119
|
-
return /agent|chat|composer|cursor tab/i.test(label);
|
|
120
|
-
});
|
|
121
|
-
if (toggle) { toggle.click(); return 'opened'; }
|
|
122
|
-
return 'not_found';
|
|
123
|
-
} catch (e) { return 'error'; }
|
|
124
|
-
})()`;
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
};
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor v1 — list_chats
|
|
3
|
-
*
|
|
4
|
-
* CURSOR.md 4-2: React Fiber 주 수단, DOM 폴백
|
|
5
|
-
* Fiber keys: summaries, recentConversations, chatHistory, composers
|
|
6
|
-
* "More" 항목 제외, modSec 정렬, 중복 제거, 100자 truncate
|
|
7
|
-
*
|
|
8
|
-
* 최종 확인: 2026-03-06
|
|
9
|
-
*/
|
|
10
|
-
(async () => {
|
|
11
|
-
// ─── 1. React Fiber에서 히스토리 추출 ───
|
|
12
|
-
const getFiberHistory = function () {
|
|
13
|
-
// CURSOR.md: Fiber 엔트리 포인트
|
|
14
|
-
const entryPoints = ['.composer-view', '.chat-view', '.agent-sidebar-cell', '[data-past-conversations-toggle="true"]', '.history-toggle-button', '#workbench.parts.auxiliarybar'];
|
|
15
|
-
for (var i = 0; i < entryPoints.length; i++) {
|
|
16
|
-
var el = document.querySelector(entryPoints[i]);
|
|
17
|
-
if (!el) continue;
|
|
18
|
-
var fiberKey = Object.keys(el).find(function (k) { return k.startsWith('__reactFiber'); });
|
|
19
|
-
if (!fiberKey) continue;
|
|
20
|
-
var fiber = el[fiberKey];
|
|
21
|
-
var summaries = null;
|
|
22
|
-
var currentWsUris = null;
|
|
23
|
-
// CURSOR.md: Fiber 순회 (최대 200)
|
|
24
|
-
for (var d = 0; d < 200 && fiber; d++) {
|
|
25
|
-
if (fiber.memoizedState) {
|
|
26
|
-
var s = fiber.memoizedState;
|
|
27
|
-
while (s) {
|
|
28
|
-
try {
|
|
29
|
-
var ms = s.memoizedState;
|
|
30
|
-
if (ms && typeof ms === 'object') {
|
|
31
|
-
// CURSOR.md Fiber keys
|
|
32
|
-
if (ms.summaries) summaries = ms.summaries;
|
|
33
|
-
else if (ms.recentConversations) summaries = ms.recentConversations;
|
|
34
|
-
else if (ms.chatHistory) summaries = ms.chatHistory;
|
|
35
|
-
else if (ms.composers && Array.isArray(ms.composers)) {
|
|
36
|
-
summaries = {};
|
|
37
|
-
ms.composers.forEach(c => summaries[c.id || c.composerId] = c);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// CURSOR.md: 워크스페이스 URI 추출
|
|
41
|
-
var lrs = s.queue && s.queue.lastRenderedState;
|
|
42
|
-
if (lrs && lrs.workspaceUris && Array.isArray(lrs.workspaceUris)) currentWsUris = lrs.workspaceUris;
|
|
43
|
-
if (summaries) break;
|
|
44
|
-
} catch (e) { }
|
|
45
|
-
s = s.next;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (summaries) break;
|
|
49
|
-
fiber = fiber.return;
|
|
50
|
-
}
|
|
51
|
-
if (summaries) {
|
|
52
|
-
var wsUris = currentWsUris ? new Set(currentWsUris) : null;
|
|
53
|
-
var res = [];
|
|
54
|
-
var entries = Object.entries(summaries);
|
|
55
|
-
for (var j = 0; j < entries.length; j++) {
|
|
56
|
-
var pair = entries[j];
|
|
57
|
-
var id = pair[0];
|
|
58
|
-
var info = pair[1];
|
|
59
|
-
if (!id || !info) continue;
|
|
60
|
-
// CURSOR.md: 히스토리 항목 필드 (summary → title → name → historyItemName)
|
|
61
|
-
var title = info.summary || info.title || info.name || info.historyItemName || 'New Chat';
|
|
62
|
-
var status = info.mode || info.unifiedMode || 'standard';
|
|
63
|
-
var lastMod = (info.lastModifiedTime && info.lastModifiedTime.seconds) || info.lastUpdatedAt || info.createdAt || 0;
|
|
64
|
-
// 워크스페이스 필터링
|
|
65
|
-
if (wsUris && info.workspaces) {
|
|
66
|
-
var match = false;
|
|
67
|
-
for (var k = 0; k < info.workspaces.length; k++) {
|
|
68
|
-
if (wsUris.has(info.workspaces[k].workspaceFolderAbsoluteUri || '')) { match = true; break; }
|
|
69
|
-
}
|
|
70
|
-
if (!match) continue;
|
|
71
|
-
}
|
|
72
|
-
// CURSOR.md: "More" 항목 제외 필수
|
|
73
|
-
if (id === 'More' || (title || '').toLowerCase() === 'more' || id === 'history.more') continue;
|
|
74
|
-
res.push({ id: id, title: title, status: status, modSec: lastMod });
|
|
75
|
-
}
|
|
76
|
-
// modSec 내림차순 정렬
|
|
77
|
-
res.sort(function (a, b) { return b.modSec - a.modSec; });
|
|
78
|
-
return res;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return null;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// ─── 2. DOM 폴백 (CURSOR.md 셀렉터 참조표) ───
|
|
85
|
-
const getDomHistory = function () {
|
|
86
|
-
const selectors = ['.composer-below-chat-history-item', '.agent-sidebar-cell', '.chat-history-item', '.composer-history-item', '.history-item-container', '.monaco-list-row[aria-label*="Chat"]', '.monaco-list-row[aria-label*="Composer"]', '.monaco-list-row[aria-label*="Conversation"]'];
|
|
87
|
-
const items = document.querySelectorAll(selectors.join(', '));
|
|
88
|
-
return Array.from(items).map(function (el) {
|
|
89
|
-
// .agent-sidebar-cell-text, .auxiliary-bar-chat-title (CURSOR.md)
|
|
90
|
-
const titleEl = el.querySelector('.agent-sidebar-cell-text, .auxiliary-bar-chat-title, [class*="title"], [class*="label"], .composer-history-item-title') || el;
|
|
91
|
-
let title = titleEl && titleEl.textContent ? titleEl.textContent.trim() : (el.getAttribute('title') || el.getAttribute('aria-label'));
|
|
92
|
-
if (title) title = title.replace(/\s+\d+[hdmyws]$/, '').replace(/^\d+[hdmyws]\s+/, '').trim();
|
|
93
|
-
return { id: el.getAttribute('data-composer-id') || el.getAttribute('data-id') || el.id || title, status: el.getAttribute('data-composer-status') || '', title: title || 'New Chat' };
|
|
94
|
-
});
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// ─── 3. 결과 조합 ───
|
|
98
|
-
let history = getFiberHistory();
|
|
99
|
-
if (!history || history.length === 0) history = getDomHistory();
|
|
100
|
-
if (history && history.length > 0) {
|
|
101
|
-
// 중복 제거 + 100자 truncate
|
|
102
|
-
const seen = new Set();
|
|
103
|
-
return history.filter(item => {
|
|
104
|
-
const key = item.id || item.title;
|
|
105
|
-
if (seen.has(key)) return false;
|
|
106
|
-
seen.add(key);
|
|
107
|
-
return true;
|
|
108
|
-
}).map(item => ({ id: item.id, title: item.title && item.title.length > 100 ? item.title.substring(0, 100) + '...' : item.title, status: item.status }));
|
|
109
|
-
}
|
|
110
|
-
return [];
|
|
111
|
-
})()
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor v1 — new_session
|
|
3
|
-
*
|
|
4
|
-
* Cursor는 workbench DOM 직접 접근.
|
|
5
|
-
* "New Chat" / "New Composer" 버튼 찾기.
|
|
6
|
-
*
|
|
7
|
-
* 전략:
|
|
8
|
-
* 1. 키보드 단축키 Ctrl+L (새 composer)
|
|
9
|
-
* 2. aria-label 기반
|
|
10
|
-
* 3. 텍스트 기반
|
|
11
|
-
*
|
|
12
|
-
* 최종 확인: 2026-03-07
|
|
13
|
-
*/
|
|
14
|
-
(() => {
|
|
15
|
-
try {
|
|
16
|
-
// ─── 1. aria-label 기반 ───
|
|
17
|
-
const allBtns = Array.from(document.querySelectorAll('button, [role="button"], .action-item'))
|
|
18
|
-
.filter(b => b.offsetWidth > 0);
|
|
19
|
-
|
|
20
|
-
for (const btn of allBtns) {
|
|
21
|
-
const label = (btn.getAttribute('aria-label') || '').toLowerCase();
|
|
22
|
-
if (label.includes('new chat') || label.includes('new composer') ||
|
|
23
|
-
label.includes('new conversation') || label.includes('start new')) {
|
|
24
|
-
btn.click();
|
|
25
|
-
return 'clicked (aria)';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ─── 2. 텍스트 기반 ───
|
|
30
|
-
for (const btn of allBtns) {
|
|
31
|
-
const text = (btn.textContent || '').trim();
|
|
32
|
-
if (text === '+' || text === 'New Chat' || text === 'New Composer' ||
|
|
33
|
-
text === 'Start New Chat') {
|
|
34
|
-
btn.click();
|
|
35
|
-
return 'clicked (text)';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ─── 3. Codicon 아이콘 기반 (Cursor 스타일) ───
|
|
40
|
-
for (const btn of allBtns) {
|
|
41
|
-
const hasPlus = btn.querySelector('.codicon-plus, .codicon-add, [class*="plus"]');
|
|
42
|
-
if (hasPlus) {
|
|
43
|
-
const label = (btn.getAttribute('aria-label') || btn.getAttribute('title') || '').toLowerCase();
|
|
44
|
-
if (label.includes('chat') || label.includes('composer') || label.includes('new') || label === '') {
|
|
45
|
-
btn.click();
|
|
46
|
-
return 'clicked (icon)';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ─── 4. 키보드 단축키 Ctrl+L (Cursor 기본 바인딩) ───
|
|
52
|
-
document.dispatchEvent(new KeyboardEvent('keydown', {
|
|
53
|
-
key: 'l', code: 'KeyL', keyCode: 76,
|
|
54
|
-
ctrlKey: true, metaKey: false,
|
|
55
|
-
bubbles: true, cancelable: true,
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
return 'sent Ctrl+L';
|
|
59
|
-
} catch (e) {
|
|
60
|
-
return 'error: ' + e.message;
|
|
61
|
-
}
|
|
62
|
-
})()
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor v1 — open_panel
|
|
3
|
-
*
|
|
4
|
-
* Cursor Agent 패널이 숨겨져 있을 때 패널 열기.
|
|
5
|
-
* Cursor 워크벤치 페이지에서 실행 (type=page, workbench URL)
|
|
6
|
-
*
|
|
7
|
-
* 반환: 'visible' | 'opened' | 'error: ...'
|
|
8
|
-
* 최종 확인: 2026-03-07
|
|
9
|
-
*/
|
|
10
|
-
(() => {
|
|
11
|
-
try {
|
|
12
|
-
// Cursor Agent 패널 확인
|
|
13
|
-
const sidebar = document.getElementById('workbench.parts.auxiliarybar') ||
|
|
14
|
-
document.getElementById('workbench.parts.unifiedsidebar');
|
|
15
|
-
if (sidebar && sidebar.offsetWidth > 0 && sidebar.offsetHeight > 0) {
|
|
16
|
-
return 'visible';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Toggle 버튼 클릭
|
|
20
|
-
const toggleBtns = Array.from(document.querySelectorAll('li.action-item a'));
|
|
21
|
-
for (const btn of toggleBtns) {
|
|
22
|
-
const label = (btn.getAttribute('aria-label') || '').toLowerCase();
|
|
23
|
-
if (label.includes('toggle agents') || label.includes('toggle primary')) {
|
|
24
|
-
btn.click();
|
|
25
|
-
return 'opened (toggle)';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return 'error: no toggle button found';
|
|
30
|
-
} catch (e) { return 'error: ' + e.message; }
|
|
31
|
-
})()
|