ata-coder 2.4.2__py3-none-any.whl

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 (118) hide show
  1. ata_coder/__init__.py +1 -0
  2. ata_coder/agent.py +874 -0
  3. ata_coder/agent_compact.py +190 -0
  4. ata_coder/agent_controller.py +218 -0
  5. ata_coder/agent_extension.py +69 -0
  6. ata_coder/agent_routing.py +105 -0
  7. ata_coder/agent_subsystems.py +72 -0
  8. ata_coder/agent_tools.py +318 -0
  9. ata_coder/agent_undo.py +63 -0
  10. ata_coder/anthropic_client.py +465 -0
  11. ata_coder/change_tracker.py +368 -0
  12. ata_coder/clawd_integration.py +574 -0
  13. ata_coder/commands/__init__.py +128 -0
  14. ata_coder/commands/_core.py +184 -0
  15. ata_coder/commands/_safety.py +95 -0
  16. ata_coder/commands/_settings.py +241 -0
  17. ata_coder/commands/_workflow.py +451 -0
  18. ata_coder/commands.py +974 -0
  19. ata_coder/config.py +257 -0
  20. ata_coder/core/__init__.py +35 -0
  21. ata_coder/core/events.py +73 -0
  22. ata_coder/core/queue.py +85 -0
  23. ata_coder/core/state.py +17 -0
  24. ata_coder/event_queue.py +5 -0
  25. ata_coder/extension.py +654 -0
  26. ata_coder/extensions/__init__.py +1 -0
  27. ata_coder/extensions/hello_skill.py +47 -0
  28. ata_coder/fool_proof.py +295 -0
  29. ata_coder/git_workflow.py +371 -0
  30. ata_coder/gui.py +511 -0
  31. ata_coder/llm_client.py +543 -0
  32. ata_coder/main.py +814 -0
  33. ata_coder/mcp_client.py +1095 -0
  34. ata_coder/memory.py +539 -0
  35. ata_coder/model_registry.py +134 -0
  36. ata_coder/model_router.py +105 -0
  37. ata_coder/permissions.py +274 -0
  38. ata_coder/privilege.py +464 -0
  39. ata_coder/project.py +273 -0
  40. ata_coder/prompt_template.py +423 -0
  41. ata_coder/prompts/auto-mode.md +7 -0
  42. ata_coder/prompts/coding-rules.md +40 -0
  43. ata_coder/prompts/execution-guardrails.md +14 -0
  44. ata_coder/prompts/memory-system.md +24 -0
  45. ata_coder/prompts/output-style.md +23 -0
  46. ata_coder/prompts/safety.md +17 -0
  47. ata_coder/prompts/slash-commands.md +24 -0
  48. ata_coder/prompts/sub-agents.md +38 -0
  49. ata_coder/prompts/system-reminders.md +17 -0
  50. ata_coder/prompts/system.md +105 -0
  51. ata_coder/prompts/tool-policy.md +46 -0
  52. ata_coder/repl_theme.py +99 -0
  53. ata_coder/repl_tracker.py +89 -0
  54. ata_coder/repl_ui.py +1214 -0
  55. ata_coder/safety_guard.py +434 -0
  56. ata_coder/self_correct.py +346 -0
  57. ata_coder/server.py +882 -0
  58. ata_coder/server_session.py +159 -0
  59. ata_coder/server_shell.py +129 -0
  60. ata_coder/session.py +431 -0
  61. ata_coder/settings.py +439 -0
  62. ata_coder/setup_wizard.py +136 -0
  63. ata_coder/skill_extension.py +92 -0
  64. ata_coder/skills/architect/SKILL.md +42 -0
  65. ata_coder/skills/code-reviewer/SKILL.md +37 -0
  66. ata_coder/skills/codecraft/SKILL.md +452 -0
  67. ata_coder/skills/debugger/SKILL.md +45 -0
  68. ata_coder/skills/doc-writer/SKILL.md +36 -0
  69. ata_coder/skills/general-coder/SKILL.md +76 -0
  70. ata_coder/skills/math-calculator/README.md +40 -0
  71. ata_coder/skills/math-calculator/SKILL.md +59 -0
  72. ata_coder/skills/math-calculator/handler.py +103 -0
  73. ata_coder/skills/math-calculator/prompts/system.md +8 -0
  74. ata_coder/skills/math-calculator/requirements.txt +2 -0
  75. ata_coder/skills/math-calculator/resources/constants.json +8 -0
  76. ata_coder/skills/math-calculator/tests/test_handler.py +53 -0
  77. ata_coder/skills/security-auditor/SKILL.md +40 -0
  78. ata_coder/skills/test-writer/SKILL.md +36 -0
  79. ata_coder/skills/weather-skill/README.md +45 -0
  80. ata_coder/skills/weather-skill/handler.py +76 -0
  81. ata_coder/skills/weather-skill/manifest.json +48 -0
  82. ata_coder/skills/weather-skill/prompts/system_prompt.txt +9 -0
  83. ata_coder/skills/weather-skill/prompts/user_prompt_template.txt +3 -0
  84. ata_coder/skills/weather-skill/requirements.txt +1 -0
  85. ata_coder/skills/weather-skill/resources/city_list.json +17 -0
  86. ata_coder/skills/weather-skill/resources/error_messages.json +7 -0
  87. ata_coder/skills/weather-skill/tests/test_handler.py +28 -0
  88. ata_coder/skills/weather-skill/weather_utils.py +50 -0
  89. ata_coder/skills.py +1014 -0
  90. ata_coder/sub_agent.py +273 -0
  91. ata_coder/sub_agent_manager.py +203 -0
  92. ata_coder/system_prompt_builder.py +146 -0
  93. ata_coder/task_planner.py +391 -0
  94. ata_coder/terminal.py +318 -0
  95. ata_coder/test_runner.py +219 -0
  96. ata_coder/thread_supervisor.py +195 -0
  97. ata_coder/tool_defs.py +335 -0
  98. ata_coder/tools/__init__.py +11 -0
  99. ata_coder/tools/definitions.py +335 -0
  100. ata_coder/tools/executor.py +1036 -0
  101. ata_coder/tools/result.py +26 -0
  102. ata_coder/tools/subagent.py +332 -0
  103. ata_coder/tools/web.py +361 -0
  104. ata_coder/tools.py +1576 -0
  105. ata_coder/types.py +92 -0
  106. ata_coder/utils.py +113 -0
  107. ata_coder/web/css/style.css +180 -0
  108. ata_coder/web/index.html +84 -0
  109. ata_coder/web/js/app.js +489 -0
  110. ata_coder/web/package-lock.json +25 -0
  111. ata_coder/web/package.json +10 -0
  112. ata_coder/web/tsconfig.json +13 -0
  113. ata_coder-2.4.2.dist-info/METADATA +799 -0
  114. ata_coder-2.4.2.dist-info/RECORD +118 -0
  115. ata_coder-2.4.2.dist-info/WHEEL +5 -0
  116. ata_coder-2.4.2.dist-info/entry_points.txt +2 -0
  117. ata_coder-2.4.2.dist-info/licenses/LICENSE +21 -0
  118. ata_coder-2.4.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,489 @@
1
+ "use strict";
2
+ const TOKEN_ESTIMATE_PER_TOOL = 200;
3
+ const ARG_STRING_MAX_LEN = 60;
4
+ const TOOL_OUTPUT_MAX_LEN = 150;
5
+ const state = {
6
+ sessionId: '',
7
+ model: '…',
8
+ skill: 'general-coder',
9
+ workspace: '…',
10
+ tokens: 0,
11
+ thinking: false,
12
+ streaming: false,
13
+ startTime: 0,
14
+ commands: [],
15
+ };
16
+ const el = {
17
+ chat: document.getElementById('chat'),
18
+ input: document.getElementById('cmd-input'),
19
+ sendBtn: document.getElementById('send-btn'),
20
+ stopBtn: document.getElementById('stop-btn'),
21
+ popup: document.getElementById('cmd-popup'),
22
+ dot: document.getElementById('status-dot'),
23
+ subtitle: document.getElementById('top-subtitle'),
24
+ initInfo: document.getElementById('init-info'),
25
+ modelSelect: document.getElementById('model-select'),
26
+ skillTags: document.getElementById('skill-tags'),
27
+ wsPath: document.getElementById('ws-path'),
28
+ stTokens: document.getElementById('st-tokens'),
29
+ stSkill: document.getElementById('st-skill'),
30
+ stModel: document.getElementById('st-model'),
31
+ stTime: document.getElementById('st-time'),
32
+ };
33
+ let popupIdx = -1;
34
+ let activeReq = null;
35
+ function esc(s) {
36
+ const d = document.createElement('div');
37
+ d.textContent = s;
38
+ return d.innerHTML;
39
+ }
40
+ function fmtTime(s) { return s.toFixed(1) + 's'; }
41
+ function appendBubble(msg) {
42
+ const div = document.createElement('div');
43
+ div.className = `msg ${msg.role}`;
44
+ div.innerHTML = renderMarkdown(msg.text);
45
+ el.chat.appendChild(div);
46
+ scrollBottom();
47
+ }
48
+ function appendStreamBubble(text, cls) {
49
+ let div = el.chat.lastElementChild;
50
+ if (!div || !div.classList.contains(cls) || !div.classList.contains('streaming')) {
51
+ div = document.createElement('div');
52
+ div.className = `msg ${cls} streaming`;
53
+ div.dataset.raw = text;
54
+ div.innerHTML = renderMarkdown(text);
55
+ el.chat.appendChild(div);
56
+ }
57
+ else {
58
+ div.dataset.raw = (div.dataset.raw || '') + text;
59
+ div.innerHTML = renderMarkdown(div.dataset.raw);
60
+ }
61
+ scrollBottom();
62
+ return div;
63
+ }
64
+ function flushStreamBubbles() {
65
+ el.chat.querySelectorAll('.streaming').forEach(d => {
66
+ const el = d;
67
+ el.classList.remove('streaming');
68
+ delete el.dataset.raw;
69
+ });
70
+ }
71
+ function renderMarkdown(text) {
72
+ const codeBlocks = [];
73
+ let html = esc(text);
74
+ html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
75
+ const idx = codeBlocks.length;
76
+ codeBlocks.push(`<pre><code class="${esc(lang)}">${esc(code.trimEnd())}</code></pre>`);
77
+ return `\x00CB${idx}\x00`;
78
+ });
79
+ html = html.replace(/(^|\n)### (.+?)(?=\n|$)/gm, '$1</p><h3>$2</h3><p>');
80
+ html = html.replace(/(^|\n)## (.+?)(?=\n|$)/gm, '$1</p><h2>$2</h2><p>');
81
+ html = html.replace(/(^|\n)# (.+?)(?=\n|$)/gm, '$1</p><h1>$2</h1><p>');
82
+ html = html.replace(/\n\n/g, '</p><p>');
83
+ html = html.replace(/\n/g, '<br>');
84
+ html = `<p>${html}</p>`;
85
+ html = html.replace(/<p><\/p>/g, '');
86
+ html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
87
+ html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
88
+ html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
89
+ html = html.replace(/\x00CB(\d+)\x00/g, (_m, idx) => codeBlocks[parseInt(idx)]);
90
+ return html;
91
+ }
92
+ function scrollBottom() {
93
+ requestAnimationFrame(() => {
94
+ el.chat.scrollTop = el.chat.scrollHeight;
95
+ });
96
+ }
97
+ function setThinking(on) {
98
+ state.thinking = on;
99
+ el.dot.className = `dot ${on ? 'thinking' : 'ready'}`;
100
+ el.subtitle.textContent = on ? 'Thinking…' : '';
101
+ }
102
+ function updateStatus() {
103
+ el.stTokens.textContent = `tokens: ~${state.tokens.toLocaleString()}`;
104
+ el.stSkill.textContent = `skill: ${state.skill}`;
105
+ el.stModel.textContent = `model: ${state.model}`;
106
+ if (state.startTime > 0) {
107
+ el.stTime.textContent = fmtTime((Date.now() - state.startTime) / 1000);
108
+ }
109
+ }
110
+ function setStreaming(on) {
111
+ state.streaming = on;
112
+ document.body.classList.toggle('streaming', on);
113
+ el.input.disabled = on;
114
+ }
115
+ function safeFocusInput() {
116
+ requestAnimationFrame(() => {
117
+ el.input.disabled = false;
118
+ el.input.focus();
119
+ });
120
+ }
121
+ async function apiGet(path) {
122
+ const r = await fetch(path);
123
+ if (!r.ok)
124
+ throw new Error(`HTTP ${r.status}`);
125
+ return r.json();
126
+ }
127
+ async function apiPost(path, body) {
128
+ const r = await fetch(path, {
129
+ method: 'POST',
130
+ headers: { 'Content-Type': 'application/json' },
131
+ body: JSON.stringify(body),
132
+ });
133
+ if (!r.ok) {
134
+ const err = await r.json().catch(() => ({ error: `HTTP ${r.status}` }));
135
+ throw new Error(err.error || `HTTP ${r.status}`);
136
+ }
137
+ return r.json();
138
+ }
139
+ async function fetchInit() {
140
+ try {
141
+ const [health, skills, models] = await Promise.all([
142
+ apiGet('/health'),
143
+ apiGet('/skills'),
144
+ apiGet('/models'),
145
+ ]);
146
+ state.model = health.model;
147
+ state.workspace = health.workspace;
148
+ el.modelSelect.innerHTML = (models.models || []).map(m => `<option ${m.id === models.current ? 'selected' : ''}>${m.id}</option>`).join('');
149
+ if ((models.models || []).length === 0) {
150
+ el.modelSelect.innerHTML = `<option>${state.model}</option>`;
151
+ }
152
+ el.skillTags.innerHTML = (skills.skills || []).map(s => `<span class="skill-tag">${esc(s.name)}</span>`).join('');
153
+ el.wsPath.textContent = state.workspace;
154
+ el.initInfo.textContent = `Model: ${state.model} · Workspace: ${state.workspace}`;
155
+ el.stModel.textContent = `model: ${state.model}`;
156
+ updateStatus();
157
+ }
158
+ catch {
159
+ el.initInfo.textContent = '⚠ Server unreachable — start with: ata gui';
160
+ }
161
+ }
162
+ async function sendChat(message) {
163
+ if (state.streaming || !message.trim())
164
+ return;
165
+ appendBubble({ role: 'user', text: message });
166
+ el.input.value = '';
167
+ hidePopup();
168
+ flushStreamBubbles();
169
+ state.startTime = Date.now();
170
+ setStreaming(true);
171
+ setThinking(true);
172
+ try {
173
+ activeReq = new AbortController();
174
+ const resp = await fetch('/chat/stream', {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({
178
+ message,
179
+ session_id: state.sessionId || undefined,
180
+ skill: state.skill,
181
+ model: state.model,
182
+ }),
183
+ signal: activeReq.signal,
184
+ });
185
+ if (!resp.ok) {
186
+ const err = await resp.json().catch(() => ({ error: `HTTP ${resp.status}` }));
187
+ throw new Error(err.error || `HTTP ${resp.status}`);
188
+ }
189
+ const reader = resp.body?.getReader();
190
+ if (!reader)
191
+ throw new Error('No response body');
192
+ const decoder = new TextDecoder();
193
+ let buffer = '';
194
+ let currentEvent = '';
195
+ let toolCount = 0;
196
+ let totalTime = 0;
197
+ let firstContent = false;
198
+ let streamDone = false;
199
+ while (true) {
200
+ const { done, value } = await reader.read();
201
+ if (done)
202
+ break;
203
+ buffer += decoder.decode(value, { stream: true });
204
+ const lines = buffer.split('\n');
205
+ buffer = lines.pop() || '';
206
+ for (const line of lines) {
207
+ if (line.startsWith('event: ')) {
208
+ currentEvent = line.slice(7).trim();
209
+ continue;
210
+ }
211
+ if (!line.startsWith('data: '))
212
+ continue;
213
+ try {
214
+ const data = JSON.parse(line.slice(6));
215
+ if (currentEvent === 'done') {
216
+ if (data.session_id)
217
+ state.sessionId = data.session_id;
218
+ streamDone = true;
219
+ break;
220
+ }
221
+ if (!firstContent && (data.type === 'text' || data.type === 'thinking' || data.type === 'tool_call')) {
222
+ firstContent = true;
223
+ setThinking(false);
224
+ }
225
+ handleSSE(data);
226
+ if (data.type === 'tool_call')
227
+ toolCount++;
228
+ if (data.type === 'complete')
229
+ totalTime = data.time || 0;
230
+ }
231
+ catch (e) {
232
+ if (e instanceof SyntaxError)
233
+ continue;
234
+ console.warn('SSE parse error:', e);
235
+ }
236
+ }
237
+ if (streamDone)
238
+ break;
239
+ }
240
+ setThinking(false);
241
+ flushStreamBubbles();
242
+ if (toolCount > 0 || totalTime > 0) {
243
+ appendBubble({ role: 'sep', text: `${toolCount} tools · ${fmtTime(totalTime)}` });
244
+ }
245
+ updateStatus();
246
+ }
247
+ catch (e) {
248
+ if (e instanceof DOMException && e.name === 'AbortError') {
249
+ appendBubble({ role: 'status', text: '⏹ Stopped' });
250
+ }
251
+ else if (e instanceof Error) {
252
+ appendBubble({ role: 'error', text: `Error: ${e.message}` });
253
+ }
254
+ else {
255
+ appendBubble({ role: 'error', text: 'Unknown error' });
256
+ }
257
+ }
258
+ finally {
259
+ setStreaming(false);
260
+ setThinking(false);
261
+ activeReq = null;
262
+ safeFocusInput();
263
+ updateStatus();
264
+ }
265
+ }
266
+ function handleSSE(evt) {
267
+ switch (evt.type) {
268
+ case 'text':
269
+ appendStreamBubble(evt.text || '', 'agent');
270
+ break;
271
+ case 'thinking':
272
+ appendStreamBubble(evt.text || '', 'think');
273
+ break;
274
+ case 'tool_call':
275
+ appendBubble({ role: 'tool', text: `◆ ${evt.tool} ${fmtArgs(evt.args)}` });
276
+ break;
277
+ case 'tool_result':
278
+ if (evt.ok) {
279
+ const out = (evt.output || '').replace(/\n/g, ' ').slice(0, TOOL_OUTPUT_MAX_LEN);
280
+ appendBubble({ role: 'tool-ok', text: `✓ ${out}` });
281
+ }
282
+ else {
283
+ appendBubble({ role: 'tool-err', text: `✗ ${(evt.error || 'unknown').slice(0, TOOL_OUTPUT_MAX_LEN)}` });
284
+ }
285
+ break;
286
+ case 'error':
287
+ appendBubble({ role: 'error', text: `● ${evt.error}` });
288
+ break;
289
+ case 'complete':
290
+ state.tokens += (evt.tools || 0) * TOKEN_ESTIMATE_PER_TOOL;
291
+ updateStatus();
292
+ break;
293
+ }
294
+ }
295
+ function fmtArgs(args) {
296
+ if (!args)
297
+ return '';
298
+ const parts = [];
299
+ for (const [k, v] of Object.entries(args)) {
300
+ if (typeof v === 'string') {
301
+ parts.push(`${k}="${v.length > ARG_STRING_MAX_LEN ? v.slice(0, ARG_STRING_MAX_LEN) + '…' : v}"`);
302
+ }
303
+ else {
304
+ const s = JSON.stringify(v);
305
+ parts.push(`${k}=${s.length > ARG_STRING_MAX_LEN ? s.slice(0, ARG_STRING_MAX_LEN) + '…' : s}`);
306
+ }
307
+ }
308
+ return parts.join(' ');
309
+ }
310
+ function onInputChange() {
311
+ const val = el.input.value;
312
+ if (val.startsWith('/')) {
313
+ showPopup(val);
314
+ }
315
+ else {
316
+ hidePopup();
317
+ }
318
+ }
319
+ function onInputKey(e) {
320
+ if (e.key === 'Enter' && !e.shiftKey) {
321
+ e.preventDefault();
322
+ const val = el.input.value.trim();
323
+ if (val.startsWith('/') && popupIdx >= 0 && el.popup.classList.contains('show')) {
324
+ selectPopupItem(popupIdx);
325
+ return;
326
+ }
327
+ send();
328
+ }
329
+ else if (e.key === 'ArrowDown' && el.popup.classList.contains('show')) {
330
+ e.preventDefault();
331
+ popupIdx = Math.min(popupIdx + 1, el.popup.children.length - 1);
332
+ highlightPopup();
333
+ }
334
+ else if (e.key === 'ArrowUp' && el.popup.classList.contains('show')) {
335
+ e.preventDefault();
336
+ popupIdx = Math.max(popupIdx - 1, 0);
337
+ highlightPopup();
338
+ }
339
+ else if (e.key === 'Escape') {
340
+ hidePopup();
341
+ }
342
+ }
343
+ function showPopup(filter) {
344
+ const matches = state.commands.filter(([name]) => name.startsWith(filter));
345
+ if (matches.length === 0 || (matches.length === 1 && matches[0][0] === filter)) {
346
+ hidePopup();
347
+ return;
348
+ }
349
+ el.popup.innerHTML = matches.map(([name, desc], i) => `<div class="cmd-item${i === 0 ? ' active' : ''}" data-idx="${i}">
350
+ <span class="name">${esc(name)}</span>
351
+ <span class="desc">${esc(desc)}</span>
352
+ </div>`).join('');
353
+ el.popup.classList.add('show');
354
+ popupIdx = 0;
355
+ }
356
+ function hidePopup() {
357
+ el.popup.classList.remove('show');
358
+ popupIdx = -1;
359
+ }
360
+ function highlightPopup() {
361
+ const items = el.popup.querySelectorAll('.cmd-item');
362
+ items.forEach((item, i) => item.classList.toggle('active', i === popupIdx));
363
+ }
364
+ function selectPopupItem(idx) {
365
+ const items = el.popup.querySelectorAll('.cmd-item');
366
+ const itemEl = items[idx];
367
+ if (!itemEl)
368
+ return;
369
+ const name = itemEl.querySelector('.name')?.textContent || '';
370
+ el.input.value = name + ' ';
371
+ el.input.focus();
372
+ hidePopup();
373
+ }
374
+ function send() {
375
+ const val = el.input.value.trim();
376
+ if (!val)
377
+ return;
378
+ sendChat(val);
379
+ }
380
+ function sendQuick(cmd) {
381
+ el.input.value = cmd;
382
+ sendChat(cmd);
383
+ }
384
+ function stop() {
385
+ if (activeReq) {
386
+ activeReq.abort();
387
+ activeReq = null;
388
+ }
389
+ setStreaming(false);
390
+ setThinking(false);
391
+ safeFocusInput();
392
+ }
393
+ function onModelChange() {
394
+ state.model = el.modelSelect.value;
395
+ updateStatus();
396
+ }
397
+ function toggleSidebar() {
398
+ document.getElementById('sidebar').classList.toggle('open');
399
+ document.getElementById('overlay').classList.toggle('show');
400
+ }
401
+ function bindEvents() {
402
+ el.input.addEventListener('input', onInputChange);
403
+ el.input.addEventListener('paste', () => { setTimeout(onInputChange, 0); });
404
+ el.input.addEventListener('keydown', onInputKey);
405
+ el.sendBtn.addEventListener('click', send);
406
+ el.stopBtn.addEventListener('click', stop);
407
+ el.modelSelect.addEventListener('change', onModelChange);
408
+ // Ensure model dropdown opens on click (for automation tools)
409
+ el.modelSelect.addEventListener('mousedown', (e) => {
410
+ el.modelSelect.focus();
411
+ if (el.modelSelect.showPicker) el.modelSelect.showPicker();
412
+ });
413
+ document.querySelector('.hamburger')?.addEventListener('click', toggleSidebar);
414
+ document.getElementById('overlay')?.addEventListener('click', toggleSidebar);
415
+ document.querySelectorAll('.shortcut[data-cmd]').forEach(btn => {
416
+ const cmd = btn.dataset.cmd;
417
+ if (cmd)
418
+ btn.addEventListener('click', () => sendQuick(cmd));
419
+ });
420
+ // Skill tag click: send a message to activate that skill
421
+ el.skillTags?.addEventListener('click', (e) => {
422
+ const tag = e.target.closest('.skill-tag');
423
+ if (tag && tag.textContent) {
424
+ const skillName = tag.textContent.trim();
425
+ el.input.value = '/skill ' + skillName;
426
+ sendChat('/skill ' + skillName);
427
+ }
428
+ });
429
+ el.popup.addEventListener('mousedown', (e) => {
430
+ const item = e.target.closest('.cmd-item');
431
+ if (item && item.dataset.idx !== undefined) {
432
+ const idx = parseInt(item.dataset.idx, 10);
433
+ if (!isNaN(idx))
434
+ selectPopupItem(idx);
435
+ }
436
+ });
437
+ // Global keyboard shortcuts
438
+ document.addEventListener('keydown', (e) => {
439
+ if (e.ctrlKey && e.key === 'k') {
440
+ e.preventDefault();
441
+ el.input.focus();
442
+ el.input.select();
443
+ }
444
+ if (e.key === 'Escape') {
445
+ hidePopup();
446
+ if (document.activeElement === el.input && el.input.value === '') {
447
+ el.input.blur();
448
+ }
449
+ }
450
+ });
451
+ }
452
+ async function main() {
453
+ bindEvents();
454
+ await fetchInit();
455
+ try {
456
+ const skillsData = await apiGet('/skills');
457
+ state.commands = (skillsData.skills || []).map(s => ['/' + s.name, s.description]);
458
+ const coreCmds = [
459
+ ['/help', 'Show help'],
460
+ ['/clear', 'Clear conversation'],
461
+ ['/compact', 'Compact history'],
462
+ ['/context', 'Show token usage'],
463
+ ['/cost', 'Estimate cost'],
464
+ ['/model', 'Change model'],
465
+ ['/workspace', 'Change workspace'],
466
+ ['/skills', 'List skills'],
467
+ ['/skill', 'Switch skill'],
468
+ ['/history', 'Browse sessions'],
469
+ ['/save', 'Save session'],
470
+ ['/undo', 'Undo changes'],
471
+ ['/review', 'Code review'],
472
+ ['/fix', 'Auto-fix issues'],
473
+ ['/git', 'Git operations'],
474
+ ['/dangerous', 'Dangerous mode'],
475
+ ['/think', 'Thinking mode'],
476
+ ];
477
+ for (const cmd of coreCmds) {
478
+ if (!state.commands.find(c => c[0] === cmd[0])) {
479
+ state.commands.push(cmd);
480
+ }
481
+ }
482
+ state.commands.sort((a, b) => a[0].localeCompare(b[0]));
483
+ }
484
+ catch {
485
+ }
486
+ el.input.focus();
487
+ updateStatus();
488
+ }
489
+ main();
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "web",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "dependencies": {
8
+ "typescript": "^6.0.3"
9
+ }
10
+ },
11
+ "node_modules/typescript": {
12
+ "version": "6.0.3",
13
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz",
14
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
15
+ "license": "Apache-2.0",
16
+ "bin": {
17
+ "tsc": "bin/tsc",
18
+ "tsserver": "bin/tsserver"
19
+ },
20
+ "engines": {
21
+ "node": ">=14.17"
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "ata-coder-web",
3
+ "private": true,
4
+ "engines": {
5
+ "node": ">=24"
6
+ },
7
+ "dependencies": {
8
+ "typescript": "^6.0.3"
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "outDir": "js",
7
+ "rootDir": "ts",
8
+ "strict": true,
9
+ "removeComments": true,
10
+ "sourceMap": false
11
+ },
12
+ "files": ["ts/app.ts"]
13
+ }