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.
- ata_coder/__init__.py +1 -0
- ata_coder/agent.py +874 -0
- ata_coder/agent_compact.py +190 -0
- ata_coder/agent_controller.py +218 -0
- ata_coder/agent_extension.py +69 -0
- ata_coder/agent_routing.py +105 -0
- ata_coder/agent_subsystems.py +72 -0
- ata_coder/agent_tools.py +318 -0
- ata_coder/agent_undo.py +63 -0
- ata_coder/anthropic_client.py +465 -0
- ata_coder/change_tracker.py +368 -0
- ata_coder/clawd_integration.py +574 -0
- ata_coder/commands/__init__.py +128 -0
- ata_coder/commands/_core.py +184 -0
- ata_coder/commands/_safety.py +95 -0
- ata_coder/commands/_settings.py +241 -0
- ata_coder/commands/_workflow.py +451 -0
- ata_coder/commands.py +974 -0
- ata_coder/config.py +257 -0
- ata_coder/core/__init__.py +35 -0
- ata_coder/core/events.py +73 -0
- ata_coder/core/queue.py +85 -0
- ata_coder/core/state.py +17 -0
- ata_coder/event_queue.py +5 -0
- ata_coder/extension.py +654 -0
- ata_coder/extensions/__init__.py +1 -0
- ata_coder/extensions/hello_skill.py +47 -0
- ata_coder/fool_proof.py +295 -0
- ata_coder/git_workflow.py +371 -0
- ata_coder/gui.py +511 -0
- ata_coder/llm_client.py +543 -0
- ata_coder/main.py +814 -0
- ata_coder/mcp_client.py +1095 -0
- ata_coder/memory.py +539 -0
- ata_coder/model_registry.py +134 -0
- ata_coder/model_router.py +105 -0
- ata_coder/permissions.py +274 -0
- ata_coder/privilege.py +464 -0
- ata_coder/project.py +273 -0
- ata_coder/prompt_template.py +423 -0
- ata_coder/prompts/auto-mode.md +7 -0
- ata_coder/prompts/coding-rules.md +40 -0
- ata_coder/prompts/execution-guardrails.md +14 -0
- ata_coder/prompts/memory-system.md +24 -0
- ata_coder/prompts/output-style.md +23 -0
- ata_coder/prompts/safety.md +17 -0
- ata_coder/prompts/slash-commands.md +24 -0
- ata_coder/prompts/sub-agents.md +38 -0
- ata_coder/prompts/system-reminders.md +17 -0
- ata_coder/prompts/system.md +105 -0
- ata_coder/prompts/tool-policy.md +46 -0
- ata_coder/repl_theme.py +99 -0
- ata_coder/repl_tracker.py +89 -0
- ata_coder/repl_ui.py +1214 -0
- ata_coder/safety_guard.py +434 -0
- ata_coder/self_correct.py +346 -0
- ata_coder/server.py +882 -0
- ata_coder/server_session.py +159 -0
- ata_coder/server_shell.py +129 -0
- ata_coder/session.py +431 -0
- ata_coder/settings.py +439 -0
- ata_coder/setup_wizard.py +136 -0
- ata_coder/skill_extension.py +92 -0
- ata_coder/skills/architect/SKILL.md +42 -0
- ata_coder/skills/code-reviewer/SKILL.md +37 -0
- ata_coder/skills/codecraft/SKILL.md +452 -0
- ata_coder/skills/debugger/SKILL.md +45 -0
- ata_coder/skills/doc-writer/SKILL.md +36 -0
- ata_coder/skills/general-coder/SKILL.md +76 -0
- ata_coder/skills/math-calculator/README.md +40 -0
- ata_coder/skills/math-calculator/SKILL.md +59 -0
- ata_coder/skills/math-calculator/handler.py +103 -0
- ata_coder/skills/math-calculator/prompts/system.md +8 -0
- ata_coder/skills/math-calculator/requirements.txt +2 -0
- ata_coder/skills/math-calculator/resources/constants.json +8 -0
- ata_coder/skills/math-calculator/tests/test_handler.py +53 -0
- ata_coder/skills/security-auditor/SKILL.md +40 -0
- ata_coder/skills/test-writer/SKILL.md +36 -0
- ata_coder/skills/weather-skill/README.md +45 -0
- ata_coder/skills/weather-skill/handler.py +76 -0
- ata_coder/skills/weather-skill/manifest.json +48 -0
- ata_coder/skills/weather-skill/prompts/system_prompt.txt +9 -0
- ata_coder/skills/weather-skill/prompts/user_prompt_template.txt +3 -0
- ata_coder/skills/weather-skill/requirements.txt +1 -0
- ata_coder/skills/weather-skill/resources/city_list.json +17 -0
- ata_coder/skills/weather-skill/resources/error_messages.json +7 -0
- ata_coder/skills/weather-skill/tests/test_handler.py +28 -0
- ata_coder/skills/weather-skill/weather_utils.py +50 -0
- ata_coder/skills.py +1014 -0
- ata_coder/sub_agent.py +273 -0
- ata_coder/sub_agent_manager.py +203 -0
- ata_coder/system_prompt_builder.py +146 -0
- ata_coder/task_planner.py +391 -0
- ata_coder/terminal.py +318 -0
- ata_coder/test_runner.py +219 -0
- ata_coder/thread_supervisor.py +195 -0
- ata_coder/tool_defs.py +335 -0
- ata_coder/tools/__init__.py +11 -0
- ata_coder/tools/definitions.py +335 -0
- ata_coder/tools/executor.py +1036 -0
- ata_coder/tools/result.py +26 -0
- ata_coder/tools/subagent.py +332 -0
- ata_coder/tools/web.py +361 -0
- ata_coder/tools.py +1576 -0
- ata_coder/types.py +92 -0
- ata_coder/utils.py +113 -0
- ata_coder/web/css/style.css +180 -0
- ata_coder/web/index.html +84 -0
- ata_coder/web/js/app.js +489 -0
- ata_coder/web/package-lock.json +25 -0
- ata_coder/web/package.json +10 -0
- ata_coder/web/tsconfig.json +13 -0
- ata_coder-2.4.2.dist-info/METADATA +799 -0
- ata_coder-2.4.2.dist-info/RECORD +118 -0
- ata_coder-2.4.2.dist-info/WHEEL +5 -0
- ata_coder-2.4.2.dist-info/entry_points.txt +2 -0
- ata_coder-2.4.2.dist-info/licenses/LICENSE +21 -0
- ata_coder-2.4.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
"""Command handlers — auto-split from commands.py."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_commands(r: Any) -> None:
|
|
9
|
+
"""Register this group's commands on the registry."""
|
|
10
|
+
# ── Sessions ────────────────────────────────────────────────────────────
|
|
11
|
+
# ── Sessions ───────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
@r.register("/save", "Save session", "session")
|
|
14
|
+
def cmd_save(arg: str, ctx: dict) -> bool:
|
|
15
|
+
print(f"Saved: {ctx['agent'].save_session(arg)}")
|
|
16
|
+
return True
|
|
17
|
+
|
|
18
|
+
@r.register("/sessions", "List all sessions", "session")
|
|
19
|
+
@r.register("/history", "Search/browse history", "session")
|
|
20
|
+
def cmd_history(arg: str, ctx: dict) -> bool:
|
|
21
|
+
sm = ctx.get("session_mgr")
|
|
22
|
+
if not sm:
|
|
23
|
+
print("Session manager not available.")
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
# Get current workspace for filtering
|
|
27
|
+
agent = ctx["agent"]
|
|
28
|
+
ws = getattr(agent.tools, "workspace", None)
|
|
29
|
+
workspace = str(ws) if ws else None
|
|
30
|
+
|
|
31
|
+
if arg:
|
|
32
|
+
# Try to resume by index number
|
|
33
|
+
if arg.isdigit():
|
|
34
|
+
sessions = sm.list_sessions(limit=50, workspace=workspace)
|
|
35
|
+
idx = int(arg) - 1
|
|
36
|
+
if 0 <= idx < len(sessions):
|
|
37
|
+
meta = sessions[idx]
|
|
38
|
+
msgs = sm.load(meta.id)
|
|
39
|
+
if msgs:
|
|
40
|
+
agent._state.messages = msgs
|
|
41
|
+
print(f"Resumed: {meta.id}")
|
|
42
|
+
print(f" {meta.summary[:100]}")
|
|
43
|
+
print(f" Messages: {len(msgs)}, Tools: {meta.tool_call_count}")
|
|
44
|
+
return True
|
|
45
|
+
print(f"No session at index {arg} (found {len(sessions)} sessions)")
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
# Try to resume by session ID
|
|
49
|
+
msgs = sm.load(arg)
|
|
50
|
+
if msgs:
|
|
51
|
+
agent._state.messages = msgs
|
|
52
|
+
meta = sm.get_meta(arg)
|
|
53
|
+
print(f"Resumed: {arg} ({len(msgs)} msgs)")
|
|
54
|
+
if meta:
|
|
55
|
+
print(f" {meta.summary[:100]}")
|
|
56
|
+
else:
|
|
57
|
+
# Search by keyword
|
|
58
|
+
results = sm.search_sessions(arg, workspace=workspace)
|
|
59
|
+
if results:
|
|
60
|
+
print(f"Search '{arg}': {len(results)} matches")
|
|
61
|
+
for i, meta in enumerate(results[:10], 1):
|
|
62
|
+
date = meta.created[:10] if meta.created else "?"
|
|
63
|
+
print(f" [{i}] {date} | {meta.skill:15s} | {meta.summary[:60]}")
|
|
64
|
+
else:
|
|
65
|
+
print(f"No matches for: {arg}")
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
# No args — list recent sessions for this workspace
|
|
69
|
+
sessions = sm.list_sessions(limit=20, workspace=workspace)
|
|
70
|
+
ws_name = Path(workspace).name if workspace else "all"
|
|
71
|
+
|
|
72
|
+
if not sessions:
|
|
73
|
+
print(f"No sessions for workspace: {ws_name}")
|
|
74
|
+
print("Try /history <keyword> to search all sessions.")
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
print(f"History ({ws_name}/):")
|
|
78
|
+
for i, meta in enumerate(sessions, 1):
|
|
79
|
+
date = meta.created[:10] if meta.created else "?"
|
|
80
|
+
icon = {"general-coder": "💻", "debugger": "🐛", "code-reviewer": "🔍",
|
|
81
|
+
"architect": "🏗️", "test-writer": "🧪"}.get(meta.skill, "📝")
|
|
82
|
+
print(f" [{i}] {icon} {date} | {meta.skill:15s} | {meta.summary[:60]}")
|
|
83
|
+
if meta.tool_call_count:
|
|
84
|
+
print(f" {meta.message_count} msgs, {meta.tool_call_count} tools")
|
|
85
|
+
print("\n/history <number> to resume, /history <keyword> to search")
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
@r.register("/resume", "Resume or list sessions", "session")
|
|
89
|
+
def cmd_resume(arg: str, ctx: dict) -> bool:
|
|
90
|
+
"""
|
|
91
|
+
/resume → list sessions in current workspace
|
|
92
|
+
/resume <hash> → resume a specific session by ID or hash
|
|
93
|
+
"""
|
|
94
|
+
sm = ctx.get("session_mgr")
|
|
95
|
+
if not sm:
|
|
96
|
+
print("Session storage not available.")
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
# ── No arg: list sessions in current workspace ───────────────
|
|
100
|
+
if not arg:
|
|
101
|
+
import hashlib
|
|
102
|
+
try:
|
|
103
|
+
workspace = str(ctx["agent"].tools.workspace) if ctx.get("agent") else ""
|
|
104
|
+
except Exception:
|
|
105
|
+
workspace = ""
|
|
106
|
+
ws_hash = hashlib.sha256(workspace.encode()).hexdigest()[:8]
|
|
107
|
+
sessions = sm.list_sessions(limit=50)
|
|
108
|
+
matches = [s for s in sessions if s.id.startswith(ws_hash)]
|
|
109
|
+
|
|
110
|
+
if not matches:
|
|
111
|
+
print(f"No sessions in this workspace.\n hash: {ws_hash}")
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
print(f"\nSessions ({workspace}):\n")
|
|
115
|
+
for i, s in enumerate(matches):
|
|
116
|
+
parts = s.id.split("-")
|
|
117
|
+
resume_hash = parts[-1] if len(parts) >= 3 else s.id[-8:]
|
|
118
|
+
title = s.summary[:60] if s.summary else "(untitled)"
|
|
119
|
+
print(f" [{i+1}] {title}")
|
|
120
|
+
print(f" ata --resume {resume_hash}")
|
|
121
|
+
print(f" {s.updated} {s.message_count} msgs\n")
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
# ── Hash arg: resume a specific session ─────────────────────
|
|
125
|
+
msgs = sm.load(arg)
|
|
126
|
+
if msgs:
|
|
127
|
+
ctx["agent"]._state.messages = msgs
|
|
128
|
+
ctx["agent"]._current_session_id = sm.resolve_session_id(arg) or arg
|
|
129
|
+
print(f"Resumed: {arg} ({len(msgs)} msgs)")
|
|
130
|
+
else:
|
|
131
|
+
print(f"Not found: {arg}")
|
|
132
|
+
return True
|
|
133
|
+
|
|
134
|
+
@r.register("/export", "Export session", "session")
|
|
135
|
+
def cmd_export(arg: str, ctx: dict) -> bool:
|
|
136
|
+
sm = ctx.get("session_mgr")
|
|
137
|
+
if not sm or not arg:
|
|
138
|
+
print("Usage: /export <id> [path]")
|
|
139
|
+
return True
|
|
140
|
+
parts = arg.split(maxsplit=1)
|
|
141
|
+
sid = parts[0]
|
|
142
|
+
out = parts[1] if len(parts) > 1 else None
|
|
143
|
+
md = sm.export_markdown(sid, out)
|
|
144
|
+
if md:
|
|
145
|
+
print(f"Exported {sid}" + (f" to {out}" if out else ""))
|
|
146
|
+
else:
|
|
147
|
+
print(f"Not found: {sid}")
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ── Git ────────────────────────────────────────────────────────────
|
|
152
|
+
# ── Git ────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
@r.register("/git", "Git operations", "git")
|
|
155
|
+
def cmd_git(arg: str, ctx: dict) -> bool:
|
|
156
|
+
git = ctx["agent"].git
|
|
157
|
+
if not git:
|
|
158
|
+
print("Not available.")
|
|
159
|
+
return True
|
|
160
|
+
if arg == "status" or not arg:
|
|
161
|
+
s = git.get_status()
|
|
162
|
+
print(f"Branch: {s.branch}\nStatus: {s.summary()}")
|
|
163
|
+
elif arg == "diff":
|
|
164
|
+
print(git.get_diff())
|
|
165
|
+
elif arg == "log":
|
|
166
|
+
print(git.get_log())
|
|
167
|
+
elif arg.startswith("commit"):
|
|
168
|
+
ok, out = git.commit(arg[6:].strip())
|
|
169
|
+
print(out)
|
|
170
|
+
elif arg.startswith("branch "):
|
|
171
|
+
ok, out = git.create_branch(arg[7:].strip())
|
|
172
|
+
print(out)
|
|
173
|
+
elif arg == "branch" or arg == "branches":
|
|
174
|
+
print(git.list_branches())
|
|
175
|
+
elif arg == "undo":
|
|
176
|
+
ok, out = git.undo_commit()
|
|
177
|
+
print(out)
|
|
178
|
+
elif arg == "stash":
|
|
179
|
+
git.stash()
|
|
180
|
+
print("Stashed.")
|
|
181
|
+
elif arg == "unstash":
|
|
182
|
+
git.stash_pop()
|
|
183
|
+
print("Unstashed.")
|
|
184
|
+
elif arg == "summary":
|
|
185
|
+
print(git.session_summary())
|
|
186
|
+
else:
|
|
187
|
+
print("/git [status|diff|log|commit|branch|undo|branches|stash|unstash|summary]")
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ── Review & Fix ────────────────────────────────────────────────────────────
|
|
192
|
+
# ── Review & Fix ────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
@r.register("/review", "AI code review of current changes", "review")
|
|
195
|
+
async def cmd_review(arg: str, ctx: dict) -> bool:
|
|
196
|
+
agent = ctx["agent"]
|
|
197
|
+
git = agent.git
|
|
198
|
+
diff_text = git.get_diff() if git else "(no git repo)"
|
|
199
|
+
if not diff_text or diff_text == "(no changes)":
|
|
200
|
+
print("No changes to review.")
|
|
201
|
+
return True
|
|
202
|
+
task = (
|
|
203
|
+
"Review the following code changes. Output a structured report:\n"
|
|
204
|
+
"## Issues Found\n"
|
|
205
|
+
"For each: severity (critical/high/medium/low), file, line, problem, fix\n\n"
|
|
206
|
+
f"```diff\n{diff_text[:8000]}\n```"
|
|
207
|
+
)
|
|
208
|
+
print("Reviewing changes...\n")
|
|
209
|
+
await agent.run(task, stream=True)
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
@r.register("/fix", "AI apply review suggestions", "review")
|
|
213
|
+
async def cmd_fix(arg: str, ctx: dict) -> bool:
|
|
214
|
+
agent = ctx["agent"]
|
|
215
|
+
git = agent.git
|
|
216
|
+
diff_text = git.get_diff() if git else "(no git repo)"
|
|
217
|
+
if not diff_text or diff_text == "(no changes)":
|
|
218
|
+
print("No changes to fix.")
|
|
219
|
+
return True
|
|
220
|
+
severity = arg if arg else "all"
|
|
221
|
+
task = (
|
|
222
|
+
f"Review this diff and fix issues. Focus on {severity} severity issues.\n"
|
|
223
|
+
"Apply the fixes directly to the files.\n\n"
|
|
224
|
+
f"```diff\n{diff_text[:8000]}\n```"
|
|
225
|
+
)
|
|
226
|
+
print(f"Fixing {severity} issues...\n")
|
|
227
|
+
await agent.run(task, stream=True)
|
|
228
|
+
return True
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ── Planner ────────────────────────────────────────────────────────────
|
|
232
|
+
# ── Planner ────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
@r.register("/plan", "Task plan", "plan")
|
|
235
|
+
def cmd_plan(arg: str, ctx: dict) -> bool:
|
|
236
|
+
p = ctx["agent"].planner
|
|
237
|
+
if arg:
|
|
238
|
+
agent = ctx["agent"]
|
|
239
|
+
plan = p.decompose(arg, llm_client=agent.llm)
|
|
240
|
+
print(plan.to_prompt())
|
|
241
|
+
elif p.current_plan:
|
|
242
|
+
print(p.current_plan.to_prompt())
|
|
243
|
+
else:
|
|
244
|
+
print("Usage: /plan <task>")
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
@r.register("/tasks", "List plan tasks", "plan")
|
|
248
|
+
def cmd_tasks(arg: str, ctx: dict) -> bool:
|
|
249
|
+
p = ctx["agent"].planner
|
|
250
|
+
if not p.current_plan:
|
|
251
|
+
print("No active plan.")
|
|
252
|
+
return True
|
|
253
|
+
print(p.current_plan.progress_bar())
|
|
254
|
+
icons = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]", "failed": "[!]", "skipped": "[-]"}
|
|
255
|
+
for t in p.current_plan.subtasks:
|
|
256
|
+
print(f" {icons.get(t.status.value, '[?]')} #{t.id} {t.subject}")
|
|
257
|
+
return True
|
|
258
|
+
|
|
259
|
+
@r.register("/plan-next", "Start next task", "plan")
|
|
260
|
+
def cmd_plan_next(arg: str, ctx: dict) -> bool:
|
|
261
|
+
t = ctx["agent"].planner.auto_advance()
|
|
262
|
+
print(f"Starting: #{t.id} {t.subject}" if t else "No pending tasks.")
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
@r.register("/plan-done", "Complete task", "plan")
|
|
266
|
+
def cmd_plan_done(arg: str, ctx: dict) -> bool:
|
|
267
|
+
p = ctx["agent"].planner
|
|
268
|
+
try:
|
|
269
|
+
tid = int(arg) if arg else 0
|
|
270
|
+
except ValueError:
|
|
271
|
+
tid = 0
|
|
272
|
+
if tid and p.current_plan:
|
|
273
|
+
t = p.complete_task(tid)
|
|
274
|
+
elif p.current_plan and p.current_plan.current:
|
|
275
|
+
t = p.complete_task(p.current_plan.current.id)
|
|
276
|
+
else:
|
|
277
|
+
print("No task to complete.")
|
|
278
|
+
return True
|
|
279
|
+
print(f"Completed: #{t.id} {t.subject}" if t else "Failed.")
|
|
280
|
+
return True
|
|
281
|
+
|
|
282
|
+
@r.register("/retry", "Self-correct stats", "plan")
|
|
283
|
+
def cmd_retry(arg: str, ctx: dict) -> bool:
|
|
284
|
+
sc = ctx["agent"].self_correct
|
|
285
|
+
s = sc.stats
|
|
286
|
+
print(f"Retries: {s['total_retries']} Successful: {s['successful_retries']} "
|
|
287
|
+
f"Auto-fix rate: {s['auto_fix_rate']}")
|
|
288
|
+
return True
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# ── Extensions ────────────────────────────────────────────────────────────
|
|
292
|
+
# ── Extensions ─────────────────────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
@r.register("/extensions", "List extensions", "extension")
|
|
295
|
+
def cmd_extensions(arg: str, ctx: dict) -> bool:
|
|
296
|
+
agent = ctx["agent"]
|
|
297
|
+
if not hasattr(agent, "ext_mgr") or not agent.ext_mgr:
|
|
298
|
+
print("Extension manager not available.")
|
|
299
|
+
return True
|
|
300
|
+
active_names = {e.meta.name for e in agent.ext_mgr.list_active()}
|
|
301
|
+
for ext in agent.ext_mgr.list_extensions():
|
|
302
|
+
status = "[active]" if ext.meta.name in active_names else "[loaded]"
|
|
303
|
+
print(f" {status} {ext.meta.name} v{ext.meta.version} — {ext.meta.description[:60]}")
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
@r.register("/ext-activate", "Activate extension", "extension")
|
|
307
|
+
def cmd_ext_activate(arg: str, ctx: dict) -> bool:
|
|
308
|
+
if not arg:
|
|
309
|
+
print("Usage: /ext-activate <name>")
|
|
310
|
+
return True
|
|
311
|
+
agent = ctx["agent"]
|
|
312
|
+
if not hasattr(agent, "ext_mgr") or not agent.ext_mgr:
|
|
313
|
+
print("Extension manager not available.")
|
|
314
|
+
return True
|
|
315
|
+
ok = agent.ext_mgr.activate(arg)
|
|
316
|
+
print(f"Activated: {arg}" if ok else f"Failed: {arg}")
|
|
317
|
+
return True
|
|
318
|
+
|
|
319
|
+
@r.register("/ext-deactivate", "Deactivate extension", "extension")
|
|
320
|
+
def cmd_ext_deactivate(arg: str, ctx: dict) -> bool:
|
|
321
|
+
if not arg:
|
|
322
|
+
print("Usage: /ext-deactivate <name>")
|
|
323
|
+
return True
|
|
324
|
+
agent = ctx["agent"]
|
|
325
|
+
if not hasattr(agent, "ext_mgr") or not agent.ext_mgr:
|
|
326
|
+
print("Extension manager not available.")
|
|
327
|
+
return True
|
|
328
|
+
ok = agent.ext_mgr.deactivate(arg)
|
|
329
|
+
print(f"Deactivated: {arg}" if ok else f"Failed: {arg}")
|
|
330
|
+
return True
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ── Sub-agents ────────────────────────────────────────────────────────────
|
|
334
|
+
# ── Sub-agents ─────────────────────────────────────────────────────
|
|
335
|
+
|
|
336
|
+
@r.register("/sub-agents", "List sub-agents", "subagent")
|
|
337
|
+
def cmd_sub_agents(arg: str, ctx: dict) -> bool:
|
|
338
|
+
agent = ctx["agent"]
|
|
339
|
+
mgr = getattr(agent, "_sub_agent_mgr", None)
|
|
340
|
+
if not mgr:
|
|
341
|
+
print("SubAgentManager not available.")
|
|
342
|
+
return True
|
|
343
|
+
agents = mgr.list_all()
|
|
344
|
+
if not agents:
|
|
345
|
+
print("No sub-agents.")
|
|
346
|
+
return True
|
|
347
|
+
icons = {"running": "R", "done": "D", "failed": "F", "cancelled": "C", "idle": "I"}
|
|
348
|
+
for a in agents:
|
|
349
|
+
icon = icons.get(a.status, "?")
|
|
350
|
+
print(f" [{icon}] {a.id} — {a.status} (tool_calls={a.tool_call_count})")
|
|
351
|
+
if a.status == "done" and a.result:
|
|
352
|
+
print(f" result: {a.result[:100]}...")
|
|
353
|
+
return True
|
|
354
|
+
|
|
355
|
+
@r.register("/sub-cancel", "Cancel sub-agent", "subagent")
|
|
356
|
+
def cmd_sub_cancel(arg: str, ctx: dict) -> bool:
|
|
357
|
+
if not arg:
|
|
358
|
+
print("Usage: /sub-cancel <agent_id|all>")
|
|
359
|
+
return True
|
|
360
|
+
agent = ctx["agent"]
|
|
361
|
+
mgr = getattr(agent, "_sub_agent_mgr", None)
|
|
362
|
+
if not mgr:
|
|
363
|
+
print("SubAgentManager not available.")
|
|
364
|
+
return True
|
|
365
|
+
if arg == "all":
|
|
366
|
+
mgr.cancel_all()
|
|
367
|
+
print("All sub-agents cancelled.")
|
|
368
|
+
else:
|
|
369
|
+
ok = mgr.cancel(arg)
|
|
370
|
+
print(f"Cancelled: {arg}" if ok else f"Not found: {arg}")
|
|
371
|
+
return True
|
|
372
|
+
|
|
373
|
+
@r.register("/config", "Show current configuration", "settings")
|
|
374
|
+
def cmd_config(arg: str, ctx: dict) -> bool:
|
|
375
|
+
agent = ctx["agent"]
|
|
376
|
+
cfg = agent.config
|
|
377
|
+
print(f" Model: {cfg.llm.model}")
|
|
378
|
+
print(f" API Base: {cfg.llm.base_url}")
|
|
379
|
+
print(f" Workspace: {cfg.agent.workspace_dir}")
|
|
380
|
+
print(f" Max Tokens: {cfg.llm.max_output_tokens}")
|
|
381
|
+
print(f" Temperature: {cfg.llm.temperature}")
|
|
382
|
+
print(f" Thinking: {cfg.llm.thinking_strength}")
|
|
383
|
+
print(f" Max Context: {cfg.agent.max_context_tokens:,}")
|
|
384
|
+
print(f" Max Tools: {cfg.agent.max_tool_calls}")
|
|
385
|
+
print(f" Session: {agent.session_id}")
|
|
386
|
+
token_est = agent.get_token_estimate()
|
|
387
|
+
print(f" Tokens used: ~{token_est:,}")
|
|
388
|
+
print(f" Anthropic: {'yes' if getattr(agent, '_use_anthropic', False) else 'no'}")
|
|
389
|
+
if agent.skills and agent.skills.active_skill:
|
|
390
|
+
print(f" Active skill: {agent.skills.active_skill.name}")
|
|
391
|
+
return True
|
|
392
|
+
|
|
393
|
+
@r.register("/status", "Show conversation window", "basic")
|
|
394
|
+
def cmd_status(arg: str, ctx: dict) -> bool:
|
|
395
|
+
agent = ctx["agent"]
|
|
396
|
+
ctx["ui"].show_context(
|
|
397
|
+
total_messages=len(agent._state.messages),
|
|
398
|
+
tool_calls=agent._state.tool_call_count,
|
|
399
|
+
skill=agent.skills.active_skill.name if agent.skills and agent.skills.active_skill else "default",
|
|
400
|
+
model=ctx["config"].llm.model,
|
|
401
|
+
estimated_tokens=agent.get_token_estimate(),
|
|
402
|
+
max_tokens=ctx["config"].agent.max_context_tokens,
|
|
403
|
+
)
|
|
404
|
+
return True
|
|
405
|
+
|
|
406
|
+
@r.register("/vision", "Analyze image with multimodal vision", "settings")
|
|
407
|
+
def cmd_vision(arg: str, ctx: dict) -> bool:
|
|
408
|
+
if not arg:
|
|
409
|
+
print("Usage: /vision <image_path> [prompt]")
|
|
410
|
+
print(" Analyze an image using the configured vision model.")
|
|
411
|
+
print(" Configure in ~/.ata_coder/settings.json:")
|
|
412
|
+
print(' {"vision": {"model": "...", "api_base": "...", "api_key": "..."}}')
|
|
413
|
+
print(" Or set VISION_MODEL / VISION_API_KEY env vars.")
|
|
414
|
+
print(" Falls back to main API config if not set.")
|
|
415
|
+
return True
|
|
416
|
+
parts = arg.split(maxsplit=2)
|
|
417
|
+
image_path = parts[0]
|
|
418
|
+
prompt = parts[1] if len(parts) > 1 else "Describe this image in detail."
|
|
419
|
+
agent = ctx["agent"]
|
|
420
|
+
result = agent.tools._tool_analyze_image(image_path, prompt)
|
|
421
|
+
if result.success:
|
|
422
|
+
print(result.output)
|
|
423
|
+
else:
|
|
424
|
+
print(f"Error: {result.error}")
|
|
425
|
+
return True
|
|
426
|
+
|
|
427
|
+
@r.register("/auto-skill", "Smart skill detection (LLM router)", "skill")
|
|
428
|
+
def cmd_auto_skill(arg: str, ctx: dict) -> bool:
|
|
429
|
+
if not arg:
|
|
430
|
+
print("Usage: /auto-skill <task description>")
|
|
431
|
+
print(" Uses LLM to intelligently route to the best skill.")
|
|
432
|
+
return True
|
|
433
|
+
agent = ctx["agent"]
|
|
434
|
+
skill_mgr = ctx.get("skill_mgr")
|
|
435
|
+
if not skill_mgr or not agent:
|
|
436
|
+
print("Skills or agent not available.")
|
|
437
|
+
return True
|
|
438
|
+
results = skill_mgr.detect_skills_smart(arg, max_results=5, llm_client=agent.llm)
|
|
439
|
+
if not results:
|
|
440
|
+
print("No matching skills found.")
|
|
441
|
+
return True
|
|
442
|
+
print(f"Smart routing for: {arg[:80]}...")
|
|
443
|
+
print(f"{'Skill':<22} {'Confidence':>10}")
|
|
444
|
+
print("-" * 34)
|
|
445
|
+
for skill, conf in results:
|
|
446
|
+
bar = "█" * int(conf * 10) + "░" * (10 - int(conf * 10))
|
|
447
|
+
print(f"{skill.name:<22} {bar} {conf:.0%}")
|
|
448
|
+
return True
|
|
449
|
+
|
|
450
|
+
return r
|
|
451
|
+
|