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 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)`.
@@ -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