ErrorAI 0.9.0__tar.gz → 1.2.0__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 (26) hide show
  1. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/PKG-INFO +1 -1
  2. {errorai-0.9.0 → errorai-1.2.0}/PKG-INFO +1 -1
  3. {errorai-0.9.0 → errorai-1.2.0}/errorai/__init__.py +3 -4
  4. errorai-1.2.0/errorai/cli.py +172 -0
  5. {errorai-0.9.0 → errorai-1.2.0}/errorai/compat.py +18 -0
  6. {errorai-0.9.0 → errorai-1.2.0}/errorai/runtime.py +8 -2
  7. {errorai-0.9.0 → errorai-1.2.0}/pyproject.toml +1 -1
  8. errorai-0.9.0/errorai/cli.py +0 -67
  9. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/SOURCES.txt +0 -0
  10. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/dependency_links.txt +0 -0
  11. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/entry_points.txt +0 -0
  12. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/requires.txt +0 -0
  13. {errorai-0.9.0 → errorai-1.2.0}/ErrorAI.egg-info/top_level.txt +0 -0
  14. {errorai-0.9.0 → errorai-1.2.0}/README.md +0 -0
  15. {errorai-0.9.0 → errorai-1.2.0}/errorai/__main__.py +0 -0
  16. {errorai-0.9.0 → errorai-1.2.0}/errorai/bootstrap.py +0 -0
  17. {errorai-0.9.0 → errorai-1.2.0}/errorai/config.py +0 -0
  18. {errorai-0.9.0 → errorai-1.2.0}/errorai/environment.py +0 -0
  19. {errorai-0.9.0 → errorai-1.2.0}/errorai/pipeline.py +0 -0
  20. {errorai-0.9.0 → errorai-1.2.0}/errorai/providers.py +0 -0
  21. {errorai-0.9.0 → errorai-1.2.0}/errorai/universal.py +0 -0
  22. {errorai-0.9.0 → errorai-1.2.0}/setup.cfg +0 -0
  23. {errorai-0.9.0 → errorai-1.2.0}/tests/test_bootstrap.py +0 -0
  24. {errorai-0.9.0 → errorai-1.2.0}/tests/test_config.py +0 -0
  25. {errorai-0.9.0 → errorai-1.2.0}/tests/test_runtime.py +0 -0
  26. {errorai-0.9.0 → errorai-1.2.0}/tests/test_safety.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErrorAI
3
- Version: 0.9.0
3
+ Version: 1.2.0
4
4
  Summary: Python-only autonomous runtime that watches/analyzes errors with safe local auto-fix fallbacks.
5
5
  Author: Aswanth R
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErrorAI
3
- Version: 0.9.0
3
+ Version: 1.2.0
4
4
  Summary: Python-only autonomous runtime that watches/analyzes errors with safe local auto-fix fallbacks.
5
5
  Author: Aswanth R
6
6
  License: MIT
@@ -1,9 +1,8 @@
1
1
  from .compat import catch_errors, global_activate, watch
2
2
  from .runtime import configure, get_runtime
3
3
 
4
- __version__ = "2.0.0"
4
+ __version__ = "2.1.0"
5
5
  __all__ = ["get_runtime", "configure", "watch", "catch_errors", "global_activate"]
6
6
 
7
- # Import-time auto-start for zero-decorator usage.
8
- runtime = get_runtime()
9
- runtime.initialize()
7
+ # Auto-start runtime on import
8
+ get_runtime().initialize()
@@ -0,0 +1,172 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import ast
5
+ import traceback
6
+ from pathlib import Path
7
+
8
+ from .bootstrap import ensure_model
9
+ from .config import config_template, load_config
10
+ from .runtime import get_runtime
11
+
12
+
13
+ def cmd_init(args) -> int:
14
+ root = Path.cwd()
15
+ config_path = root / ".errorai.toml"
16
+ if not config_path.exists() or args.force:
17
+ config_path.write_text(config_template(), encoding="utf-8")
18
+ print(f"[errorai] Wrote {config_path}")
19
+ else:
20
+ print(f"[errorai] Config already exists: {config_path}")
21
+ return 0
22
+
23
+
24
+ def cmd_doctor(_args) -> int:
25
+ runtime = get_runtime().initialize()
26
+ status = runtime.status_report()
27
+ print("ErrorAI Doctor")
28
+ for key in (
29
+ "environment",
30
+ "can_watch_fs",
31
+ "can_apply_patches",
32
+ "mode",
33
+ "model_ready",
34
+ "model_mode",
35
+ "model_detail",
36
+ "dry_run",
37
+ "project_root",
38
+ ):
39
+ print(f"- {key}: {status[key]}")
40
+ return 0
41
+
42
+
43
+ def cmd_install_model(_args) -> int:
44
+ cfg = load_config()
45
+ status = ensure_model(cfg.model, explicit=True)
46
+ print(status.detail)
47
+ return 0 if status.ready else 1
48
+
49
+
50
+ def _prompt_fix() -> bool:
51
+ answer = input("Should I fix it [Y/N]: ").strip().lower()
52
+ return answer in {"y", "yes"}
53
+
54
+
55
+ def _attempt_simple_syntax_fix(content: str, err: SyntaxError) -> tuple[str, bool, str]:
56
+ if err.msg and "expected ':'" in err.msg and err.lineno:
57
+ lines = content.splitlines()
58
+ idx = err.lineno - 1
59
+ if 0 <= idx < len(lines):
60
+ line = lines[idx].rstrip()
61
+ if line and not line.endswith(":"):
62
+ lines[idx] = line + ":"
63
+ return "\n".join(lines) + ("\n" if content.endswith("\n") else ""), True, "Added missing ':'"
64
+ return content, False, "No safe automatic syntax fix available"
65
+
66
+
67
+ def _attempt_simple_runtime_fix(content: str, exc: Exception) -> tuple[str, bool, str]:
68
+ if isinstance(exc, TypeError) and "unsupported operand type(s) for +:" in str(exc):
69
+ # Minimal safe stub: no automatic source mutation yet.
70
+ return content, False, "Detected TypeError (+ with incompatible types), but no safe auto-fix rule matched"
71
+ return content, False, "No safe automatic runtime fix available"
72
+
73
+
74
+ def cmd_check(args) -> int:
75
+ file_path = Path(args.file_path).expanduser().resolve()
76
+ if not file_path.exists() or not file_path.is_file():
77
+ print(f"[errorai] File not found: {file_path}")
78
+ return 2
79
+
80
+ source = file_path.read_text(encoding="utf-8")
81
+
82
+ try:
83
+ ast.parse(source, filename=str(file_path))
84
+ print(f"[errorai] ✅ No syntax errors in {file_path}")
85
+ return 0
86
+ except SyntaxError as err:
87
+ print(f"[errorai] ❌ SyntaxError in {file_path}")
88
+ print(f"[errorai] Line {err.lineno}, Col {err.offset}: {err.msg}")
89
+ if err.text:
90
+ print(f"[errorai] >> {err.text.rstrip()}")
91
+
92
+ if not _prompt_fix():
93
+ return 1
94
+
95
+ fixed_source, changed, detail = _attempt_simple_syntax_fix(source, err)
96
+ print(f"[errorai] {detail}")
97
+
98
+ if changed:
99
+ file_path.write_text(fixed_source, encoding="utf-8")
100
+ print(f"[errorai] Applied fix to {file_path}")
101
+ try:
102
+ ast.parse(fixed_source, filename=str(file_path))
103
+ print("[errorai] ✅ Re-check passed")
104
+ return 0
105
+ except SyntaxError as second:
106
+ print(f"[errorai] Still invalid after fix: {second}")
107
+ return 1
108
+ return 1
109
+
110
+
111
+ def cmd_run(args) -> int:
112
+ file_path = Path(args.file_path).expanduser().resolve()
113
+ if not file_path.exists() or not file_path.is_file():
114
+ print(f"[errorai] File not found: {file_path}")
115
+ return 2
116
+
117
+ # Run syntax check first
118
+ check_result = cmd_check(argparse.Namespace(file_path=str(file_path)))
119
+ if check_result != 0:
120
+ return check_result
121
+
122
+ source = file_path.read_text(encoding="utf-8")
123
+ globals_dict = {"__name__": "__main__", "__file__": str(file_path)}
124
+
125
+ try:
126
+ exec(compile(source, str(file_path), "exec"), globals_dict, globals_dict)
127
+ return 0
128
+ except Exception as exc:
129
+ print(f"[errorai] ❌ Runtime error: {type(exc).__name__}: {exc}")
130
+ if not _prompt_fix():
131
+ return 1
132
+ fixed_source, changed, detail = _attempt_simple_runtime_fix(source, exc)
133
+ print(f"[errorai] {detail}")
134
+ if changed:
135
+ file_path.write_text(fixed_source, encoding="utf-8")
136
+ print(f"[errorai] Applied runtime fix to {file_path}")
137
+ print("[errorai] Re-run with: errorai run <file path>")
138
+ return 0
139
+ print("[errorai] No fix applied")
140
+ traceback.print_exc()
141
+ return 1
142
+
143
+
144
+ def build_parser() -> argparse.ArgumentParser:
145
+ parser = argparse.ArgumentParser(prog="errorai")
146
+ sub = parser.add_subparsers(dest="command", required=True)
147
+
148
+ init_parser = sub.add_parser("init", help="Create default .errorai.toml config.")
149
+ init_parser.add_argument("--force", action="store_true", help="Overwrite existing config.")
150
+ init_parser.set_defaults(func=cmd_init)
151
+
152
+ doctor_parser = sub.add_parser("doctor", help="Check runtime readiness and fallback state.")
153
+ doctor_parser.set_defaults(func=cmd_doctor)
154
+
155
+ install_parser = sub.add_parser("install-model", help="Install or retry local model bootstrap.")
156
+ install_parser.set_defaults(func=cmd_install_model)
157
+
158
+ check_parser = sub.add_parser("check", help="Check a Python file for syntax errors and offer auto-fix.")
159
+ check_parser.add_argument("file_path", help="Path to .py file")
160
+ check_parser.set_defaults(func=cmd_check)
161
+
162
+ run_parser = sub.add_parser("run", help="Check then execute a Python file with runtime error interception.")
163
+ run_parser.add_argument("file_path", help="Path to .py file")
164
+ run_parser.set_defaults(func=cmd_run)
165
+
166
+ return parser
167
+
168
+
169
+ def main(argv: list[str] | None = None) -> int:
170
+ parser = build_parser()
171
+ args = parser.parse_args(argv)
172
+ return args.func(args)
@@ -20,6 +20,9 @@ def watch(func):
20
20
 
21
21
 
22
22
  class catch_errors:
23
+ def __init__(self, func=None):
24
+ self.func = func
25
+
23
26
  def __enter__(self):
24
27
  return self
25
28
 
@@ -29,8 +32,23 @@ class catch_errors:
29
32
  get_runtime().process_exception(exc_type, exc_value, exc_tb)
30
33
  return True
31
34
 
35
+ def __call__(self, *args, **kwargs):
36
+ if callable(self.func):
37
+ @functools.wraps(self.func)
38
+ def inner(*wargs, **wkwargs):
39
+ try:
40
+ return self.func(*wargs, **wkwargs)
41
+ except Exception as e:
42
+ get_runtime().process_exception(type(e), e, e.__traceback__)
43
+ raise
44
+ return inner(*args, **kwargs)
45
+
32
46
 
33
47
  def global_activate():
34
48
  runtime = get_runtime()
35
49
  runtime.initialize()
36
50
  return runtime
51
+
52
+
53
+ # Automatically trigger global activation on import so zero-config works!
54
+ global_activate()
@@ -95,12 +95,18 @@ class RuntimeManager:
95
95
  threading.excepthook = self._thread_excepthook
96
96
 
97
97
  def _sys_excepthook(self, exc_type, exc_value, exc_tb):
98
- self.process_exception(exc_type, exc_value, exc_tb)
98
+ handled = self.process_exception(exc_type, exc_value, exc_tb)
99
+ if handled:
100
+ print("[errorai] exception intercepted")
101
+ return
99
102
  if self._orig_sys_hook:
100
103
  self._orig_sys_hook(exc_type, exc_value, exc_tb)
101
104
 
102
105
  def _thread_excepthook(self, args):
103
- self.process_exception(args.exc_type, args.exc_value, args.exc_traceback)
106
+ handled = self.process_exception(args.exc_type, args.exc_value, args.exc_traceback)
107
+ if handled:
108
+ print("[errorai] thread exception intercepted")
109
+ return
104
110
  if self._orig_thread_hook:
105
111
  self._orig_thread_hook(args)
106
112
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ErrorAI"
7
- version = "0.9.0"
7
+ version = "1.2.0"
8
8
  description = "Python-only autonomous runtime that watches/analyzes errors with safe local auto-fix fallbacks."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,67 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import argparse
4
- from pathlib import Path
5
-
6
- from .bootstrap import ensure_model
7
- from .config import config_template, load_config
8
- from .runtime import get_runtime
9
-
10
-
11
- def cmd_init(args) -> int:
12
- root = Path.cwd()
13
- config_path = root / ".errorai.toml"
14
- if not config_path.exists() or args.force:
15
- config_path.write_text(config_template(), encoding="utf-8")
16
- print(f"[errorai] Wrote {config_path}")
17
- else:
18
- print(f"[errorai] Config already exists: {config_path}")
19
- return 0
20
-
21
-
22
- def cmd_doctor(_args) -> int:
23
- runtime = get_runtime().initialize()
24
- status = runtime.status_report()
25
- print("ErrorAI Doctor")
26
- for key in (
27
- "environment",
28
- "can_watch_fs",
29
- "can_apply_patches",
30
- "mode",
31
- "model_ready",
32
- "model_mode",
33
- "model_detail",
34
- "dry_run",
35
- "project_root",
36
- ):
37
- print(f"- {key}: {status[key]}")
38
- return 0
39
-
40
-
41
- def cmd_install_model(_args) -> int:
42
- cfg = load_config()
43
- status = ensure_model(cfg.model, explicit=True)
44
- print(status.detail)
45
- return 0 if status.ready else 1
46
-
47
-
48
- def build_parser() -> argparse.ArgumentParser:
49
- parser = argparse.ArgumentParser(prog="errorai")
50
- sub = parser.add_subparsers(dest="command", required=True)
51
-
52
- init_parser = sub.add_parser("init", help="Create default .errorai.toml config.")
53
- init_parser.add_argument("--force", action="store_true", help="Overwrite existing config.")
54
- init_parser.set_defaults(func=cmd_init)
55
-
56
- doctor_parser = sub.add_parser("doctor", help="Check runtime readiness and fallback state.")
57
- doctor_parser.set_defaults(func=cmd_doctor)
58
-
59
- install_parser = sub.add_parser("install-model", help="Install or retry local model bootstrap.")
60
- install_parser.set_defaults(func=cmd_install_model)
61
- return parser
62
-
63
-
64
- def main(argv: list[str] | None = None) -> int:
65
- parser = build_parser()
66
- args = parser.parse_args(argv)
67
- return args.func(args)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes