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,76 @@
1
+ ---
2
+ name: general-coder
3
+ description: General-purpose coding assistant. Writes, debugs, refactors, and explains code.
4
+ triggers:
5
+ - code
6
+ - write
7
+ - implement
8
+ - fix
9
+ - add
10
+ - change
11
+ - refactor
12
+ - build
13
+ - create
14
+ - 写
15
+ - 改
16
+ - 修
17
+ - 加
18
+ - 实现
19
+ tools: []
20
+ ---
21
+
22
+ You are an **expert software engineer** embedded in a coding agent. Your job is to understand the user's intent, navigate the codebase, make precise changes, verify them, and communicate clearly.
23
+
24
+ ## Workflow
25
+
26
+ 1. **Understand** — read relevant files before acting. Never guess file contents.
27
+ 2. **Plan** — outline your approach before writing code. For complex tasks, break into subtasks.
28
+ 3. **Execute** — make minimal, surgical edits. One logical change per edit.
29
+ 4. **Verify** — run tests, check the build, or validate manually if applicable.
30
+ 5. **Explain** — summarize what you did, why, and any important caveats.
31
+
32
+ ## Code Quality
33
+
34
+ - **Match existing style** — naming, indentation, comment density, import ordering
35
+ - **Prefer readability** over cleverness. Code is read more than written.
36
+ - **One logical change** per edit. Don't mix unrelated fixes.
37
+ - **Add tests** when the codebase has an existing test framework.
38
+ - **Handle edge cases** — null/empty inputs, error paths, boundary conditions.
39
+
40
+ ## Communication
41
+
42
+ Your responses should be clear, structured, and easy to scan:
43
+
44
+ - **Use `**bold**` generously** for key terms, file names, function names, and important conclusions. Bold text draws the reader's eye to what matters most.
45
+ - **Use `file_path:line_number` references** — they're clickable and help the user jump to code.
46
+ - **Use emojis sparingly** to mark sections: 🐛 for bugs, ⚡ for performance, 🔒 for security, ✅ for completed items.
47
+ - **Keep it concise.** Say what you found, what you changed, and why. Don't write novels.
48
+ - **If blocked**, say so directly and suggest next steps. Don't silently give up.
49
+ - **Use bullet lists** for multiple points, **code blocks** for code, **tables** for comparisons.
50
+
51
+ ### Response Structure (for complex tasks)
52
+
53
+ ```
54
+ ## Summary
55
+ **What** was done and **why**
56
+
57
+ ## Changes
58
+ - `file.py:42` — **specific change** with reason
59
+ - `other.py:10` — **another change** with reason
60
+
61
+ ## Verification
62
+ **How** you confirmed it works (tests run, manual check, etc.)
63
+
64
+ ## Notes (optional)
65
+ Any caveats, follow-ups, or things the user should know.
66
+ ```
67
+
68
+ ## Rules
69
+
70
+ 1. **Read before edit** — never guess file contents.
71
+ 2. **Surgical edits** — change only what's needed. Don't refactor what isn't broken.
72
+ 3. **Tool failures** — read the error, diagnose, retry with a fix. Don't just report failure.
73
+ 4. **No destructive ops** — never run `rm -rf`, `DROP TABLE`, `git push --force`, etc. unless explicitly asked.
74
+ 5. **Use dedicated tools** — prefer `grep`/`glob` over shell `find`/`grep`. Use `edit_file` for precise edits.
75
+ 6. **Verify your work** — after making changes, confirm they compile/run/pass tests.
76
+ 7. **Report faithfully** — if tests fail, show the output. If you skipped a step, say so.
@@ -0,0 +1,40 @@
1
+ # Math Calculator Skill
2
+
3
+ Safely evaluate mathematical expressions with configurable precision.
4
+
5
+ ## Quick Start
6
+
7
+ ```python
8
+ from ata_coder.skills import get_skill_manager
9
+
10
+ mgr = get_skill_manager()
11
+ result = mgr.execute_skill("math-calculator", {
12
+ "expression": "2 + 3 * 4",
13
+ "precision": 2,
14
+ })
15
+ print(result) # {"success": True, "result": 14, ...}
16
+ ```
17
+
18
+ ## Parameters
19
+
20
+ | Name | Type | Required | Default | Description |
21
+ |------|------|----------|---------|-------------|
22
+ | expression | string | yes | — | Math expression |
23
+ | precision | integer | no | 6 | Decimal places |
24
+ | format | string | no | number | `number` or `steps` |
25
+
26
+ ## Supported Operations
27
+
28
+ - Arithmetic: `+ - * / ** %`
29
+ - Functions: `sqrt sin cos tan log abs round`
30
+ - Constants: `pi e`
31
+
32
+ ## Security
33
+
34
+ Input is sanitized against code injection. Expressions over 500 chars are rejected.
35
+
36
+ ## Testing
37
+
38
+ ```bash
39
+ pytest tests/
40
+ ```
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: math-calculator
3
+ version: "1.0.0"
4
+ description: Safely evaluate mathematical expressions with configurable precision
5
+ type: skill
6
+ call:
7
+ function: calculate
8
+ parameters:
9
+ expression:
10
+ type: string
11
+ description: "Mathematical expression to evaluate (e.g., '2 + 3 * 4')"
12
+ required: true
13
+ precision:
14
+ type: integer
15
+ description: "Number of decimal places in result"
16
+ default: 6
17
+ format:
18
+ type: string
19
+ description: "Output format: 'number' or 'steps' (show intermediate steps)"
20
+ default: number
21
+ output:
22
+ format: json
23
+ schema:
24
+ result:
25
+ type: number
26
+ description: "The computed result"
27
+ expression:
28
+ type: string
29
+ description: "The original expression"
30
+ precision:
31
+ type: integer
32
+ description: "Precision used"
33
+ permissions:
34
+ network: false
35
+ filesystem: none
36
+ allowed_commands: []
37
+ tags: [math, utility, calculator]
38
+ ---
39
+
40
+ # Math Calculator
41
+
42
+ You are a precise mathematical calculator. When asked to compute an expression:
43
+
44
+ 1. Parse the expression carefully, respecting operator precedence
45
+ 2. Evaluate step by step if format='steps'
46
+ 3. Return the result to the specified precision
47
+
48
+ ## Supported Operations
49
+
50
+ - Basic: +, -, *, /, ** (power), % (modulo)
51
+ - Functions: sqrt, sin, cos, tan, log, abs, round
52
+ - Constants: pi, e
53
+
54
+ ## Important
55
+
56
+ - Never execute shell commands to calculate
57
+ - Use Python's `math` module internally
58
+ - Sanitize input — reject expressions over 500 characters
59
+ - For security, only allow safe mathematical expressions (no `__import__`, `eval` injection, etc.)
@@ -0,0 +1,103 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Math Calculator Skill — safe expression evaluation."""
3
+
4
+ import math
5
+ import re
6
+ from typing import Any
7
+
8
+
9
+ # Allowed builtins for safe evaluation
10
+ _SAFE_BUILTINS = {
11
+ "abs": abs, "round": round, "int": int, "float": float,
12
+ "max": max, "min": min, "sum": sum, "pow": pow,
13
+ "len": len, "range": range, "list": list, "tuple": tuple,
14
+ }
15
+ _SAFE_MATH = {
16
+ "sqrt": math.sqrt, "sin": math.sin, "cos": math.cos,
17
+ "tan": math.tan, "log": math.log, "log10": math.log10,
18
+ "log2": math.log2, "exp": math.exp, "pi": math.pi, "e": math.e,
19
+ "ceil": math.ceil, "floor": math.floor, "degrees": math.degrees,
20
+ "radians": math.radians, "asin": math.asin, "acos": math.acos,
21
+ "atan": math.atan, "sinh": math.sinh, "cosh": math.cosh,
22
+ "tanh": math.tanh, "fabs": math.fabs, "factorial": math.factorial,
23
+ "gcd": math.gcd, "trunc": math.trunc, "modf": math.modf,
24
+ "isclose": math.isclose,
25
+ }
26
+ _SAFE_NAMES = {**_SAFE_BUILTINS, **_SAFE_MATH}
27
+
28
+ # Regex for sanitizing expressions
29
+ _UNSAFE_PATTERNS = [
30
+ r"__", # dunder attributes
31
+ r"import", # imports
32
+ r"exec\s*\(", # exec()
33
+ r"eval\s*\(", # recursive eval
34
+ r"open\s*\(", # file open
35
+ r"os\.", # os module
36
+ r"sys\.", # sys module
37
+ r"subprocess", # subprocess
38
+ r"lambda", # lambda (can create arbitrary code)
39
+ r"globals?\s*\(", # globals()
40
+ r"locals?\s*\(", # locals()
41
+ r"getattr\s*\(", # getattr()
42
+ r"setattr\s*\(", # setattr()
43
+ ]
44
+
45
+
46
+ def run(input_data: dict[str, Any]) -> dict[str, Any]:
47
+ """
48
+ Evaluate a mathematical expression safely.
49
+
50
+ Args:
51
+ input_data: {"expression": str, "precision": int = 6, "format": str = "number"}
52
+
53
+ Returns:
54
+ {"success": bool, "result": number|null, "expression": str, "error": str|null}
55
+ """
56
+ expression = input_data.get("expression", "").strip()
57
+ precision = input_data.get("precision", 6)
58
+ output_format = input_data.get("format", "number")
59
+
60
+ # Validate
61
+ if not expression:
62
+ return _error("No expression provided", expression, 400)
63
+
64
+ if len(expression) > 500:
65
+ return _error("Expression too long (>500 chars)", expression, 400)
66
+
67
+ # Sanitize — check for unsafe patterns
68
+ lowered = expression.lower()
69
+ for pattern in _UNSAFE_PATTERNS:
70
+ if re.search(pattern, lowered):
71
+ return _error(f"Unsafe pattern detected: {pattern}", expression, 403)
72
+
73
+ # Evaluate safely
74
+ try:
75
+ result = eval(expression, {"__builtins__": {}}, _SAFE_NAMES)
76
+ except SyntaxError as e:
77
+ return _error(f"Syntax error: {e}", expression, 400)
78
+ except ZeroDivisionError:
79
+ return _error("Division by zero", expression, 400)
80
+ except Exception as e:
81
+ return _error(f"Evaluation error: {type(e).__name__}: {e}", expression, 400)
82
+
83
+ # Format
84
+ if isinstance(result, float):
85
+ result = round(result, precision)
86
+
87
+ return {
88
+ "success": True,
89
+ "result": result,
90
+ "expression": expression,
91
+ "precision": precision,
92
+ "error": None,
93
+ }
94
+
95
+
96
+ def _error(msg: str, expr: str, code: int) -> dict[str, Any]:
97
+ return {
98
+ "success": False,
99
+ "result": None,
100
+ "expression": expr,
101
+ "error": msg,
102
+ "status_code": code,
103
+ }
@@ -0,0 +1,8 @@
1
+ You are a precise mathematical calculator.
2
+
3
+ Rules:
4
+ - Parse expressions respecting standard operator precedence (PEMDAS)
5
+ - Use Python's math module internally
6
+ - Return results rounded to the requested precision
7
+ - Reject any expression containing non-mathematical operations
8
+ - If format='steps', show each intermediate step
@@ -0,0 +1,2 @@
1
+ # Math Calculator — no external dependencies
2
+ # Uses only Python stdlib (math, re)
@@ -0,0 +1,8 @@
1
+ {
2
+ "pi": 3.141592653589793,
3
+ "e": 2.718281828459045,
4
+ "golden_ratio": 1.618033988749895,
5
+ "speed_of_light": 299792458,
6
+ "planck_constant": 6.62607015e-34,
7
+ "avogadro": 6.02214076e23
8
+ }
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Tests for math-calculator handler."""
3
+
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Add parent to path so we can import handler
8
+ sys.path.insert(0, str(Path(__file__).parent.parent))
9
+ from handler import run
10
+
11
+
12
+ def test_basic_addition():
13
+ result = run({"expression": "2 + 3"})
14
+ assert result["success"]
15
+ assert result["result"] == 5
16
+
17
+
18
+ def test_precedence():
19
+ result = run({"expression": "2 + 3 * 4"})
20
+ assert result["success"]
21
+ assert result["result"] == 14
22
+
23
+
24
+ def test_sqrt():
25
+ result = run({"expression": "sqrt(16)"})
26
+ assert result["success"]
27
+ assert result["result"] == 4.0
28
+
29
+
30
+ def test_empty_expression():
31
+ result = run({"expression": ""})
32
+ assert not result["success"]
33
+ assert result["status_code"] == 400
34
+
35
+
36
+ def test_unsafe_import():
37
+ result = run({"expression": "__import__('os')"})
38
+ assert not result["success"]
39
+
40
+
41
+ def test_unsafe_eval():
42
+ result = run({"expression": "eval('1+1')"})
43
+ assert not result["success"]
44
+
45
+
46
+ def test_division_by_zero():
47
+ result = run({"expression": "1/0"})
48
+ assert not result["success"]
49
+
50
+
51
+ def test_long_expression():
52
+ result = run({"expression": "1+" * 300})
53
+ assert not result["success"]
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: security-auditor
3
+ description: Security-focused code auditor. Finds vulnerabilities, hardcoded secrets, and unsafe patterns.
4
+ triggers:
5
+ - security audit
6
+ - security review
7
+ - hardcoded key
8
+ - vulnerability
9
+ - CVE
10
+ - 安全
11
+ - 漏洞
12
+ - secret
13
+ - password
14
+ - token leak
15
+ tools: []
16
+ ---
17
+
18
+ You are a security engineer performing a code audit. Find real vulnerabilities, not theoretical ones.
19
+
20
+ ## Audit Checklist
21
+ 1. **Secrets exposure** — API keys, tokens, passwords in code or config
22
+ 2. **Injection risks** — SQL, command, code injection vectors
23
+ 3. **Authentication** — weak or missing auth, session issues
24
+ 4. **Authorization** — missing access controls, privilege escalation
25
+ 5. **Data exposure** — sensitive data in logs, errors, or client-side
26
+ 6. **Dependencies** — known vulnerable packages (check versions)
27
+ 7. **Crypto** — weak algorithms, hardcoded keys, bad random
28
+
29
+ ## Output Format
30
+ For each finding:
31
+ - **Severity**: critical / high / medium / low
32
+ - **Location**: file:line
33
+ - **Finding**: what the vulnerability is
34
+ - **Exploit**: how it could be exploited
35
+ - **Fix**: concrete remediation
36
+
37
+ ## Rules
38
+ - Only report findings you can confirm from the code
39
+ - Prioritize exploitable issues over theoretical ones
40
+ - If no serious issues found, say so — don't invent problems
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: test-writer
3
+ description: Writes unit tests, integration tests, and test fixtures. Follows testing best practices.
4
+ triggers:
5
+ - test
6
+ - unit test
7
+ - spec
8
+ - coverage
9
+ - pytest
10
+ - jest
11
+ - vitest
12
+ - 测试
13
+ - 写测试
14
+ tools: []
15
+ ---
16
+
17
+ You are a test engineer. Write tests that matter, not tests that inflate coverage numbers.
18
+
19
+ ## Principles
20
+ - **Test behavior, not implementation** — what it does, not how
21
+ - **AAA pattern** — Arrange, Act, Assert
22
+ - **One concept per test** — clear failure messages
23
+ - **Deterministic** — no flaky tests, no random without seeds
24
+ - **Fast** — unit tests in milliseconds
25
+
26
+ ## Coverage Targets
27
+ - Happy path (expected case)
28
+ - Edge cases (empty, null, boundary)
29
+ - Error cases (invalid input, failures)
30
+ - Integration boundaries (DB, API, FS)
31
+
32
+ ## Process
33
+ 1. Detect the project's test framework
34
+ 2. Study existing test style
35
+ 3. Write tests following the same conventions
36
+ 4. Run them to confirm they pass
@@ -0,0 +1,45 @@
1
+ # Weather Skill
2
+
3
+ Query real-time weather for cities worldwide using the free Open-Meteo API (no API key required).
4
+
5
+ ## Usage
6
+
7
+ ```python
8
+ from ata_coder.skills import get_skill_manager
9
+ mgr = get_skill_manager()
10
+ result = mgr.execute_skill("weather-skill", {
11
+ "city": "Tokyo",
12
+ "units": "celsius",
13
+ "forecast_days": 3,
14
+ })
15
+ ```
16
+
17
+ ## Parameters
18
+
19
+ | Name | Type | Required | Default | Description |
20
+ |------|------|----------|---------|-------------|
21
+ | city | string | yes | — | City name |
22
+ | units | string | no | celsius | `celsius` or `fahrenheit` |
23
+ | forecast_days | integer | no | 1 | 1-7 days |
24
+
25
+ ## Response Format
26
+
27
+ ```json
28
+ {
29
+ "success": true,
30
+ "result": {
31
+ "city": "Tokyo",
32
+ "country": "Japan",
33
+ "temperature": "22.5°C",
34
+ "windspeed": "12 km/h",
35
+ "conditions": "Clear sky",
36
+ "humidity": "N/A (free API limitation)"
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install -r requirements.txt
45
+ ```
@@ -0,0 +1,76 @@
1
+ """Weather Skill — query weather via Open-Meteo API (free, no API key)."""
2
+
3
+ from typing import Any
4
+
5
+ try:
6
+ import httpx
7
+ except ImportError:
8
+ httpx = None
9
+
10
+ from utils import load_city_coords, format_weather_response, GEO_URL, WEATHER_URL
11
+
12
+
13
+ def run(input_data: dict[str, Any]) -> dict[str, Any]:
14
+ """
15
+ Get weather for a city.
16
+
17
+ Args:
18
+ input_data: {"city": str, "units": str = "celsius", "forecast_days": int = 1}
19
+
20
+ Returns:
21
+ {"success": bool, "result": dict|null, "error": str|null}
22
+ """
23
+ city = input_data.get("city", "").strip()
24
+ units = input_data.get("units", "celsius")
25
+ forecast_days = min(max(int(input_data.get("forecast_days", 1)), 1), 7)
26
+
27
+ if not city:
28
+ return {"success": False, "result": None, "error": "City name required"}
29
+
30
+ # Look up coordinates from local resource
31
+ cities = load_city_coords()
32
+ coords = cities.get(city.lower())
33
+ if not coords:
34
+ # Try geocoding API
35
+ coords = _geocode_city(city)
36
+ if not coords:
37
+ return {
38
+ "success": False, "result": None,
39
+ "error": f"City not found: {city}",
40
+ }
41
+
42
+ lat, lon = coords["lat"], coords["lon"]
43
+
44
+ # Fetch weather
45
+ if httpx is None:
46
+ return {"success": False, "result": None, "error": "httpx not installed"}
47
+ try:
48
+ params = {
49
+ "latitude": lat, "longitude": lon,
50
+ "current_weather": "true",
51
+ "forecast_days": forecast_days,
52
+ "temperature_unit": "celsius" if units == "celsius" else "fahrenheit",
53
+ }
54
+ resp = httpx.get(WEATHER_URL, params=params, timeout=10.0)
55
+ resp.raise_for_status()
56
+ data = resp.json()
57
+ except Exception as e:
58
+ return {"success": False, "result": None, "error": f"API error: {e}"}
59
+
60
+ return format_weather_response(city, coords.get("country", ""), data, units)
61
+
62
+
63
+ def _geocode_city(city: str) -> dict | None:
64
+ """Fallback: geocode city name via Open-Meteo Geocoding API."""
65
+ if httpx is None:
66
+ return None
67
+ try:
68
+ resp = httpx.get(GEO_URL, params={"name": city, "count": 1}, timeout=5.0)
69
+ resp.raise_for_status()
70
+ results = resp.json().get("results", [])
71
+ if results:
72
+ r = results[0]
73
+ return {"lat": r["latitude"], "lon": r["longitude"], "country": r.get("country", "")}
74
+ except Exception:
75
+ pass
76
+ return None
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "weather-skill",
3
+ "version": "1.0.0",
4
+ "description": "Query weather information for cities worldwide via Open-Meteo API",
5
+ "type": "skill",
6
+ "author": "ATA Coder Team",
7
+ "license": "MIT",
8
+ "call": {
9
+ "function": "get_weather",
10
+ "parameters": {
11
+ "city": {
12
+ "type": "string",
13
+ "description": "City name (e.g., 'Beijing', 'Tokyo', 'London')",
14
+ "required": true
15
+ },
16
+ "units": {
17
+ "type": "string",
18
+ "description": "Temperature unit: 'celsius' or 'fahrenheit'",
19
+ "default": "celsius"
20
+ },
21
+ "forecast_days": {
22
+ "type": "integer",
23
+ "description": "Number of forecast days (1-7)",
24
+ "default": 1
25
+ }
26
+ }
27
+ },
28
+ "output": {
29
+ "format": "json",
30
+ "schema": {
31
+ "city": "string",
32
+ "country": "string",
33
+ "temperature": "number",
34
+ "humidity": "number",
35
+ "conditions": "string",
36
+ "forecast": "array"
37
+ }
38
+ },
39
+ "permissions": {
40
+ "network": true,
41
+ "filesystem": "read_only",
42
+ "allowed_domains": ["api.open-meteo.com", "geocoding-api.open-meteo.com"],
43
+ "allowed_commands": []
44
+ },
45
+ "triggers": ["weather", "temperature", "forecast", "天气", "气温", "预报"],
46
+ "tags": ["weather", "utility", "api"],
47
+ "dependencies": []
48
+ }
@@ -0,0 +1,9 @@
1
+ You are a weather information assistant. When asked about weather:
2
+
3
+ 1. Use the get_weather function to query real-time data
4
+ 2. Present results in a clear, structured format
5
+ 3. Include temperature, conditions, and wind speed
6
+ 4. If the city is not found, suggest the user check spelling
7
+ 5. For forecasts, present each day clearly
8
+
9
+ Always use the Open-Meteo API (free, no key required).
@@ -0,0 +1,3 @@
1
+ What is the weather in {{city}}?
2
+
3
+ Show temperature in {{units}} and include a {{forecast_days}}-day forecast.
@@ -0,0 +1 @@
1
+ httpx>=0.27.0
@@ -0,0 +1,17 @@
1
+ {
2
+ "beijing": {"lat": 39.9042, "lon": 116.4074, "country": "China"},
3
+ "shanghai": {"lat": 31.2304, "lon": 121.4737, "country": "China"},
4
+ "guangzhou": {"lat": 23.1291, "lon": 113.2644, "country": "China"},
5
+ "shenzhen": {"lat": 22.5431, "lon": 114.0579, "country": "China"},
6
+ "tokyo": {"lat": 35.6762, "lon": 139.6503, "country": "Japan"},
7
+ "osaka": {"lat": 34.6937, "lon": 135.5023, "country": "Japan"},
8
+ "seoul": {"lat": 37.5665, "lon": 126.9780, "country": "South Korea"},
9
+ "singapore": {"lat": 1.3521, "lon": 103.8198, "country": "Singapore"},
10
+ "london": {"lat": 51.5074, "lon": -0.1278, "country": "United Kingdom"},
11
+ "paris": {"lat": 48.8566, "lon": 2.3522, "country": "France"},
12
+ "new york": {"lat": 40.7128, "lon": -74.0060, "country": "United States"},
13
+ "san francisco": {"lat": 37.7749, "lon": -122.4194, "country": "United States"},
14
+ "sydney": {"lat": -33.8688, "lon": 151.2093, "country": "Australia"},
15
+ "berlin": {"lat": 52.5200, "lon": 13.4050, "country": "Germany"},
16
+ "moscow": {"lat": 55.7558, "lon": 37.6173, "country": "Russia"}
17
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "city_not_found": "City '{{city}}' not found. Check spelling or try a nearby major city.",
3
+ "api_error": "Weather service temporarily unavailable ({{detail}}). Try again later.",
4
+ "no_network": "Network access required for weather queries.",
5
+ "invalid_units": "Units must be 'celsius' or 'fahrenheit', got '{{units}}'.",
6
+ "forecast_range": "Forecast days must be between 1 and 7, got {{days}}."
7
+ }
@@ -0,0 +1,28 @@
1
+ """Tests for weather-skill handler (unit tests, no network)."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent))
6
+
7
+ from handler import run
8
+
9
+
10
+ def test_missing_city():
11
+ result = run({"city": ""})
12
+ assert not result["success"]
13
+ assert "City name required" in result["error"]
14
+
15
+
16
+ def test_unknown_city_no_network():
17
+ """Without httpx installed, unknown cities should fail gracefully."""
18
+ result = run({"city": "asdfghjkl"})
19
+ assert not result["success"]
20
+
21
+
22
+ def test_known_city_no_httpx():
23
+ """With city_list.json but no httpx, should report httpx missing."""
24
+ result = run({"city": "Beijing"})
25
+ # Either finds coords in city_list but fails on httpx,
26
+ # or succeeds if httpx is installed
27
+ if not result["success"]:
28
+ assert "httpx" in result.get("error", "").lower() or "city" in result.get("error", "").lower()