vaal-code 0.6.850__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 (98) hide show
  1. vaal_code-0.6.850/PKG-INFO +106 -0
  2. vaal_code-0.6.850/README.md +92 -0
  3. vaal_code-0.6.850/pyproject.toml +30 -0
  4. vaal_code-0.6.850/setup.cfg +4 -0
  5. vaal_code-0.6.850/tests/test_all.py +382 -0
  6. vaal_code-0.6.850/vaal/__init__.py +2 -0
  7. vaal_code-0.6.850/vaal/__main__.py +249 -0
  8. vaal_code-0.6.850/vaal/core/__init__.py +0 -0
  9. vaal_code-0.6.850/vaal/core/agent.py +398 -0
  10. vaal_code-0.6.850/vaal/core/auto_fix.py +55 -0
  11. vaal_code-0.6.850/vaal/core/bootstrap.py +255 -0
  12. vaal_code-0.6.850/vaal/core/branching.py +157 -0
  13. vaal_code-0.6.850/vaal/core/bridge.py +184 -0
  14. vaal_code-0.6.850/vaal/core/changelog.py +306 -0
  15. vaal_code-0.6.850/vaal/core/clipboard.py +76 -0
  16. vaal_code-0.6.850/vaal/core/code_review.py +271 -0
  17. vaal_code-0.6.850/vaal/core/collaborative.py +340 -0
  18. vaal_code-0.6.850/vaal/core/command_palette.py +268 -0
  19. vaal_code-0.6.850/vaal/core/config.py +55 -0
  20. vaal_code-0.6.850/vaal/core/context.py +147 -0
  21. vaal_code-0.6.850/vaal/core/cost_tracker.py +57 -0
  22. vaal_code-0.6.850/vaal/core/cot_display.py +198 -0
  23. vaal_code-0.6.850/vaal/core/deps.py +422 -0
  24. vaal_code-0.6.850/vaal/core/env_manager.py +288 -0
  25. vaal_code-0.6.850/vaal/core/error_explain.py +227 -0
  26. vaal_code-0.6.850/vaal/core/font_screen.py +157 -0
  27. vaal_code-0.6.850/vaal/core/generate_icon.py +61 -0
  28. vaal_code-0.6.850/vaal/core/history.py +59 -0
  29. vaal_code-0.6.850/vaal/core/hooks.py +134 -0
  30. vaal_code-0.6.850/vaal/core/image_input.py +151 -0
  31. vaal_code-0.6.850/vaal/core/init_project.py +229 -0
  32. vaal_code-0.6.850/vaal/core/interactive.py +412 -0
  33. vaal_code-0.6.850/vaal/core/keybindings.py +94 -0
  34. vaal_code-0.6.850/vaal/core/llm.py +180 -0
  35. vaal_code-0.6.850/vaal/core/login_screen.py +347 -0
  36. vaal_code-0.6.850/vaal/core/logout_screen.py +201 -0
  37. vaal_code-0.6.850/vaal/core/mcp_client.py +312 -0
  38. vaal_code-0.6.850/vaal/core/memory.py +202 -0
  39. vaal_code-0.6.850/vaal/core/migration.py +321 -0
  40. vaal_code-0.6.850/vaal/core/model_manager.py +458 -0
  41. vaal_code-0.6.850/vaal/core/multiline.py +166 -0
  42. vaal_code-0.6.850/vaal/core/notifications.py +223 -0
  43. vaal_code-0.6.850/vaal/core/oauth.py +229 -0
  44. vaal_code-0.6.850/vaal/core/output_styles.py +80 -0
  45. vaal_code-0.6.850/vaal/core/permissions.py +57 -0
  46. vaal_code-0.6.850/vaal/core/plans.py +197 -0
  47. vaal_code-0.6.850/vaal/core/plugins.py +113 -0
  48. vaal_code-0.6.850/vaal/core/preview_overlay.py +701 -0
  49. vaal_code-0.6.850/vaal/core/preview_server.py +781 -0
  50. vaal_code-0.6.850/vaal/core/progress.py +244 -0
  51. vaal_code-0.6.850/vaal/core/providers.py +780 -0
  52. vaal_code-0.6.850/vaal/core/raw_input.py +530 -0
  53. vaal_code-0.6.850/vaal/core/remote_control.py +191 -0
  54. vaal_code-0.6.850/vaal/core/repl.py +4767 -0
  55. vaal_code-0.6.850/vaal/core/scheduler.py +242 -0
  56. vaal_code-0.6.850/vaal/core/self_repair.py +276 -0
  57. vaal_code-0.6.850/vaal/core/server.py +330 -0
  58. vaal_code-0.6.850/vaal/core/session.py +89 -0
  59. vaal_code-0.6.850/vaal/core/skills.py +148 -0
  60. vaal_code-0.6.850/vaal/core/split_pane.py +198 -0
  61. vaal_code-0.6.850/vaal/core/ssh_remote.py +250 -0
  62. vaal_code-0.6.850/vaal/core/task_manager.py +167 -0
  63. vaal_code-0.6.850/vaal/core/terminal_profile.py +180 -0
  64. vaal_code-0.6.850/vaal/core/terminal_theme.py +201 -0
  65. vaal_code-0.6.850/vaal/core/theme.py +48 -0
  66. vaal_code-0.6.850/vaal/core/theme_screen.py +185 -0
  67. vaal_code-0.6.850/vaal/core/tool_executor.py +105 -0
  68. vaal_code-0.6.850/vaal/core/undo_system.py +313 -0
  69. vaal_code-0.6.850/vaal/core/user_config.py +125 -0
  70. vaal_code-0.6.850/vaal/core/vim_mode.py +30 -0
  71. vaal_code-0.6.850/vaal/core/voice.py +211 -0
  72. vaal_code-0.6.850/vaal/core/watch_mode.py +169 -0
  73. vaal_code-0.6.850/vaal/tools/__init__.py +0 -0
  74. vaal_code-0.6.850/vaal/tools/agent_tools.py +225 -0
  75. vaal_code-0.6.850/vaal/tools/api_tools.py +319 -0
  76. vaal_code-0.6.850/vaal/tools/bash_tool.py +179 -0
  77. vaal_code-0.6.850/vaal/tools/db_tools.py +407 -0
  78. vaal_code-0.6.850/vaal/tools/diff_tools.py +140 -0
  79. vaal_code-0.6.850/vaal/tools/file_tools.py +116 -0
  80. vaal_code-0.6.850/vaal/tools/git_tools.py +311 -0
  81. vaal_code-0.6.850/vaal/tools/interaction_tools.py +58 -0
  82. vaal_code-0.6.850/vaal/tools/lsp_tools.py +304 -0
  83. vaal_code-0.6.850/vaal/tools/memory_tools.py +79 -0
  84. vaal_code-0.6.850/vaal/tools/notebook_tools.py +310 -0
  85. vaal_code-0.6.850/vaal/tools/plugin_tools.py +272 -0
  86. vaal_code-0.6.850/vaal/tools/regex_tools.py +209 -0
  87. vaal_code-0.6.850/vaal/tools/registry.py +57 -0
  88. vaal_code-0.6.850/vaal/tools/search_tools.py +117 -0
  89. vaal_code-0.6.850/vaal/tools/sql_tools.py +222 -0
  90. vaal_code-0.6.850/vaal/tools/ssh_tools.py +36 -0
  91. vaal_code-0.6.850/vaal/tools/todo_tools.py +96 -0
  92. vaal_code-0.6.850/vaal/tools/web_tools.py +152 -0
  93. vaal_code-0.6.850/vaal_code.egg-info/PKG-INFO +106 -0
  94. vaal_code-0.6.850/vaal_code.egg-info/SOURCES.txt +96 -0
  95. vaal_code-0.6.850/vaal_code.egg-info/dependency_links.txt +1 -0
  96. vaal_code-0.6.850/vaal_code.egg-info/entry_points.txt +2 -0
  97. vaal_code-0.6.850/vaal_code.egg-info/requires.txt +5 -0
  98. vaal_code-0.6.850/vaal_code.egg-info/top_level.txt +1 -0
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: vaal-code
3
+ Version: 0.6.850
4
+ Summary: AI coding agent CLI — local LLMs via Ollama or cloud models via API
5
+ License: MIT
6
+ Project-URL: Homepage, https://e-e.tools/vaal
7
+ Project-URL: Repository, https://github.com/0xE666/vaal
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: httpx>=0.25.0
11
+ Requires-Dist: rich>=13.0.0
12
+ Provides-Extra: voice
13
+ Requires-Dist: SpeechRecognition; extra == "voice"
14
+
15
+ # Vaal
16
+
17
+ AI coding agent CLI. Runs local LLMs via Ollama or cloud models via API. Named after the Vaal Orb from Path of Exile.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ git clone https://github.com/0xE666/vaal.git
23
+ cd vaal
24
+ python install.py
25
+ ```
26
+
27
+ That's it. `vaal` is now available as a command from anywhere, just like `claude`.
28
+
29
+ Or manually:
30
+
31
+ ```bash
32
+ pip install -e .
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```bash
38
+ # With Ollama (free, local)
39
+ vaal
40
+
41
+ # With Claude
42
+ vaal -m anthropic:claude-sonnet-4-20250514
43
+
44
+ # With OpenAI
45
+ vaal -m openai:gpt-4o
46
+ ```
47
+
48
+ On first run with a cloud model, use `/login` to add your API key:
49
+
50
+ ```
51
+ /login anthropic sk-ant-api03-your-key-here
52
+ /login openai sk-your-key-here
53
+ ```
54
+
55
+ Or create a vaal account at [e-e.tools/vaal](https://e-e.tools/vaal):
56
+
57
+ ```
58
+ /register
59
+ ```
60
+
61
+ ## Features
62
+
63
+ - **45 tools** — bash, file read/write/edit, git, grep, glob, web search, agents, notebooks, and more
64
+ - **Subagents** — spawn background agents (explore, plan, verify, code-review) that work in parallel
65
+ - **80+ slash commands** — `/commit`, `/test`, `/pr`, `/diff`, `/pin`, `/remote`, `/plugin`, and more
66
+ - **Multi-provider** — Ollama, Anthropic, OpenAI, Groq, OpenRouter, DeepSeek, Together
67
+ - **Self-repair** — catches its own crashes, asks the LLM to fix the bug, patches itself
68
+ - **Remote control** — control vaal from a browser at e-e.tools/vaal
69
+ - **Plugins** — create custom tools with `/plugin create` or let the LLM generate them
70
+ - **Tab completion** — file paths auto-complete on Tab
71
+ - **Persistent history** — Up arrow recalls prompts across sessions
72
+
73
+ ## Providers
74
+
75
+ | Provider | Auth | Example |
76
+ |----------|------|---------|
77
+ | Ollama | None (local) | `vaal -m qwen3:8b` |
78
+ | Anthropic | API key or Max/Pro OAuth | `vaal -m anthropic:claude-opus-4-6` |
79
+ | OpenAI | API key | `vaal -m openai:gpt-4o` |
80
+ | Groq | API key | `vaal -m groq:llama-3.3-70b-versatile` |
81
+ | OpenRouter | API key | `vaal -m openrouter:anthropic/claude-sonnet-4` |
82
+ | DeepSeek | API key | `vaal -m deepseek:deepseek-chat` |
83
+ | Together | API key | `vaal -m together:meta-llama/Llama-3.3-70B` |
84
+
85
+ ## Key Commands
86
+
87
+ ```
88
+ /help — All commands
89
+ /login — Add API key
90
+ /register — Create vaal account
91
+ /commit — Auto-generate commit message
92
+ /test — Run tests, auto-fix failures
93
+ /pr — Create pull request
94
+ /pin <file> — Keep file in context
95
+ /remote — Control from browser
96
+ /plugin create — Create custom tools
97
+ /effort — Set inference level
98
+ /doctor — System diagnostics
99
+ ```
100
+
101
+ ## Requirements
102
+
103
+ - Python 3.11+
104
+ - httpx, rich (auto-installed)
105
+ - Ollama (optional, for local models)
106
+ - Git (optional, for git commands)
@@ -0,0 +1,92 @@
1
+ # Vaal
2
+
3
+ AI coding agent CLI. Runs local LLMs via Ollama or cloud models via API. Named after the Vaal Orb from Path of Exile.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ git clone https://github.com/0xE666/vaal.git
9
+ cd vaal
10
+ python install.py
11
+ ```
12
+
13
+ That's it. `vaal` is now available as a command from anywhere, just like `claude`.
14
+
15
+ Or manually:
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # With Ollama (free, local)
25
+ vaal
26
+
27
+ # With Claude
28
+ vaal -m anthropic:claude-sonnet-4-20250514
29
+
30
+ # With OpenAI
31
+ vaal -m openai:gpt-4o
32
+ ```
33
+
34
+ On first run with a cloud model, use `/login` to add your API key:
35
+
36
+ ```
37
+ /login anthropic sk-ant-api03-your-key-here
38
+ /login openai sk-your-key-here
39
+ ```
40
+
41
+ Or create a vaal account at [e-e.tools/vaal](https://e-e.tools/vaal):
42
+
43
+ ```
44
+ /register
45
+ ```
46
+
47
+ ## Features
48
+
49
+ - **45 tools** — bash, file read/write/edit, git, grep, glob, web search, agents, notebooks, and more
50
+ - **Subagents** — spawn background agents (explore, plan, verify, code-review) that work in parallel
51
+ - **80+ slash commands** — `/commit`, `/test`, `/pr`, `/diff`, `/pin`, `/remote`, `/plugin`, and more
52
+ - **Multi-provider** — Ollama, Anthropic, OpenAI, Groq, OpenRouter, DeepSeek, Together
53
+ - **Self-repair** — catches its own crashes, asks the LLM to fix the bug, patches itself
54
+ - **Remote control** — control vaal from a browser at e-e.tools/vaal
55
+ - **Plugins** — create custom tools with `/plugin create` or let the LLM generate them
56
+ - **Tab completion** — file paths auto-complete on Tab
57
+ - **Persistent history** — Up arrow recalls prompts across sessions
58
+
59
+ ## Providers
60
+
61
+ | Provider | Auth | Example |
62
+ |----------|------|---------|
63
+ | Ollama | None (local) | `vaal -m qwen3:8b` |
64
+ | Anthropic | API key or Max/Pro OAuth | `vaal -m anthropic:claude-opus-4-6` |
65
+ | OpenAI | API key | `vaal -m openai:gpt-4o` |
66
+ | Groq | API key | `vaal -m groq:llama-3.3-70b-versatile` |
67
+ | OpenRouter | API key | `vaal -m openrouter:anthropic/claude-sonnet-4` |
68
+ | DeepSeek | API key | `vaal -m deepseek:deepseek-chat` |
69
+ | Together | API key | `vaal -m together:meta-llama/Llama-3.3-70B` |
70
+
71
+ ## Key Commands
72
+
73
+ ```
74
+ /help — All commands
75
+ /login — Add API key
76
+ /register — Create vaal account
77
+ /commit — Auto-generate commit message
78
+ /test — Run tests, auto-fix failures
79
+ /pr — Create pull request
80
+ /pin <file> — Keep file in context
81
+ /remote — Control from browser
82
+ /plugin create — Create custom tools
83
+ /effort — Set inference level
84
+ /doctor — System diagnostics
85
+ ```
86
+
87
+ ## Requirements
88
+
89
+ - Python 3.11+
90
+ - httpx, rich (auto-installed)
91
+ - Ollama (optional, for local models)
92
+ - Git (optional, for git commands)
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vaal-code"
7
+ dynamic = ["version"]
8
+ description = "AI coding agent CLI — local LLMs via Ollama or cloud models via API"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.11"
12
+ dependencies = [
13
+ "httpx>=0.25.0",
14
+ "rich>=13.0.0",
15
+ ]
16
+ [project.optional-dependencies]
17
+ voice = ["SpeechRecognition"]
18
+
19
+ [project.scripts]
20
+ vaal = "vaal.__main__:main"
21
+
22
+ [project.urls]
23
+ Homepage = "https://e-e.tools/vaal"
24
+ Repository = "https://github.com/0xE666/vaal"
25
+
26
+ [tool.setuptools.dynamic]
27
+ version = {attr = "vaal.__version__"}
28
+
29
+ [tool.setuptools.packages.find]
30
+ include = ["vaal*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,382 @@
1
+ """Comprehensive test of all vaal features."""
2
+ import os
3
+ import sys
4
+ import json
5
+ import tempfile
6
+ import shutil
7
+
8
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
9
+ os.chdir(os.path.join(os.path.dirname(__file__), ".."))
10
+
11
+ R = {}
12
+
13
+ def t(name, cond):
14
+ R[name] = bool(cond)
15
+ print(f' {"OK" if cond else "FAIL"}: {name}')
16
+
17
+
18
+ # ── TOOLS ──────────────────────────────────────────────
19
+
20
+ td = tempfile.mkdtemp(prefix="vt_")
21
+ os.makedirs(os.path.join(td, "src"))
22
+ hp = os.path.join(td, "src/hello.py")
23
+ open(hp, "w").write('def greet(name):\n return f"Hello, {name}!"\nclass Foo:\n pass\n')
24
+ open(os.path.join(td, "src/util.py"), "w").write("import os\ndef get_cwd():\n return os.getcwd()\n")
25
+ open(os.path.join(td, "notes.md"), "w").write("# Notes\n")
26
+
27
+ print("=== BASH ===")
28
+ from vaal.tools.bash_tool import bash_tool
29
+ t("bash:echo", "hello" in bash_tool(command="echo hello"))
30
+ t("bash:ver", "Python" in bash_tool(command="python --version"))
31
+
32
+ print("=== FILE TOOLS ===")
33
+ from vaal.tools.file_tools import read_tool, write_tool, edit_tool
34
+ t("read", "def greet" in read_tool(file_path=hp))
35
+ t("read:miss", "error" in read_tool(file_path="/no/file").lower())
36
+ nf = os.path.join(td, "o.txt")
37
+ write_tool(file_path=nf, content="ok")
38
+ t("write", open(nf).read() == "ok")
39
+ edit_tool(file_path=hp, old_string="Hello", new_string="Hi")
40
+ t("edit", "Hi" in open(hp).read())
41
+ t("edit:miss", "not found" in edit_tool(file_path=hp, old_string="ZZ", new_string="x").lower())
42
+
43
+ print("=== SEARCH ===")
44
+ from vaal.tools.search_tools import glob_tool, grep_tool
45
+ t("glob", "hello.py" in glob_tool(pattern="**/*.py", path=td))
46
+ t("grep", "hello.py" in grep_tool(pattern="def greet", path=td))
47
+
48
+ print("=== LSP ===")
49
+ from vaal.tools.lsp_tools import find_definition, find_references, list_symbols, file_tree
50
+ t("lsp:def", "greet" in find_definition(symbol="greet", path=td))
51
+ t("lsp:refs", "greet" in find_references(symbol="greet", path=td))
52
+ t("lsp:sym", "greet" in list_symbols(file_path=hp))
53
+ t("lsp:tree", "src" in file_tree(path=td))
54
+
55
+ print("=== DIFF ===")
56
+ from vaal.tools.diff_tools import show_diff
57
+ f1, f2 = os.path.join(td, "a.txt"), os.path.join(td, "b.txt")
58
+ open(f1, "w").write("a\nb\n")
59
+ open(f2, "w").write("a\nc\n")
60
+ r = show_diff(file_a=f1, file_b=f2)
61
+ t("diff", "b" in r or "c" in r)
62
+
63
+ print("=== WEB ===")
64
+ from vaal.tools.web_tools import web_fetch, web_search
65
+ t("web:fetch", len(web_fetch(url="https://httpbin.org/get")) > 10)
66
+ t("web:search", len(web_search(query="python")) > 10)
67
+
68
+ print("=== MEMORY TOOLS ===")
69
+ from vaal.tools.memory_tools import save_memory_tool, search_memory_tool, list_memories_tool
70
+ save_memory_tool(name="_vt", content="t", memory_type="project", description="t")
71
+ t("mem:save", True)
72
+ t("mem:list", len(list_memories_tool()) >= 0)
73
+ t("mem:search", True)
74
+
75
+ print("=== NOTEBOOK ===")
76
+ from vaal.tools.notebook_tools import run_python, notebook_read, notebook_edit
77
+ t("py:print", "4" in run_python(code="print(2+2)"))
78
+ t("py:err", "Error" in run_python(code="1/0"))
79
+ t("py:expr", "[1, 2, 3]" in run_python(code="[1,2,3]"))
80
+ nbp = os.path.join(td, "t.ipynb")
81
+ json.dump({"nbformat": 4, "nbformat_minor": 5, "metadata": {}, "cells": [
82
+ {"cell_type": "code", "metadata": {}, "source": ["x=1"], "execution_count": None, "outputs": []}
83
+ ]}, open(nbp, "w"))
84
+ t("nb:read", "x=1" in notebook_read(path=nbp))
85
+ notebook_edit(path=nbp, action="insert_after", cell_index="0", content="y=2")
86
+ t("nb:edit", "2 cells" in notebook_read(path=nbp))
87
+ notebook_edit(path=nbp, action="replace", cell_index="0", content="z=3")
88
+ t("nb:replace", "z=3" in notebook_read(path=nbp, cell_index="0"))
89
+ notebook_edit(path=nbp, action="delete", cell_index="1")
90
+ t("nb:delete", "1 cells" in notebook_read(path=nbp))
91
+
92
+ print("=== SSH ===")
93
+ from vaal.tools.ssh_tools import ssh_exec
94
+ t("ssh:nc", "not connected" in ssh_exec(command="hi").lower())
95
+
96
+ print("=== TODO ===")
97
+ from vaal.core.task_manager import reset_task_manager
98
+ from vaal.tools.todo_tools import todo_create, todo_update, todo_list
99
+ reset_task_manager()
100
+ r = json.loads(todo_create(title="T"))
101
+ t("todo:create", r["status"] == "pending")
102
+ r2 = json.loads(todo_update(id=r["id"], status="completed"))
103
+ t("todo:update", r2["status"] == "completed")
104
+ t("todo:list", len(json.loads(todo_list())["todos"]) == 1)
105
+
106
+ print("=== AGENT TOOLS ===")
107
+ from vaal.tools.agent_tools import check_agent, list_agents, AGENT_TYPES
108
+ t("agent:types", len(AGENT_TYPES) == 5)
109
+ t("agent:list", "agents" in json.loads(list_agents()))
110
+ t("agent:miss", "error" in json.loads(check_agent(agent_id="xx")))
111
+
112
+ print("=== API ===")
113
+ from vaal.tools.api_tools import api_request, handle_api_command
114
+ t("api:get", len(api_request(method="GET", url="https://httpbin.org/get")) > 10)
115
+ t("api:hist", isinstance(handle_api_command("history"), str))
116
+
117
+ print("=== DB ===")
118
+ from vaal.tools.db_tools import handle_db_command
119
+ t("db:cmd", len(handle_db_command("")) > 0)
120
+
121
+ # ── CORE ──────────────────────────────────────────────
122
+
123
+ print("\n=== SESSION ===")
124
+ from vaal.core.session import Session
125
+ s = Session(model="test")
126
+ s.add_message("user", "hello")
127
+ s.add_message("assistant", "hi")
128
+ t("session:add", len(s.messages) == 2)
129
+ t("session:chat", len(s.get_chat_messages()) == 2)
130
+ s.save()
131
+ s2 = Session.load(s.session_id)
132
+ t("session:load", len(s2.messages) == 2)
133
+ t("session:list", isinstance(Session.list_sessions(), list))
134
+
135
+ print("=== CONTEXT ===")
136
+ from vaal.core.context import ContextManager, estimate_tokens, estimate_messages_tokens
137
+ cm = ContextManager(max_tokens=1000)
138
+ t("ctx:est", estimate_tokens("hello world") > 0)
139
+ t("ctx:pct", cm.usage_pct([{"role": "user", "content": "hi"}]) >= 0)
140
+
141
+ print("=== COST ===")
142
+ from vaal.core.cost_tracker import CostTracker
143
+ ct = CostTracker()
144
+ ct.record(label="t", input_tokens=100, output_tokens=50)
145
+ t("cost:total", ct.total_tokens == 150)
146
+ t("cost:summary", len(ct.summary()) > 0)
147
+
148
+ print("=== HISTORY ===")
149
+ from vaal.core.history import record_prompt, load_history, format_history
150
+ record_prompt("test", "m", "s")
151
+ t("hist:load", isinstance(load_history(limit=5), list))
152
+ t("hist:format", isinstance(format_history(load_history()), str))
153
+
154
+ print("=== AUTO_FIX ===")
155
+ from vaal.core.auto_fix import is_enabled, toggle, detect_bash_failure, build_fix_prompt
156
+ t("fix:enabled", isinstance(is_enabled(), bool))
157
+ old = is_enabled()
158
+ toggle()
159
+ t("fix:toggle", is_enabled() != old)
160
+ toggle()
161
+ t("fix:detect_ok", detect_bash_failure("read", "ok") is None)
162
+ t("fix:detect_err", detect_bash_failure("bash", "[exit code: 1] not found") is not None)
163
+ t("fix:detect_err2", detect_bash_failure("bash", "[error: timeout]") is not None)
164
+ t("fix:prompt", len(build_fix_prompt("ls", "not found", 1)) > 0)
165
+
166
+ print("=== BOOTSTRAP ===")
167
+ from vaal.core.bootstrap import scan_project
168
+ pi = scan_project(os.getcwd())
169
+ t("boot:scan", pi is not None)
170
+
171
+ print("=== USER_CONFIG ===")
172
+ from vaal.core.user_config import UserConfig
173
+ uc = UserConfig()
174
+ t("config:model", isinstance(uc.default_model, str))
175
+ t("config:dict", isinstance(uc.as_dict(), dict))
176
+
177
+ print("=== KEYBINDINGS ===")
178
+ from vaal.core.keybindings import KeybindingManager
179
+ kb = KeybindingManager()
180
+ t("keys:all", isinstance(kb.all_bindings(), dict))
181
+ t("keys:table", len(kb.format_table()) > 0)
182
+
183
+ print("=== VIM ===")
184
+ from vaal.core.vim_mode import VimMode
185
+ vm = VimMode()
186
+ t("vim:init", not vm.enabled)
187
+ vm.toggle()
188
+ t("vim:toggle", vm.enabled)
189
+ vm.toggle()
190
+
191
+ print("=== OUTPUT STYLES ===")
192
+ from vaal.core.output_styles import OutputStyleManager
193
+ osm = OutputStyleManager()
194
+ t("style:current", osm.current == "default")
195
+ t("style:avail", len(osm.available_styles()) > 0)
196
+
197
+ print("=== THEMES ===")
198
+ from vaal.core.terminal_theme import get_theme_names, get_theme, load_preference
199
+ t("theme:names", len(get_theme_names()) >= 5)
200
+ t("theme:get", get_theme("vaal") is not None)
201
+
202
+ print("=== PLANS ===")
203
+ from vaal.core.plans import create_plan, list_plans
204
+ plan = create_plan(title="Test", step_descriptions=["S1", "S2", "S3"])
205
+ t("plan:create", plan is not None)
206
+ t("plan:progress", "0/3" in plan.progress)
207
+ plan.advance()
208
+ t("plan:advance", "1/3" in plan.progress)
209
+
210
+ print("=== MEMORY STORE ===")
211
+ from vaal.core.memory import MemoryStore
212
+ ms = MemoryStore()
213
+ t("memstore:list", isinstance(ms.list_memories(), list))
214
+
215
+ print("=== BRANCHING ===")
216
+ from vaal.core.branching import BranchManager
217
+ bm = BranchManager()
218
+ bm.create_branch("test-b", [{"role": "user", "content": "hi"}])
219
+ t("branch:create", bm.has_branch("test-b"))
220
+ t("branch:list", len(bm.list_branches()) > 0)
221
+
222
+ print("=== MULTILINE ===")
223
+ from vaal.core.multiline import is_multiline_trigger
224
+ t("multi:triple", is_multiline_trigger('"""'))
225
+ t("multi:normal", not is_multiline_trigger("hello"))
226
+
227
+ print("=== SKILLS ===")
228
+ from vaal.core.skills import load_skills
229
+ t("skills:load", isinstance(load_skills(), list))
230
+
231
+ print("=== HOOKS ===")
232
+ from vaal.core.hooks import load_hooks, run_hooks
233
+ hooks = load_hooks()
234
+ t("hooks:load", isinstance(hooks, (dict, list)))
235
+ run_hooks("pre_tool", {"tool_name": "test"}, hooks)
236
+ t("hooks:run", True)
237
+
238
+ print("=== TASK_MANAGER ===")
239
+ reset_task_manager()
240
+ from vaal.core.task_manager import get_task_manager, TaskStatus
241
+ tm = get_task_manager()
242
+ task = tm.create(title="T")
243
+ t("tm:create", task.title == "T")
244
+ tm.update(task.id, status=TaskStatus.IN_PROGRESS)
245
+ t("tm:update", tm.get(task.id).status == TaskStatus.IN_PROGRESS)
246
+ tm.complete(task.id)
247
+ t("tm:complete", tm.get(task.id).status == TaskStatus.COMPLETED)
248
+ t("tm:summary", "1/1" in tm.summary)
249
+
250
+ print("=== AGENT POOL ===")
251
+ from vaal.core.agent import AgentPool, reset_agent_pool
252
+ reset_agent_pool()
253
+ pool = AgentPool()
254
+ t("pool:empty", pool.total_count == 0)
255
+
256
+ print("=== SCHEDULER ===")
257
+ from vaal.core.scheduler import get_scheduler
258
+ sched = get_scheduler()
259
+ t("sched:list", len(sched.list_tasks()) == 0)
260
+
261
+ print("=== COLLAB ===")
262
+ from vaal.core.collaborative import collab_start, collab_leave, collab_info
263
+ ok, code = collab_start()
264
+ t("collab:start", ok)
265
+ t("collab:info", collab_info() is not None)
266
+ collab_leave()
267
+ t("collab:leave", collab_info() is None)
268
+
269
+ print("=== PERMISSIONS ===")
270
+ from vaal.core.permissions import PermissionManager
271
+ pm = PermissionManager(mode="auto")
272
+ t("perm:safe", not pm.needs_approval("read"))
273
+ t("perm:danger", pm.needs_approval("bash"))
274
+ pm.approve("bash", remember=True)
275
+ t("perm:approve", not pm.needs_approval("bash"))
276
+ pm2 = PermissionManager(mode="yolo")
277
+ t("perm:yolo", not pm2.needs_approval("bash"))
278
+ pm3 = PermissionManager(mode="auto")
279
+ pm3.deny_tool("grep")
280
+ t("perm:deny", pm3.is_denied("grep"))
281
+ pm3.deny_prefix("git_")
282
+ t("perm:pfx", pm3.is_denied("git_commit"))
283
+
284
+ print("=== PLUGINS ===")
285
+ from vaal.core.plugins import load_plugins
286
+ t("plugins:load", isinstance(load_plugins(), list))
287
+
288
+ print("=== MCP ===")
289
+ from vaal.core.mcp_client import get_mcp_manager
290
+ mgr = get_mcp_manager()
291
+ t("mcp:list", isinstance(mgr.list_servers(), list))
292
+
293
+ print("=== UNDO ===")
294
+ from vaal.core.undo_system import get_undo_system
295
+ undo = get_undo_system()
296
+ undo.clear() # Clear any state from earlier tests
297
+ t("undo:empty", not undo.undo()[0])
298
+ t("redo:empty", not undo.redo()[0])
299
+
300
+ print("=== ENV_MANAGER ===")
301
+ from vaal.core.env_manager import EnvManager
302
+ em = EnvManager()
303
+ t("env:list", len(em.list_env()) > 0)
304
+ t("env:cmd", len(em.handle_command("")) > 0)
305
+
306
+ print("=== SPLIT_PANE ===")
307
+ from vaal.core.split_pane import SplitPane
308
+ sp = SplitPane()
309
+ t("split:cmd", len(sp.handle_command("")) > 0)
310
+
311
+ print("=== NOTIFICATIONS ===")
312
+ from vaal.core.notifications import get_notification_manager
313
+ nm = get_notification_manager()
314
+ t("notify:cmd", len(nm.handle_command("")) > 0)
315
+
316
+ print("=== ERROR_EXPLAIN ===")
317
+ from vaal.core.error_explain import detect_error, build_explain_prompt
318
+ t("err:yes", detect_error("Traceback (most recent call last):"))
319
+ t("err:no", not detect_error("all good"))
320
+ t("err:prompt", len(build_explain_prompt("TypeError: bad")) > 0)
321
+
322
+ print("=== MIGRATION ===")
323
+ from vaal.core.migration import get_migration_profile
324
+ t("migrate:flask", get_migration_profile("flask", "fastapi") is not None)
325
+ t("migrate:none", get_migration_profile("x", "y") is None)
326
+
327
+ print("=== CODE_REVIEW ===")
328
+ from vaal.core.code_review import build_review_prompt, parse_review_response
329
+ t("review:prompt", len(build_review_prompt("diff")) > 0)
330
+ parsed = parse_review_response("[CRITICAL] f.py:10\nSQL injection\n-> parameterize")
331
+ t("review:parse", len(parsed) > 0 and parsed[0].severity.value == "critical")
332
+
333
+ print("=== DEPS ===")
334
+ from vaal.core.deps import scan_dep_files, format_dep_report
335
+ deps = scan_dep_files()
336
+ t("deps:scan", isinstance(deps, list))
337
+
338
+ print("=== COT_DISPLAY ===")
339
+ from vaal.core.cot_display import CotDisplay
340
+ t("cot:init", CotDisplay() is not None)
341
+
342
+ print("=== PROGRESS ===")
343
+ from vaal.core.progress import get_progress_tracker
344
+ t("progress:init", get_progress_tracker().active_count == 0)
345
+
346
+ print("=== CLIPBOARD ===")
347
+ from vaal.core.clipboard import copy_to_clipboard, paste_from_clipboard
348
+ copy_to_clipboard("vaal_test_123")
349
+ t("clip:copy", True)
350
+ t("clip:paste", "vaal_test_123" in (paste_from_clipboard() or ""))
351
+
352
+ print("=== IMAGE_INPUT ===")
353
+ from vaal.core.image_input import is_image_path, encode_image, build_image_message, build_ollama_image_message, get_clipboard_image
354
+ t("img:path_yes", is_image_path("photo.png"))
355
+ t("img:path_no", not is_image_path("readme.md"))
356
+ t("img:clipboard", True) # just verify no crash
357
+ get_clipboard_image()
358
+
359
+ print("=== SSH_REMOTE ===")
360
+ from vaal.core.ssh_remote import get_ssh_manager
361
+ sm = get_ssh_manager()
362
+ t("sshm:info", sm.info() is None) # not connected
363
+ t("sshm:connected", not sm.is_connected)
364
+
365
+ # ── CLEANUP & SUMMARY ──────────────────────────────────
366
+
367
+ try:
368
+ shutil.rmtree(td, ignore_errors=True)
369
+ except Exception:
370
+ pass
371
+
372
+ passed = sum(1 for v in R.values() if v)
373
+ failed = sum(1 for v in R.values() if not v)
374
+ print(f"\n{'=' * 50}")
375
+ print(f" {passed}/{len(R)} PASSED, {failed} FAILED")
376
+ print(f"{'=' * 50}")
377
+ if failed:
378
+ print("FAILURES:")
379
+ for k, v in R.items():
380
+ if not v:
381
+ print(f" FAIL: {k}")
382
+ sys.exit(1)
@@ -0,0 +1,2 @@
1
+ """Vaal — Local LLM coding assistant CLI."""
2
+ __version__ = "0.6.850"