eljay-ai 1.1.0

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 (64) hide show
  1. package/AGENTS.md +57 -0
  2. package/README.md +479 -0
  3. package/agent/__init__.py +10 -0
  4. package/agent/agent.py +269 -0
  5. package/agent/agents/__init__.py +34 -0
  6. package/agent/agents/registry.py +212 -0
  7. package/agent/builtin_tools.py +591 -0
  8. package/agent/chat.py +354 -0
  9. package/agent/config.py +67 -0
  10. package/agent/context.py +63 -0
  11. package/agent/edits.py +167 -0
  12. package/agent/gitaware.py +126 -0
  13. package/agent/hardware.py +331 -0
  14. package/agent/knowledge.py +133 -0
  15. package/agent/memory.py +98 -0
  16. package/agent/ollama_client.py +133 -0
  17. package/agent/permissions.py +93 -0
  18. package/agent/providers/__init__.py +51 -0
  19. package/agent/providers/base.py +78 -0
  20. package/agent/providers/image.py +110 -0
  21. package/agent/providers/ollama.py +111 -0
  22. package/agent/providers/video.py +96 -0
  23. package/agent/providers/web.py +155 -0
  24. package/agent/router.py +200 -0
  25. package/agent/rules.py +52 -0
  26. package/agent/runner.py +168 -0
  27. package/agent/skills.py +161 -0
  28. package/agent/tools.py +102 -0
  29. package/agent/verification.py +80 -0
  30. package/agent/workspace.py +487 -0
  31. package/bin/eljay +5 -0
  32. package/bin/eljay.cmd +4 -0
  33. package/bin/myagent +4 -0
  34. package/bin/myagent.cmd +4 -0
  35. package/eljay-ai-1.1.0.tgz +0 -0
  36. package/eljay.js +56 -0
  37. package/eljay.py +150 -0
  38. package/install.ps1 +28 -0
  39. package/knowledge/reference/diffusers.md +32 -0
  40. package/knowledge/reference/video-providers.md +21 -0
  41. package/knowledge/setup/comfyui.md +35 -0
  42. package/knowledge/setup/image-providers.md +16 -0
  43. package/knowledge/test-category/test-entry.md +5 -0
  44. package/myagent.py +30 -0
  45. package/package.json +40 -0
  46. package/skills/coding/code-review.md +3 -0
  47. package/skills/coding/fix-attempt-protocol.md +9 -0
  48. package/skills/debugging/root-cause-analysis.md +9 -0
  49. package/skills/general/communication.md +7 -0
  50. package/skills/laravel/authentication.md +15 -0
  51. package/skills/mysql/performance.md +9 -0
  52. package/skills/php/standards.md +8 -0
  53. package/skills/react/component-best-practices.md +8 -0
  54. package/skills/research/source-tracking.md +7 -0
  55. package/skills/security/input-validation.md +9 -0
  56. package/skills/testing/pytest-best-practices.md +7 -0
  57. package/tests/run_all.py +34 -0
  58. package/tests/test_agent_core.py +385 -0
  59. package/tests/test_capabilities.py +169 -0
  60. package/tests/test_edits.py +117 -0
  61. package/tests/test_eljay.py +209 -0
  62. package/tests/test_runner.py +114 -0
  63. package/tests/test_universal.py +458 -0
  64. package/tests/test_workspace.py +221 -0
@@ -0,0 +1,209 @@
1
+ """Tests for the ElJay AI rename, version, compatibility alias, and npm packaging.
2
+
3
+ Run from the project root:
4
+ python tests/test_eljay.py
5
+
6
+ These are deterministic and do NOT require Ollama or network access.
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import sys
12
+ import unittest
13
+ from pathlib import Path
14
+
15
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
16
+
17
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent
18
+
19
+
20
+ class VersionTests(unittest.TestCase):
21
+ def test_version_string(self):
22
+ from agent import __version__
23
+ self.assertEqual(__version__, "1.1.0")
24
+
25
+ def test_version_is_semver(self):
26
+ from agent import __version__
27
+ parts = __version__.split(".")
28
+ self.assertEqual(len(parts), 3)
29
+ for p in parts:
30
+ self.assertTrue(p.isdigit())
31
+
32
+
33
+ class BannerTests(unittest.TestCase):
34
+ def test_banner_contains_eljay_ai(self):
35
+ from agent.chat import BANNER
36
+ self.assertIn("ElJay AI", BANNER)
37
+
38
+ def test_banner_does_not_say_myagent(self):
39
+ from agent.chat import BANNER
40
+ self.assertNotIn("LOCAL AI CODING AGENT", BANNER)
41
+
42
+
43
+ class EljayEntryTests(unittest.TestCase):
44
+ def test_eljay_module_importable(self):
45
+ import eljay # noqa: F401
46
+ self.assertTrue(hasattr(eljay, "main"))
47
+
48
+ def test_eljay_has_resolve_model(self):
49
+ import eljay
50
+ self.assertTrue(callable(eljay.resolve_model))
51
+
52
+ def test_eljay_has_build_registry(self):
53
+ import eljay
54
+ self.assertTrue(callable(eljay.build_registry))
55
+
56
+
57
+ class MyagentAliasTests(unittest.TestCase):
58
+ def test_myagent_module_importable(self):
59
+ import myagent # noqa: F401
60
+ self.assertTrue(hasattr(myagent, "main"))
61
+
62
+ def test_myagent_main_delegates_to_eljay(self):
63
+ import myagent
64
+ import eljay
65
+ # myagent.main should be eljay.main (same function object)
66
+ self.assertIs(myagent.main, eljay.main)
67
+
68
+ def test_myagent_resolve_model_delegates(self):
69
+ import myagent
70
+ import eljay
71
+ self.assertIs(myagent.resolve_model, eljay.resolve_model)
72
+
73
+
74
+ class HelpTextTests(unittest.TestCase):
75
+ def test_help_mentions_eljay(self):
76
+ # Read eljay.py source and check the --help text
77
+ src = (PROJECT_ROOT / "eljay.py").read_text(encoding="utf-8")
78
+ self.assertIn("eljay", src)
79
+ # The help line should reference eljay
80
+ self.assertIn("usage: eljay", src)
81
+
82
+ def test_version_flag_mentions_eljay_ai(self):
83
+ src = (PROJECT_ROOT / "eljay.py").read_text(encoding="utf-8")
84
+ self.assertIn("ElJay AI {__version__}", src)
85
+
86
+
87
+ class PackageJsonTests(unittest.TestCase):
88
+ def test_package_json_exists(self):
89
+ self.assertTrue((PROJECT_ROOT / "package.json").exists())
90
+
91
+ def test_package_json_name(self):
92
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
93
+ self.assertEqual(data["name"], "eljay-ai")
94
+
95
+ def test_package_json_version(self):
96
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
97
+ self.assertEqual(data["version"], "1.1.0")
98
+
99
+ def test_package_json_bin_field(self):
100
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
101
+ bins = data["bin"]
102
+ self.assertIn("eljay", bins)
103
+ self.assertEqual(bins["eljay"], "eljay.js")
104
+
105
+ def test_package_json_prefer_global(self):
106
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
107
+ self.assertTrue(data.get("preferGlobal", False))
108
+
109
+ def test_package_json_has_engines(self):
110
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
111
+ self.assertIn("node", data["engines"])
112
+ self.assertIn("python", data["engines"])
113
+
114
+ def test_package_json_keywords(self):
115
+ data = json.loads((PROJECT_ROOT / "package.json").read_text())
116
+ self.assertIsInstance(data["keywords"], list)
117
+ self.assertTrue(len(data["keywords"]) > 0)
118
+
119
+
120
+ class EljayJsTests(unittest.TestCase):
121
+ def test_eljay_js_exists(self):
122
+ self.assertTrue((PROJECT_ROOT / "eljay.js").exists())
123
+
124
+ def test_eljay_js_is_node_script(self):
125
+ first_line = (PROJECT_ROOT / "eljay.js").read_text(encoding="utf-8").splitlines()[0]
126
+ self.assertTrue("node" in first_line)
127
+
128
+ def test_eljay_js_references_eljay_py(self):
129
+ content = (PROJECT_ROOT / "eljay.js").read_text(encoding="utf-8")
130
+ self.assertIn("eljay.py", content)
131
+
132
+ def test_eljay_js_inherits_stdio(self):
133
+ content = (PROJECT_ROOT / "eljay.js").read_text(encoding="utf-8")
134
+ self.assertIn("stdio", content)
135
+
136
+
137
+ class LauncherTests(unittest.TestCase):
138
+ def test_bin_eljay_exists(self):
139
+ path = PROJECT_ROOT / "bin" / "eljay"
140
+ self.assertTrue(path.exists())
141
+ self.assertTrue(os.access(path, os.X_OK), "bin/eljay should be executable")
142
+
143
+ def test_bin_eljay_cmd_exists(self):
144
+ self.assertTrue((PROJECT_ROOT / "bin" / "eljay.cmd").exists())
145
+
146
+ def test_bin_myagent_still_exists(self):
147
+ self.assertTrue((PROJECT_ROOT / "bin" / "myagent").exists())
148
+ self.assertTrue((PROJECT_ROOT / "bin" / "myagent.cmd").exists())
149
+
150
+ def test_bin_eljay_references_eljay_py(self):
151
+ content = (PROJECT_ROOT / "bin" / "eljay").read_text(encoding="utf-8")
152
+ self.assertIn("eljay.py", content)
153
+
154
+ def test_bin_eljay_cmd_references_eljay_py(self):
155
+ content = (PROJECT_ROOT / "bin" / "eljay.cmd").read_text(encoding="utf-8")
156
+ self.assertIn("eljay.py", content)
157
+
158
+ def test_bin_myagent_references_eljay_py(self):
159
+ """myagent launcher should delegate to eljay.py (compatibility)."""
160
+ content = (PROJECT_ROOT / "bin" / "myagent").read_text(encoding="utf-8")
161
+ self.assertIn("eljay.py", content)
162
+
163
+ def test_bin_myagent_cmd_references_eljay_py(self):
164
+ content = (PROJECT_ROOT / "bin" / "myagent.cmd").read_text(encoding="utf-8")
165
+ self.assertIn("eljay.py", content)
166
+
167
+
168
+ class InstallPs1Tests(unittest.TestCase):
169
+ def test_install_ps1_references_eljay(self):
170
+ content = (PROJECT_ROOT / "install.ps1").read_text(encoding="utf-8")
171
+ self.assertIn("eljay", content)
172
+ # Should not say 'run myagent' as the primary instruction
173
+ self.assertNotIn(" myagent", content)
174
+
175
+
176
+ class GitignoreTests(unittest.TestCase):
177
+ def test_gitignore_has_agent_dir(self):
178
+ content = (PROJECT_ROOT / ".gitignore").read_text(encoding="utf-8")
179
+ self.assertIn(".agent/", content)
180
+
181
+ def test_gitignore_has_pycache(self):
182
+ content = (PROJECT_ROOT / ".gitignore").read_text(encoding="utf-8")
183
+ self.assertIn("__pycache__/", content)
184
+
185
+
186
+ class RulesTestsAlias(unittest.TestCase):
187
+ def test_agents_md_references_eljay(self):
188
+ content = (PROJECT_ROOT / "AGENTS.md").read_text(encoding="utf-8")
189
+ self.assertIn("eljay.py", content.lower())
190
+
191
+
192
+ class ProviderFixTests(unittest.TestCase):
193
+ """Verify the _do_text fix in the Ollama provider."""
194
+
195
+ def test_ollama_provider_do_text_signature(self):
196
+ from agent.providers.ollama import OllamaProvider
197
+ # Just verify the method exists and is callable
198
+ self.assertTrue(hasattr(OllamaProvider, "_do_text"))
199
+
200
+ def test_ollama_provider_no_model_kwarg_in_client(self):
201
+ """The provider should not pass model= to OllamaClient (it doesn't accept it)."""
202
+ from agent.providers.ollama import OllamaProvider
203
+ import inspect
204
+ src = inspect.getsource(OllamaProvider._do_text)
205
+ self.assertNotIn("OllamaClient(host=self.host, model=", src)
206
+
207
+
208
+ if __name__ == "__main__":
209
+ unittest.main(verbosity=2)
@@ -0,0 +1,114 @@
1
+ """Tests for the command runner — classification + real (safe) execution.
2
+
3
+ Run from the project root:
4
+ python tests/test_runner.py
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import tempfile
10
+ import unittest
11
+ from pathlib import Path
12
+
13
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14
+
15
+ from agent.runner import analyse, classify_command, run_command # noqa: E402
16
+ from agent.tools import RISK_ASK, RISK_HIGH # noqa: E402
17
+ from agent.workspace import Workspace # noqa: E402
18
+
19
+
20
+ class ClassifyTests(unittest.TestCase):
21
+ def test_allow_listed_programs_are_ask(self):
22
+ for cmd in (
23
+ "python --version",
24
+ "npm run build",
25
+ "php artisan test",
26
+ "git status",
27
+ "node -v",
28
+ "pytest",
29
+ ):
30
+ self.assertEqual(classify_command(cmd), RISK_ASK, cmd)
31
+
32
+ def test_unknown_program_is_high(self):
33
+ self.assertEqual(classify_command("some_random_tool --go"), RISK_HIGH)
34
+
35
+ def test_dangerous_commands_are_high(self):
36
+ for cmd in (
37
+ "rm -rf /",
38
+ "rmdir somedir",
39
+ "del important.txt",
40
+ "git push origin main",
41
+ "git reset --hard HEAD",
42
+ "git clean -fd",
43
+ "npm uninstall react",
44
+ "composer remove laravel/framework",
45
+ "pip uninstall requests",
46
+ "drop database prod",
47
+ "shutdown now",
48
+ "sudo reboot",
49
+ ):
50
+ self.assertEqual(classify_command(cmd), RISK_HIGH, cmd)
51
+
52
+ def test_empty_command_is_high(self):
53
+ self.assertEqual(classify_command(""), RISK_HIGH)
54
+
55
+
56
+ class AnalyseTests(unittest.TestCase):
57
+ def test_simple_command_parses(self):
58
+ argv, err = analyse("npm run build")
59
+ self.assertEqual(argv, ["npm", "run", "build"])
60
+ self.assertEqual(err, "")
61
+
62
+ def test_shell_operators_rejected(self):
63
+ for cmd in ("a | b", "a > file.txt", "a && b", "a; b", "a `b`", "a $(b)"):
64
+ argv, err = analyse(cmd)
65
+ self.assertIsNone(argv, cmd)
66
+ self.assertIn("ERROR", err)
67
+
68
+ def test_empty_rejected(self):
69
+ argv, err = analyse(" ")
70
+ self.assertIsNone(argv)
71
+ self.assertIn("ERROR", err)
72
+
73
+
74
+ class RunTests(unittest.TestCase):
75
+ def setUp(self):
76
+ self._tmp = tempfile.TemporaryDirectory()
77
+ self.root = Path(self._tmp.name)
78
+ self.ws = Workspace(self.root)
79
+
80
+ def tearDown(self):
81
+ self._tmp.cleanup()
82
+
83
+ def test_runs_and_captures_output(self):
84
+ out = run_command(self.ws, "python --version")
85
+ self.assertIn("Exit code: 0", out)
86
+ self.assertIn("Python", out)
87
+
88
+ def test_failing_command_reports_failure(self):
89
+ out = run_command(self.ws, "python does_not_exist_xyz.py")
90
+ self.assertIn("FAILED", out)
91
+ self.assertNotIn("Exit code: 0", out)
92
+
93
+ def test_unknown_program_reports_error(self):
94
+ out = run_command(self.ws, "definitely_not_a_real_program_xyz")
95
+ self.assertIn("command not found", out)
96
+
97
+ def test_timeout_is_enforced(self):
98
+ (self.root / "slow.py").write_text(
99
+ "import time\ntime.sleep(10)\n", encoding="utf-8"
100
+ )
101
+ out = run_command(self.ws, "python slow.py", timeout=1)
102
+ self.assertIn("timed out", out)
103
+
104
+ def test_cwd_outside_root_rejected(self):
105
+ out = run_command(self.ws, "python --version", cwd="../..")
106
+ self.assertIn("ERROR", out)
107
+
108
+ def test_shell_operator_rejected_at_run(self):
109
+ out = run_command(self.ws, "python --version | more")
110
+ self.assertIn("ERROR", out)
111
+
112
+
113
+ if __name__ == "__main__":
114
+ unittest.main(verbosity=2)