@adhdev/daemon-core 0.6.48 → 0.6.50
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.d.ts +5 -1
- package/dist/index.js +29 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/cli/claude-cli/provider.json +1 -7
- package/providers/_builtin/ide/kiro/provider.json +0 -1
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_dump_html.js +1 -0
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_list_models.js +25 -22
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_list_modes.js +10 -27
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_read_chat.js +103 -10
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_resolve_action.js +62 -0
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_send_message.js +23 -1
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_set_mode.js +25 -6
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_set_model.js +38 -6
- package/providers/_builtin/ide/windsurf/scripts/1.0/read_chat.js +1 -1
- package/src/commands/cli-manager.ts +2 -2
- package/src/commands/router.ts +12 -0
- package/src/config/config.ts +27 -4
package/package.json
CHANGED
|
@@ -67,9 +67,6 @@
|
|
|
67
67
|
"patterns": {
|
|
68
68
|
"prompt": [
|
|
69
69
|
{ "source": "Type your message", "flags": "i" },
|
|
70
|
-
{ "source": "^>\\s*$", "flags": "m" },
|
|
71
|
-
{ "source": "[›❯]\\s*[\\r\\n]", "flags": "" },
|
|
72
|
-
{ "source": "[›❯]\\s*$", "flags": "m" },
|
|
73
70
|
{ "source": "for\\s*shortcuts", "flags": "i" },
|
|
74
71
|
{ "source": "\\?\\s*for\\s*help", "flags": "i" },
|
|
75
72
|
{ "source": "Press enter", "flags": "i" }
|
|
@@ -85,10 +82,7 @@
|
|
|
85
82
|
{ "source": "Allow\\s*once", "flags": "i" },
|
|
86
83
|
{ "source": "Always\\s*allow", "flags": "i" },
|
|
87
84
|
{ "source": "\\(y/n\\)", "flags": "i" },
|
|
88
|
-
{ "source": "\\[Y/n\\]", "flags": "i" }
|
|
89
|
-
{ "source": "Run\\s+\\w+\\s+command", "flags": "i" },
|
|
90
|
-
{ "source": "Yes,?\\s*don'?t\\s*ask", "flags": "i" },
|
|
91
|
-
{ "source": "\\bDeny\\b", "flags": "i" }
|
|
85
|
+
{ "source": "\\[Y/n\\]", "flags": "i" }
|
|
92
86
|
],
|
|
93
87
|
"ready": [
|
|
94
88
|
{ "source": "for\\s*shortcuts", "flags": "i" },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(() => { return document.documentElement.outerHTML; })()
|
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kiro — webview_list_models
|
|
3
3
|
*/
|
|
4
|
-
(() => {
|
|
4
|
+
(async () => {
|
|
5
5
|
try {
|
|
6
|
-
const
|
|
7
|
-
|
|
6
|
+
const trigger = document.querySelector('.kiro-dropdown-trigger');
|
|
7
|
+
if (!trigger) {
|
|
8
|
+
return JSON.stringify({ models: [], current: 'Default', error: 'No dropdown found' });
|
|
9
|
+
}
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
}
|
|
11
|
+
const current = (trigger.querySelector('.kiro-dropdown-selected-text')?.textContent || '').trim();
|
|
12
|
+
|
|
13
|
+
// Check if menu is already open
|
|
14
|
+
const wasExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
15
|
+
|
|
16
|
+
if (!wasExpanded) {
|
|
17
|
+
trigger.click();
|
|
18
|
+
await new Promise(r => setTimeout(r, 150));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
current = txt;
|
|
28
|
-
}
|
|
21
|
+
const models = [];
|
|
22
|
+
const items = document.querySelectorAll('.kiro-dropdown-item, [role="menuitem"], [role="option"]');
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
const txt = (item.textContent || '').trim();
|
|
25
|
+
if (txt && txt.length < 50) {
|
|
26
|
+
models.push(txt);
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
|
|
30
|
+
// Close menu
|
|
31
|
+
if (!wasExpanded) {
|
|
32
|
+
trigger.click();
|
|
33
|
+
}
|
|
34
|
+
|
|
32
35
|
return JSON.stringify({
|
|
33
36
|
models: [...new Set(models)],
|
|
34
37
|
current: current || 'Default'
|
|
@@ -1,39 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kiro — webview_list_modes
|
|
3
|
+
* Maps the Autopilot toggle to modes "Autopilot" and "Manual".
|
|
3
4
|
*/
|
|
4
5
|
(() => {
|
|
5
6
|
try {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
}
|
|
7
|
+
const toggle = document.querySelector('#autonomy-mode-toggle-switch');
|
|
8
|
+
if (!toggle) {
|
|
9
|
+
// Fallback for older versions or if UI changed
|
|
10
|
+
return JSON.stringify({ modes: ['Default'], current: 'Default' });
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
|
|
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
|
-
}
|
|
13
|
+
const isAutopilot = toggle.checked;
|
|
31
14
|
|
|
32
15
|
return JSON.stringify({
|
|
33
|
-
|
|
34
|
-
current:
|
|
16
|
+
modes: ['Autopilot', 'Manual'],
|
|
17
|
+
current: isAutopilot ? 'Autopilot' : 'Manual'
|
|
35
18
|
});
|
|
36
19
|
} catch (e) {
|
|
37
|
-
return JSON.stringify({
|
|
20
|
+
return JSON.stringify({ modes: [], current: '', error: e.message });
|
|
38
21
|
}
|
|
39
22
|
})()
|
|
@@ -20,30 +20,122 @@
|
|
|
20
20
|
msgElements.forEach((msg, idx) => {
|
|
21
21
|
const roleMeta = msg.querySelector('.kiro-chat-message-role');
|
|
22
22
|
const roleText = (roleMeta?.textContent || '').trim();
|
|
23
|
-
const isKiro = roleText.toLowerCase()
|
|
23
|
+
const isKiro = roleText.toLowerCase().includes('kiro');
|
|
24
24
|
const role = isKiro ? 'assistant' : 'user';
|
|
25
25
|
|
|
26
|
+
let parts = [];
|
|
27
|
+
|
|
28
|
+
// A helper to traverse and build rough markdown
|
|
29
|
+
const parseNode = (node) => {
|
|
30
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
31
|
+
return node.nodeValue;
|
|
32
|
+
}
|
|
33
|
+
const tag = node.tagName?.toLowerCase();
|
|
34
|
+
if (!tag) return '';
|
|
35
|
+
|
|
36
|
+
// Kiro Thought / Tool executions
|
|
37
|
+
if (tag === 'div' && node.className.includes('kiro-thought')) {
|
|
38
|
+
const toggle = node.querySelector('.summary')?.textContent || 'thought';
|
|
39
|
+
const content = Array.from(node.querySelectorAll('.details')).map(n => n.textContent).join('\n');
|
|
40
|
+
parts.push({ kind: 'thought', content });
|
|
41
|
+
return `\n<details><summary>${toggle}</summary>\n${content}\n</details>\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Kiro Agent Outcomes (Terminal / Command)
|
|
45
|
+
if (tag === 'div' && node.className.includes('agent-outcome')) {
|
|
46
|
+
const label = (node.querySelector('.agent-outcome-label')?.textContent || '').toLowerCase();
|
|
47
|
+
const pre = node.querySelector('.agent-outcome-details pre, .agent-outcome-details code');
|
|
48
|
+
const codeContent = (pre?.textContent || '').trim();
|
|
49
|
+
if (codeContent) {
|
|
50
|
+
const kind = label.includes('command') || label.includes('terminal') ? 'terminal' : 'tool';
|
|
51
|
+
// Add directly to parts since this is a structured execution
|
|
52
|
+
parts.push({ kind, content: codeContent });
|
|
53
|
+
return `\n> [${kind}] ${codeContent}\n`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (tag === 'pre') {
|
|
58
|
+
const code = node.querySelector('code');
|
|
59
|
+
const lang = code?.className?.replace('language-', '') || '';
|
|
60
|
+
return `\n\`\`\`${lang}\n${node.textContent}\n\`\`\`\n`;
|
|
61
|
+
}
|
|
62
|
+
if (tag === 'code') {
|
|
63
|
+
return `\`${node.textContent}\``;
|
|
64
|
+
}
|
|
65
|
+
if (tag === 'table') {
|
|
66
|
+
let str = '\n';
|
|
67
|
+
const rows = Array.from(node.querySelectorAll('tr'));
|
|
68
|
+
rows.forEach((row, i) => {
|
|
69
|
+
const cells = Array.from(row.querySelectorAll('td, th')).map(c => c.textContent.trim());
|
|
70
|
+
str += '| ' + cells.join(' | ') + ' |\n';
|
|
71
|
+
if (i === 0 && row.querySelector('th')) {
|
|
72
|
+
str += '|' + cells.map(() => '---').join('|') + '|\n';
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return str + '\n';
|
|
76
|
+
}
|
|
77
|
+
if (tag === 'p') {
|
|
78
|
+
const text = Array.from(node.childNodes).map(parseNode).join('');
|
|
79
|
+
return text + '\n\n';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Recursively parse children for span, div, etc
|
|
83
|
+
return Array.from(node.childNodes).map(parseNode).join('');
|
|
84
|
+
};
|
|
85
|
+
|
|
26
86
|
const body = msg.querySelector('.kiro-chat-message-body');
|
|
27
87
|
let content = '';
|
|
28
88
|
if (body) {
|
|
29
|
-
|
|
30
|
-
content = (
|
|
89
|
+
// Parse the ENTIRE body recursively so we don't skip elements!
|
|
90
|
+
content = parseNode(body).trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fallback content parsing if recursive failed
|
|
94
|
+
if (!content) {
|
|
95
|
+
content = body?.textContent?.trim() || '';
|
|
31
96
|
}
|
|
32
97
|
|
|
33
98
|
if (content) {
|
|
34
|
-
|
|
99
|
+
parts.push({ kind: 'text', content });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (parts.length > 0) {
|
|
103
|
+
messages.push({ role, content: parts.map(p => p.content).join('\n'), parts });
|
|
35
104
|
}
|
|
36
105
|
});
|
|
37
106
|
|
|
38
|
-
// 상태 감지
|
|
107
|
+
// 상태 감지 및 activeModal 추출
|
|
39
108
|
let status = 'idle';
|
|
109
|
+
let activeModal = undefined;
|
|
40
110
|
|
|
41
111
|
// "Working" / "Cancel" 버튼 → generating
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
const barText = (
|
|
112
|
+
const snackbar = document.querySelector('.kiro-snackbar');
|
|
113
|
+
if (snackbar && snackbar.offsetWidth > 0) {
|
|
114
|
+
const barText = (snackbar.textContent || '').toLowerCase();
|
|
45
115
|
if (barText.includes('working') || barText.includes('cancel')) {
|
|
46
116
|
status = 'generating';
|
|
117
|
+
} else if (barText.includes('waiting') || barText.includes('input')) {
|
|
118
|
+
// 승인 대기 중 (waiting on your input)
|
|
119
|
+
const titleEl = snackbar.querySelector('.kiro-snackbar-title, .thinking-text');
|
|
120
|
+
const actionsEl = snackbar.querySelectorAll('.kiro-snackbar-actions button');
|
|
121
|
+
const buttons = Array.from(actionsEl).map(b => (b.textContent || '').trim());
|
|
122
|
+
|
|
123
|
+
if (buttons.length > 0) {
|
|
124
|
+
activeModal = {
|
|
125
|
+
title: (titleEl?.textContent || '').trim(),
|
|
126
|
+
buttons,
|
|
127
|
+
type: 'approval'
|
|
128
|
+
};
|
|
129
|
+
status = 'waiting_approval';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If snackbar didn't catch it, fallback to Stop button checking
|
|
135
|
+
if (status === 'idle') {
|
|
136
|
+
const hasStopBtn = document.querySelector('.codicon-debug-stop, [aria-label*="stop" i], [title*="stop" i], [title*="cancel generation" i], .kiro-button[data-loading="true"]');
|
|
137
|
+
if (hasStopBtn) {
|
|
138
|
+
status = 'generating';
|
|
47
139
|
}
|
|
48
140
|
}
|
|
49
141
|
|
|
@@ -59,8 +151,9 @@
|
|
|
59
151
|
id: title || 'kiro-default',
|
|
60
152
|
status,
|
|
61
153
|
messages,
|
|
62
|
-
title: title ||
|
|
63
|
-
inputContent: inputContent ||
|
|
154
|
+
title: title || '',
|
|
155
|
+
inputContent: inputContent || '',
|
|
156
|
+
...(activeModal ? { activeModal } : {})
|
|
64
157
|
});
|
|
65
158
|
} catch (e) {
|
|
66
159
|
return JSON.stringify({ id: '', status: 'error', messages: [], error: e.message });
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kiro — webview_resolve_action
|
|
3
|
+
* Kiro's approval dialog uses buttons in the kiro-snackbar.
|
|
4
|
+
* 파라미터: ${ BUTTON_TEXT }
|
|
5
|
+
*/
|
|
6
|
+
(() => {
|
|
7
|
+
try {
|
|
8
|
+
const want = ${ BUTTON_TEXT };
|
|
9
|
+
const wantNorm = (want || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
10
|
+
|
|
11
|
+
function matches(el) {
|
|
12
|
+
const t = (el.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
13
|
+
if (!t) return false;
|
|
14
|
+
if (t === wantNorm || t.startsWith(wantNorm) || wantNorm.startsWith(t)) return true;
|
|
15
|
+
if (/^(run|approve|allow|accept|yes|trust)\b/.test(wantNorm)) {
|
|
16
|
+
if (/^(run|allow|accept|approve|trust)\b/.test(t)) return true;
|
|
17
|
+
}
|
|
18
|
+
if (/^(reject|deny|no|abort|cancel)\b/.test(wantNorm)) {
|
|
19
|
+
if (/^(reject|deny|cancel)\b/.test(t)) return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const btns = Array.from(document.querySelectorAll('button, [role="button"]'));
|
|
25
|
+
let found = null;
|
|
26
|
+
for (const b of btns.slice().reverse()) {
|
|
27
|
+
const attrText = (b.getAttribute('title') || b.getAttribute('aria-label') || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
28
|
+
const hasPlay = b.querySelector('.codicon-play, .codicon-check') !== null;
|
|
29
|
+
const hasReject = b.querySelector('.codicon-chrome-close, .codicon-close') !== null;
|
|
30
|
+
|
|
31
|
+
// Check direct match
|
|
32
|
+
if (matches(b)) {
|
|
33
|
+
found = b;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Checks for 'run/approve' intent
|
|
38
|
+
if (/^(run|approve|allow|accept|yes|trust)\b/.test(wantNorm)) {
|
|
39
|
+
if (hasPlay || attrText.includes('run') || attrText.includes('approve') || attrText.includes('allow') || attrText.includes('trust')) {
|
|
40
|
+
found = b;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Checks for 'reject/deny/cancel' intent
|
|
46
|
+
if (/^(reject|deny|no|abort|cancel)\b/.test(wantNorm)) {
|
|
47
|
+
if (hasReject || attrText.includes('reject') || attrText.includes('deny') || attrText.includes('cancel')) {
|
|
48
|
+
found = b;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (found) {
|
|
55
|
+
found.click();
|
|
56
|
+
return JSON.stringify({ resolved: true, method: 'webview_button_click' });
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify({ resolved: false, want: wantNorm, error: 'Button not found' });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return JSON.stringify({ resolved: false, error: e.message });
|
|
61
|
+
}
|
|
62
|
+
})()
|
|
@@ -54,7 +54,29 @@
|
|
|
54
54
|
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
55
55
|
await new Promise(r => setTimeout(r, 400));
|
|
56
56
|
|
|
57
|
-
// ─── 3. Enter
|
|
57
|
+
// ─── 3. 전송 버튼 클릭 (Enter 키가 안 먹힐 때 대비) ───
|
|
58
|
+
const sendBtns = Array.from(document.querySelectorAll('button, div[role="button"], span[role="button"]'))
|
|
59
|
+
.filter(b => {
|
|
60
|
+
const aria = (b.getAttribute('aria-label') || '').toLowerCase();
|
|
61
|
+
const title = (b.getAttribute('title') || '').toLowerCase();
|
|
62
|
+
const text = (b.textContent || '').toLowerCase();
|
|
63
|
+
const className = (b.className || '').toLowerCase();
|
|
64
|
+
return aria.includes('send') || aria.includes('submit') ||
|
|
65
|
+
title.includes('send') || title.includes('submit') ||
|
|
66
|
+
className.includes('send') || className.includes('submit') ||
|
|
67
|
+
b.querySelector('svg'); // Fallback for icon-only buttons next to input
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Find the button closest to the editor
|
|
71
|
+
let submitBtn = null;
|
|
72
|
+
if (sendBtns.length > 0) {
|
|
73
|
+
// grab the one visually right/bottom to the editor, or just the last svg button
|
|
74
|
+
submitBtn = sendBtns[sendBtns.length - 1];
|
|
75
|
+
submitBtn.click();
|
|
76
|
+
await new Promise(r => setTimeout(r, 100));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── 4. Enter 키 전송 (Fallback) ───
|
|
58
80
|
const enterOpts = {
|
|
59
81
|
key: 'Enter', code: 'Enter',
|
|
60
82
|
keyCode: 13, which: 13,
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Kiro — webview_set_mode
|
|
3
|
+
* Toggles the Autopilot switch based on requested mode.
|
|
4
|
+
* 파라미터: ${ MODE }
|
|
4
5
|
*/
|
|
5
6
|
(() => {
|
|
6
7
|
try {
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const mode = ${ MODE };
|
|
9
|
+
if (!mode) return JSON.stringify({ success: false, error: 'No mode specified' });
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const toggle = document.querySelector('#autonomy-mode-toggle-switch');
|
|
12
|
+
if (!toggle) {
|
|
13
|
+
return JSON.stringify({ success: false, error: 'Autonomy toggle not found' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const isAutopilot = toggle.checked;
|
|
17
|
+
|
|
18
|
+
const wantAutopilot = mode.toLowerCase() === 'autopilot';
|
|
19
|
+
const wantManual = mode.toLowerCase() === 'manual';
|
|
20
|
+
|
|
21
|
+
if (wantAutopilot && !isAutopilot) {
|
|
22
|
+
toggle.click();
|
|
23
|
+
return JSON.stringify({ success: true });
|
|
24
|
+
} else if (wantManual && isAutopilot) {
|
|
25
|
+
toggle.click();
|
|
26
|
+
return JSON.stringify({ success: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Already in the right mode
|
|
30
|
+
return JSON.stringify({ success: true });
|
|
12
31
|
} catch (e) {
|
|
13
32
|
return JSON.stringify({ success: false, error: e.message });
|
|
14
33
|
}
|
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* ${ MODEL }
|
|
2
|
+
* Kiro — webview_set_model
|
|
3
|
+
* 파라미터: ${ MODEL }
|
|
4
4
|
*/
|
|
5
|
-
(() => {
|
|
5
|
+
(async () => {
|
|
6
6
|
try {
|
|
7
7
|
const want = ${ MODEL } || '';
|
|
8
|
-
const norm = (t) => t.toLowerCase().trim();
|
|
8
|
+
const norm = (t) => (t || '').toLowerCase().trim();
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if (!want) return JSON.stringify({ success: false, error: 'No model specified' });
|
|
11
|
+
|
|
12
|
+
const trigger = document.querySelector('.kiro-dropdown-trigger');
|
|
13
|
+
if (!trigger) return JSON.stringify({ success: false, error: 'Trigger not found' });
|
|
14
|
+
|
|
15
|
+
const current = norm(trigger.querySelector('.kiro-dropdown-selected-text')?.textContent);
|
|
16
|
+
if (current === norm(want) || current.includes(norm(want))) {
|
|
17
|
+
return JSON.stringify({ success: true, already: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const wasExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
21
|
+
if (!wasExpanded) {
|
|
22
|
+
trigger.click();
|
|
23
|
+
await new Promise(r => setTimeout(r, 150));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const items = document.querySelectorAll('.kiro-dropdown-item, [role="menuitem"], [role="option"]');
|
|
27
|
+
let found = null;
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
const txt = norm(item.textContent);
|
|
30
|
+
if (txt === norm(want) || txt.includes(norm(want)) || norm(want).includes(txt)) {
|
|
31
|
+
found = item;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (found) {
|
|
37
|
+
found.click();
|
|
38
|
+
return JSON.stringify({ success: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!wasExpanded) trigger.click(); // Close if we opened but didn't find
|
|
42
|
+
|
|
43
|
+
return JSON.stringify({ success: false, error: 'Model not found in list' });
|
|
12
44
|
} catch (e) {
|
|
13
45
|
return JSON.stringify({ success: false, error: e.message });
|
|
14
46
|
}
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
status = 'waiting_approval';
|
|
138
138
|
activeModal = {
|
|
139
139
|
message: normalize(cascade.querySelector('button')?.closest('[class*="terminal"],[class*="shadow-step"],.monaco-dialog-box,[role="dialog"]')?.innerText || 'Approval required'),
|
|
140
|
-
|
|
140
|
+
buttons: approvalActions,
|
|
141
141
|
};
|
|
142
142
|
} else {
|
|
143
143
|
const stopButton = Array.from(cascade.querySelectorAll('button, [role="button"]')).find(el => {
|
|
@@ -165,7 +165,7 @@ export class DaemonCliManager {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
try { addCliHistory({ cliType: normalizedType, dir: resolvedDir, cliArgs }); } catch (e) { LOG.warn('CLI', `ACP history save failed: ${(e as Error)?.message}`); }
|
|
168
|
+
try { addCliHistory({ category: 'acp', cliType: normalizedType, dir: resolvedDir, workspace: resolvedDir, cliArgs, model: initialModel }); } catch (e) { LOG.warn('CLI', `ACP history save failed: ${(e as Error)?.message}`); }
|
|
169
169
|
this.deps.onStatusChange();
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
@@ -261,7 +261,7 @@ export class DaemonCliManager {
|
|
|
261
261
|
console.log(chalk.green(` ✓ CLI started: ${cliInfo.displayName} v${cliInfo.version || 'unknown'} in ${resolvedDir}`));
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
try { addCliHistory({ cliType, dir: resolvedDir, cliArgs }); } catch (e) { LOG.warn('CLI', `CLI history save failed: ${(e as Error)?.message}`); }
|
|
264
|
+
try { addCliHistory({ category: 'cli', cliType, dir: resolvedDir, workspace: resolvedDir, cliArgs, model: initialModel }); } catch (e) { LOG.warn('CLI', `CLI history save failed: ${(e as Error)?.message}`); }
|
|
265
265
|
|
|
266
266
|
this.deps.onStatusChange();
|
|
267
267
|
}
|
package/src/commands/router.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { launchWithCdp, killIdeProcess, isIdeRunning } from '../launch.js';
|
|
|
20
20
|
import { loadConfig, saveConfig, updateConfig } from '../config/config.js';
|
|
21
21
|
import { resolveIdeLaunchWorkspace } from '../config/workspaces.js';
|
|
22
22
|
import { appendWorkspaceActivity } from '../config/workspace-activity.js';
|
|
23
|
+
import { addCliHistory } from '../config/config.js';
|
|
23
24
|
import { detectIDEs } from '../detection/ide-detector.js';
|
|
24
25
|
import { LOG } from '../logging/logger.js';
|
|
25
26
|
import { logCommand } from '../logging/command-log.js';
|
|
@@ -207,6 +208,17 @@ export class DaemonCommandRouter {
|
|
|
207
208
|
};
|
|
208
209
|
LOG.info('LaunchIDE', `target=${ideKey || 'auto'}`);
|
|
209
210
|
const result = await launchWithCdp(launchArgs);
|
|
211
|
+
if (result.success && (result.ideId || ideKey)) {
|
|
212
|
+
try {
|
|
213
|
+
addCliHistory({
|
|
214
|
+
category: 'ide',
|
|
215
|
+
cliType: result.ideId || ideKey,
|
|
216
|
+
dir: resolvedWorkspace || '',
|
|
217
|
+
workspace: resolvedWorkspace || '',
|
|
218
|
+
newWindow: args?.newWindow === true,
|
|
219
|
+
});
|
|
220
|
+
} catch { /* ignore history failure */ }
|
|
221
|
+
}
|
|
210
222
|
|
|
211
223
|
if (result.success && result.port && result.ideId && !this.deps.cdpManagers.has(result.ideId)) {
|
|
212
224
|
const logFn = this.deps.getCdpLogFn
|
package/src/config/config.ts
CHANGED
|
@@ -87,9 +87,13 @@ export interface ADHDevConfig {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export interface CliHistoryEntry {
|
|
90
|
+
category?: 'ide' | 'cli' | 'acp';
|
|
90
91
|
cliType: string;
|
|
91
92
|
dir: string;
|
|
92
93
|
cliArgs?: string[];
|
|
94
|
+
workspace?: string;
|
|
95
|
+
newWindow?: boolean;
|
|
96
|
+
model?: string;
|
|
93
97
|
timestamp: number;
|
|
94
98
|
label?: string;
|
|
95
99
|
}
|
|
@@ -255,24 +259,43 @@ export function generateConnectionToken(): string {
|
|
|
255
259
|
return token;
|
|
256
260
|
}
|
|
257
261
|
/**
|
|
258
|
-
* Add
|
|
262
|
+
* Add launch to history (max 20, dedup by category+type+dir+args+workspace+model)
|
|
259
263
|
*/
|
|
260
264
|
export function addCliHistory(entry: Omit<CliHistoryEntry, 'timestamp'>): void {
|
|
261
265
|
const config = loadConfig();
|
|
262
266
|
const history = config.cliHistory || [];
|
|
263
267
|
const argsKey = (entry.cliArgs || []).join(' ');
|
|
268
|
+
const category = entry.category || 'cli';
|
|
269
|
+
const workspaceKey = entry.workspace || '';
|
|
270
|
+
const modelKey = entry.model || '';
|
|
264
271
|
|
|
265
|
-
// Remove duplicate (same
|
|
272
|
+
// Remove duplicate (same category + type + dir + args + workspace + model)
|
|
266
273
|
const filtered = history.filter(h => {
|
|
267
274
|
const hArgsKey = (h.cliArgs || []).join(' ');
|
|
268
|
-
return !(
|
|
275
|
+
return !(
|
|
276
|
+
(h.category || 'cli') === category &&
|
|
277
|
+
h.cliType === entry.cliType &&
|
|
278
|
+
h.dir === entry.dir &&
|
|
279
|
+
hArgsKey === argsKey &&
|
|
280
|
+
(h.workspace || '') === workspaceKey &&
|
|
281
|
+
(h.model || '') === modelKey
|
|
282
|
+
);
|
|
269
283
|
});
|
|
270
284
|
|
|
271
285
|
// Add to front
|
|
272
286
|
filtered.unshift({
|
|
273
287
|
...entry,
|
|
288
|
+
category,
|
|
274
289
|
timestamp: Date.now(),
|
|
275
|
-
label: entry.label ||
|
|
290
|
+
label: entry.label || (() => {
|
|
291
|
+
const base = `${entry.cliType} · ${entry.dir.split('/').filter(Boolean).pop() || 'root'}`;
|
|
292
|
+
const suffix: string[] = [];
|
|
293
|
+
if (entry.workspace && entry.workspace !== entry.dir) suffix.push(entry.workspace.split('/').filter(Boolean).pop() || entry.workspace);
|
|
294
|
+
if (entry.model) suffix.push(`model=${entry.model}`);
|
|
295
|
+
if (argsKey) suffix.push(argsKey);
|
|
296
|
+
if (entry.newWindow) suffix.push('new window');
|
|
297
|
+
return suffix.length > 0 ? `${base} (${suffix.join(' · ')})` : base;
|
|
298
|
+
})(),
|
|
276
299
|
});
|
|
277
300
|
|
|
278
301
|
// Keep max 20
|