adhdev 0.2.0 → 0.4.1

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 (145) hide show
  1. package/dist/cli-entrypoint.js +18710 -1198
  2. package/dist/cli-entrypoint.js.map +1 -1
  3. package/dist/index.js +18720 -1228
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/providers/_builtin/CONTRIBUTING.md +0 -141
  7. package/providers/_builtin/README.md +0 -51
  8. package/providers/_builtin/_helpers/index.js +0 -188
  9. package/providers/_builtin/acp/agentpool/provider.json +0 -47
  10. package/providers/_builtin/acp/amp/provider.json +0 -45
  11. package/providers/_builtin/acp/auggie/provider.json +0 -50
  12. package/providers/_builtin/acp/autodev/provider.json +0 -47
  13. package/providers/_builtin/acp/autohand/provider.json +0 -45
  14. package/providers/_builtin/acp/blackbox-ai/provider.json +0 -47
  15. package/providers/_builtin/acp/claude-agent/provider.json +0 -50
  16. package/providers/_builtin/acp/cline-acp/provider.json +0 -47
  17. package/providers/_builtin/acp/code-assistant/provider.json +0 -47
  18. package/providers/_builtin/acp/codebuddy/provider.json +0 -47
  19. package/providers/_builtin/acp/codex-cli/provider.json +0 -50
  20. package/providers/_builtin/acp/corust-agent/provider.json +0 -45
  21. package/providers/_builtin/acp/crow-cli/provider.json +0 -47
  22. package/providers/_builtin/acp/cursor-acp/provider.json +0 -47
  23. package/providers/_builtin/acp/deepagents/provider.json +0 -45
  24. package/providers/_builtin/acp/dimcode/provider.json +0 -47
  25. package/providers/_builtin/acp/docker-cagent/provider.json +0 -50
  26. package/providers/_builtin/acp/factory-droid/provider.json +0 -53
  27. package/providers/_builtin/acp/fast-agent/provider.json +0 -45
  28. package/providers/_builtin/acp/fount/provider.json +0 -47
  29. package/providers/_builtin/acp/gemini-cli/provider.json +0 -107
  30. package/providers/_builtin/acp/github-copilot/provider.json +0 -47
  31. package/providers/_builtin/acp/goose/provider.json +0 -50
  32. package/providers/_builtin/acp/junie/provider.json +0 -45
  33. package/providers/_builtin/acp/kilo/provider.json +0 -45
  34. package/providers/_builtin/acp/kimi-cli/provider.json +0 -50
  35. package/providers/_builtin/acp/kiro-cli/provider.json +0 -47
  36. package/providers/_builtin/acp/minion-code/provider.json +0 -45
  37. package/providers/_builtin/acp/mistral-vibe/provider.json +0 -50
  38. package/providers/_builtin/acp/nova/provider.json +0 -47
  39. package/providers/_builtin/acp/openclaw/provider.json +0 -47
  40. package/providers/_builtin/acp/opencode/provider.json +0 -45
  41. package/providers/_builtin/acp/openhands/provider.json +0 -47
  42. package/providers/_builtin/acp/pi-acp/provider.json +0 -45
  43. package/providers/_builtin/acp/qoder/provider.json +0 -47
  44. package/providers/_builtin/acp/qwen-code/provider.json +0 -53
  45. package/providers/_builtin/acp/stakpak/provider.json +0 -47
  46. package/providers/_builtin/acp/vtcode/provider.json +0 -47
  47. package/providers/_builtin/cli/claude-cli/provider.json +0 -78
  48. package/providers/_builtin/cli/codex-cli/provider.json +0 -60
  49. package/providers/_builtin/cli/gemini-cli/provider.json +0 -64
  50. package/providers/_builtin/extension/cline/provider.json +0 -11
  51. package/providers/_builtin/extension/cline/scripts/focus_editor.js +0 -48
  52. package/providers/_builtin/extension/cline/scripts/list_chats.js +0 -100
  53. package/providers/_builtin/extension/cline/scripts/list_models.js +0 -43
  54. package/providers/_builtin/extension/cline/scripts/list_modes.js +0 -35
  55. package/providers/_builtin/extension/cline/scripts/new_session.js +0 -85
  56. package/providers/_builtin/extension/cline/scripts/open_panel.js +0 -25
  57. package/providers/_builtin/extension/cline/scripts/read_chat.js +0 -257
  58. package/providers/_builtin/extension/cline/scripts/resolve_action.js +0 -83
  59. package/providers/_builtin/extension/cline/scripts/send_message.js +0 -95
  60. package/providers/_builtin/extension/cline/scripts/set_mode.js +0 -36
  61. package/providers/_builtin/extension/cline/scripts/set_model.js +0 -36
  62. package/providers/_builtin/extension/cline/scripts/switch_session.js +0 -206
  63. package/providers/_builtin/extension/cline/scripts.js +0 -73
  64. package/providers/_builtin/extension/roo-code/provider.json +0 -11
  65. package/providers/_builtin/extension/roo-code/scripts.js +0 -659
  66. package/providers/_builtin/ide/antigravity/provider.json +0 -32
  67. package/providers/_builtin/ide/antigravity/scripts/focus_editor.js +0 -20
  68. package/providers/_builtin/ide/antigravity/scripts/list_chats.js +0 -137
  69. package/providers/_builtin/ide/antigravity/scripts/list_models.js +0 -38
  70. package/providers/_builtin/ide/antigravity/scripts/list_modes.js +0 -48
  71. package/providers/_builtin/ide/antigravity/scripts/new_session.js +0 -75
  72. package/providers/_builtin/ide/antigravity/scripts/read_chat.js +0 -262
  73. package/providers/_builtin/ide/antigravity/scripts/resolve_action.js +0 -68
  74. package/providers/_builtin/ide/antigravity/scripts/send_message.js +0 -56
  75. package/providers/_builtin/ide/antigravity/scripts/set_mode.js +0 -34
  76. package/providers/_builtin/ide/antigravity/scripts/set_model.js +0 -47
  77. package/providers/_builtin/ide/antigravity/scripts/switch_session.js +0 -114
  78. package/providers/_builtin/ide/antigravity/scripts.js +0 -73
  79. package/providers/_builtin/ide/cursor/provider.json +0 -35
  80. package/providers/_builtin/ide/cursor/scripts.js +0 -452
  81. package/providers/_builtin/ide/kiro/provider.json +0 -36
  82. package/providers/_builtin/ide/kiro/scripts/focus_editor.js +0 -20
  83. package/providers/_builtin/ide/kiro/scripts/open_panel.js +0 -47
  84. package/providers/_builtin/ide/kiro/scripts/resolve_action.js +0 -54
  85. package/providers/_builtin/ide/kiro/scripts/send_message.js +0 -29
  86. package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +0 -39
  87. package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +0 -39
  88. package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +0 -21
  89. package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +0 -34
  90. package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +0 -68
  91. package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +0 -72
  92. package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +0 -15
  93. package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +0 -15
  94. package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +0 -26
  95. package/providers/_builtin/ide/kiro/scripts.js +0 -62
  96. package/providers/_builtin/ide/pearai/provider.json +0 -36
  97. package/providers/_builtin/ide/pearai/scripts/focus_editor.js +0 -20
  98. package/providers/_builtin/ide/pearai/scripts/list_sessions.js +0 -38
  99. package/providers/_builtin/ide/pearai/scripts/new_session.js +0 -55
  100. package/providers/_builtin/ide/pearai/scripts/open_panel.js +0 -46
  101. package/providers/_builtin/ide/pearai/scripts/resolve_action.js +0 -54
  102. package/providers/_builtin/ide/pearai/scripts/send_message.js +0 -29
  103. package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +0 -43
  104. package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +0 -35
  105. package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +0 -62
  106. package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +0 -49
  107. package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +0 -92
  108. package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +0 -59
  109. package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +0 -72
  110. package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +0 -36
  111. package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +0 -36
  112. package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +0 -34
  113. package/providers/_builtin/ide/pearai/scripts.js +0 -74
  114. package/providers/_builtin/ide/trae/provider.json +0 -35
  115. package/providers/_builtin/ide/trae/scripts/focus_editor.js +0 -20
  116. package/providers/_builtin/ide/trae/scripts/list_chats.js +0 -24
  117. package/providers/_builtin/ide/trae/scripts/list_models.js +0 -39
  118. package/providers/_builtin/ide/trae/scripts/list_modes.js +0 -39
  119. package/providers/_builtin/ide/trae/scripts/new_session.js +0 -30
  120. package/providers/_builtin/ide/trae/scripts/open_panel.js +0 -44
  121. package/providers/_builtin/ide/trae/scripts/read_chat.js +0 -113
  122. package/providers/_builtin/ide/trae/scripts/resolve_action.js +0 -54
  123. package/providers/_builtin/ide/trae/scripts/send_message.js +0 -69
  124. package/providers/_builtin/ide/trae/scripts/set_mode.js +0 -15
  125. package/providers/_builtin/ide/trae/scripts/set_model.js +0 -15
  126. package/providers/_builtin/ide/trae/scripts/switch_session.js +0 -23
  127. package/providers/_builtin/ide/trae/scripts.js +0 -57
  128. package/providers/_builtin/ide/vscode/provider.json +0 -33
  129. package/providers/_builtin/ide/vscode-insiders/provider.json +0 -31
  130. package/providers/_builtin/ide/vscodium/provider.json +0 -32
  131. package/providers/_builtin/ide/windsurf/provider.json +0 -22
  132. package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +0 -30
  133. package/providers/_builtin/ide/windsurf/scripts/list_chats.js +0 -117
  134. package/providers/_builtin/ide/windsurf/scripts/list_models.js +0 -39
  135. package/providers/_builtin/ide/windsurf/scripts/list_modes.js +0 -39
  136. package/providers/_builtin/ide/windsurf/scripts/new_session.js +0 -69
  137. package/providers/_builtin/ide/windsurf/scripts/open_panel.js +0 -58
  138. package/providers/_builtin/ide/windsurf/scripts/read_chat.js +0 -297
  139. package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +0 -68
  140. package/providers/_builtin/ide/windsurf/scripts/send_message.js +0 -87
  141. package/providers/_builtin/ide/windsurf/scripts/set_mode.js +0 -15
  142. package/providers/_builtin/ide/windsurf/scripts/set_model.js +0 -15
  143. package/providers/_builtin/ide/windsurf/scripts/switch_session.js +0 -58
  144. package/providers/_builtin/ide/windsurf/scripts.js +0 -57
  145. package/providers/_builtin/validate.js +0 -156
@@ -1,68 +0,0 @@
1
- /**
2
- * Windsurf v1 — resolve_action
3
- *
4
- * 승인 다이얼로그/인라인 버튼을 클릭합니다.
5
- *
6
- * 파라미터: ${ BUTTON_TEXT } — 클릭할 버튼 텍스트 (lowercase)
7
- *
8
- * 최종 확인: Windsurf (2026-03-06)
9
- */
10
- (() => {
11
- try {
12
- const want = ${ BUTTON_TEXT };
13
- const normalize = (s) => (s || '').replace(/[\s\u200b\u00a0]+/g, ' ').trim().toLowerCase();
14
- const matches = (text) => {
15
- const t = normalize(text);
16
- if (!t) return false;
17
- if (t === want) return true;
18
- if (t.indexOf(want) === 0) return true;
19
- if (want === 'run' && (/^run\s*/.test(t) || t === 'enter' || t === '⏎')) return true;
20
- if (want === 'approve' && (t.includes('approve') || t === 'always allow' || t === 'allow')) return true;
21
- if (want === 'reject' && (t.includes('reject') || t === 'deny' || t === 'always deny')) return true;
22
- return false;
23
- };
24
- const click = (el) => {
25
- el.focus?.();
26
- const rect = el.getBoundingClientRect();
27
- const x = rect.left + rect.width / 2;
28
- const y = rect.top + rect.height / 2;
29
- for (const type of ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click']) {
30
- el.dispatchEvent(new PointerEvent(type, {
31
- bubbles: true, cancelable: true, view: window,
32
- clientX: x, clientY: y, pointerId: 1, pointerType: 'mouse'
33
- }));
34
- }
35
- return true;
36
- };
37
-
38
- // 1. Dialog 내부
39
- const dialog = document.querySelector('.monaco-dialog-box, [role="dialog"]');
40
- if (dialog && dialog.offsetWidth > 80) {
41
- const btns = dialog.querySelectorAll('.monaco-button, button');
42
- for (const b of btns) {
43
- if (matches(b.textContent)) return click(b);
44
- }
45
- }
46
-
47
- // 2. 모든 보이는 버튼
48
- const sel = 'button, [role="button"], .monaco-button';
49
- const allBtns = Array.from(document.querySelectorAll(sel))
50
- .filter(b => b.offsetWidth > 0 && b.offsetHeight > 0);
51
- for (const b of allBtns) {
52
- if (matches(b.textContent)) return click(b);
53
- }
54
-
55
- // 3. Enter 키 폴백 (run)
56
- if (want === 'run') {
57
- document.activeElement?.dispatchEvent(new KeyboardEvent('keydown', {
58
- key: 'Enter', code: 'Enter', keyCode: 13,
59
- bubbles: true, cancelable: true
60
- }));
61
- return true;
62
- }
63
-
64
- return false;
65
- } catch (e) {
66
- return false;
67
- }
68
- })()
@@ -1,87 +0,0 @@
1
- /**
2
- * Windsurf v1 — send_message
3
- *
4
- * Windsurf는 Lexical 에디터 ([data-lexical-editor="true"])를 사용합니다.
5
- * contenteditable div이므로 execCommand('insertText') 또는 InputEvent로 입력 가능.
6
- *
7
- * ⚠️ Lexical은 입력 이벤트를 정밀하게 감지하므로:
8
- * - execCommand('insertText')가 가장 안정적
9
- * - nativeSetter 방식은 동작하지 않음
10
- * - InputEvent('insertText')를 폴백으로 사용
11
- *
12
- * Enter 키는 KeyboardEvent 전체 시퀀스 필요 (keydown + keypress + keyup).
13
- *
14
- * 파라미터: ${ MESSAGE }
15
- * 최종 확인: Windsurf 1.108.x (2026-03-10)
16
- */
17
- (async () => {
18
- try {
19
- const msg = ${ MESSAGE };
20
-
21
- // ─── 1. Lexical 에디터 찾기 (폴백 체인) ───
22
- const editor =
23
- document.querySelector('[data-lexical-editor="true"]') ||
24
- document.querySelector('[contenteditable="true"][role="textbox"]') ||
25
- document.querySelector('.cascade-input [contenteditable="true"]') ||
26
- document.querySelector('.chat-input textarea') ||
27
- document.querySelector('textarea:not(.xterm-helper-textarea)');
28
-
29
- if (!editor) return 'error: no input found';
30
-
31
- const isTextarea = editor.tagName === 'TEXTAREA';
32
-
33
- if (isTextarea) {
34
- // ─── textarea 폴백 (미로그인 등) ───
35
- editor.focus();
36
- const proto = HTMLTextAreaElement.prototype;
37
- const nativeSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
38
- if (nativeSetter) nativeSetter.call(editor, msg);
39
- else editor.value = msg;
40
-
41
- editor.dispatchEvent(new Event('input', { bubbles: true }));
42
- editor.dispatchEvent(new Event('change', { bubbles: true }));
43
-
44
- await new Promise(r => setTimeout(r, 300));
45
-
46
- const enterOpts = {
47
- key: 'Enter', code: 'Enter',
48
- keyCode: 13, which: 13,
49
- bubbles: true, cancelable: true, composed: true,
50
- };
51
- editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
52
- editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
53
- editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
54
-
55
- return 'sent';
56
- }
57
-
58
- // ─── 2. contenteditable (Lexical) 에디터 ───
59
- editor.focus();
60
-
61
- // 기존 내용 선택 후 삭제
62
- document.execCommand('selectAll', false, null);
63
- document.execCommand('delete', false, null);
64
-
65
- // 텍스트 삽입 (Lexical은 execCommand('insertText')를 인식)
66
- document.execCommand('insertText', false, msg);
67
-
68
- // React/Lexical에 변경 알림
69
- editor.dispatchEvent(new Event('input', { bubbles: true }));
70
-
71
- await new Promise(r => setTimeout(r, 300));
72
-
73
- // ─── 3. Enter 키 전송 (전체 시퀀스) ───
74
- const enterOpts = {
75
- key: 'Enter', code: 'Enter',
76
- keyCode: 13, which: 13,
77
- bubbles: true, cancelable: true, composed: true,
78
- };
79
- editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
80
- editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
81
- editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
82
-
83
- return 'sent';
84
- } catch (e) {
85
- return 'error: ' + e.message;
86
- }
87
- })()
@@ -1,15 +0,0 @@
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
- })()
@@ -1,15 +0,0 @@
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
- })()
@@ -1,58 +0,0 @@
1
- /**
2
- * Windsurf v1 — switch_session
3
- *
4
- * Cascade 세션(대화) 탭을 전환합니다.
5
- * cascade-tab-{id} 내부의 실제 클릭 가능한 자식 DIV의
6
- * React onClick 핸들러를 직접 호출합니다.
7
- *
8
- * 파라미터: ${ SESSION_ID } — 전환할 세션 ID (cascade-tab의 UUID)
9
- *
10
- * 최종 확인: Windsurf 1.108.x (2026-03-10)
11
- */
12
- (() => {
13
- try {
14
- const id = ${ SESSION_ID };
15
-
16
- // Helper: find React onClick on an element or its children
17
- function clickReact(el) {
18
- // Check element itself and children for React onClick
19
- const targets = [el, ...el.querySelectorAll('*')];
20
- for (const t of targets) {
21
- const rp = Object.keys(t).find(k => k.startsWith('__reactProps'));
22
- if (rp && typeof t[rp].onClick === 'function') {
23
- t[rp].onClick({
24
- preventDefault: () => { },
25
- stopPropagation: () => { },
26
- nativeEvent: { stopImmediatePropagation: () => { } },
27
- target: t, currentTarget: t, button: 0, type: 'click'
28
- });
29
- return true;
30
- }
31
- }
32
- return false;
33
- }
34
-
35
- // 1. cascade-tab-{id} 요소 찾기
36
- const tab = document.getElementById('cascade-tab-' + id);
37
- if (tab) {
38
- if (clickReact(tab)) return 'switched';
39
- // Fallback: DOM click
40
- tab.click();
41
- return 'switched-dom';
42
- }
43
-
44
- // 2. 제목으로 매칭
45
- const tabs = document.querySelectorAll('[id^="cascade-tab-"]');
46
- for (const t of tabs) {
47
- if (t.textContent?.trim() === id) {
48
- if (clickReact(t)) return 'switched-by-title';
49
- t.click();
50
- return 'switched-by-title-dom';
51
- }
52
- }
53
-
54
- return 'not_found';
55
- } catch (e) {
56
- return 'error: ' + e.message;
57
- }
58
- })()
@@ -1,57 +0,0 @@
1
- /**
2
- * CDP Scripts for Windsurf
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.listSessions = function listSessions() { return loadScript('list_chats.js'); };
22
-
23
- module.exports.switchSession = function switchSession(sessionId) {
24
- const s = loadScript('switch_session.js');
25
- return s ? s.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId)) : null;
26
- };
27
-
28
- module.exports.newSession = function newSession() { return loadScript('new_session.js'); };
29
-
30
- module.exports.resolveAction = function resolveAction(params) {
31
- const action = typeof params === 'string' ? params : params?.action || 'approve';
32
- const buttonText = params?.button || params?.buttonText
33
- || (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
34
- const s = loadScript('resolve_action.js');
35
- return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
36
- };
37
-
38
- module.exports.focusEditor = function focusEditor() { return loadScript('focus_editor.js'); };
39
-
40
- module.exports.listModels = function listModels() { return loadScript('list_models.js'); };
41
-
42
- module.exports.setModel = function setModel(params) {
43
- const model = typeof params === 'string' ? params : params?.model;
44
- const s = loadScript('set_model.js');
45
- return s ? s.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model)) : null;
46
- };
47
-
48
- module.exports.listModes = function listModes() { return loadScript('list_modes.js'); };
49
-
50
- module.exports.setMode = function setMode(params) {
51
- const mode = typeof params === 'string' ? params : params?.mode;
52
- const s = loadScript('set_mode.js');
53
- return s ? s.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode)) : null;
54
- };
55
-
56
- module.exports.openPanel = function openPanel() { return loadScript('open_panel.js'); };
57
-
@@ -1,156 +0,0 @@
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);