sql-harness 0.2.2__tar.gz → 0.2.3__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.
- {sql_harness-0.2.2 → sql_harness-0.2.3}/PKG-INFO +4 -3
- {sql_harness-0.2.2 → sql_harness-0.2.3}/README.md +3 -2
- {sql_harness-0.2.2 → sql_harness-0.2.3}/SKILL.md +5 -7
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/docker-via-ssh.md +1 -2
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/ssh.md +1 -1
- {sql_harness-0.2.2 → sql_harness-0.2.3}/pyproject.toml +2 -2
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/__init__.py +6 -1
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/cli.py +28 -22
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/helpers.py +17 -18
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/paths.py +1 -1
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_cli.py +1 -1
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_manager.py +4 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/uv.lock +1 -1
- sql_harness-0.2.2/agent-workspace/skills/README.md +0 -26
- sql_harness-0.2.2/agent-workspace/skills/docker-deploy.md +0 -85
- sql_harness-0.2.2/agent-workspace/skills/ssh.md +0 -70
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.claude-plugin/marketplace.json +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.claude-plugin/plugin.json +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.env.example +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/.gitignore +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/AGENTS.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/README.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/agent_helpers.example.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/agent_helpers.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/zones/example_pg/scripts/sh_demo_crud.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/zones/example_pg/skills/sh_demo-schema.md +0 -0
- {sql_harness-0.2.2/agent-workspace → sql_harness-0.2.3/agent-workspace/zones/meta}/skills/pool.md +0 -0
- {sql_harness-0.2.2/agent-workspace → sql_harness-0.2.3/agent-workspace/zones/meta}/skills/workspace.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docker-services/coturn/README.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docker-services/coturn/docker-compose.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docker-services/coturn/turnserver.conf +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docker-services/mysql/docker-compose.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docker-services/redis/docker-compose.yml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docs/connections-toml-example.toml +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docs/headless-notes.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docs/maintainer.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/docs/standalone-install.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/install.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/aggregations.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/encoding-and-charset.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/indexes-and-explain.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/joins.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/json-columns.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/large-result-sets.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/migrations.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/pooling.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/postgres/btree-indexes.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/postgres/plan-reading.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/postgres/slow-queries-joins.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/postgres/specialized-indexes.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/postgres/table-optimization.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/recursive-ctes.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/aggregations-advanced.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/joins-advanced.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/recursive-ctes-patterns.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/window-functions-catalog.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/save-run-cycle.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/schema-introspection.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/timeouts-and-cancellation.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/transactions.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/window-functions.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/zone-skill-auto-surface.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/practice/pgexercises/README.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/skills/sql-harness/README.txt +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/skills/sql-harness/SKILL.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/skills/sql-harness/references/install.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/sql-harness +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/SKILL.md +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/agent_loader.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/config.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/__init__.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/mysql.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/postgres.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/redis.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/sqlite.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/drivers/ssh.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/manager.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/src/sql_harness/run.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/__init__.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/conftest.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/__init__.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/test_mysql.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/test_pg_optimization.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/test_pgexercises_practice.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/test_postgres.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/integration/test_ssh.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/__init__.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_agent_loader.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_config.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_helpers.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_paths.py +0 -0
- {sql_harness-0.2.2 → sql_harness-0.2.3}/tests/unit/test_ssh.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql-harness
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Single-process SQL + SSH CLI for LLM agents. PostgreSQL/MySQL/SSH via SQLAlchemy + paramiko. Plaintext credentials in one TOML file. Helpers auto-injected into the heredoc namespace.
|
|
5
5
|
Project-URL: Source, https://github.com/zhaoliuxue/much_bigpy/tree/master/lab/sql_harness
|
|
6
6
|
Project-URL: Issues, https://github.com/zhaoliuxue/much_bigpy/issues
|
|
@@ -58,7 +58,8 @@ PY
|
|
|
58
58
|
- `lab/sql_harness/src/sql_harness/` — protected core package
|
|
59
59
|
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/connections.toml` — plaintext credentials in ONE place
|
|
60
60
|
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/agent_helpers.py` — task-specific helpers
|
|
61
|
-
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/skills/` — per-
|
|
61
|
+
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/zones/<conn>/skills/` — per-DSN skills (one zone per connection)
|
|
62
|
+
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/zones/meta/skills/` — cross-DSN strategy skills (passive fallback for `apply_skill`)
|
|
62
63
|
|
|
63
64
|
## Why mirror browser-harness?
|
|
64
65
|
|
|
@@ -78,7 +79,7 @@ PY
|
|
|
78
79
|
|
|
79
80
|
PRs and improvements welcome. See `AGENTS.md` for code priorities.
|
|
80
81
|
|
|
81
|
-
- **Skills are written by the harness, not by you.** When you figure out a non-obvious SQL flow (a weird schema, a slow query, a JSON column trick), file a skill in `agent-workspace/skills/<name>.md
|
|
82
|
+
- **Skills are written by the harness, not by you.** When you figure out a non-obvious SQL flow (a weird schema, a slow query, a JSON column trick), file a skill in `agent-workspace/zones/<conn>/skills/<name>.md` for that DSN, or `agent-workspace/zones/meta/skills/<name>.md` if it generalizes. Future sessions will read it before re-discovering it.
|
|
82
83
|
- Bug fixes, new drivers, helper additions all welcome.
|
|
83
84
|
|
|
84
85
|
## License
|
|
@@ -35,7 +35,8 @@ PY
|
|
|
35
35
|
- `lab/sql_harness/src/sql_harness/` — protected core package
|
|
36
36
|
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/connections.toml` — plaintext credentials in ONE place
|
|
37
37
|
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/agent_helpers.py` — task-specific helpers
|
|
38
|
-
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/skills/` — per-
|
|
38
|
+
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/zones/<conn>/skills/` — per-DSN skills (one zone per connection)
|
|
39
|
+
- `${XDG_CONFIG_HOME:-~/.config}/sql-harness/agent-workspace/zones/meta/skills/` — cross-DSN strategy skills (passive fallback for `apply_skill`)
|
|
39
40
|
|
|
40
41
|
## Why mirror browser-harness?
|
|
41
42
|
|
|
@@ -55,7 +56,7 @@ PY
|
|
|
55
56
|
|
|
56
57
|
PRs and improvements welcome. See `AGENTS.md` for code priorities.
|
|
57
58
|
|
|
58
|
-
- **Skills are written by the harness, not by you.** When you figure out a non-obvious SQL flow (a weird schema, a slow query, a JSON column trick), file a skill in `agent-workspace/skills/<name>.md
|
|
59
|
+
- **Skills are written by the harness, not by you.** When you figure out a non-obvious SQL flow (a weird schema, a slow query, a JSON column trick), file a skill in `agent-workspace/zones/<conn>/skills/<name>.md` for that DSN, or `agent-workspace/zones/meta/skills/<name>.md` if it generalizes. Future sessions will read it before re-discovering it.
|
|
59
60
|
- Bug fixes, new drivers, helper additions all welcome.
|
|
60
61
|
|
|
61
62
|
## License
|
|
@@ -58,14 +58,12 @@ PG optimization depth — `interaction-skills/postgres/`:
|
|
|
58
58
|
- `interaction-skills/postgres/specialized-indexes.md`
|
|
59
59
|
- `interaction-skills/postgres/table-optimization.md`
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## Cross-DSN strategy skills
|
|
62
62
|
|
|
63
|
-
Read these when working across multiple connections, tuning pool behavior, or operating remote hosts:
|
|
63
|
+
Read these when working across multiple connections, tuning pool behavior, or operating remote hosts. Reached via `apply_skill("pool")` etc. from any active zone — `zones/meta/skills/` is the passive fallback layer (the legacy global `agent-workspace/skills/` was collapsed into a regular zone so `apply_skill()` has a single shape: active zone or meta).
|
|
64
64
|
|
|
65
|
-
- `agent-workspace/skills/pool.md` — pool sizing, `pre_ping`, idle reuse.
|
|
66
|
-
- `agent-workspace/skills/workspace.md` — workspace isolation, multi-DB workflows.
|
|
67
|
-
- `agent-workspace/skills/ssh.md` — strategy for hitting remote DB hosts over SSH.
|
|
68
|
-
- `agent-workspace/skills/docker-deploy.md` — strategy: when to use docker-via-ssh, the `~/sql-harness/<svc>/` cohesion convention, service catalog (coturn/mysql/redis/...).
|
|
65
|
+
- `agent-workspace/zones/meta/skills/pool.md` — pool sizing, `pre_ping`, idle reuse.
|
|
66
|
+
- `agent-workspace/zones/meta/skills/workspace.md` — workspace isolation, multi-DB workflows.
|
|
69
67
|
|
|
70
68
|
## What actually works (field-tested)
|
|
71
69
|
|
|
@@ -98,4 +96,4 @@ Read these when working across multiple connections, tuning pool behavior, or op
|
|
|
98
96
|
|
|
99
97
|
## Domain / table skills
|
|
100
98
|
|
|
101
|
-
PG optimization practice: `practice/pgexercises/` (71 pgexercises problems, see its README). Per-table or per-schema skills: drop a markdown file into `agent-workspace/skills/<name>.md`, read with `apply_skill(name)`.
|
|
99
|
+
PG optimization practice: `practice/pgexercises/` (71 pgexercises problems, see its README). Per-table or per-schema skills: drop a markdown file into `agent-workspace/zones/<conn>/skills/<name>.md`, read with `apply_skill(name)`. Cross-DSN strategy goes to `zones/meta/skills/` instead.
|
|
@@ -130,8 +130,7 @@ Those live in `docker-services/coturn/README.md` — read before deploying cotur
|
|
|
130
130
|
|
|
131
131
|
## See also
|
|
132
132
|
|
|
133
|
-
- `agent-workspace/skills/docker-deploy.md`
|
|
134
|
-
service catalog, why `~/sql-harness/<svc>/` convention)
|
|
133
|
+
- This file's "Detection" / "Approach" sections (decision P: the legacy strategy stub in `agent-workspace/zones/meta/skills/docker-deploy.md` was collapsed into the mechanic; no separate doc).
|
|
135
134
|
- `interaction-skills/ssh.md` — the raw ssh_exec/upload/download helpers
|
|
136
135
|
- `docker-services/<svc>/` — shipped templates (coturn, mysql, redis, ...)
|
|
137
136
|
- `docker-services/<svc>/README.md` — service-specific gotchas (e.g. coturn)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SSH mechanic — how to talk to an SSH workspace
|
|
2
2
|
|
|
3
|
-
> **Mechanic guide.**
|
|
3
|
+
> **Mechanic guide.** Strategy notes (when to reach for the SSH driver, auth choice) are folded into this file's "Detection" / "Approach" sections below (decision P: the legacy strategy stub was collapsed into the mechanic).
|
|
4
4
|
|
|
5
5
|
## Activation
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sql-harness"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "Single-process SQL + SSH CLI for LLM agents. PostgreSQL/MySQL/SSH via SQLAlchemy + paramiko. Plaintext credentials in one TOML file. Helpers auto-injected into the heredoc namespace."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -52,7 +52,7 @@ artifacts = ["src/sql_harness/SKILL.md"]
|
|
|
52
52
|
# Paths are relative to this pyproject.toml (lab/sql_harness/).
|
|
53
53
|
"SKILL.md" = "sql_harness/_skills/SKILL.md"
|
|
54
54
|
"interaction-skills" = "sql_harness/_skills/interaction-skills"
|
|
55
|
-
"agent-workspace/skills" = "sql_harness/_skills/
|
|
55
|
+
"agent-workspace/zones/meta/skills" = "sql_harness/_skills/zones-meta-skills"
|
|
56
56
|
# Docker service templates (coturn/mysql/redis/...) — the docker-via-ssh mechanic
|
|
57
57
|
# uploads these to ~/sql-harness/<svc>/ on a remote host.
|
|
58
58
|
"docker-services" = "sql_harness/_skills/docker-services"
|
|
@@ -7,11 +7,16 @@ agent-editable workspace. Connections live in plaintext in one TOML file
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
try:
|
|
11
|
+
from importlib.metadata import version as _pkg_version
|
|
12
|
+
except Exception:
|
|
13
|
+
_pkg_version = None # type: ignore
|
|
14
|
+
|
|
10
15
|
from .config import ConnectionConfig, ConnectionsConfig, PoolConfig, load, save
|
|
11
16
|
from .drivers import Driver, get_driver
|
|
12
17
|
from .manager import SqlHarness, Workspace
|
|
13
18
|
|
|
14
|
-
__version__ = "0.
|
|
19
|
+
__version__ = _pkg_version("sql-harness") if _pkg_version else "0.0.0+local"
|
|
15
20
|
|
|
16
21
|
__all__ = [
|
|
17
22
|
"ConnectionConfig",
|
|
@@ -289,29 +289,27 @@ def cmd_skill(args, _harness: SqlHarness) -> int:
|
|
|
289
289
|
if code:
|
|
290
290
|
return code
|
|
291
291
|
if sub == "list":
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
#
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
seen.add(p.stem)
|
|
302
|
-
names.append(p.stem)
|
|
292
|
+
seen: set[str] = set()
|
|
293
|
+
names: list[str] = []
|
|
294
|
+
# Two-layer lookup: active zone wins, zones/meta supplies cross-DSN fallback.
|
|
295
|
+
for d in (zone_skills_dir(conn), zone_skills_dir("meta")):
|
|
296
|
+
if d.is_dir():
|
|
297
|
+
for p in sorted(d.glob("*.md")):
|
|
298
|
+
if p.stem not in seen:
|
|
299
|
+
seen.add(p.stem)
|
|
300
|
+
names.append(p.stem)
|
|
303
301
|
_emit({"connection": conn, "skills": names})
|
|
304
302
|
return 0
|
|
305
303
|
if sub == "show":
|
|
306
304
|
zone_path = zone_skills_dir(conn) / f"{args.name}.md"
|
|
307
|
-
|
|
305
|
+
meta_path = zone_skills_dir("meta") / f"{args.name}.md"
|
|
308
306
|
if zone_path.is_file():
|
|
309
307
|
print(zone_path.read_text(encoding="utf-8"))
|
|
310
|
-
elif
|
|
311
|
-
print(
|
|
308
|
+
elif meta_path.is_file():
|
|
309
|
+
print(meta_path.read_text(encoding="utf-8"))
|
|
312
310
|
else:
|
|
313
311
|
print(
|
|
314
|
-
f"error: no skill named {args.name!r} in zone {conn!r} or
|
|
312
|
+
f"error: no skill named {args.name!r} in zone {conn!r} or zones/meta",
|
|
315
313
|
file=sys.stderr,
|
|
316
314
|
)
|
|
317
315
|
return 2
|
|
@@ -331,8 +329,13 @@ def _package_root_dir() -> Path:
|
|
|
331
329
|
|
|
332
330
|
|
|
333
331
|
def _shipped_skills_dir() -> Path:
|
|
334
|
-
"""Shipped preset skills:
|
|
335
|
-
|
|
332
|
+
"""Shipped preset (cross-DSN) skills source: zones/meta/skills/.
|
|
333
|
+
|
|
334
|
+
`sql-harness init` copies every .md under this dir into the runtime
|
|
335
|
+
workspace's zones/meta/skills/ so `apply_skill()` can find them as
|
|
336
|
+
fallback from any active zone.
|
|
337
|
+
"""
|
|
338
|
+
return _package_root_dir() / "agent-workspace" / "zones" / "meta" / "skills"
|
|
336
339
|
|
|
337
340
|
|
|
338
341
|
def _packaged_skill_body() -> str | None:
|
|
@@ -487,9 +490,12 @@ def cmd_init(_args, _harness: SqlHarness) -> int:
|
|
|
487
490
|
else:
|
|
488
491
|
_emit({"path": str(path), "existed": True})
|
|
489
492
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
+
# Shipped cross-DSN strategy skills land in zones/meta/skills/ (decision P:
|
|
494
|
+
# collapsed the legacy global layer into a regular zone so apply_skill()
|
|
495
|
+
# has a single shape: zone or meta).
|
|
496
|
+
target_meta = zone_skills_dir("meta")
|
|
497
|
+
ensure_private_dir(target_meta)
|
|
498
|
+
copied = _copy_shipped_skills(target_meta)
|
|
493
499
|
if copied:
|
|
494
500
|
print(
|
|
495
501
|
f"installed {len(copied)} preset skills: {', '.join(copied)}",
|
|
@@ -655,7 +661,7 @@ def cmd_paths(args, harness: SqlHarness) -> int:
|
|
|
655
661
|
_path_record("config_dir", P.config_dir()),
|
|
656
662
|
_path_record("config_file", P.config_file()),
|
|
657
663
|
_path_record("agent_workspace", P.workspace_dir()),
|
|
658
|
-
_path_record("
|
|
664
|
+
_path_record("meta_skills", P.zone_skills_dir("meta")),
|
|
659
665
|
_path_record("runtime_dir", P.runtime_dir()),
|
|
660
666
|
_path_record("tmp_dir", P.tmp_dir()),
|
|
661
667
|
_path_record("package_root", _package_root_dir()),
|
|
@@ -697,7 +703,7 @@ def _resolve_open_path(target: str, conn: str | None) -> Path:
|
|
|
697
703
|
"home": P.home_dir(),
|
|
698
704
|
"config": P.config_dir(),
|
|
699
705
|
"workspace": P.workspace_dir(),
|
|
700
|
-
"skills": P.
|
|
706
|
+
"skills": P.zone_skills_dir("meta"),
|
|
701
707
|
"runtime": P.runtime_dir(),
|
|
702
708
|
"tmp": P.tmp_dir(),
|
|
703
709
|
"package": _package_root_dir(),
|
|
@@ -311,35 +311,34 @@ def with_transaction():
|
|
|
311
311
|
# --- Skill / script helpers (per-DSN zone) ---------------------------------
|
|
312
312
|
|
|
313
313
|
def apply_skill(name: str) -> str:
|
|
314
|
-
"""Read a skill markdown from the ACTIVE
|
|
314
|
+
"""Read a skill markdown from the ACTIVE zone, with zones/meta as fallback.
|
|
315
315
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
316
|
+
Two-layer fallback (decision P): the active zone wins if it has the skill;
|
|
317
|
+
otherwise zones/meta/skills/ (cross-DSN strategy knowledge, populated by
|
|
318
|
+
`sql-harness init`) supplies the answer. This collapses the legacy
|
|
319
|
+
global `agent-workspace/skills/` layer into a regular zone so the lookup
|
|
320
|
+
is single-shape: zone or meta.
|
|
319
321
|
"""
|
|
320
322
|
conn = _require_connection()
|
|
321
323
|
zone_path = paths.zone_skills_dir(conn) / f"{name}.md"
|
|
322
324
|
if zone_path.is_file():
|
|
323
325
|
return zone_path.read_text(encoding="utf-8")
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return global_path.read_text(encoding="utf-8")
|
|
326
|
+
meta_path = paths.zone_skills_dir("meta") / f"{name}.md"
|
|
327
|
+
if meta_path.is_file():
|
|
328
|
+
return meta_path.read_text(encoding="utf-8")
|
|
328
329
|
raise FileNotFoundError(
|
|
329
|
-
f"no skill named {name!r} in zone {conn!r} or
|
|
330
|
+
f"no skill named {name!r} in zone {conn!r} or zones/meta"
|
|
330
331
|
)
|
|
331
332
|
|
|
332
333
|
|
|
333
334
|
def list_skills() -> list[str]:
|
|
334
|
-
"""List skills in the ACTIVE
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
zone.update(p.stem for p in global_d.glob("*.md"))
|
|
342
|
-
return sorted(zone)
|
|
335
|
+
"""List skills in the ACTIVE zone UNION zones/meta (cross-DSN fallback)."""
|
|
336
|
+
conn = _require_connection()
|
|
337
|
+
seen: set[str] = set()
|
|
338
|
+
for d in (paths.zone_skills_dir(conn), paths.zone_skills_dir("meta")):
|
|
339
|
+
if d.is_dir():
|
|
340
|
+
seen.update(p.stem for p in d.glob("*.md"))
|
|
341
|
+
return sorted(seen)
|
|
343
342
|
|
|
344
343
|
|
|
345
344
|
def list_scripts() -> list[str]:
|
|
@@ -47,8 +47,8 @@ def workspace_dir() -> Path:
|
|
|
47
47
|
- helpers.py per-connection helpers (merged over the shared base)
|
|
48
48
|
- scripts/<name>.py saved heredocs for this connection
|
|
49
49
|
- skills/<name>.md per-connection skill knowledge
|
|
50
|
+
- zones/meta/skills/ cross-DSN strategy skills (apply_skill fallback)
|
|
50
51
|
- scripts/ (legacy global scripts, pre-zone)
|
|
51
|
-
- skills/ (legacy global skills, pre-zone)
|
|
52
52
|
"""
|
|
53
53
|
override = os.environ.get("BH_SQL_AGENT_WORKSPACE")
|
|
54
54
|
return Path(override).expanduser() if override else home_dir() / "agent-workspace"
|
|
@@ -190,7 +190,7 @@ def test_paths_lists_global_dirs(tmp_path: Path) -> None:
|
|
|
190
190
|
data = json.loads(p.stdout)
|
|
191
191
|
names = {r["name"] for r in data["global"]}
|
|
192
192
|
# The key folders a user wants to open must all be present.
|
|
193
|
-
assert {"home", "config_file", "agent_workspace", "
|
|
193
|
+
assert {"home", "config_file", "agent_workspace", "meta_skills",
|
|
194
194
|
"package_root", "package_source"} <= names
|
|
195
195
|
# config_file should point inside the isolated BH_SQL_HOME.
|
|
196
196
|
cf = next(r for r in data["global"] if r["name"] == "config_file")
|
|
@@ -63,6 +63,10 @@ def test_close_workspace_unknown_is_noop() -> None:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def test_skills_no_dir(tmp_path, monkeypatch) -> None:
|
|
66
|
+
"""SqlHarness.apply_skill/list_skills is the legacy class-level API
|
|
67
|
+
(decision C in the P-plan): it still reads workspace_dir()/skills/ unchanged.
|
|
68
|
+
The canonical entry is the module-level helpers.py:apply_skill, which uses
|
|
69
|
+
the zones/meta fallback. See test_helpers.py for the new behavior."""
|
|
66
70
|
monkeypatch.setenv("BH_SQL_AGENT_WORKSPACE", str(tmp_path / "isolated_ws"))
|
|
67
71
|
h = SqlHarness(_sqlite_config())
|
|
68
72
|
assert h.list_skills() == []
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# agent-workspace/skills — strategy layer
|
|
2
|
-
|
|
3
|
-
Strategy-altitude notes for sql-harness: **when** and **why** to reach for a
|
|
4
|
-
pattern. The **how** (helpers, commands, gotchas) lives one level down in
|
|
5
|
-
`interaction-skills/` — these docs point there, they don't duplicate it.
|
|
6
|
-
|
|
7
|
-
## Files in this folder
|
|
8
|
-
|
|
9
|
-
- `pool.md` — ships the `[pool_defaults]` block + recycle-timing rule; points at the pooling mechanic for sizing
|
|
10
|
-
- `workspace.md` — one connection = one workspace; isolation, lifecycle, multi-DB, cross-workspace-transaction warning
|
|
11
|
-
- `ssh.md` — when to reach for `driver = "ssh"` vs a DB workspace; auth reference; host-key-trust decision
|
|
12
|
-
- `docker-deploy.md` — when to deploy docker-compose over SSH; service catalog; `~/sql-harness/<svc>/` convention; lifecycle decisions
|
|
13
|
-
|
|
14
|
-
## When to read which
|
|
15
|
-
|
|
16
|
-
- Queries stall / connections go stale → `pool.md` (then the pooling mechanic)
|
|
17
|
-
- Juggling >1 database, or "can I share a connection?" → `workspace.md`
|
|
18
|
-
- Need to run shell / move files on a remote box → `ssh.md`
|
|
19
|
-
- Standing up mysql / redis / coturn / nginx on a remote host → `docker-deploy.md`
|
|
20
|
-
|
|
21
|
-
## When to skip
|
|
22
|
-
|
|
23
|
-
Most pool / workspace / ssh / docker tuning is **mechanic work** — read
|
|
24
|
-
`interaction-skills/` first (`pooling.md`, `ssh.md`, `docker-via-ssh.md`,
|
|
25
|
-
`transactions.md`). Come here only when a mechanic doc points at strategy, or
|
|
26
|
-
when you're deciding *whether* to use a pattern rather than *how* to run it.
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Docker-via-SSH deployment — strategy
|
|
2
|
-
|
|
3
|
-
> **Strategy layer** for deploying docker-compose services to remote hosts via
|
|
4
|
-
> sql-harness SSH. For the **mechanic** (upload + `docker compose up` + verify),
|
|
5
|
-
> see `interaction-skills/docker-via-ssh.md`. This doc is about **when**, **why**,
|
|
6
|
-
> and **which service** — the convention, the catalog, the lifecycle decisions.
|
|
7
|
-
|
|
8
|
-
## When to use this pattern
|
|
9
|
-
|
|
10
|
-
Use `docker-via-ssh` when:
|
|
11
|
-
|
|
12
|
-
- You have **1–N remote hosts** (Aliyun ECS, a VPS, a home lab box) reachable by SSH
|
|
13
|
-
- You want **standard services** (mysql / postgres / redis / coturn / nginx / grafana) running, self-contained
|
|
14
|
-
- You want **re-deployable / scriptable** deploys (not manual `docker run` you'll forget)
|
|
15
|
-
- You **don't** need k8s-scale orchestration (no auto-scaling, no multi-node clustering)
|
|
16
|
-
|
|
17
|
-
Don't use it when:
|
|
18
|
-
|
|
19
|
-
- You need **multi-node clustering** (Hadoop / HBase across N hosts) → k8s / Nomad
|
|
20
|
-
- The service needs **bare-metal perf** (GPU passthrough, hugepages) → systemd unit
|
|
21
|
-
- You're on a **managed platform** (Aliyun RDS, Redis Cloud) → use the managed one
|
|
22
|
-
|
|
23
|
-
## The convention: `~/sql-harness/<service>/` on the remote
|
|
24
|
-
|
|
25
|
-
Every deployed service lives in its own dir under `~/sql-harness/<service>/` **on
|
|
26
|
-
the remote host** — compose + config + data + logs, all with relative mounts
|
|
27
|
-
(`./data`, not absolute paths). The mechanic doc shows the layout tree and the
|
|
28
|
-
relative-mount rationale in full; the strategy payoff is:
|
|
29
|
-
|
|
30
|
-
| Property | How the convention delivers it |
|
|
31
|
-
|---|---|
|
|
32
|
-
| **Cohesion** | A service's compose + config + data + logs are all in one dir |
|
|
33
|
-
| **Backup** | `tar czf coturn.tgz ~/sql-harness/coturn/` — one command, complete |
|
|
34
|
-
| **Teardown** | `docker compose down && rm -rf ~/sql-harness/<svc>` — clean |
|
|
35
|
-
| **Upgrade** | re-upload compose → `docker compose up -d` (rolling, keeps data) |
|
|
36
|
-
| **Discovery** | `ls ~/sql-harness/` shows what's deployed |
|
|
37
|
-
| **Isolation** | services don't share mounts/networks unless explicitly composed together |
|
|
38
|
-
| **Portability** | relative mounts follow the dir — move or migrate hosts without editing paths |
|
|
39
|
-
|
|
40
|
-
## Service catalog (shipped templates in `docker-services/`)
|
|
41
|
-
|
|
42
|
-
| Service | Image | Ports | Use case |
|
|
43
|
-
|---|---|---|---|
|
|
44
|
-
| `coturn` | `coturn/coturn` | 3478, 5349, 49152-65535/udp | WebRTC TURN/STUN (P2P hole-punching) |
|
|
45
|
-
| `mysql` | `mysql:8.0` | 3306 | Relational DB (sql-harness `mysql` driver connects here) |
|
|
46
|
-
| `postgresql` | `postgres:16` | 5432 | Relational DB (sql-harness `postgres` driver connects here) |
|
|
47
|
-
| `redis` | `redis:7-alpine` | 6379 | Cache / KV (sql-harness `redis` driver — when implemented) |
|
|
48
|
-
| `nginx` | `nginx:alpine` | 80, 443 | Reverse proxy / static hosting |
|
|
49
|
-
| `rabbitmq` | `rabbitmq:3-management` | 5672, 15672 | Message queue |
|
|
50
|
-
| `cloud-grafana` | `grafana/grafana` + `prom/prometheus` | 3000, 9090 | Monitoring stack |
|
|
51
|
-
|
|
52
|
-
Each template in `docker-services/<svc>/` ships a `docker-compose.yml` + config
|
|
53
|
-
files, all with relative mounts. Pick one → the mechanic's 4-step loop uploads
|
|
54
|
-
and starts it. Pair a deployed DB service with a matching **DB workspace**
|
|
55
|
-
(`driver = "mysql"`/`postgres`) to run SQL against it — SSH-level container
|
|
56
|
-
management + DB-level queries on the same host is the typical diagnostic loop.
|
|
57
|
-
|
|
58
|
-
## Lifecycle decisions
|
|
59
|
-
|
|
60
|
-
| Question | Answer |
|
|
61
|
-
|---|---|
|
|
62
|
-
| New service? | pick template → upload → `up -d` (4-step loop in mechanic doc) |
|
|
63
|
-
| Image update? | edit compose tag → re-upload → `up -d` (keeps data) |
|
|
64
|
-
| Config change? | edit conf → re-upload → `restart` (or `up -d` if compose changed) |
|
|
65
|
-
| Host migration? | `tar` the service dir → scp to new host → `up -d` |
|
|
66
|
-
| Out of disk? | `docker system prune -af` (careful) + check service `data/` growth |
|
|
67
|
-
| Service flapping? | `docker logs -f` → `docker compose ps` → healthcheck output |
|
|
68
|
-
| Stop paying for it? | `down -v` + `rm -rf ~/sql-harness/<svc>` + snapshot data first |
|
|
69
|
-
|
|
70
|
-
## Gotchas (strategy-level)
|
|
71
|
-
|
|
72
|
-
- **One host = one `~/sql-harness/` root**. Multi-host deploys loop the deploy per
|
|
73
|
-
host — one sql-harness SSH workspace per host.
|
|
74
|
-
- **Don't mount the SAME host port from two services** — mysql + postgres coexist
|
|
75
|
-
fine on their defaults, but two mysql instances need port remapping.
|
|
76
|
-
- **Healthy ≠ reachable by the DB workspace**: if a container isn't healthy,
|
|
77
|
-
`sql-harness test <db-workspace>` fails confusingly — check `docker compose ps`
|
|
78
|
-
before assuming a sql-harness bug. (Per-service startup gotchas live with the
|
|
79
|
-
mechanic and `docker-services/<svc>/README.md`.)
|
|
80
|
-
|
|
81
|
-
## See also
|
|
82
|
-
|
|
83
|
-
- `interaction-skills/docker-via-ssh.md` — the mechanic (layout tree, relative-mount
|
|
84
|
-
rationale, 4-step deploy loop, per-op lifecycle commands, deploy gotchas)
|
|
85
|
-
- `docker-services/<svc>/` — shipped templates (and `<svc>/README.md` service gotchas)
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# SSH workspaces — when to use the SSH driver
|
|
2
|
-
|
|
3
|
-
> **Strategy guide.** For the mechanics (helper calls, CLI subcommands, SFTP /
|
|
4
|
-
> timeout / quoting gotchas), see `interaction-skills/ssh.md`.
|
|
5
|
-
|
|
6
|
-
## When to reach for `driver = "ssh"`
|
|
7
|
-
|
|
8
|
-
Use an SSH workspace to **operate on a remote host** (not a database). It reuses
|
|
9
|
-
the same `use_workspace("name")` abstraction as DB workspaces, but opens a
|
|
10
|
-
paramiko `SSHClient` + SFTP instead of a SQLAlchemy engine — so you get shell +
|
|
11
|
-
file ops with the same ergonomics.
|
|
12
|
-
|
|
13
|
-
Reach for it to: run ad-hoc shell commands (`ps`, `systemctl`, `cat /var/log/...`);
|
|
14
|
-
do a one-shot deploy / restart / log inspection without a full Ansible playbook;
|
|
15
|
-
upload-then-run a script (`ssh_run_script`); pull a log/artifact down for analysis;
|
|
16
|
-
or pair with a DB workspace (shell check, then `query(...)`).
|
|
17
|
-
|
|
18
|
-
Don't use it for: heavy interactive shells (no TUI/scrollback/resize — use
|
|
19
|
-
`term-cli` from `lab.subprocess`); long-running daemons (the channel is
|
|
20
|
-
short-lived — use systemd / supervisord); or anything that must survive the
|
|
21
|
-
sql-harness process exit (the connection closes on exit).
|
|
22
|
-
|
|
23
|
-
## Auth reference
|
|
24
|
-
|
|
25
|
-
Configure in `connections.toml` (`driver = "ssh"`, `url = "ssh://user@host:port"`).
|
|
26
|
-
Pick an auth strategy:
|
|
27
|
-
|
|
28
|
-
| Strategy | When | Example URL |
|
|
29
|
-
|---|---|---|
|
|
30
|
-
| SSH key (preferred) | CI / dev boxes with `~/.ssh/id_*` | `ssh://user@host` (auto-resolves key) |
|
|
31
|
-
| Explicit key path | Specific key per connection | `ssh://user@host?key=/opt/keys/prod.pem` |
|
|
32
|
-
| Password | Quick lab use; NOT for prod | `ssh://user:pw@host` (works but key is better) |
|
|
33
|
-
| `ssh+password://` | Force password (no key lookup) | `ssh+password://user:pw@host` |
|
|
34
|
-
| `ssh+key://` | Force key-only auth (no password fallback) | `ssh+key://user@host?key=/path` |
|
|
35
|
-
|
|
36
|
-
Key resolution order (first hit wins): (1) `?key=...` in the URL, (2) `$BH_SSH_KEY`
|
|
37
|
-
env var, (3) `~/.ssh/id_ed25519` → `id_rsa` → `id_ecdsa`, (4) password from URL or
|
|
38
|
-
`$BH_SSH_PASSWORD`.
|
|
39
|
-
|
|
40
|
-
## Workspace lifecycle parity
|
|
41
|
-
|
|
42
|
-
SSH workspaces follow the **same** lazy-open / cached / dispose pattern as DB
|
|
43
|
-
workspaces: `use_workspace("prod-app")` opens TCP+SSH+shell+SFTP and caches it;
|
|
44
|
-
re-opening returns the cached handle for free; process exit / `dispose()` /
|
|
45
|
-
`close_workspace(name)` tears it down.
|
|
46
|
-
|
|
47
|
-
## Combining with DB workspaces
|
|
48
|
-
|
|
49
|
-
SSH and DB workspaces are independent — keep one of each open at once. The typical
|
|
50
|
-
diagnostic flow: shell-level process check, then in-process DB check.
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
ssh_use_workspace("prod-app")
|
|
54
|
-
print(ssh_exec("ps aux | grep postgres | head -3")["stdout"])
|
|
55
|
-
|
|
56
|
-
use_workspace("prod_pg")
|
|
57
|
-
print(query("SELECT count(*) FROM pg_stat_activity")["count"])
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Safety — the host-key-trust decision
|
|
61
|
-
|
|
62
|
-
The driver trusts host keys on first connect (`AutoAddPolicy()`, like
|
|
63
|
-
`StrictHostKeyChecking=accept-new`). Fine for trusted networks; for hostile ones,
|
|
64
|
-
harden this in a subclass. Also prefer a non-root user, and set an explicit
|
|
65
|
-
`timeout=` for long commands. (Connection pooling, shell-vs-exec, path handling,
|
|
66
|
-
non-ASCII decoding and other operational gotchas live in the mechanic doc.)
|
|
67
|
-
|
|
68
|
-
## See also
|
|
69
|
-
|
|
70
|
-
- `interaction-skills/ssh.md` — the mechanic (helpers, CLI, SFTP/timeout/quoting gotchas)
|
|
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
|
{sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/zones/example_pg/scripts/sh_demo_crud.py
RENAMED
|
File without changes
|
{sql_harness-0.2.2 → sql_harness-0.2.3}/agent-workspace/zones/example_pg/skills/sh_demo-schema.md
RENAMED
|
File without changes
|
{sql_harness-0.2.2/agent-workspace → sql_harness-0.2.3/agent-workspace/zones/meta}/skills/pool.md
RENAMED
|
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
|
|
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
|
{sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/aggregations-advanced.md
RENAMED
|
File without changes
|
|
File without changes
|
{sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/recursive-ctes-patterns.md
RENAMED
|
File without changes
|
{sql_harness-0.2.2 → sql_harness-0.2.3}/interaction-skills/references/window-functions-catalog.md
RENAMED
|
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
|
|
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
|
|
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
|