@adhdev/daemon-core 0.6.35 → 0.6.37

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.
Files changed (93) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +112 -26
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/providers/_builtin/acp/codex-cli/provider.json +2 -2
  6. package/providers/_builtin/cli/codex-cli/provider.json +8 -18
  7. package/providers/_builtin/ide/antigravity/provider.json +2 -3
  8. package/providers/_builtin/ide/cursor/provider.json +2 -2
  9. package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/read_chat.js +1 -1
  10. package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/resolve_action.js +1 -1
  11. package/providers/_builtin/ide/windsurf/scripts/1.0/focus_editor.js +16 -10
  12. package/providers/_builtin/ide/windsurf/scripts/1.0/list_chats.js +9 -103
  13. package/providers/_builtin/ide/windsurf/scripts/1.0/list_models.js +43 -26
  14. package/providers/_builtin/ide/windsurf/scripts/1.0/list_modes.js +51 -27
  15. package/providers/_builtin/ide/windsurf/scripts/1.0/list_sessions.js +79 -0
  16. package/providers/_builtin/ide/windsurf/scripts/1.0/new_session.js +30 -41
  17. package/providers/_builtin/ide/windsurf/scripts/1.0/open_panel.js +44 -35
  18. package/providers/_builtin/ide/windsurf/scripts/1.0/read_chat.js +211 -273
  19. package/providers/_builtin/ide/windsurf/scripts/1.0/resolve_action.js +64 -50
  20. package/providers/_builtin/ide/windsurf/scripts/1.0/send_message.js +14 -81
  21. package/providers/_builtin/ide/windsurf/scripts/1.0/set_mode.js +34 -6
  22. package/providers/_builtin/ide/windsurf/scripts/1.0/set_model.js +40 -6
  23. package/providers/_builtin/ide/windsurf/scripts/1.0/switch_session.js +66 -42
  24. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/focus_editor.js +30 -0
  25. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_chats.js +117 -0
  26. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_models.js +39 -0
  27. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_modes.js +39 -0
  28. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/new_session.js +69 -0
  29. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/open_panel.js +58 -0
  30. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/read_chat.js +315 -0
  31. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/resolve_action.js +68 -0
  32. package/providers/_builtin/ide/{cursor/scripts/0.49 → windsurf/scripts/1.0.bak}/scripts.js +27 -23
  33. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/send_message.js +87 -0
  34. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/set_mode.js +15 -0
  35. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/set_model.js +15 -0
  36. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/switch_session.js +58 -0
  37. package/providers/_builtin/registry.json +5 -9
  38. package/src/cdp/manager.ts +38 -12
  39. package/src/commands/handler.ts +1 -8
  40. package/src/daemon/dev-server.ts +66 -8
  41. package/src/detection/ide-detector.ts +0 -3
  42. package/src/providers/provider-loader.ts +4 -2
  43. package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +0 -38
  44. package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +0 -48
  45. package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +0 -290
  46. package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +0 -57
  47. package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +0 -34
  48. package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +0 -47
  49. package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +0 -20
  50. package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +0 -137
  51. package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +0 -75
  52. package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +0 -63
  53. package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +0 -56
  54. package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +0 -114
  55. package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +0 -30
  56. package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +0 -13
  57. package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +0 -78
  58. package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +0 -40
  59. package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +0 -23
  60. package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +0 -20
  61. package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +0 -23
  62. package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +0 -23
  63. package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +0 -38
  64. package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +0 -81
  65. package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +0 -28
  66. package/providers/_builtin/ide/cursor/scripts/0.49.bak/list_sessions.js +0 -42
  67. package/providers/_builtin/ide/cursor/scripts/0.49.bak/read_chat.js +0 -79
  68. package/providers/_builtin/ide/cursor/scripts/0.49.bak/resolve_action.js +0 -19
  69. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/focus_editor.js +0 -0
  70. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/list_chats.js +0 -0
  71. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/list_models.js +0 -0
  72. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/list_modes.js +0 -0
  73. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/new_session.js +0 -0
  74. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/read_chat.js +0 -0
  75. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/resolve_action.js +0 -0
  76. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/scripts.js +0 -0
  77. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/send_message.js +0 -0
  78. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/set_mode.js +0 -0
  79. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/set_model.js +0 -0
  80. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/switch_session.js +0 -0
  81. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/dismiss_notification.js +0 -0
  82. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/focus_editor.js +0 -0
  83. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_models.js +0 -0
  84. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_modes.js +0 -0
  85. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_notifications.js +0 -0
  86. /package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/list_sessions.js +0 -0
  87. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/new_session.js +0 -0
  88. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/open_panel.js +0 -0
  89. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/scripts.js +0 -0
  90. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/send_message.js +0 -0
  91. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/set_mode.js +0 -0
  92. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/set_model.js +0 -0
  93. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/switch_session.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.6.35",
3
+ "version": "0.6.37",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -6,8 +6,8 @@
6
6
  "icon": "🤖",
7
7
  "install": "npm install -g @zed-industries/codex-acp (requires OPENAI_API_KEY)",
8
8
  "spawn": {
9
- "command": "codex-acp",
10
- "args": [],
9
+ "command": "codex",
10
+ "args": ["--acp"],
11
11
  "shell": false
12
12
  },
13
13
  "auth": [
@@ -57,33 +57,23 @@
57
57
  "binary": "codex",
58
58
  "spawn": {
59
59
  "command": "codex",
60
- "args": [
61
- "--full-auto"
62
- ],
60
+ "args": [],
63
61
  "shell": false,
64
62
  "env": {}
65
63
  },
66
64
  "patterns": {
67
65
  "prompt": [
68
- {},
69
- {},
70
- {}
66
+ { "source": "To get started, describe a task or try one of these commands", "flags": "i" },
67
+ { "source": "^>\\s*$", "flags": "m" }
71
68
  ],
72
69
  "generating": [
73
- {},
74
- {},
75
- {}
70
+ { "source": "Working \\(\\d+s", "flags": "i" }
76
71
  ],
77
72
  "approval": [
78
- {},
79
- {},
80
- {},
81
- {},
82
- {}
73
+ { "source": "Always approve this session", "flags": "i" },
74
+ { "source": "Approve and run now", "flags": "i" },
75
+ { "source": "Allow command\\?", "flags": "i" }
83
76
  ],
84
- "ready": [
85
- {},
86
- {}
87
- ]
77
+ "ready": []
88
78
  }
89
79
  }
@@ -37,10 +37,9 @@
37
37
  "versionCommand": "antigravity --version",
38
38
  "providerVersion": "1.0.0",
39
39
  "compatibility": [
40
- { "ideVersion": ">=1.107.0", "scriptDir": "scripts/1.107" },
41
- { "ideVersion": ">=1.105.0", "scriptDir": "scripts/1.106" }
40
+ { "ideVersion": ">=1.0.0", "scriptDir": "scripts/1.0" }
42
41
  ],
43
- "defaultScriptDir": "scripts/1.107",
42
+ "defaultScriptDir": "scripts/1.0",
44
43
  "settings": {
45
44
  "approvalAlert": {
46
45
  "type": "boolean",
@@ -37,9 +37,9 @@
37
37
  "versionCommand": "cursor --version",
38
38
  "providerVersion": "1.0.0",
39
39
  "compatibility": [
40
- { "ideVersion": ">=0.49.0", "scriptDir": "scripts/0.49" }
40
+ { "ideVersion": ">=1.0.0", "scriptDir": "scripts/1.0" }
41
41
  ],
42
- "defaultScriptDir": "scripts/0.49",
42
+ "defaultScriptDir": "scripts/1.0",
43
43
  "vscodeCommands": {
44
44
  "changeModel": "cursor.model"
45
45
  },
@@ -21,7 +21,7 @@
21
21
  const rawStatus = c?.getAttribute('data-composer-status') || 'idle';
22
22
  let status = rawStatus;
23
23
  if (rawStatus === 'thinking' || rawStatus === 'streaming') status = 'generating';
24
- else if (rawStatus === 'completed' || rawStatus === 'idle' || !rawStatus) status = 'idle';
24
+ else if (rawStatus === 'completed' || rawStatus === 'cancelled' || rawStatus === 'idle' || !rawStatus) status = 'idle';
25
25
 
26
26
  // ─── Approval Detection ───
27
27
  let activeModal = null;
@@ -11,7 +11,7 @@
11
11
  (params) => {
12
12
  try {
13
13
  const btns = [...document.querySelectorAll('button, [role="button"], .cursor-pointer')].filter(b => b.offsetWidth > 0);
14
- const searchText = (params.buttonText || '').toLowerCase();
14
+ const searchText = (params.buttonText || params.action || params.button || '').toLowerCase();
15
15
  const target = btns.find(b => (b.textContent||'').trim().replace(/[⏎↵]/g, '').trim().toLowerCase().includes(searchText));
16
16
  if (target) { target.click(); return JSON.stringify({ resolved: true, clicked: target.textContent.trim() }); }
17
17
  return JSON.stringify({ resolved: false, available: btns.map(b => b.textContent.trim()).filter(Boolean).slice(0, 15) });
@@ -13,18 +13,24 @@
13
13
  */
14
14
  (() => {
15
15
  try {
16
+ const cascade = document.querySelector('#windsurf\\.cascadePanel') || document.querySelector('.chat-client-root');
17
+ const root = cascade || document;
16
18
  const editor =
17
- document.querySelector('[contenteditable="true"][role="textbox"]') ||
18
- document.querySelector('[data-lexical-editor="true"]') ||
19
- document.querySelector('.chat-input textarea') ||
20
- document.querySelector('.cascade-input [contenteditable="true"]') ||
21
- document.querySelector('textarea:not(.xterm-helper-textarea)');
22
- if (editor) {
23
- editor.focus();
24
- return true;
19
+ root.querySelector('[data-lexical-editor="true"]') ||
20
+ root.querySelector('[contenteditable="true"][role="textbox"]') ||
21
+ root.querySelector('.chat-input textarea') ||
22
+ root.querySelector('.cascade-input [contenteditable="true"]') ||
23
+ root.querySelector('textarea:not(.xterm-helper-textarea)');
24
+
25
+ if (!editor) {
26
+ return JSON.stringify({ focused: false });
25
27
  }
26
- return false;
28
+
29
+ editor.focus();
30
+ if (typeof editor.click === 'function') editor.click();
31
+
32
+ return JSON.stringify({ focused: document.activeElement === editor || editor.contains?.(document.activeElement) || true });
27
33
  } catch (e) {
28
- return false;
34
+ return JSON.stringify({ focused: false, error: e.message });
29
35
  }
30
36
  })()
@@ -9,109 +9,15 @@
9
9
  */
10
10
  (async () => {
11
11
  try {
12
- // ─── 1. 패널이 닫혀 있으면 열기 ───
13
- let tabs = document.querySelectorAll('[id^="cascade-tab-"]');
14
- if (tabs.length === 0) {
15
- // Cascade 패널 보이는지 확인
16
- const cascade = document.querySelector('#windsurf\\.cascadePanel') ||
17
- document.querySelector('.chat-client-root');
18
- const sidebar = document.getElementById('workbench.parts.auxiliarybar');
19
- const panelVisible = (cascade && cascade.offsetWidth > 0) ||
20
- (sidebar && sidebar.offsetWidth > 0 && cascade);
21
-
22
- if (!panelVisible) {
23
- // Toggle 버튼 클릭 시도
24
- const toggleBtns = Array.from(document.querySelectorAll('li.action-item a, button, [role="button"]'));
25
- let toggled = false;
26
- for (const btn of toggleBtns) {
27
- const label = (btn.getAttribute('aria-label') || '').toLowerCase();
28
- if (label.includes('toggle cascade') || label.includes('toggle secondary') ||
29
- label.includes('toggle auxiliary') || label.includes('cascade')) {
30
- if (btn.offsetWidth > 0 || btn.offsetHeight > 0) {
31
- btn.click();
32
- toggled = true;
33
- break;
34
- }
35
- }
36
- }
37
- // 버튼 없으면 Cmd+L
38
- if (!toggled) {
39
- document.dispatchEvent(new KeyboardEvent('keydown', {
40
- key: 'l', code: 'KeyL', keyCode: 76,
41
- metaKey: true, ctrlKey: false,
42
- bubbles: true, cancelable: true,
43
- }));
44
- document.dispatchEvent(new KeyboardEvent('keyup', {
45
- key: 'l', code: 'KeyL', keyCode: 76,
46
- metaKey: true, ctrlKey: false,
47
- bubbles: true, cancelable: true,
48
- }));
49
- }
50
-
51
- // 패널 렌더링 대기 (최대 3초)
52
- for (let i = 0; i < 30; i++) {
53
- await new Promise(r => setTimeout(r, 100));
54
- tabs = document.querySelectorAll('[id^="cascade-tab-"]');
55
- if (tabs.length > 0) break;
56
- }
57
- }
58
- }
59
-
60
- // ─── 2. 탭 정보 수집 ───
61
- tabs = document.querySelectorAll('[id^="cascade-tab-"]');
62
- if (tabs.length === 0) return [];
63
-
64
- const result = [];
65
- const seen = new Set();
66
-
67
- tabs.forEach(tab => {
68
- const tabId = tab.id.replace('cascade-tab-', '');
69
- if (seen.has(tabId)) return;
70
- seen.add(tabId);
71
-
72
- let title = '';
73
- let cascadeId = tabId;
74
- let status = 'completed';
75
-
76
- // React Fiber에서 제목 추출
77
- const fk = Object.keys(tab).find(k => k.startsWith('__reactFiber'));
78
- if (fk) {
79
- let fiber = tab[fk];
80
- for (let d = 0; d < 30 && fiber; d++) {
81
- const p = fiber.memoizedProps;
82
- if (p) {
83
- if (p.title && typeof p.title === 'string') {
84
- title = p.title;
85
- }
86
- if (p.cascadeId) {
87
- cascadeId = p.cascadeId;
88
- }
89
- if (p.status && typeof p.status === 'string') {
90
- status = p.status;
91
- }
92
- if (title) break;
93
- }
94
- fiber = fiber.return;
95
- }
96
- }
97
-
98
- // DOM 폴백
99
- if (!title) {
100
- title = tab.textContent?.trim().substring(0, 100) || ('Chat ' + (result.length + 1));
101
- }
102
-
103
- const isVisible = tab.offsetHeight > 0 && tab.offsetWidth > 0;
104
-
105
- result.push({
106
- id: tabId,
107
- title: title.substring(0, 100),
108
- status: status,
109
- active: isVisible
110
- });
111
- });
112
-
113
- return result;
12
+ const tabs = Array.from(document.querySelectorAll('[id^="cascade-tab-"]')).filter(el => el.offsetWidth > 0 && el.offsetHeight > 0);
13
+ const sessions = tabs.map((tab, index) => ({
14
+ id: tab.id.replace(/^cascade-tab-/, ''),
15
+ title: (tab.textContent || '').replace(/\s+/g, ' ').trim() || `Chat ${index + 1}`,
16
+ active: /bg-ide-tab-active-background-color/.test(tab.innerHTML || ''),
17
+ index,
18
+ }));
19
+ return JSON.stringify({ sessions });
114
20
  } catch (e) {
115
- return [];
21
+ return JSON.stringify({ sessions: [], error: e.message });
116
22
  }
117
23
  })()
@@ -1,38 +1,55 @@
1
1
  /**
2
2
  * Generic fallback — list_models
3
3
  */
4
- (() => {
4
+ (async () => {
5
5
  try {
6
- const models = [];
7
- let current = '';
6
+ const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
7
+ const cascade = document.querySelector('#windsurf\\.cascadePanel') || document.querySelector('.chat-client-root');
8
+ if (!cascade) {
9
+ return JSON.stringify({ models: [], current: '' });
10
+ }
11
+
12
+ const normalize = (text) => (text || '').replace(/\s+/g, ' ').trim();
13
+ const modelRegex = /(claude|gpt|gemini|sonnet|opus|swe|deepseek|qwen|grok|o1|o3|codex)/i;
14
+ const getCurrentButton = () => {
15
+ const buttons = Array.from(cascade.querySelectorAll('button')).filter(el => el.offsetWidth > 0 && el.offsetHeight > 0);
16
+ const composerButtons = buttons.filter(el => el.getBoundingClientRect().y > window.innerHeight - 90);
17
+ return composerButtons.find(el => {
18
+ const text = normalize(el.innerText || el.textContent);
19
+ return text && text.length <= 60 && modelRegex.test(text) && !/run|skip|accept|reject/i.test(text);
20
+ }) || null;
21
+ };
22
+ const getMenuButtons = () => Array.from(document.querySelectorAll('button')).filter(el => {
23
+ if (!(el.offsetWidth > 0 && el.offsetHeight > 0)) return false;
24
+ const rect = el.getBoundingClientRect();
25
+ const text = normalize(el.innerText || el.textContent);
26
+ return rect.width >= 180 && rect.height >= 20 && rect.x > window.innerWidth - 320 && modelRegex.test(text);
27
+ });
28
+
29
+ const currentButton = getCurrentButton();
30
+ const current = currentButton ? normalize(currentButton.innerText || currentButton.textContent) : '';
31
+ const wasOpen = getMenuButtons().length > 0;
8
32
 
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
- }
33
+ if (!wasOpen && currentButton) {
34
+ currentButton.click();
35
+ await wait(250);
19
36
  }
20
37
 
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
- }
38
+ const models = getMenuButtons().map(el => normalize(el.innerText || el.textContent)).filter(Boolean);
39
+ const unique = [];
40
+ const seen = new Set();
41
+ for (const name of models) {
42
+ const key = name.toLowerCase();
43
+ if (seen.has(key)) continue;
44
+ seen.add(key);
45
+ unique.push({ name, id: key.replace(/[^a-z0-9]+/g, '-') });
30
46
  }
31
47
 
32
- return JSON.stringify({
33
- models: [...new Set(models)],
34
- current: current || 'Default'
35
- });
48
+ if (!wasOpen) {
49
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true, cancelable: true }));
50
+ }
51
+
52
+ return JSON.stringify({ models: unique, current });
36
53
  } catch (e) {
37
54
  return JSON.stringify({ models: [], current: '', error: e.message });
38
55
  }
@@ -1,39 +1,63 @@
1
1
  /**
2
2
  * Generic fallback — list_models
3
3
  */
4
- (() => {
4
+ (async () => {
5
5
  try {
6
- const models = [];
7
- let current = '';
6
+ const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
7
+ const cascade = document.querySelector('#windsurf\\.cascadePanel') || document.querySelector('.chat-client-root');
8
+ if (!cascade) {
9
+ return JSON.stringify({ modes: [], current: '' });
10
+ }
11
+
12
+ const normalize = (text) => (text || '').replace(/\s+/g, ' ').trim();
13
+ const getModeButton = () => {
14
+ const buttons = Array.from(cascade.querySelectorAll('button')).filter(el => el.offsetWidth > 0 && el.offsetHeight > 0);
15
+ return buttons.find(el => {
16
+ const rect = el.getBoundingClientRect();
17
+ return rect.y > window.innerHeight - 90 && /lucide-code|lucide-message|lucide-search|lucide-wand|lucide-pencil/i.test(el.innerHTML);
18
+ }) || null;
19
+ };
20
+ const inferCurrent = (button) => {
21
+ if (!button) return '';
22
+ if (/lucide-code/i.test(button.innerHTML)) return 'Code';
23
+ if (/lucide-message/i.test(button.innerHTML)) return 'Ask';
24
+ if (/lucide-search/i.test(button.innerHTML)) return 'Search';
25
+ if (/lucide-pencil|lucide-pen|lucide-wand/i.test(button.innerHTML)) return 'Write';
26
+ return '';
27
+ };
28
+ const getMenuButtons = () => Array.from(document.querySelectorAll('button')).filter(el => {
29
+ if (!(el.offsetWidth > 0 && el.offsetHeight > 0)) return false;
30
+ const rect = el.getBoundingClientRect();
31
+ const text = normalize(el.innerText || el.textContent);
32
+ return rect.x > window.innerWidth - 320 && rect.width >= 180 && /^(Code|Ask|Search|Write)\b/i.test(text);
33
+ });
34
+
35
+ const modeButton = getModeButton();
36
+ const current = inferCurrent(modeButton);
37
+ const wasOpen = getMenuButtons().length > 0;
8
38
 
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
- }
39
+ if (!wasOpen && modeButton) {
40
+ modeButton.click();
41
+ await wait(200);
19
42
  }
20
43
 
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
- }
44
+ const modes = [];
45
+ const seen = new Set();
46
+ for (const button of getMenuButtons()) {
47
+ const name = normalize((button.innerText || button.textContent || '').split('\n')[0]);
48
+ if (!name) continue;
49
+ const key = name.toLowerCase();
50
+ if (seen.has(key)) continue;
51
+ seen.add(key);
52
+ modes.push({ name, id: key });
30
53
  }
31
54
 
32
- return JSON.stringify({
33
- models: [...new Set(models)],
34
- current: current || 'Default'
35
- });
55
+ if (!wasOpen) {
56
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true, cancelable: true }));
57
+ }
58
+
59
+ return JSON.stringify({ modes, current });
36
60
  } catch (e) {
37
- return JSON.stringify({ models: [], current: '', error: e.message });
61
+ return JSON.stringify({ modes: [], current: '', error: e.message });
38
62
  }
39
63
  })()
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Windsurf v1 — list_sessions
3
+ *
4
+ * Cascade 세션 리스트를 가져옵니다.
5
+ * 1. 상단 탭 ([id^="cascade-tab-"])
6
+ * 2. 히스토리 패널 ([data-kb-navigate="true"])
7
+ */
8
+ (async () => {
9
+ try {
10
+ const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
11
+ const isVisible = (el) => !!(el && el.offsetWidth > 0 && el.offsetHeight > 0);
12
+ const cascade = document.querySelector('#windsurf\\.cascadePanel') || document.querySelector('.chat-client-root');
13
+ if (!cascade) return JSON.stringify({ sessions: [] });
14
+
15
+ const normalize = (text) => (text || '').replace(/\s+/g, ' ').trim();
16
+ const activeTabEl = document.querySelector('[id^="cascade-tab-"] .bg-ide-tab-active-background-color');
17
+ const activeTabTitle = normalize(activeTabEl?.textContent || document.querySelector('.bg-ide-tab-active-background-color .truncate')?.textContent || '');
18
+
19
+ const sessions = [];
20
+ const seenIds = new Set();
21
+ const seenTitles = new Set();
22
+
23
+ // 1. 상단 탭
24
+ const tabs = Array.from(document.querySelectorAll('[id^="cascade-tab-"]')).filter(isVisible);
25
+ for (const tab of tabs) {
26
+ const fullId = tab.id || '';
27
+ const id = fullId.replace('cascade-tab-', '');
28
+ const title = normalize(tab.textContent);
29
+ if (!title || title === 'New Cascade') continue;
30
+
31
+ // 활성 상태 확인: 특정 클래스 or 내부 요소의 클래스 or ARIA 속성
32
+ const isActive = tab.classList.contains('bg-ide-tab-active-background-color') ||
33
+ !!tab.querySelector('.bg-ide-tab-active-background-color') ||
34
+ tab.getAttribute('aria-selected') === 'true';
35
+
36
+ seenIds.add(id);
37
+ seenTitles.add(title.toLowerCase());
38
+ sessions.push({
39
+ id,
40
+ title,
41
+ active: isActive,
42
+ index: sessions.length
43
+ });
44
+ }
45
+
46
+ // 2. 히스토리 패널
47
+ const historyButton = Array.from(cascade.querySelectorAll('button')).find(el => isVisible(el) && /lucide-history/.test(el.innerHTML || ''));
48
+ const existingPanel = Array.from(cascade.querySelectorAll('div')).find(el => isVisible(el) && (String(el.className || '').includes('group/panel') || el.hasAttribute('data-kb-navigate')));
49
+ const wasOpen = !!existingPanel;
50
+
51
+ if (!wasOpen && historyButton) {
52
+ historyButton.click();
53
+ await wait(300);
54
+ }
55
+
56
+ const historyRows = Array.from(document.querySelectorAll('[data-kb-navigate="true"]')).filter(isVisible);
57
+ for (const row of historyRows) {
58
+ const title = normalize(row.querySelector('.truncate, span')?.textContent || row.textContent);
59
+ if (!title || seenTitles.has(title.toLowerCase())) continue;
60
+
61
+ seenTitles.add(title.toLowerCase());
62
+ sessions.push({
63
+ id: title.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
64
+ title,
65
+ active: title === activeTabTitle,
66
+ index: sessions.length
67
+ });
68
+ }
69
+
70
+ if (!wasOpen && historyButton) {
71
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true, cancelable: true }));
72
+ }
73
+
74
+ return JSON.stringify({ sessions });
75
+ } catch (e) {
76
+ return JSON.stringify({ sessions: [], error: e.message });
77
+ }
78
+ })()
79
+
@@ -14,56 +14,45 @@
14
14
  *
15
15
  * 최종 확인: Windsurf 1.108.x (2026-03-10)
16
16
  */
17
- (() => {
17
+ (async () => {
18
18
  try {
19
- // ─── 1. aria-label 기반 ───
20
- const allBtns = Array.from(document.querySelectorAll('button, [role="button"], .action-item'))
21
- .filter(b => b.offsetWidth > 0);
19
+ const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
20
+ const cascade = document.querySelector('#windsurf\\.cascadePanel') || document.querySelector('.chat-client-root');
21
+ const beforeTabs = document.querySelectorAll('[id^="cascade-tab-"]').length;
22
+ const headerButtons = Array.from((cascade || document).querySelectorAll('button')).filter(el => el.offsetWidth > 0 && el.offsetHeight > 0);
22
23
 
23
- for (const btn of allBtns) {
24
- const label = (btn.getAttribute('aria-label') || '').toLowerCase();
25
- if (label.includes('new chat') || label.includes('new cascade') ||
26
- label.includes('new conversation') || label.includes('start new') ||
27
- label.includes('new session')) {
28
- btn.click();
29
- return 'clicked (aria)';
24
+ const clickSequence = (el) => {
25
+ const rect = el.getBoundingClientRect();
26
+ const init = { bubbles: true, cancelable: true, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, pointerId: 1, pointerType: 'mouse' };
27
+ el.focus?.();
28
+ for (const type of ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click']) {
29
+ el.dispatchEvent(new PointerEvent(type, init));
30
30
  }
31
- }
31
+ };
32
32
 
33
- // ─── 2. 텍스트 기반 ───
34
- for (const btn of allBtns) {
35
- const text = (btn.textContent || '').trim();
36
- if (text === '+' || text === 'New Chat' || text === 'New Cascade' ||
37
- text === 'Start New Chat' || text === 'New Session') {
38
- btn.click();
39
- return 'clicked (text)';
40
- }
33
+ let button = headerButtons.find(el => /lucide-plus/.test(el.innerHTML));
34
+
35
+ if (!button) {
36
+ button = headerButtons.find(el => {
37
+ const text = (el.innerText || el.textContent || '').trim().toLowerCase();
38
+ const label = ((el.getAttribute('aria-label') || '') + ' ' + (el.getAttribute('title') || '')).toLowerCase();
39
+ return text === '+' || label.includes('new chat') || label.includes('new cascade') || label.includes('new conversation') || label.includes('new session');
40
+ });
41
41
  }
42
42
 
43
- // ─── 3. Codicon 아이콘(+) 기반 ───
44
- for (const btn of allBtns) {
45
- const hasPlus = btn.querySelector('.codicon-plus, .codicon-add, [class*="plus"]');
46
- if (hasPlus) {
47
- const label = (btn.getAttribute('aria-label') || btn.getAttribute('title') || '').toLowerCase();
48
- // Cascade 관련 컨텍스트이거나 라벨이 비어있으면 새 세션 버튼일 가능성
49
- if (label.includes('chat') || label.includes('cascade') ||
50
- label.includes('new') || label === '') {
51
- btn.click();
52
- return 'clicked (icon)';
53
- }
54
- }
43
+ if (button) {
44
+ clickSequence(button);
45
+ await wait(300);
46
+ const afterTabs = document.querySelectorAll('[id^="cascade-tab-"]').length;
47
+ return JSON.stringify({ created: afterTabs >= beforeTabs });
55
48
  }
56
49
 
57
- // ─── 4. Cmd+L 단축키 (macOS: metaKey, Windows/Linux: ctrlKey) ───
58
- // Windsurf에서 Cmd+L은 Cascade 패널 토글/새 세션 생성
59
- document.dispatchEvent(new KeyboardEvent('keydown', {
60
- key: 'l', code: 'KeyL', keyCode: 76,
61
- metaKey: true, ctrlKey: false,
62
- bubbles: true, cancelable: true,
63
- }));
50
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'l', code: 'KeyL', keyCode: 76, which: 76, metaKey: true, bubbles: true, cancelable: true }));
51
+ document.dispatchEvent(new KeyboardEvent('keyup', { key: 'l', code: 'KeyL', keyCode: 76, which: 76, metaKey: true, bubbles: true, cancelable: true }));
52
+ await wait(300);
64
53
 
65
- return 'sent Cmd+L';
54
+ return JSON.stringify({ created: document.querySelectorAll('[id^="cascade-tab-"]').length >= beforeTabs });
66
55
  } catch (e) {
67
- return 'error: ' + e.message;
56
+ return JSON.stringify({ created: false, error: e.message });
68
57
  }
69
58
  })()