sql-harness 0.1.0__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.
- sql_harness/SKILL.md +61 -0
- sql_harness/__init__.py +27 -0
- sql_harness/agent_loader.py +55 -0
- sql_harness/cli.py +869 -0
- sql_harness/config.py +241 -0
- sql_harness/drivers/__init__.py +59 -0
- sql_harness/drivers/mysql.py +43 -0
- sql_harness/drivers/postgres.py +55 -0
- sql_harness/drivers/redis.py +33 -0
- sql_harness/drivers/sqlite.py +39 -0
- sql_harness/drivers/ssh.py +293 -0
- sql_harness/helpers.py +789 -0
- sql_harness/manager.py +122 -0
- sql_harness/paths.py +112 -0
- sql_harness/run.py +85 -0
- sql_harness-0.1.0.dist-info/METADATA +137 -0
- sql_harness-0.1.0.dist-info/RECORD +19 -0
- sql_harness-0.1.0.dist-info/WHEEL +4 -0
- sql_harness-0.1.0.dist-info/entry_points.txt +2 -0
sql_harness/SKILL.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sql-harness
|
|
3
|
+
description: "Use sql-harness for SQL work — querying, schema inspection, migrations, and cross-database workflows."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sql-harness
|
|
7
|
+
|
|
8
|
+
Direct SQL access via a thin Python heredoc CLI. Connections live in plaintext in a single TOML file (default: `~/.config/sql-harness/connections.toml`).
|
|
9
|
+
|
|
10
|
+
For setup or install problems, read `install.md`. For stuck-point mechanics, see `interaction-skills/` (cross-DB) and `interaction-skills/postgres/` (PG optimization depth: plan reading, B-Tree scans, specialized indexes, table optimization, slow-query detection).
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
sql-harness --help
|
|
16
|
+
sql-harness list
|
|
17
|
+
sql-harness add <name> --driver postgres --url 'postgresql://...'
|
|
18
|
+
sql-harness test <name>
|
|
19
|
+
|
|
20
|
+
sql-harness <<'PY'
|
|
21
|
+
use_workspace("local_pg")
|
|
22
|
+
print(query("SELECT version()"))
|
|
23
|
+
print(list_tables())
|
|
24
|
+
PY
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- Helpers are pre-imported; you can call them by name in heredoc mode.
|
|
28
|
+
- **First call requires `use_workspace(name)`** — there is no implicit default.
|
|
29
|
+
- For task-specific helpers, drop them into `$BH_SQL_AGENT_WORKSPACE/agent_helpers.py` and they'll merge into the heredoc namespace.
|
|
30
|
+
|
|
31
|
+
## Connection-pool skill + workspace skill
|
|
32
|
+
|
|
33
|
+
Read these when working across multiple connections or tuning pool behavior:
|
|
34
|
+
|
|
35
|
+
- `agent-workspace/skills/pool.md` — pool sizing, `pre_ping`, idle reuse
|
|
36
|
+
- `agent-workspace/skills/workspace.md` — workspace isolation, multi-DB workflows
|
|
37
|
+
- `interaction-skills/save-run-cycle.md` — the correct workflow: write a block, `save` it, `run` it; repeat. Don't batch-save at the end.
|
|
38
|
+
- `interaction-skills/zone-skill-auto-surface.md` — set `$BH_SQL_ZONE_SKILLS=1` and `use_workspace(name)` returns a dict auto-attaching `zone_skills` + `zone_scripts` (mirrors browser-harness's `BH_DOMAIN_SKILLS=1` + `goto_url` returning domain skills).
|
|
39
|
+
|
|
40
|
+
## Local Chrome? No, this is SQL.
|
|
41
|
+
|
|
42
|
+
There is no Chrome and no remote "cloud" backend. SQL is single-process. All engines live in `SqlHarness`'s in-memory registry.
|
|
43
|
+
|
|
44
|
+
## Design Constraints
|
|
45
|
+
|
|
46
|
+
- One connection = one workspace; never share engines across workspaces.
|
|
47
|
+
- Connection pool defaults: `size=5, recycle=3600, pre_ping=True` (per-connection overrides win).
|
|
48
|
+
- `with_transaction()` yields a `Connection`; use `conn.execute(text(...))` for raw control.
|
|
49
|
+
- `query()` returns `list[dict]`; use `execute()` for INSERT/UPDATE/DELETE.
|
|
50
|
+
- `list_tables()` and `describe()` are read-only schema introspection helpers.
|
|
51
|
+
|
|
52
|
+
## Gotchas
|
|
53
|
+
|
|
54
|
+
- Driver label in TOML must match the URL scheme (`postgres` ⇄ `postgresql://`, `mysql` ⇄ `mysql+pymysql://`).
|
|
55
|
+
- Passwords in TOML are plaintext; use `${env:VAR}` indirection for prod secrets.
|
|
56
|
+
- `query()` is for SELECTs only. For INSERTs, use `execute()`.
|
|
57
|
+
- For tables > 10k rows, use `engine.connect().execution_options(stream_results=True)` and iterate manually — `query()` loads everything into memory.
|
|
58
|
+
|
|
59
|
+
## Domain / table skills
|
|
60
|
+
|
|
61
|
+
PG optimization practice: 71 real [pgexercises](https://pgexercises.com/) problems in `practice/pgexercises/` — one `.sql` file per problem (header = question, body = answer). Load schema/seed and replay an answer with `run_sql_file`; tune it with `explain_analyze`. To add a per-table or per-schema skill, drop a markdown file into `agent-workspace/skills/<name>.md`. Read with `apply_skill(name)`.
|
sql_harness/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""sql-harness — single-process SQL CLI for LLM agents.
|
|
2
|
+
|
|
3
|
+
Mirrors browser-harness's structure: a small protected core plus an
|
|
4
|
+
agent-editable workspace. Connections live in plaintext in one TOML file
|
|
5
|
+
(default: ~/.config/sql-harness/connections.toml).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .config import ConnectionConfig, ConnectionsConfig, PoolConfig, load, save
|
|
11
|
+
from .drivers import Driver, get_driver
|
|
12
|
+
from .manager import SqlHarness, Workspace
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ConnectionConfig",
|
|
18
|
+
"ConnectionsConfig",
|
|
19
|
+
"Driver",
|
|
20
|
+
"PoolConfig",
|
|
21
|
+
"SqlHarness",
|
|
22
|
+
"Workspace",
|
|
23
|
+
"__version__",
|
|
24
|
+
"get_driver",
|
|
25
|
+
"load",
|
|
26
|
+
"save",
|
|
27
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Auto-load agent-editable helpers from $BH_SQL_AGENT_WORKSPACE/agent_helpers.py.
|
|
2
|
+
|
|
3
|
+
Mirrors browser-harness's helpers.py:493-508 pattern. Public attributes
|
|
4
|
+
(no leading underscore) are merged into the supplied globals dict.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import importlib.util
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from types import ModuleType
|
|
13
|
+
|
|
14
|
+
from .paths import workspace_dir
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _resolve_agent_file() -> Path | None:
|
|
18
|
+
override = os.environ.get("BH_SQL_AGENT_WORKSPACE")
|
|
19
|
+
if override:
|
|
20
|
+
return Path(override).expanduser() / "agent_helpers.py"
|
|
21
|
+
return workspace_dir() / "agent_helpers.py"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_agent_helpers(target_globals: dict) -> int:
|
|
25
|
+
"""Load agent_helpers.py and merge public names into `target_globals`.
|
|
26
|
+
|
|
27
|
+
The agent_helpers.py module is exec'd in a namespace pre-seeded with the
|
|
28
|
+
core helpers (query, execute, use_workspace, ...) so that functions defined
|
|
29
|
+
there can call them directly. Returns the number of names merged
|
|
30
|
+
(0 if no file present).
|
|
31
|
+
"""
|
|
32
|
+
path = _resolve_agent_file()
|
|
33
|
+
if path is None or not path.is_file():
|
|
34
|
+
return 0
|
|
35
|
+
spec = importlib.util.spec_from_file_location("sql_harness_agent_helpers", path)
|
|
36
|
+
if spec is None or spec.loader is None:
|
|
37
|
+
return 0
|
|
38
|
+
module = importlib.util.module_from_spec(spec)
|
|
39
|
+
|
|
40
|
+
# Pre-seed the module namespace with core helpers so that functions
|
|
41
|
+
# defined in agent_helpers.py can call query()/execute()/etc. by name.
|
|
42
|
+
from . import helpers as _helpers
|
|
43
|
+
|
|
44
|
+
for _n, _v in vars(_helpers).items():
|
|
45
|
+
if not _n.startswith("_"):
|
|
46
|
+
setattr(module, _n, _v)
|
|
47
|
+
|
|
48
|
+
spec.loader.exec_module(module)
|
|
49
|
+
merged = 0
|
|
50
|
+
for name, value in vars(module).items():
|
|
51
|
+
if name.startswith("_"):
|
|
52
|
+
continue
|
|
53
|
+
target_globals[name] = value
|
|
54
|
+
merged += 1
|
|
55
|
+
return merged
|