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,391 @@
1
+ """
2
+ Task Planner — automatic decomposition of complex requests.
3
+
4
+ When the user says "Build a REST API with auth, tests, and docs",
5
+ the planner breaks it into ordered subtasks:
6
+
7
+ 1. Set up project structure
8
+ 2. Implement auth module
9
+ 3. Create API endpoints
10
+ 4. Write tests
11
+ 5. Add documentation
12
+
13
+ Features:
14
+ - Automatic decomposition via LLM (or pattern-based fallback)
15
+ - Dependency ordering
16
+ - Progress tracking
17
+ - Parallel subtask marking
18
+ - Status: pending → in_progress → completed → failed
19
+ """
20
+
21
+ import json
22
+ import logging
23
+ import re
24
+ import time
25
+ from dataclasses import dataclass, field
26
+ from enum import Enum
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ # ═══════════════════════════════════════════════════════════════════════════════
32
+ # Data model
33
+ # ═══════════════════════════════════════════════════════════════════════════════
34
+
35
+ class TaskStatus(Enum):
36
+ PENDING = "pending"
37
+ IN_PROGRESS = "in_progress"
38
+ COMPLETED = "completed"
39
+ FAILED = "failed"
40
+ SKIPPED = "skipped"
41
+
42
+
43
+ @dataclass
44
+ class SubTask:
45
+ id: int
46
+ subject: str
47
+ description: str = ""
48
+ status: TaskStatus = TaskStatus.PENDING
49
+ depends_on: list[int] = field(default_factory=list)
50
+ parallel_ok: bool = False # Can run in parallel with siblings
51
+ tool_count: int = 0
52
+ started_at: float = 0.0
53
+ completed_at: float = 0.0
54
+ error: str = ""
55
+ result_summary: str = ""
56
+
57
+ @property
58
+ def elapsed(self) -> float:
59
+ if self.started_at == 0:
60
+ return 0
61
+ end = self.completed_at if self.completed_at > 0 else time.time()
62
+ return end - self.started_at
63
+
64
+ def to_dict(self) -> dict:
65
+ return {
66
+ "id": self.id, "subject": self.subject,
67
+ "description": self.description, "status": self.status.value,
68
+ "depends_on": self.depends_on, "parallel_ok": self.parallel_ok,
69
+ }
70
+
71
+
72
+ @dataclass
73
+ class Plan:
74
+ task_id: str
75
+ title: str
76
+ subtasks: list[SubTask] = field(default_factory=list)
77
+ created_at: str = ""
78
+
79
+ @property
80
+ def completed(self) -> int:
81
+ return sum(1 for t in self.subtasks if t.status == TaskStatus.COMPLETED)
82
+
83
+ @property
84
+ def total(self) -> int:
85
+ return len(self.subtasks)
86
+
87
+ @property
88
+ def progress_pct(self) -> float:
89
+ if self.total == 0:
90
+ return 0
91
+ return (self.completed / self.total) * 100
92
+
93
+ @property
94
+ def current(self) -> SubTask | None:
95
+ """Get the current in-progress task."""
96
+ for t in self.subtasks:
97
+ if t.status == TaskStatus.IN_PROGRESS:
98
+ return t
99
+ return None
100
+
101
+ def next_pending(self) -> SubTask | None:
102
+ """Get the next task that can be started (dependencies satisfied)."""
103
+ for t in self.subtasks:
104
+ if t.status != TaskStatus.PENDING:
105
+ continue
106
+ # Check dependencies
107
+ deps_met = all(
108
+ self._get(d).status == TaskStatus.COMPLETED
109
+ for d in t.depends_on
110
+ )
111
+ if deps_met:
112
+ return t
113
+ return None
114
+
115
+ def _get(self, task_id: int) -> SubTask | None:
116
+ for t in self.subtasks:
117
+ if t.id == task_id:
118
+ return t
119
+ return None
120
+
121
+ def progress_bar(self, width: int = 30) -> str:
122
+ filled = int(self.progress_pct / 100 * width)
123
+ bar = "█" * filled + "░" * (width - filled)
124
+ return f"[{self.completed}/{self.total}] {bar} {self.progress_pct:.0f}%"
125
+
126
+ def to_prompt(self) -> str:
127
+ """Format the plan as a section for the system prompt."""
128
+ lines = [f"## Task Plan: {self.title}"]
129
+ lines.append(f"Progress: {self.progress_bar()}")
130
+ lines.append("")
131
+ for t in self.subtasks:
132
+ icon = {
133
+ TaskStatus.PENDING: "⬜",
134
+ TaskStatus.IN_PROGRESS: "🔄",
135
+ TaskStatus.COMPLETED: "✅",
136
+ TaskStatus.FAILED: "❌",
137
+ TaskStatus.SKIPPED: "⏭️",
138
+ }.get(t.status, "❓")
139
+ deps = f" (depends on: {t.depends_on})" if t.depends_on else ""
140
+ lines.append(f"{icon} {t.subject}{deps}")
141
+ if t.status == TaskStatus.IN_PROGRESS:
142
+ lines.append(" → Currently working on this")
143
+ elif t.status == TaskStatus.FAILED and t.error:
144
+ lines.append(f" → Failed: {t.error[:100]}")
145
+ return "\n".join(lines)
146
+
147
+
148
+ # ═══════════════════════════════════════════════════════════════════════════════
149
+ # Task Planner
150
+ # ═══════════════════════════════════════════════════════════════════════════════
151
+
152
+ class TaskPlanner:
153
+ """
154
+ Decomposes complex user requests into ordered subtasks.
155
+
156
+ Uses pattern-based decomposition as the primary method
157
+ (LLM-based decomposition can be added as an enhancement).
158
+ """
159
+
160
+ # Keywords that signal complex multi-step tasks
161
+ COMPLEX_MARKERS = [
162
+ "build", "create", "implement", "add", "set up",
163
+ "and also", "with", "including", "that has", "which includes",
164
+ "full stack", "complete", "end-to-end", "entire",
165
+ ]
166
+
167
+ def __init__(self):
168
+ self._current_plan: Plan | None = None
169
+ self._plan_history: list[Plan] = []
170
+
171
+ # ── Decomposition ───────────────────────────────────────────────────
172
+
173
+ def decompose(self, task: str, llm_client=None) -> Plan:
174
+ """
175
+ Break a task into subtasks.
176
+
177
+ Args:
178
+ task: The user's task description
179
+ llm_client: Optional LLM client for smart decomposition
180
+
181
+ Returns a Plan with ordered subtasks.
182
+ """
183
+ plan_id = time.strftime("%Y%m%d-%H%M%S")
184
+
185
+ # If LLM is available, use it for smarter decomposition
186
+ if llm_client:
187
+ subtasks = self._llm_decompose(task, llm_client)
188
+ else:
189
+ subtasks = self._pattern_decompose(task)
190
+
191
+ plan = Plan(
192
+ task_id=plan_id,
193
+ title=task[:100],
194
+ subtasks=subtasks,
195
+ created_at=time.strftime("%Y-%m-%dT%H:%M:%SZ"),
196
+ )
197
+ self._current_plan = plan
198
+ self._plan_history.append(plan)
199
+ return plan
200
+
201
+ def _llm_decompose(self, task: str, llm_client) -> list[SubTask]:
202
+ """Use LLM to intelligently decompose a task."""
203
+ prompt = f"""Break this coding task into ordered subtasks. Return ONLY a JSON array.
204
+
205
+ Task: {task}
206
+
207
+ Rules:
208
+ - 3-7 subtasks
209
+ - Each has: subject (short), description (1 sentence), depends_on (list of IDs that must complete first), parallel_ok (true if can run alongside siblings with no deps)
210
+ - Order logically: setup → core → polish → test
211
+ - Dependencies: later tasks depend on earlier ones
212
+
213
+ Format:
214
+ [{{"subject":"...", "description":"...", "depends_on":[1], "parallel_ok":false}}, ...]"""
215
+
216
+ try:
217
+ response = llm_client.simple_chat(prompt)
218
+ # Extract JSON array from response
219
+ match = re.search(r"\[.*\]", response, re.DOTALL)
220
+ if match:
221
+ data = json.loads(match.group())
222
+ subtasks = []
223
+ for i, item in enumerate(data, 1):
224
+ subtasks.append(SubTask(
225
+ id=i,
226
+ subject=item.get("subject", f"Step {i}"),
227
+ description=item.get("description", ""),
228
+ depends_on=item.get("depends_on", []),
229
+ parallel_ok=item.get("parallel_ok", False),
230
+ ))
231
+ if subtasks:
232
+ return subtasks
233
+ except Exception as e:
234
+ logger.warning("LLM decomposition failed: %s, using pattern fallback", e)
235
+
236
+ return self._pattern_decompose(task)
237
+
238
+ def _pattern_decompose(self, task: str) -> list[SubTask]:
239
+ """Pattern-based task decomposition (no LLM required)."""
240
+ task_lower = task.lower()
241
+ subtasks = []
242
+ task_id = 1
243
+
244
+ # Detect common patterns
245
+ patterns = [
246
+ # (keywords in task, subtask subject, description, depends_on)
247
+ (["project", "structure", "setup", "init", "scaffold"],
248
+ "Set up project structure", "Initialize project structure and dependencies", []),
249
+ (["model", "schema", "database", "migration", "entity"],
250
+ "Define data models/schema", "Create database models, schemas, or entity definitions", [1]),
251
+ (["auth", "login", "authentication", "jwt", "oauth", "session"],
252
+ "Implement authentication", "Add user authentication and authorization", [1]),
253
+ (["api", "endpoint", "route", "controller", "handler", "rest"],
254
+ "Create API endpoints", "Implement REST API routes and handlers", [1]),
255
+ (["service", "business logic", "domain"],
256
+ "Implement business logic", "Core service/business logic layer", [2]),
257
+ (["ui", "frontend", "component", "page", "view", "template"],
258
+ "Build UI components", "Create frontend components and views", [1]),
259
+ (["test", "spec", "unit test", "integration test"],
260
+ "Write tests", "Add unit and integration tests", []),
261
+ (["doc", "readme", "documentation", "comment"],
262
+ "Add documentation", "Write documentation and code comments", []),
263
+ (["deploy", "docker", "ci/cd", "pipeline"],
264
+ "Configure deployment", "Set up deployment and CI/CD pipeline", [1]),
265
+ (["error handling", "validation", "logging"],
266
+ "Add error handling & validation", "Implement error handling, input validation, and logging", [1]),
267
+ (["config", "environment", "settings", ".env"],
268
+ "Configure environment", "Set up configuration and environment variables", [1]),
269
+ (["refactor", "clean up", "optimize"],
270
+ "Refactor and optimize", "Clean up code and optimize performance", [1]),
271
+ ]
272
+
273
+ matched_ids = set()
274
+ for keywords, subject, desc, deps in patterns:
275
+ if any(kw in task_lower for kw in keywords):
276
+ # Adjust dependency IDs
277
+ adjusted_deps = [d for d in deps if d in matched_ids] if deps else []
278
+ if not deps and task_id > 1:
279
+ adjusted_deps = [task_id - 1] # sequential by default
280
+
281
+ subtasks.append(SubTask(
282
+ id=task_id, subject=subject, description=desc,
283
+ depends_on=adjusted_deps,
284
+ ))
285
+ matched_ids.add(task_id)
286
+ task_id += 1
287
+
288
+ # If no patterns matched or only 1 matched, create generic decomposition
289
+ if len(subtasks) < 2:
290
+ if any(m in task_lower for m in ["and", ",", "also", "then", "after"]):
291
+ # Try to split on natural language breaks
292
+ parts = re.split(r",\s*(?:and\s+|then\s+|also\s+)?", task)
293
+ parts = [p.strip() for p in parts if len(p.strip()) > 10]
294
+ for i, part in enumerate(parts[:6], 1):
295
+ deps = [i - 1] if i > 1 else []
296
+ subtasks.append(SubTask(
297
+ id=i, subject=part[:80],
298
+ depends_on=deps,
299
+ ))
300
+ else:
301
+ # Simple sequential breakdown
302
+ subtasks = [
303
+ SubTask(id=1, subject="Analyze requirements", description="Understand what needs to be done"),
304
+ SubTask(id=2, subject="Implement solution", description="Write the code", depends_on=[1]),
305
+ SubTask(id=3, subject="Test and verify", description="Run tests and verify correctness", depends_on=[2]),
306
+ ]
307
+
308
+ return subtasks
309
+
310
+ # ── Plan management ─────────────────────────────────────────────────
311
+
312
+ @property
313
+ def current_plan(self) -> Plan | None:
314
+ return self._current_plan
315
+
316
+ def start_task(self, task_id: int) -> SubTask | None:
317
+ """Mark a task as in-progress."""
318
+ if not self._current_plan:
319
+ return None
320
+ for t in self._current_plan.subtasks:
321
+ if t.id == task_id and t.status == TaskStatus.PENDING:
322
+ t.status = TaskStatus.IN_PROGRESS
323
+ t.started_at = time.time()
324
+ return t
325
+ return None
326
+
327
+ def complete_task(self, task_id: int, result: str = "") -> SubTask | None:
328
+ """Mark a task as completed."""
329
+ if not self._current_plan:
330
+ return None
331
+ for t in self._current_plan.subtasks:
332
+ if t.id == task_id and t.status == TaskStatus.IN_PROGRESS:
333
+ t.status = TaskStatus.COMPLETED
334
+ t.completed_at = time.time()
335
+ t.result_summary = result
336
+ return t
337
+ return None
338
+
339
+ def fail_task(self, task_id: int, error: str = "") -> SubTask | None:
340
+ """Mark a task as failed."""
341
+ if not self._current_plan:
342
+ return None
343
+ for t in self._current_plan.subtasks:
344
+ if t.id == task_id:
345
+ t.status = TaskStatus.FAILED
346
+ t.error = error
347
+ return t
348
+ return None
349
+
350
+ def skip_task(self, task_id: int) -> SubTask | None:
351
+ """Skip a task."""
352
+ if not self._current_plan:
353
+ return None
354
+ for t in self._current_plan.subtasks:
355
+ if t.id == task_id and t.status == TaskStatus.PENDING:
356
+ t.status = TaskStatus.SKIPPED
357
+ return t
358
+ return None
359
+
360
+ def finish_plan(self) -> Plan | None:
361
+ """Archive the current plan."""
362
+ plan = self._current_plan
363
+ self._current_plan = None
364
+ return plan
365
+
366
+ def get_status(self) -> str:
367
+ """Get a status string for the UI."""
368
+ if not self._current_plan:
369
+ return "No active plan."
370
+ return self._current_plan.progress_bar()
371
+
372
+ def get_context_for_prompt(self) -> str:
373
+ """Get plan context for injection into the system prompt."""
374
+ if not self._current_plan:
375
+ return ""
376
+ return "\n" + self._current_plan.to_prompt()
377
+
378
+ def auto_advance(self) -> SubTask | None:
379
+ """Automatically start the next pending task. Returns it or None."""
380
+ if not self._current_plan:
381
+ return None
382
+ # Check if there's already an in-progress task
383
+ current = self._current_plan.current
384
+ if current:
385
+ return current
386
+
387
+ next_task = self._current_plan.next_pending()
388
+ if next_task:
389
+ self.start_task(next_task.id)
390
+ return next_task
391
+ return None