ata-coder 2.4.3__tar.gz → 2.4.5__tar.gz

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 (140) hide show
  1. {ata_coder-2.4.3/ata_coder.egg-info → ata_coder-2.4.5}/PKG-INFO +21 -3
  2. {ata_coder-2.4.3 → ata_coder-2.4.5}/README.md +20 -2
  3. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent.py +4 -9
  4. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_compact.py +11 -4
  5. {ata_coder-2.4.3 → ata_coder-2.4.5/ata_coder.egg-info}/PKG-INFO +21 -3
  6. {ata_coder-2.4.3 → ata_coder-2.4.5}/ata_coder.egg-info/SOURCES.txt +2 -0
  7. {ata_coder-2.4.3 → ata_coder-2.4.5}/config.py +10 -2
  8. {ata_coder-2.4.3 → ata_coder-2.4.5}/extension.py +38 -38
  9. {ata_coder-2.4.3 → ata_coder-2.4.5}/git_workflow.py +9 -7
  10. {ata_coder-2.4.3 → ata_coder-2.4.5}/main.py +31 -14
  11. {ata_coder-2.4.3 → ata_coder-2.4.5}/mcp_client.py +25 -3
  12. ata_coder-2.4.5/py.typed +0 -0
  13. {ata_coder-2.4.3 → ata_coder-2.4.5}/pyproject.toml +1 -1
  14. {ata_coder-2.4.3 → ata_coder-2.4.5}/safety_guard.py +47 -32
  15. {ata_coder-2.4.3 → ata_coder-2.4.5}/server.py +212 -69
  16. {ata_coder-2.4.3 → ata_coder-2.4.5}/server_session.py +263 -178
  17. {ata_coder-2.4.3 → ata_coder-2.4.5}/server_shell.py +8 -2
  18. {ata_coder-2.4.3 → ata_coder-2.4.5}/settings.py +139 -2
  19. {ata_coder-2.4.3 → ata_coder-2.4.5}/setup_wizard.py +1 -1
  20. {ata_coder-2.4.3 → ata_coder-2.4.5}/sub_agent.py +3 -8
  21. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_server.py +1 -1
  22. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/executor.py +7 -4
  23. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/web.py +60 -7
  24. {ata_coder-2.4.3 → ata_coder-2.4.5}/utils.py +20 -4
  25. {ata_coder-2.4.3 → ata_coder-2.4.5}/LICENSE +0 -0
  26. {ata_coder-2.4.3 → ata_coder-2.4.5}/MANIFEST.in +0 -0
  27. {ata_coder-2.4.3 → ata_coder-2.4.5}/__init__.py +0 -0
  28. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_controller.py +0 -0
  29. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_extension.py +0 -0
  30. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_routing.py +0 -0
  31. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_subsystems.py +0 -0
  32. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_tools.py +0 -0
  33. {ata_coder-2.4.3 → ata_coder-2.4.5}/agent_undo.py +0 -0
  34. {ata_coder-2.4.3 → ata_coder-2.4.5}/anthropic_client.py +0 -0
  35. {ata_coder-2.4.3 → ata_coder-2.4.5}/ata_coder.egg-info/dependency_links.txt +0 -0
  36. {ata_coder-2.4.3 → ata_coder-2.4.5}/ata_coder.egg-info/entry_points.txt +0 -0
  37. {ata_coder-2.4.3 → ata_coder-2.4.5}/ata_coder.egg-info/requires.txt +0 -0
  38. {ata_coder-2.4.3 → ata_coder-2.4.5}/ata_coder.egg-info/top_level.txt +0 -0
  39. {ata_coder-2.4.3 → ata_coder-2.4.5}/change_tracker.py +0 -0
  40. {ata_coder-2.4.3 → ata_coder-2.4.5}/clawd_integration.py +0 -0
  41. {ata_coder-2.4.3 → ata_coder-2.4.5}/commands/__init__.py +0 -0
  42. {ata_coder-2.4.3 → ata_coder-2.4.5}/commands/_core.py +0 -0
  43. {ata_coder-2.4.3 → ata_coder-2.4.5}/commands/_safety.py +0 -0
  44. {ata_coder-2.4.3 → ata_coder-2.4.5}/commands/_settings.py +0 -0
  45. {ata_coder-2.4.3 → ata_coder-2.4.5}/commands/_workflow.py +0 -0
  46. {ata_coder-2.4.3 → ata_coder-2.4.5}/core/__init__.py +0 -0
  47. {ata_coder-2.4.3 → ata_coder-2.4.5}/core/events.py +0 -0
  48. {ata_coder-2.4.3 → ata_coder-2.4.5}/core/queue.py +0 -0
  49. {ata_coder-2.4.3 → ata_coder-2.4.5}/core/state.py +0 -0
  50. {ata_coder-2.4.3 → ata_coder-2.4.5}/event_queue.py +0 -0
  51. {ata_coder-2.4.3 → ata_coder-2.4.5}/extensions/__init__.py +0 -0
  52. {ata_coder-2.4.3 → ata_coder-2.4.5}/extensions/hello_skill.py +0 -0
  53. {ata_coder-2.4.3 → ata_coder-2.4.5}/fool_proof.py +0 -0
  54. {ata_coder-2.4.3 → ata_coder-2.4.5}/gui.py +0 -0
  55. {ata_coder-2.4.3 → ata_coder-2.4.5}/llm_client.py +0 -0
  56. {ata_coder-2.4.3 → ata_coder-2.4.5}/memory.py +0 -0
  57. {ata_coder-2.4.3 → ata_coder-2.4.5}/model_registry.py +0 -0
  58. {ata_coder-2.4.3 → ata_coder-2.4.5}/model_router.py +0 -0
  59. {ata_coder-2.4.3 → ata_coder-2.4.5}/permissions.py +0 -0
  60. {ata_coder-2.4.3 → ata_coder-2.4.5}/privilege.py +0 -0
  61. {ata_coder-2.4.3 → ata_coder-2.4.5}/project.py +0 -0
  62. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompt_template.py +0 -0
  63. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/auto-mode.md +0 -0
  64. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/coding-rules.md +0 -0
  65. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/execution-guardrails.md +0 -0
  66. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/memory-system.md +0 -0
  67. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/output-style.md +0 -0
  68. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/safety.md +0 -0
  69. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/slash-commands.md +0 -0
  70. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/sub-agents.md +0 -0
  71. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/system-reminders.md +0 -0
  72. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/system.md +0 -0
  73. {ata_coder-2.4.3 → ata_coder-2.4.5}/prompts/tool-policy.md +0 -0
  74. {ata_coder-2.4.3 → ata_coder-2.4.5}/repl_theme.py +0 -0
  75. {ata_coder-2.4.3 → ata_coder-2.4.5}/repl_tracker.py +0 -0
  76. {ata_coder-2.4.3 → ata_coder-2.4.5}/repl_ui.py +0 -0
  77. {ata_coder-2.4.3 → ata_coder-2.4.5}/self_correct.py +0 -0
  78. {ata_coder-2.4.3 → ata_coder-2.4.5}/session.py +0 -0
  79. {ata_coder-2.4.3 → ata_coder-2.4.5}/setup.cfg +0 -0
  80. {ata_coder-2.4.3 → ata_coder-2.4.5}/skill_extension.py +0 -0
  81. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/architect/SKILL.md +0 -0
  82. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/code-reviewer/SKILL.md +0 -0
  83. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/codecraft/SKILL.md +0 -0
  84. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/debugger/SKILL.md +0 -0
  85. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/doc-writer/SKILL.md +0 -0
  86. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/general-coder/SKILL.md +0 -0
  87. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/README.md +0 -0
  88. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/SKILL.md +0 -0
  89. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/handler.py +0 -0
  90. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/prompts/system.md +0 -0
  91. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/requirements.txt +0 -0
  92. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/resources/constants.json +0 -0
  93. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/math-calculator/tests/test_handler.py +0 -0
  94. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/security-auditor/SKILL.md +0 -0
  95. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/test-writer/SKILL.md +0 -0
  96. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/README.md +0 -0
  97. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/handler.py +0 -0
  98. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/manifest.json +0 -0
  99. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/prompts/system_prompt.txt +0 -0
  100. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/prompts/user_prompt_template.txt +0 -0
  101. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/requirements.txt +0 -0
  102. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/resources/city_list.json +0 -0
  103. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/resources/error_messages.json +0 -0
  104. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/tests/test_handler.py +0 -0
  105. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills/weather-skill/weather_utils.py +0 -0
  106. {ata_coder-2.4.3 → ata_coder-2.4.5}/skills.py +0 -0
  107. {ata_coder-2.4.3 → ata_coder-2.4.5}/sub_agent_manager.py +0 -0
  108. {ata_coder-2.4.3 → ata_coder-2.4.5}/system_prompt_builder.py +0 -0
  109. {ata_coder-2.4.3 → ata_coder-2.4.5}/task_planner.py +0 -0
  110. {ata_coder-2.4.3 → ata_coder-2.4.5}/terminal.py +0 -0
  111. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_agent.py +0 -0
  112. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_change_tracker.py +0 -0
  113. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_config.py +0 -0
  114. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_event_queue.py +0 -0
  115. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_extension.py +0 -0
  116. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_fibonacci.py +0 -0
  117. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_fool_proof.py +0 -0
  118. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_llm_client.py +0 -0
  119. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_memory.py +0 -0
  120. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_model_registry.py +0 -0
  121. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_permissions.py +0 -0
  122. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_privilege.py +0 -0
  123. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_prompt_template.py +0 -0
  124. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_safety_guard.py +0 -0
  125. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_skill_handlers.py +0 -0
  126. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_sub_agent.py +0 -0
  127. {ata_coder-2.4.3 → ata_coder-2.4.5}/tests/test_tools.py +0 -0
  128. {ata_coder-2.4.3 → ata_coder-2.4.5}/token_counter.py +0 -0
  129. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/__init__.py +0 -0
  130. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/definitions.py +0 -0
  131. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/result.py +0 -0
  132. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/strategy.py +0 -0
  133. {ata_coder-2.4.3 → ata_coder-2.4.5}/tools/subagent.py +0 -0
  134. {ata_coder-2.4.3 → ata_coder-2.4.5}/types.py +0 -0
  135. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/css/style.css +0 -0
  136. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/index.html +0 -0
  137. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/js/app.js +0 -0
  138. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/package-lock.json +0 -0
  139. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/package.json +0 -0
  140. {ata_coder-2.4.3 → ata_coder-2.4.5}/web/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ata-coder
3
- Version: 2.4.3
3
+ Version: 2.4.5
4
4
  Summary: ATA Coder — AI-powered coding assistant
5
5
  Author: ATA Coder Team
6
6
  License-Expression: MIT
@@ -21,13 +21,17 @@ Requires-Dist: pytest-timeout>=2.0; extra == "dev"
21
21
  Requires-Dist: tiktoken>=0.5.0; extra == "dev"
22
22
  Dynamic: license-file
23
23
 
24
- # ATA Coder v2.4.3
24
+ # ATA Coder v2.4.5
25
25
 
26
26
  **AI-powered coding assistant — async, AST-aware, single config file.**
27
27
 
28
28
  [English](#english) | [中文](#中文)
29
29
 
30
- > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
30
+ > **v2.4.5** — 🛡️ **Comprehensive Bug & Security Fix**: 19 bugs fixed — thread safety, SSRF IPv6, rate limiter leak, auth hardening, DRY refactoring, CI coverage. 12 files changed.
31
+ >
32
+ > > **v2.4.4** — 🔒 **Security Hardening**: 19 fixes across safety, storage, and reliability.
33
+ >
34
+ > > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
31
35
  >
32
36
  > > **v2.4.2** — 🐾 **Clawd working state + Window tokens**: Clawd shows 'working' immediately. Status line shows window tokens (~120k) not cumulative (7.8M). Surrogate-safe session saves.
33
37
  >
@@ -51,6 +55,10 @@ Dynamic: license-file
51
55
 
52
56
  ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
53
57
 
58
+ ```bash
59
+ pip install ata-coder
60
+ ```
61
+
54
62
  ### Architecture (v2.3.3)
55
63
 
56
64
  ```
@@ -95,7 +103,12 @@ asyncio Event Loop (single-threaded)
95
103
  ### Quick Start
96
104
 
97
105
  ```bash
106
+ # Install from PyPI (recommended)
107
+ pip install ata-coder
108
+
109
+ # Or install from source (development)
98
110
  pip install -e .
111
+
99
112
  ata # Interactive REPL
100
113
  ata run "Add type hints" # Single task
101
114
  ata server --port 8080 # HTTP API server
@@ -558,7 +571,12 @@ asyncio 事件循环(单线程)
558
571
  ### 快速开始
559
572
 
560
573
  ```bash
574
+ # 从 PyPI 安装(推荐)
575
+ pip install ata-coder
576
+
577
+ # 或从源码安装(开发模式)
561
578
  pip install -e .
579
+
562
580
  ata # 交互模式
563
581
  ata run "添加类型注解" # 单任务
564
582
  ata server --port 8080 # API 服务
@@ -1,10 +1,14 @@
1
- # ATA Coder v2.4.3
1
+ # ATA Coder v2.4.5
2
2
 
3
3
  **AI-powered coding assistant — async, AST-aware, single config file.**
4
4
 
5
5
  [English](#english) | [中文](#中文)
6
6
 
7
- > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
7
+ > **v2.4.5** — 🛡️ **Comprehensive Bug & Security Fix**: 19 bugs fixed — thread safety, SSRF IPv6, rate limiter leak, auth hardening, DRY refactoring, CI coverage. 12 files changed.
8
+ >
9
+ > > **v2.4.4** — 🔒 **Security Hardening**: 19 fixes across safety, storage, and reliability.
10
+ >
11
+ > > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
8
12
  >
9
13
  > > **v2.4.2** — 🐾 **Clawd working state + Window tokens**: Clawd shows 'working' immediately. Status line shows window tokens (~120k) not cumulative (7.8M). Surrogate-safe session saves.
10
14
  >
@@ -28,6 +32,10 @@
28
32
 
29
33
  ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
30
34
 
35
+ ```bash
36
+ pip install ata-coder
37
+ ```
38
+
31
39
  ### Architecture (v2.3.3)
32
40
 
33
41
  ```
@@ -72,7 +80,12 @@ asyncio Event Loop (single-threaded)
72
80
  ### Quick Start
73
81
 
74
82
  ```bash
83
+ # Install from PyPI (recommended)
84
+ pip install ata-coder
85
+
86
+ # Or install from source (development)
75
87
  pip install -e .
88
+
76
89
  ata # Interactive REPL
77
90
  ata run "Add type hints" # Single task
78
91
  ata server --port 8080 # HTTP API server
@@ -535,7 +548,12 @@ asyncio 事件循环(单线程)
535
548
  ### 快速开始
536
549
 
537
550
  ```bash
551
+ # 从 PyPI 安装(推荐)
552
+ pip install ata-coder
553
+
554
+ # 或从源码安装(开发模式)
538
555
  pip install -e .
556
+
539
557
  ata # 交互模式
540
558
  ata run "添加类型注解" # 单任务
541
559
  ata server --port 8080 # API 服务
@@ -27,8 +27,7 @@ import time
27
27
  from typing import Any, Callable
28
28
 
29
29
  from .config import AppConfig
30
- from .llm_client import LLMClient, SYSTEM_PROMPT
31
- from .anthropic_client import AnthropicClient
30
+ from .llm_client import SYSTEM_PROMPT
32
31
  from .tools import ToolExecutor, TOOL_DEFINITIONS, ToolResult
33
32
  from .types import Message
34
33
  from .agent_subsystems import AgentSubsystems
@@ -84,13 +83,9 @@ class CoderAgent(CompactionMixin, ToolExecutionMixin,
84
83
  ):
85
84
  self.config = config or AppConfig.load()
86
85
 
87
- # Choose client: Anthropic or OpenAI format
88
- if self.config.llm.use_anthropic:
89
- self.llm = AnthropicClient(self.config.llm)
90
- self._use_anthropic = True
91
- else:
92
- self.llm = LLMClient(self.config.llm)
93
- self._use_anthropic = False
86
+ # Choose client: Anthropic or OpenAI format (factory eliminates duplication)
87
+ from .utils import create_llm_client
88
+ self.llm, self._use_anthropic = create_llm_client(self.config.llm)
94
89
 
95
90
  self.tools = tool_executor or ToolExecutor(self.config.agent)
96
91
 
@@ -4,6 +4,7 @@ import json
4
4
  import logging
5
5
 
6
6
  from .types import Message
7
+ from .token_counter import TokenCounter
7
8
  from .clawd_integration import get_clawd
8
9
  from .model_router import get_subagent_model
9
10
  logger = logging.getLogger(__name__)
@@ -35,7 +36,11 @@ class CompactionMixin:
35
36
  recent_tokens = 0
36
37
  for msg in reversed(all_but_system):
37
38
  msg_tokens = self._estimate_message_tokens(msg)
38
- if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET and recent:
39
+ if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET:
40
+ if not recent:
41
+ # Single huge message — include it anyway but stop after
42
+ recent.insert(0, msg)
43
+ recent_tokens += msg_tokens
39
44
  break
40
45
  recent.insert(0, msg)
41
46
  recent_tokens += msg_tokens
@@ -79,8 +84,11 @@ class CompactionMixin:
79
84
  recent_tokens = 0
80
85
  for msg in reversed(all_but_system):
81
86
  msg_tokens = self._estimate_message_tokens(msg)
82
- if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET and recent:
83
- # Stop — we've filled the recent budget
87
+ if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET:
88
+ if not recent:
89
+ # Single huge message — include it anyway but stop after
90
+ recent.insert(0, msg)
91
+ recent_tokens += msg_tokens
84
92
  break
85
93
  recent.insert(0, msg)
86
94
  recent_tokens += msg_tokens
@@ -120,7 +128,6 @@ class CompactionMixin:
120
128
 
121
129
  def _estimate_message_tokens(self, msg: Message) -> int:
122
130
  """Rough token estimate for a single message (via TokenCounter)."""
123
- from .token_counter import TokenCounter
124
131
  model = getattr(self.llm.config, 'model', '')
125
132
  return TokenCounter.for_model(model).count_tokens([msg])
126
133
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ata-coder
3
- Version: 2.4.3
3
+ Version: 2.4.5
4
4
  Summary: ATA Coder — AI-powered coding assistant
5
5
  Author: ATA Coder Team
6
6
  License-Expression: MIT
@@ -21,13 +21,17 @@ Requires-Dist: pytest-timeout>=2.0; extra == "dev"
21
21
  Requires-Dist: tiktoken>=0.5.0; extra == "dev"
22
22
  Dynamic: license-file
23
23
 
24
- # ATA Coder v2.4.3
24
+ # ATA Coder v2.4.5
25
25
 
26
26
  **AI-powered coding assistant — async, AST-aware, single config file.**
27
27
 
28
28
  [English](#english) | [中文](#中文)
29
29
 
30
- > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
30
+ > **v2.4.5** — 🛡️ **Comprehensive Bug & Security Fix**: 19 bugs fixed — thread safety, SSRF IPv6, rate limiter leak, auth hardening, DRY refactoring, CI coverage. 12 files changed.
31
+ >
32
+ > > **v2.4.4** — 🔒 **Security Hardening**: 19 fixes across safety, storage, and reliability.
33
+ >
34
+ > > **v2.4.3** — 🧠 **Comprehensive Refactoring**: Memory overhaul, unified token counting, safety pipeline, tool call optimization. 60+ issues fixed.
31
35
  >
32
36
  > > **v2.4.2** — 🐾 **Clawd working state + Window tokens**: Clawd shows 'working' immediately. Status line shows window tokens (~120k) not cumulative (7.8M). Surrogate-safe session saves.
33
37
  >
@@ -51,6 +55,10 @@ Dynamic: license-file
51
55
 
52
56
  ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
53
57
 
58
+ ```bash
59
+ pip install ata-coder
60
+ ```
61
+
54
62
  ### Architecture (v2.3.3)
55
63
 
56
64
  ```
@@ -95,7 +103,12 @@ asyncio Event Loop (single-threaded)
95
103
  ### Quick Start
96
104
 
97
105
  ```bash
106
+ # Install from PyPI (recommended)
107
+ pip install ata-coder
108
+
109
+ # Or install from source (development)
98
110
  pip install -e .
111
+
99
112
  ata # Interactive REPL
100
113
  ata run "Add type hints" # Single task
101
114
  ata server --port 8080 # HTTP API server
@@ -558,7 +571,12 @@ asyncio 事件循环(单线程)
558
571
  ### 快速开始
559
572
 
560
573
  ```bash
574
+ # 从 PyPI 安装(推荐)
575
+ pip install ata-coder
576
+
577
+ # 或从源码安装(开发模式)
561
578
  pip install -e .
579
+
562
580
  ata # 交互模式
563
581
  ata run "添加类型注解" # 单任务
564
582
  ata server --port 8080 # API 服务
@@ -29,6 +29,7 @@ permissions.py
29
29
  privilege.py
30
30
  project.py
31
31
  prompt_template.py
32
+ py.typed
32
33
  pyproject.toml
33
34
  repl_theme.py
34
35
  repl_tracker.py
@@ -79,6 +80,7 @@ utils.py
79
80
  ./privilege.py
80
81
  ./project.py
81
82
  ./prompt_template.py
83
+ ./py.typed
82
84
  ./repl_theme.py
83
85
  ./repl_tracker.py
84
86
  ./repl_ui.py
@@ -176,7 +176,7 @@ class AppConfig:
176
176
 
177
177
  llm: LLMConfig = field(default_factory=LLMConfig)
178
178
  agent: AgentConfig = field(default_factory=AgentConfig)
179
- effort: str = field(default_factory=lambda: _from_settings("effort_level", "medium"))
179
+ effort: str = field(default_factory=lambda: (_from_settings("effort_level") or "medium"))
180
180
 
181
181
  @classmethod
182
182
  def load(cls) -> "AppConfig":
@@ -231,6 +231,9 @@ def _from_settings(attr: str, default: Any = "") -> Any:
231
231
  logger.debug("Settings property %r not found, using default %r", attr, default)
232
232
  return default
233
233
  except Exception:
234
+ # Catch-all for unexpected error types (e.g., OSError on corrupt file,
235
+ # TypeError from malformed data). These are logged at WARNING with full
236
+ # traceback so they don't go unnoticed, but the system stays running.
234
237
  logger = logging.getLogger(__name__)
235
238
  logger.warning(
236
239
  "Failed to read settings.%s — using default %r. "
@@ -246,7 +249,12 @@ def _safe_temperature() -> float:
246
249
 
247
250
 
248
251
  def _settings_api_key() -> str:
249
- return _from_settings("api_key", "")
252
+ """Resolve API key with tiered fallback (OS keychain → env vars → settings.json)."""
253
+ try:
254
+ from .settings import resolve_api_key
255
+ return resolve_api_key()
256
+ except ImportError:
257
+ return _from_settings("api_key", "")
250
258
 
251
259
 
252
260
  def _settings_base_url() -> str:
@@ -1,14 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- 正式扩展 API — 统一的插件系统。
3
+ Extension API — unified plugin system.
4
4
 
5
- 提供:
6
- - Extension 基类: 生命周期钩子 (load / unload / activate / deactivate)
7
- - ExtensionManager: 扩展发现、注册、激活、卸载
8
- - @extension 装饰器: 声明式注册
9
- - ExtensionPoint: 标记类, 用于定义可扩展点
5
+ Provides:
6
+ - Extension base class: lifecycle hooks (load / unload / activate / deactivate)
7
+ - ExtensionManager: extension discovery, registration, activation, unloading
8
+ - @extension decorator: declarative registration
9
+ - ExtensionPoint: marker class for defining extension points
10
10
 
11
- 使用示例:
11
+ Usage example:
12
12
 
13
13
  from .extension import Extension, extension
14
14
 
@@ -21,7 +21,7 @@
21
21
  def get_prompt(self) -> str:
22
22
  return "You are an expert in..."
23
23
 
24
- # 注册到全局管理器
24
+ # Register with the global manager
25
25
  from .extension import get_extension_manager
26
26
  get_extension_manager().register(MySkill())
27
27
  """
@@ -88,17 +88,17 @@ class ExtensionType:
88
88
 
89
89
  class ExtensionPoint:
90
90
  """
91
- 标记一个可扩展点。扩展可以通过名称向该点注册回调。
91
+ Marker for an extension point. Extensions can register callbacks by name.
92
92
 
93
- 用法:
93
+ Usage:
94
94
 
95
- # 定义一个扩展点
95
+ # Define an extension point
96
96
  ON_SYSTEM_PROMPT = ExtensionPoint("system_prompt")
97
97
 
98
- # 扩展注册回调
98
+ # Extension registers a callback
99
99
  ON_SYSTEM_PROMPT.register(my_callable)
100
100
 
101
- # 触发所有已注册的回调
101
+ # Fire all registered callbacks
102
102
  results = ON_SYSTEM_PROMPT.trigger(prompt="...")
103
103
  """
104
104
 
@@ -181,23 +181,23 @@ class ExtensionPoint:
181
181
 
182
182
  class Extension(ABC):
183
183
  """
184
- 扩展基类。所有 ATA Coder 扩展的基类。
184
+ Extension base class. Base class for all ATA Coder extensions.
185
185
 
186
- 生命周期:
187
- 1. __init__() — 实例化
188
- 2. on_load(manager) — 被管理器加载时调用
189
- 3. on_activate() — 被激活时调用
190
- 4. on_deactivate() — 被停用时调用
191
- 5. on_unload() — 被卸载时调用
186
+ Lifecycle:
187
+ 1. __init__() — instantiation
188
+ 2. on_load(manager) — called when loaded by the manager
189
+ 3. on_activate() — called when activated
190
+ 4. on_deactivate() — called when deactivated
191
+ 5. on_unload() — called when unloaded
192
192
 
193
- 子类必须设置:
193
+ Subclasses MUST set:
194
194
  - meta: ExtensionMeta
195
195
 
196
- 子类可选覆盖:
196
+ Subclasses MAY override:
197
197
  - on_load() / on_unload() / on_activate() / on_deactivate()
198
- - get_tools() → 返回工具定义列表
199
- - get_prompt() → 返回系统提示字符串
200
- - validate() → 验证扩展是否可用
198
+ - get_tools() → list of tool definitions
199
+ - get_prompt() → system prompt string
200
+ - validate() → verify extension is usable
201
201
  """
202
202
 
203
203
  meta: ExtensionMeta
@@ -208,27 +208,27 @@ class Extension(ABC):
208
208
  cls.meta = ExtensionMeta(name=cls.__name__)
209
209
 
210
210
  def on_load(self, manager: "ExtensionManager") -> None:
211
- """扩展被加载到管理器时调用。"""
211
+ """Extension was loaded into a manager."""
212
212
 
213
213
  def on_unload(self) -> None:
214
- """扩展被卸载时调用。"""
214
+ """Extension is being unloaded."""
215
215
 
216
216
  def on_activate(self) -> None:
217
- """扩展被激活时调用。"""
217
+ """Extension was activated."""
218
218
 
219
219
  def on_deactivate(self) -> None:
220
- """扩展被停用时调用。"""
220
+ """Extension was deactivated."""
221
221
 
222
222
  def get_tools(self) -> list[dict[str, Any]]:
223
- """返回此扩展提供的工具定义列表。"""
223
+ """Return the tool definitions provided by this extension."""
224
224
  return []
225
225
 
226
226
  def get_prompt(self) -> str:
227
- """返回此扩展提供的系统提示片段。"""
227
+ """Return the system prompt fragment provided by this extension."""
228
228
  return ""
229
229
 
230
230
  def get_middleware(self) -> list[Callable]:
231
- """返回此扩展提供的中间件列表。"""
231
+ """Return the middleware list provided by this extension."""
232
232
  return []
233
233
 
234
234
  def validate(self) -> tuple[bool, str]:
@@ -250,9 +250,9 @@ class Extension(ABC):
250
250
 
251
251
  class ExtensionManager:
252
252
  """
253
- 扩展管理器发现、加载、激活和管理扩展。
253
+ Extension manager discovers, loads, activates, and manages extensions.
254
254
 
255
- 用法:
255
+ Usage:
256
256
 
257
257
  mgr = ExtensionManager()
258
258
  mgr.discover("./extensions/")
@@ -587,9 +587,9 @@ def extension(
587
587
  **kwargs: Any,
588
588
  ) -> Callable:
589
589
  """
590
- 类装饰器声明一个扩展。
590
+ Class decorator declare an extension.
591
591
 
592
- 用法:
592
+ Usage:
593
593
 
594
594
  @extension(name="my-skill", version="1.0.0",
595
595
  tags=["skill"], priority=10)
@@ -641,7 +641,7 @@ _extension_manager: ExtensionManager | None = None
641
641
 
642
642
 
643
643
  def get_extension_manager() -> ExtensionManager:
644
- """获取全局扩展管理器单例。"""
644
+ """Get the global ExtensionManager singleton."""
645
645
  global _extension_manager
646
646
  if _extension_manager is None:
647
647
  _extension_manager = ExtensionManager()
@@ -649,6 +649,6 @@ def get_extension_manager() -> ExtensionManager:
649
649
 
650
650
 
651
651
  def reset_extension_manager() -> None:
652
- """重置全局扩展管理器(主要用于测试)"""
652
+ """Reset the global extension manager (mainly for testing)."""
653
653
  global _extension_manager
654
654
  _extension_manager = None
@@ -134,13 +134,15 @@ class GitWorkflow:
134
134
  status.modified += 1
135
135
  status.clean = (status.staged == 0 and status.modified == 0 and status.untracked == 0)
136
136
 
137
- # Ahead/behind
138
- _, ahead_str, _ = _run_git(["rev-list", "--count", "@{u}..HEAD"], self.cwd, timeout=10)
139
- if ahead_str and ahead_str.isdigit():
140
- status.ahead = int(ahead_str)
141
- _, behind_str, _ = _run_git(["rev-list", "--count", "HEAD..@{u}"], self.cwd, timeout=10)
142
- if behind_str and behind_str.isdigit():
143
- status.behind = int(behind_str)
137
+ # Ahead/behind — only if upstream is configured
138
+ has_upstream, _, _ = _run_git(["rev-parse", "--abbrev-ref", "@{u}"], self.cwd, timeout=10)
139
+ if has_upstream == 0:
140
+ _, ahead_str, _ = _run_git(["rev-list", "--count", "@{u}..HEAD"], self.cwd, timeout=10)
141
+ if ahead_str and ahead_str.isdigit():
142
+ status.ahead = int(ahead_str)
143
+ _, behind_str, _ = _run_git(["rev-list", "--count", "HEAD..@{u}"], self.cwd, timeout=10)
144
+ if behind_str and behind_str.isdigit():
145
+ status.behind = int(behind_str)
144
146
 
145
147
  # Last commit
146
148
  _, last, _ = _run_git(["log", "-1", "--format=%h %s"], self.cwd)
@@ -44,7 +44,7 @@ if sys.platform == 'win32':
44
44
  _patched_init.__ata_patched__ = True
45
45
  _sp.Popen.__init__ = _patched_init
46
46
 
47
- __version__ = "2.4.3"
47
+ __version__ = "2.4.5"
48
48
 
49
49
  import asyncio
50
50
  import logging
@@ -122,17 +122,29 @@ def _init_subsystems(config, **kwargs) -> dict:
122
122
  workspace = config.agent.workspace_dir
123
123
  errors: list[str] = []
124
124
 
125
+ def _init_subsystem(name, factory, critical=True):
126
+ """Initialize a single subsystem with consistent error handling.
127
+
128
+ Critical subsystems raise on failure; non-critical log a warning
129
+ and return None.
130
+ """
131
+ try:
132
+ return factory()
133
+ except Exception as e:
134
+ if critical:
135
+ logger.exception("%s init failed", name)
136
+ errors.append(f" {name}: {e}")
137
+ else:
138
+ logger.warning("%s unavailable: %s", name, e)
139
+ return None
140
+
125
141
  # ── Critical: agent cannot function without these ──────────────────
126
142
  for name, factory in [
127
143
  ("skills", lambda: get_skill_manager(kwargs.get("skills_dir"))),
128
144
  ("memory", lambda: get_memory_store(kwargs.get("memory_dir"))),
129
145
  ("permissions", lambda: PermissionStore()),
130
146
  ]:
131
- try:
132
- result[name] = factory()
133
- except Exception as e:
134
- logger.exception("%s init failed", name)
135
- errors.append(f" {name}: {e}")
147
+ result[name] = _init_subsystem(name, factory, critical=True)
136
148
 
137
149
  # ── Non-critical: nice-to-have, degrade gracefully ─────────────────
138
150
  for name, factory in [
@@ -140,11 +152,7 @@ def _init_subsystems(config, **kwargs) -> dict:
140
152
  ("templates", lambda: _try_init_templates(kwargs.get("prompts_dir"))),
141
153
  ("project", lambda: ProjectDetector(workspace).detect()),
142
154
  ]:
143
- try:
144
- result[name] = factory()
145
- except Exception as e:
146
- logger.warning("%s unavailable: %s", name, e)
147
- result[name] = None
155
+ result[name] = _init_subsystem(name, factory, critical=False)
148
156
 
149
157
  # MCP is special: only init if config provided
150
158
  result["mcp"] = _try_init_mcp(kwargs.get("mcp_config"))
@@ -163,11 +171,20 @@ def _try_init_templates(prompts_dir: str | None):
163
171
  return TemplateManager(prompts_dir)
164
172
 
165
173
 
166
- def _try_init_mcp(mcp_config: str | None):
167
- if not mcp_config:
174
+ def _try_init_mcp(mcp_config_path: str | None):
175
+ """Initialize MCP client from a JSON/YAML config file path.
176
+
177
+ Args:
178
+ mcp_config_path: Path to an MCP configuration file (JSON or YAML).
179
+ If None or empty, MCP is not initialized.
180
+
181
+ Returns:
182
+ MCPClient instance or None.
183
+ """
184
+ if not mcp_config_path:
168
185
  return None
169
186
  from .mcp_client import MCPClient, load_mcp_config
170
- return MCPClient(load_mcp_config(mcp_config))
187
+ return MCPClient(load_mcp_config(mcp_config_path))
171
188
 
172
189
 
173
190
  # ── Config override ─────────────────────────────────────────────────────
@@ -995,6 +995,25 @@ class MCPClient:
995
995
  """Register a callback for health check failures."""
996
996
  self._on_health_fail = callback
997
997
 
998
+ async def reconnect_server(self, name: str) -> bool:
999
+ """Attempt to reconnect a failed MCP server. Returns True on success."""
1000
+ conn = self._connections.get(name)
1001
+ if not conn:
1002
+ return False
1003
+ logger.info("[%s] Attempting reconnection...", name)
1004
+ try:
1005
+ await conn.stop()
1006
+ except Exception:
1007
+ pass
1008
+ try:
1009
+ await conn.start()
1010
+ self._register_server_tools(name, conn)
1011
+ logger.info("[%s] Reconnected successfully", name)
1012
+ return True
1013
+ except Exception as e:
1014
+ logger.warning("[%s] Reconnection failed: %s", name, e)
1015
+ return False
1016
+
998
1017
  def start_health_monitor(self, interval: float = 60.0) -> None:
999
1018
  """Start periodic health checks (ping every N seconds)."""
1000
1019
  if self._health_running:
@@ -1021,16 +1040,19 @@ class MCPClient:
1021
1040
  break
1022
1041
  for name, conn in list(self._connections.items()):
1023
1042
  try:
1024
- if not await conn.ping(timeout=10):
1025
- logger.warning("[%s] Health check failed: no response", name)
1043
+ alive = await conn.ping(timeout=10)
1044
+ if not alive:
1045
+ logger.warning("[%s] Health check failed — attempting reconnect", name)
1026
1046
  if self._on_health_fail:
1027
1047
  self._on_health_fail(name)
1048
+ await self.reconnect_server(name)
1028
1049
  except asyncio.CancelledError:
1029
1050
  raise
1030
1051
  except Exception as e:
1031
- logger.warning("[%s] Health check error: %s", name, e)
1052
+ logger.warning("[%s] Health check error: %s — attempting reconnect", name, e)
1032
1053
  if self._on_health_fail:
1033
1054
  self._on_health_fail(name)
1055
+ await self.reconnect_server(name)
1034
1056
 
1035
1057
  # ── Properties ──────────────────────────────────────────────────────────
1036
1058
 
File without changes
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ata-coder"
7
- version = "2.4.3"
7
+ version = "2.4.5"
8
8
  description = "ATA Coder — AI-powered coding assistant"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"