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
ata_coder/types.py ADDED
@@ -0,0 +1,92 @@
1
+ """
2
+ Shared type aliases for ATA Coder.
3
+
4
+ Single source of truth for Message, ToolDef, and other
5
+ commonly-referenced types that were previously duplicated
6
+ across llm_client.py, anthropic_client.py, and agent.py.
7
+
8
+ TypedDict variants are available for static analysis (mypy, pyright).
9
+ The legacy ``Message`` and ``ToolDef`` aliases remain for runtime
10
+ compatibility and minimal-change migrations.
11
+ """
12
+
13
+ from abc import ABC, abstractmethod
14
+ from typing import Any, AsyncIterator, Callable, TypedDict
15
+
16
+ # ── Legacy aliases (widely used, keep for backward compat) ──────────────────
17
+
18
+ # OpenAI-compatible message dict: {"role": "...", "content": "...", ...}
19
+ Message = dict[str, Any]
20
+
21
+ # OpenAI-compatible tool definition dict
22
+ ToolDef = dict[str, Any]
23
+
24
+
25
+ # ── TypedDict variants (opt-in for stricter type checking) ──────────────────
26
+
27
+ class MessageDict(TypedDict, total=False):
28
+ """OpenAI-compatible message. ``content`` may be None for tool_calls."""
29
+ role: str
30
+ content: str | None
31
+ tool_calls: list[dict[str, Any]]
32
+ tool_call_id: str
33
+ name: str
34
+ reasoning_content: str
35
+
36
+
37
+ class ToolDefDict(TypedDict, total=False):
38
+ """OpenAI-compatible tool definition."""
39
+ type: str
40
+ function: dict[str, Any]
41
+
42
+
43
+ class ToolCallDict(TypedDict, total=False):
44
+ """A single tool-call within an assistant message."""
45
+ id: str
46
+ type: str
47
+ function: dict[str, Any]
48
+
49
+
50
+ class BaseLLMClient(ABC):
51
+ """Abstract base for async LLM clients (OpenAI, Anthropic, etc.).
52
+
53
+ Both LLMClient and AnthropicClient implement this interface so
54
+ higher-level code (CoderAgent, SubAgent) can be written against
55
+ the abstraction rather than branching on provider.
56
+
57
+ All I/O methods are async — the client runs on the asyncio event loop.
58
+ """
59
+
60
+ @abstractmethod
61
+ async def chat(self, messages: list[Message], tools: list[ToolDef] | None = None,
62
+ system_prompt: str = "") -> Message:
63
+ """Send messages and return the assistant response."""
64
+ ...
65
+
66
+ @abstractmethod
67
+ async def chat_stream(self, messages: list[Message], tools: list[ToolDef] | None = None,
68
+ system_prompt: str = "") -> AsyncIterator[tuple[str, Any]]:
69
+ """Stream a chat completion. Yields (delta_type, content) tuples."""
70
+ ...
71
+
72
+ @abstractmethod
73
+ def register_tools(self, tools: list[ToolDef]) -> None:
74
+ """Register tool definitions for subsequent requests."""
75
+ ...
76
+
77
+ @abstractmethod
78
+ def count_tokens_approx(self, messages: list[Message]) -> int:
79
+ """Estimate token count for a message list."""
80
+ ...
81
+
82
+ @abstractmethod
83
+ async def close(self) -> None:
84
+ """Release resources (HTTP sessions, etc.)."""
85
+ ...
86
+
87
+ def on_usage(self, callback: Callable[[int, int], None]) -> None:
88
+ """Register a callback for token usage: callback(prompt_tokens, completion_tokens).
89
+
90
+ Default no-op — override if the client tracks usage.
91
+ """
92
+ pass
ata_coder/utils.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ Shared display utilities — used by both the GUI and the API server.
3
+
4
+ - brief_args: truncate tool-call argument dicts for compact display
5
+ - enhance_api_error: add helpful suggestions to common API errors
6
+ - try_import_yaml: shared conditional yaml import (used by memory + skills)
7
+ """
8
+
9
+ from typing import Any
10
+
11
+
12
+ # ── YAML availability (shared across memory.py, skills.py, etc.) ──────────
13
+
14
+ try:
15
+ import yaml as _yaml
16
+ HAS_YAML = True
17
+ except ImportError:
18
+ _yaml = None # type: ignore
19
+ HAS_YAML = False
20
+
21
+
22
+ def try_import_yaml():
23
+ """Return (yaml_module, True) if PyYAML is installed, else (None, False).
24
+ """
25
+ return _yaml, HAS_YAML
26
+
27
+
28
+ def brief_args(args: dict[str, Any] | None, max_str_len: int = 100) -> str:
29
+ """Format tool-call arguments into a compact single-line summary.
30
+
31
+ Returns an empty string for None/empty dicts. String values longer
32
+ than *max_str_len* are truncated with an ellipsis.
33
+
34
+ Used by:
35
+ gui.py — tool-call display in the chat pane
36
+ server.py — SSE stream event logging
37
+ """
38
+ if not args:
39
+ return ""
40
+ parts = []
41
+ for k, v in args.items():
42
+ if isinstance(v, str):
43
+ s = f"{k}="
44
+ if len(v) > max_str_len:
45
+ s += f'"{v[:max_str_len]}…"'
46
+ else:
47
+ s += f'"{v}"'
48
+ parts.append(s)
49
+ else:
50
+ parts.append(f"{k}={v}")
51
+ return " ".join(parts)
52
+
53
+
54
+ def enhance_api_error(status_code: int, error_message: str, base_url: str = "") -> str:
55
+ """Add helpful troubleshooting suggestions to common API errors."""
56
+ msg = error_message
57
+
58
+ if "model" in error_message.lower() and ("not found" in error_message.lower() or "not supported" in error_message.lower()):
59
+ msg += (
60
+ "\n\n💡 This usually means the model name is incorrect for this provider."
61
+ "\n → Check your ATA_CODER_DEFAULT_MODEL in settings.json"
62
+ "\n → For DeepSeek: try 'deepseek-chat' or 'deepseek-v4-pro' (no [1m] suffix)"
63
+ "\n → Run 'ata --list-models' to see what the API reports as available"
64
+ )
65
+ elif "unauthorized" in error_message.lower() or status_code == 401:
66
+ msg += (
67
+ "\n\n🔑 Authentication failed. Check your API key:"
68
+ "\n → Settings: ~/.ata_coder/settings.json → env.ATA_CODER_API_KEY"
69
+ "\n → Or set ATA_CODER_API_KEY environment variable"
70
+ )
71
+ elif "rate limit" in error_message.lower() or status_code == 429:
72
+ msg += (
73
+ "\n\n⏳ Rate limited — the API is throttling requests."
74
+ "\n → Wait a moment and try again"
75
+ "\n → Consider switching to a model with higher rate limits"
76
+ )
77
+ elif "connection" in error_message.lower() or status_code in (502, 503, 504):
78
+ msg += (
79
+ "\n\n🌐 Server connectivity issue:"
80
+ "\n → Check: is the API base URL correct? → " + (base_url or "check settings.json")
81
+ + "\n → The server may be temporarily overloaded"
82
+ "\n → Try again in a few seconds"
83
+ )
84
+ elif "context length" in error_message.lower() or "too long" in error_message.lower():
85
+ msg += (
86
+ "\n\n📏 Context length exceeded:"
87
+ "\n → Try breaking your task into smaller steps"
88
+ "\n → Use /compact to shrink conversation history"
89
+ "\n → Increase ATA_CODER_MAX_OUTPUT_TOKENS for larger context"
90
+ )
91
+
92
+ return msg
93
+
94
+
95
+ # ── Surrogate sanitization ─────────────────────────────────────────────────
96
+
97
+
98
+ def sanitize_surrogates(obj: Any) -> Any:
99
+ """Recursively replace lone surrogates (U+D800–U+DFFF) in all strings.
100
+
101
+ Python's ``json.dumps(ensure_ascii=False)`` can emit lone surrogates that
102
+ are invalid UTF-8. When httpx or a file write tries ``.encode("utf-8")``
103
+ on the result, Python raises ``UnicodeEncodeError: surrogates not allowed``.
104
+
105
+ This round-trip through UTF-8 with ``errors="replace"`` strips them safely.
106
+ """
107
+ if isinstance(obj, str):
108
+ return obj.encode("utf-8", errors="replace").decode("utf-8")
109
+ if isinstance(obj, dict):
110
+ return {sanitize_surrogates(k): sanitize_surrogates(v) for k, v in obj.items()}
111
+ if isinstance(obj, list):
112
+ return [sanitize_surrogates(v) for v in obj]
113
+ return obj
@@ -0,0 +1,180 @@
1
+ /* ═══════════════════════════ One Dark Pro Theme ═══════════════════════════ */
2
+ :root {
3
+ --bg: #282C34; --bg-panel: #21252B; --bg-input: #1E2127;
4
+ --fg: #ABB2BF; --comment: #5C6370;
5
+ --red: #E06C75; --green: #98C379; --yellow: #D19A66;
6
+ --blue: #61AFEF; --purple: #C678DD; --cyan: #56B6C2;
7
+ --orange: #E5C07B; --teal: #7EE787;
8
+ --border: #3A3F4B; --radius: 10px;
9
+ --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
10
+ --font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monospace;
11
+ --font-ui: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
12
+ }
13
+
14
+ * { margin: 0; padding: 0; box-sizing: border-box; }
15
+
16
+ body {
17
+ font-family: var(--font-ui); font-size: 14px;
18
+ background: var(--bg); color: var(--fg);
19
+ height: 100vh; overflow: hidden; display: flex;
20
+ -webkit-font-smoothing: antialiased;
21
+ }
22
+
23
+ /* ═══════════════════════════ Sidebar ═══════════════════════════ */
24
+ .sidebar {
25
+ width: 260px; min-width: 260px; background: var(--bg-panel);
26
+ border-right: 1px solid var(--border); display: flex;
27
+ flex-direction: column; transition: transform var(--transition);
28
+ }
29
+ .sidebar-header {
30
+ padding: 20px 18px 14px; border-bottom: 1px solid var(--border);
31
+ }
32
+ .sidebar-header .logo { font-size: 18px; font-weight: 700; color: var(--blue); }
33
+ .sidebar-header .logo span { color: var(--fg); font-weight: 400; font-size: 12px; }
34
+ .sidebar-body { flex: 1; overflow-y: auto; padding: 12px 0; }
35
+ .sidebar-section { padding: 8px 18px; }
36
+ .sidebar-section h4 { font-size: 11px; text-transform: uppercase;
37
+ color: var(--comment); letter-spacing: 0.5px; margin-bottom: 6px; }
38
+ .sidebar-section .val { font-size: 13px; color: var(--fg); word-break: break-all; }
39
+ .sidebar-section select { width: 100%; background: var(--bg-input); color: var(--fg);
40
+ border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px;
41
+ font-size: 13px; cursor: pointer; }
42
+ .skill-tag { display: inline-block; font-size: 11px; padding: 2px 8px;
43
+ background: var(--bg); border: 1px solid var(--border); border-radius: 12px;
44
+ margin: 2px 3px 2px 0; color: var(--cyan); }
45
+ .sidebar-footer { padding: 12px 18px; border-top: 1px solid var(--border); }
46
+ .shortcut { font-size: 12px; color: var(--comment); padding: 4px 0; cursor: pointer; }
47
+ .shortcut:hover { color: var(--fg); }
48
+ .shortcut kbd { font-family: var(--font-mono); font-size: 11px; background: var(--bg);
49
+ border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; margin-right: 4px; }
50
+
51
+ /* ═══════════════════════════ Main Chat ═══════════════════════════ */
52
+ .main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
53
+
54
+ /* ── Top Bar ── */
55
+ .top-bar {
56
+ height: 44px; min-height: 44px; background: var(--bg-panel);
57
+ border-bottom: 1px solid var(--border); display: flex;
58
+ align-items: center; padding: 0 16px; gap: 8px;
59
+ }
60
+ .hamburger { display: none; background: none; border: none; color: var(--fg);
61
+ font-size: 20px; cursor: pointer; padding: 4px 8px; }
62
+ .top-bar .title { font-weight: 600; font-size: 14px; color: var(--blue); }
63
+ .top-bar .subtitle { font-size: 12px; color: var(--comment); margin-left: auto; }
64
+ .top-bar .dot { width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
65
+ .dot.ready { background: var(--green); }
66
+ .dot.thinking { background: var(--yellow); animation: pulse 1s infinite; }
67
+ .dot.error { background: var(--red); }
68
+
69
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
70
+
71
+ /* ── Chat Area ── */
72
+ .chat { flex: 1; overflow-y: auto; padding: 16px 20px;
73
+ scroll-behavior: smooth; line-height: 1.6; }
74
+ .chat::-webkit-scrollbar { width: 6px; }
75
+ .chat::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
76
+
77
+ /* ── Message Bubbles ── */
78
+ .msg { margin-bottom: 14px; max-width: 85%; animation: fadeIn 0.2s ease; }
79
+ @keyframes fadeIn { from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:translateY(0)} }
80
+
81
+ .msg.user { margin-left: auto; background: var(--blue); color: #fff;
82
+ padding: 10px 16px; border-radius: 18px 18px 4px 18px; font-weight: 500; }
83
+ .msg.agent { padding: 2px 0; color: var(--fg); }
84
+ .msg.agent p { margin: 4px 0; }
85
+ .msg.agent h1, .msg.agent h2, .msg.agent h3 { font-weight: 700; margin: 10px 0 6px; }
86
+ .msg.agent h1 { font-size: 1.25em; color: var(--fg); }
87
+ .msg.agent h2 { font-size: 1.12em; color: var(--fg); }
88
+ .msg.agent h3 { font-size: 1.05em; color: var(--fg); }
89
+ .msg.agent code { font-family: var(--font-mono); font-size: 13px;
90
+ background: var(--bg-panel); padding: 2px 6px; border-radius: 4px; }
91
+ .msg.agent pre { font-family: var(--font-mono); font-size: 13px;
92
+ background: var(--bg-panel); padding: 12px 16px; border-radius: 8px;
93
+ overflow-x: auto; margin: 8px 0; border: 1px solid var(--border);
94
+ line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
95
+
96
+ .msg.think { margin-left: 28px; padding: 8px 14px; background: var(--bg-panel);
97
+ border-left: 2px solid var(--teal); border-radius: 0 8px 8px 0;
98
+ font-size: 13px; color: var(--teal); font-style: italic; }
99
+ .msg.tool { margin-left: 32px; padding: 6px 12px; font-size: 12px;
100
+ font-family: var(--font-mono); color: var(--purple); }
101
+ .msg.tool-ok { color: var(--green); margin-left: 44px; font-size: 12px; }
102
+ .msg.tool-err { color: var(--red); margin-left: 44px; font-size: 12px; }
103
+ .msg.error { padding: 10px 16px; background: rgba(224,108,117,0.1);
104
+ border: 1px solid var(--red); border-radius: 8px; color: var(--red);
105
+ font-weight: 600; }
106
+ .msg.status { text-align: center; font-size: 12px; color: var(--comment);
107
+ margin: 8px 0; }
108
+ .msg.sep { text-align: center; font-size: 11px; color: var(--comment);
109
+ border-top: 1px solid var(--border); margin: 16px 0; padding-top: 12px; }
110
+
111
+ /* ── Input Row ── */
112
+ .input-row {
113
+ display: flex; align-items: center; gap: 10px;
114
+ padding: 12px 16px; background: var(--bg-panel);
115
+ border-top: 1px solid var(--border);
116
+ }
117
+ #cmd-input {
118
+ flex: 1; background: var(--bg-input); color: var(--fg);
119
+ border: 1px solid var(--border); border-radius: 24px;
120
+ padding: 10px 18px; font-size: 14px; font-family: var(--font-ui);
121
+ outline: none; transition: border-color var(--transition);
122
+ }
123
+ #cmd-input:focus { border-color: var(--blue); }
124
+ #send-btn {
125
+ background: var(--blue); color: #fff; border: none;
126
+ border-radius: 24px; padding: 10px 24px; font-size: 14px;
127
+ font-weight: 600; cursor: pointer; transition: opacity var(--transition);
128
+ white-space: nowrap;
129
+ }
130
+ #send-btn:hover { opacity: 0.85; }
131
+ #send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
132
+ #stop-btn {
133
+ background: var(--red); color: #fff; border: none;
134
+ border-radius: 24px; padding: 10px 24px; font-size: 14px;
135
+ font-weight: 600; cursor: pointer; transition: opacity var(--transition);
136
+ white-space: nowrap;
137
+ }
138
+ #stop-btn:hover { opacity: 0.85; }
139
+ /* Streaming state: toggle buttons + disable input */
140
+ body:not(.streaming) #stop-btn { display: none; }
141
+ body.streaming #send-btn { display: none; }
142
+ body.streaming #cmd-input { opacity: 0.5; pointer-events: none; }
143
+
144
+ /* ── Command Popup ── */
145
+ .cmd-popup {
146
+ display: none; position: absolute; bottom: 72px; left: 16px; right: 16px;
147
+ background: var(--bg-panel); border: 1px solid var(--border);
148
+ border-radius: 8px; max-height: 240px; overflow-y: auto; z-index: 100;
149
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
150
+ }
151
+ .cmd-popup.show { display: block; }
152
+ .cmd-item { padding: 8px 16px; cursor: pointer; display: flex;
153
+ justify-content: space-between; font-size: 13px; }
154
+ .cmd-item:hover, .cmd-item.active { background: rgba(97,175,239,0.1); }
155
+ .cmd-item .name { font-family: var(--font-mono); color: var(--cyan); }
156
+ .cmd-item .desc { color: var(--comment); font-size: 12px; }
157
+
158
+ /* ── Status Bar ── */
159
+ .status-bar {
160
+ height: 26px; min-height: 26px; background: var(--bg-panel);
161
+ border-top: 1px solid var(--border); display: flex;
162
+ align-items: center; padding: 0 16px; font-size: 11px; color: var(--comment);
163
+ gap: 16px;
164
+ }
165
+ .status-bar span { white-space: nowrap; }
166
+
167
+ /* ═══════════════════════════ Mobile ═══════════════════════════ */
168
+ @media (max-width: 768px) {
169
+ .sidebar { position: fixed; left: 0; top: 0; bottom: 0; z-index: 200;
170
+ transform: translateX(-100%); box-shadow: 4px 0 20px rgba(0,0,0,0.3); }
171
+ .sidebar.open { transform: translateX(0); }
172
+ .hamburger { display: block; }
173
+ .msg { max-width: 95%; }
174
+ .msg.user { margin-left: 4px; }
175
+ #cmd-input { font-size: 16px; } /* prevent iOS zoom on focus */
176
+ .overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4);
177
+ z-index: 199; }
178
+ .overlay.show { display: block; }
179
+ .top-bar .subtitle { display: none; }
180
+ }
@@ -0,0 +1,84 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
8
+ <meta name="theme-color" content="#282C34">
9
+ <title>ATA Coder</title>
10
+ <link rel="stylesheet" href="css/style.css">
11
+ </head>
12
+ <body>
13
+
14
+ <!-- ── Sidebar Overlay (mobile) ── -->
15
+ <div class="overlay" id="overlay"></div>
16
+
17
+ <!-- ── Sidebar ── -->
18
+ <aside class="sidebar" id="sidebar">
19
+ <div class="sidebar-header">
20
+ <div class="logo">⚡ ATA Coder <span id="logo-ver">v2.2.0</span></div>
21
+ </div>
22
+ <div class="sidebar-body">
23
+ <div class="sidebar-section">
24
+ <h4>Model</h4>
25
+ <select id="model-select">
26
+ <option>loading…</option>
27
+ </select>
28
+ </div>
29
+ <div class="sidebar-section">
30
+ <h4>Skills</h4>
31
+ <div id="skill-tags"><span class="skill-tag">general-coder</span></div>
32
+ </div>
33
+ <div class="sidebar-section">
34
+ <h4>Workspace</h4>
35
+ <div class="val" id="ws-path">…</div>
36
+ </div>
37
+ </div>
38
+ <div class="sidebar-footer">
39
+ <div class="shortcut" data-cmd="/clear"><kbd>/clear</kbd> New chat</div>
40
+ <div class="shortcut" data-cmd="/history"><kbd>/history</kbd> Sessions</div>
41
+ <div class="shortcut" data-cmd="/skills"><kbd>/skills</kbd> List skills</div>
42
+ <div class="shortcut" data-cmd="/models"><kbd>/models</kbd> List models</div>
43
+ </div>
44
+ </aside>
45
+
46
+ <!-- ── Main Area ── -->
47
+ <div class="main">
48
+ <!-- Top Bar -->
49
+ <div class="top-bar">
50
+ <button class="hamburger" aria-label="Menu">☰</button>
51
+ <span class="dot ready" id="status-dot"></span>
52
+ <span class="title">ATA Coder</span>
53
+ <span class="subtitle" id="top-subtitle"></span>
54
+ </div>
55
+
56
+ <!-- Chat -->
57
+ <div class="chat" id="chat">
58
+ <div class="msg status">⚡ ATA Coder — AI-powered coding assistant</div>
59
+ <div class="msg status" id="init-info">Connecting…</div>
60
+ </div>
61
+
62
+ <!-- Command Popup -->
63
+ <div class="cmd-popup" id="cmd-popup"></div>
64
+
65
+ <!-- Input Row -->
66
+ <div class="input-row" id="input-row">
67
+ <input type="text" id="cmd-input" placeholder="Ask anything… / for commands"
68
+ autocomplete="off" autocapitalize="off" spellcheck="false">
69
+ <button id="send-btn">Send</button>
70
+ <button id="stop-btn">Stop</button>
71
+ </div>
72
+
73
+ <!-- Status Bar -->
74
+ <div class="status-bar">
75
+ <span id="st-tokens">tokens: ~0</span>
76
+ <span id="st-skill">skill: general-coder</span>
77
+ <span id="st-model">model: …</span>
78
+ <span id="st-time" style="margin-left:auto">0.0s</span>
79
+ </div>
80
+ </div>
81
+
82
+ <script src="js/app.js"></script>
83
+ </body>
84
+ </html>