sql-harness 0.2.3__tar.gz → 0.2.4__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 (92) hide show
  1. {sql_harness-0.2.3 → sql_harness-0.2.4}/PKG-INFO +1 -1
  2. {sql_harness-0.2.3 → sql_harness-0.2.4}/pyproject.toml +1 -1
  3. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/cli.py +10 -0
  4. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_cli.py +54 -3
  5. {sql_harness-0.2.3 → sql_harness-0.2.4}/uv.lock +1 -1
  6. {sql_harness-0.2.3 → sql_harness-0.2.4}/.claude-plugin/marketplace.json +0 -0
  7. {sql_harness-0.2.3 → sql_harness-0.2.4}/.claude-plugin/plugin.json +0 -0
  8. {sql_harness-0.2.3 → sql_harness-0.2.4}/.env.example +0 -0
  9. {sql_harness-0.2.3 → sql_harness-0.2.4}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  10. {sql_harness-0.2.3 → sql_harness-0.2.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  11. {sql_harness-0.2.3 → sql_harness-0.2.4}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  12. {sql_harness-0.2.3 → sql_harness-0.2.4}/.gitignore +0 -0
  13. {sql_harness-0.2.3 → sql_harness-0.2.4}/AGENTS.md +0 -0
  14. {sql_harness-0.2.3 → sql_harness-0.2.4}/README.md +0 -0
  15. {sql_harness-0.2.3 → sql_harness-0.2.4}/SKILL.md +0 -0
  16. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/README.md +0 -0
  17. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/agent_helpers.example.py +0 -0
  18. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/agent_helpers.py +0 -0
  19. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/zones/example_pg/scripts/sh_demo_crud.py +0 -0
  20. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/zones/example_pg/skills/sh_demo-schema.md +0 -0
  21. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/zones/meta/skills/pool.md +0 -0
  22. {sql_harness-0.2.3 → sql_harness-0.2.4}/agent-workspace/zones/meta/skills/workspace.md +0 -0
  23. {sql_harness-0.2.3 → sql_harness-0.2.4}/docker-services/coturn/README.md +0 -0
  24. {sql_harness-0.2.3 → sql_harness-0.2.4}/docker-services/coturn/docker-compose.yml +0 -0
  25. {sql_harness-0.2.3 → sql_harness-0.2.4}/docker-services/coturn/turnserver.conf +0 -0
  26. {sql_harness-0.2.3 → sql_harness-0.2.4}/docker-services/mysql/docker-compose.yml +0 -0
  27. {sql_harness-0.2.3 → sql_harness-0.2.4}/docker-services/redis/docker-compose.yml +0 -0
  28. {sql_harness-0.2.3 → sql_harness-0.2.4}/docs/connections-toml-example.toml +0 -0
  29. {sql_harness-0.2.3 → sql_harness-0.2.4}/docs/headless-notes.md +0 -0
  30. {sql_harness-0.2.3 → sql_harness-0.2.4}/docs/maintainer.md +0 -0
  31. {sql_harness-0.2.3 → sql_harness-0.2.4}/docs/standalone-install.md +0 -0
  32. {sql_harness-0.2.3 → sql_harness-0.2.4}/install.md +0 -0
  33. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/aggregations.md +0 -0
  34. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/docker-via-ssh.md +0 -0
  35. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/encoding-and-charset.md +0 -0
  36. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/indexes-and-explain.md +0 -0
  37. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/joins.md +0 -0
  38. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/json-columns.md +0 -0
  39. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/large-result-sets.md +0 -0
  40. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/migrations.md +0 -0
  41. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/pooling.md +0 -0
  42. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/postgres/btree-indexes.md +0 -0
  43. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/postgres/plan-reading.md +0 -0
  44. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/postgres/slow-queries-joins.md +0 -0
  45. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/postgres/specialized-indexes.md +0 -0
  46. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/postgres/table-optimization.md +0 -0
  47. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/recursive-ctes.md +0 -0
  48. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/references/aggregations-advanced.md +0 -0
  49. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/references/joins-advanced.md +0 -0
  50. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/references/recursive-ctes-patterns.md +0 -0
  51. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/references/window-functions-catalog.md +0 -0
  52. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/save-run-cycle.md +0 -0
  53. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/schema-introspection.md +0 -0
  54. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/ssh.md +0 -0
  55. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/timeouts-and-cancellation.md +0 -0
  56. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/transactions.md +0 -0
  57. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/window-functions.md +0 -0
  58. {sql_harness-0.2.3 → sql_harness-0.2.4}/interaction-skills/zone-skill-auto-surface.md +0 -0
  59. {sql_harness-0.2.3 → sql_harness-0.2.4}/practice/pgexercises/README.md +0 -0
  60. {sql_harness-0.2.3 → sql_harness-0.2.4}/skills/sql-harness/README.txt +0 -0
  61. {sql_harness-0.2.3 → sql_harness-0.2.4}/skills/sql-harness/SKILL.md +0 -0
  62. {sql_harness-0.2.3 → sql_harness-0.2.4}/skills/sql-harness/references/install.md +0 -0
  63. {sql_harness-0.2.3 → sql_harness-0.2.4}/sql-harness +0 -0
  64. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/SKILL.md +0 -0
  65. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/__init__.py +0 -0
  66. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/agent_loader.py +0 -0
  67. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/config.py +0 -0
  68. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/__init__.py +0 -0
  69. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/mysql.py +0 -0
  70. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/postgres.py +0 -0
  71. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/redis.py +0 -0
  72. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/sqlite.py +0 -0
  73. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/drivers/ssh.py +0 -0
  74. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/helpers.py +0 -0
  75. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/manager.py +0 -0
  76. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/paths.py +0 -0
  77. {sql_harness-0.2.3 → sql_harness-0.2.4}/src/sql_harness/run.py +0 -0
  78. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/__init__.py +0 -0
  79. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/conftest.py +0 -0
  80. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/__init__.py +0 -0
  81. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/test_mysql.py +0 -0
  82. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/test_pg_optimization.py +0 -0
  83. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/test_pgexercises_practice.py +0 -0
  84. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/test_postgres.py +0 -0
  85. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/integration/test_ssh.py +0 -0
  86. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/__init__.py +0 -0
  87. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_agent_loader.py +0 -0
  88. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_config.py +0 -0
  89. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_helpers.py +0 -0
  90. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_manager.py +0 -0
  91. {sql_harness-0.2.3 → sql_harness-0.2.4}/tests/unit/test_paths.py +0 -0
  92. {sql_harness-0.2.3 → sql_harness-0.2.4}/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
3
+ Version: 0.2.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sql-harness"
3
- version = "0.2.3"
3
+ version = "0.2.4"
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" }
@@ -558,6 +558,13 @@ def cmd_save(args, _harness: SqlHarness) -> int:
558
558
  conn, code = _require_connection_or_fail(args)
559
559
  if code:
560
560
  return code
561
+ # Read stdin as UTF-8 on Windows (default locale is GBK/CP936, which
562
+ # mangles non-ASCII bytes into surrogates that write_text rejects).
563
+ # Mirrors run.py's heredoc-mode stdin handling.
564
+ try:
565
+ sys.stdin.reconfigure(encoding="utf-8", errors="replace")
566
+ except (AttributeError, OSError):
567
+ pass
561
568
  body = sys.stdin.read()
562
569
  if not body.strip():
563
570
  print("error: stdin is empty; nothing to save", file=sys.stderr)
@@ -616,6 +623,9 @@ def cmd_run(args, harness: SqlHarness) -> int:
616
623
  for n, v in vars(helpers).items():
617
624
  if not n.startswith("_"):
618
625
  namespace[n] = v
626
+ # Parity with heredoc mode (run.py globals): expose `sys` so saved
627
+ # scripts can use sys.stdout.write etc. without importing it.
628
+ namespace["sys"] = sys
619
629
  code_text = path.read_text(encoding="utf-8")
620
630
  try:
621
631
  exec(compile(code_text, str(path), "exec"), namespace)
@@ -10,8 +10,14 @@ from pathlib import Path
10
10
  import pytest
11
11
 
12
12
 
13
- def _run_cli(*args: str, env: dict | None = None) -> subprocess.CompletedProcess:
14
- """Run sql-harness in a subprocess; capture UTF-8 output."""
13
+ def _run_cli(
14
+ *args: str, env: dict | None = None, input: str | None = None
15
+ ) -> subprocess.CompletedProcess:
16
+ """Run sql-harness in a subprocess; capture UTF-8 output.
17
+
18
+ `input` (when given) is piped to the child's stdin as UTF-8 — used to
19
+ exercise `save`, which reads its heredoc body from stdin.
20
+ """
15
21
  import os
16
22
 
17
23
  full_env = os.environ.copy()
@@ -24,6 +30,7 @@ def _run_cli(*args: str, env: dict | None = None) -> subprocess.CompletedProcess
24
30
  encoding="utf-8",
25
31
  errors="replace",
26
32
  env=full_env,
33
+ input=input,
27
34
  )
28
35
 
29
36
 
@@ -219,4 +226,48 @@ def test_open_scripts_requires_connection(tmp_path: Path) -> None:
219
226
  """`open scripts` without --connection errors with guidance."""
220
227
  p = _run_cli("open", "scripts", env={"BH_SQL_HOME": str(tmp_path)})
221
228
  assert p.returncode == 2
222
- assert "zone-scoped" in p.stderr
229
+ assert "zone-scoped" in p.stderr
230
+
231
+
232
+ def _write_sqlite_cfg(tmp_path: Path) -> None:
233
+ """Seed an isolated BH_SQL_HOME with a single sqlite connection."""
234
+ (tmp_path / "connections.toml").write_text(
235
+ '[[connections]]\n'
236
+ 'name = "mem"\n'
237
+ 'driver = "sqlite"\n'
238
+ 'url = "sqlite:///:memory:"\n',
239
+ encoding="utf-8",
240
+ )
241
+
242
+
243
+ def test_save_preserves_utf8_stdin(tmp_path: Path) -> None:
244
+ """`save` reads stdin as UTF-8 even on Windows (cp936 default locale).
245
+
246
+ Regression: non-ASCII bytes in the heredoc body were decoded into
247
+ surrogates and write_text(utf-8) raised UnicodeEncodeError.
248
+ """
249
+ _write_sqlite_cfg(tmp_path)
250
+ body = "# 服务器内存检查\nprint('ok')\n"
251
+ p = _run_cli(
252
+ "save", "utf8", "-c", "mem", input=body, env={"BH_SQL_HOME": str(tmp_path)}
253
+ )
254
+ assert p.returncode == 0, p.stderr
255
+ saved = Path(json.loads(p.stdout)["path"])
256
+ assert "服务器内存检查" in saved.read_text(encoding="utf-8")
257
+
258
+
259
+ def test_run_namespace_exposes_sys(tmp_path: Path) -> None:
260
+ """`run` exec namespace includes `sys`, matching heredoc mode.
261
+
262
+ Regression: saved scripts that used `sys.stdout.write` without
263
+ `import sys` (as ssh.md documents) failed with NameError.
264
+ """
265
+ _write_sqlite_cfg(tmp_path)
266
+ body = "sys.stdout.write('ok-from-sys')\n" # relies on pre-imported sys
267
+ p = _run_cli(
268
+ "save", "usesys", "-c", "mem", input=body, env={"BH_SQL_HOME": str(tmp_path)}
269
+ )
270
+ assert p.returncode == 0, p.stderr
271
+ p2 = _run_cli("run", "usesys", "-c", "mem", env={"BH_SQL_HOME": str(tmp_path)})
272
+ assert p2.returncode == 0, p2.stderr
273
+ assert "ok-from-sys" in p2.stdout
@@ -446,7 +446,7 @@ wheels = [
446
446
 
447
447
  [[package]]
448
448
  name = "sql-harness"
449
- version = "0.2.2"
449
+ version = "0.2.3"
450
450
  source = { editable = "." }
451
451
  dependencies = [
452
452
  { name = "paramiko" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes