pythia-plsql 0.11.1__tar.gz → 0.11.2__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 (44) hide show
  1. {pythia_plsql-0.11.1/scripts/pythia_plsql.egg-info → pythia_plsql-0.11.2}/PKG-INFO +1 -1
  2. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/pyproject.toml +1 -1
  3. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia.py +17 -5
  4. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2/scripts/pythia_plsql.egg-info}/PKG-INFO +1 -1
  5. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/tests/test_phase3.py +44 -0
  6. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/LICENSE +0 -0
  7. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/README.md +0 -0
  8. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/compile-errors.sql +0 -0
  9. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/current-schema.sql +0 -0
  10. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/dependencies.sql +0 -0
  11. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/impact.sql +0 -0
  12. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/invalid-objects.sql +0 -0
  13. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/name-occupants.sql +0 -0
  14. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/object-names.sql +0 -0
  15. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/object-source.sql +0 -0
  16. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/plscope-enabled.sql +0 -0
  17. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/plscope-statements.sql +0 -0
  18. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/plscope-usages.sql +0 -0
  19. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/session-privileges.sql +0 -0
  20. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/similar-candidates.sql +0 -0
  21. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/queries/source.sql +0 -0
  22. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
  23. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
  24. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
  25. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
  26. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
  27. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/setup.cfg +0 -0
  28. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-apply/SKILL.md +0 -0
  29. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-conventions/SKILL.md +0 -0
  30. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-explore/SKILL.md +0 -0
  31. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-explore/reference/data-dictionary.md +0 -0
  32. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-impact/SKILL.md +0 -0
  33. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-review/SKILL.md +0 -0
  34. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-review/reference/antipatterns.md +0 -0
  35. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-setup/SKILL.md +0 -0
  36. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-skill-author/SKILL.md +0 -0
  37. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-spec/SKILL.md +0 -0
  38. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-write/SKILL.md +0 -0
  39. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/pythia-write/reference/patterns.md +0 -0
  40. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/skills/using-pythia/SKILL.md +0 -0
  41. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/tests/test_install.py +0 -0
  42. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/tests/test_phase1.py +0 -0
  43. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/tests/test_phase2.py +0 -0
  44. {pythia_plsql-0.11.1 → pythia_plsql-0.11.2}/tests/test_phase5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.11.1
3
+ Version: 0.11.2
4
4
  Summary: PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/thaildhe172591/pythia
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythia-plsql"
7
- version = "0.11.1"
7
+ version = "0.11.2"
8
8
  description = "PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -2331,10 +2331,17 @@ def token_mismatch_message(ns, row_set):
2331
2331
  base = ("The confirmation token does not match: the file or the "
2332
2332
  "database object changed since that preview. Preview again:\n"
2333
2333
  f" {invocation()} apply {ns.file}")
2334
- if not row_set:
2335
- return base
2336
2334
  _, meta = find_preview_by_token(ns.project_root,
2337
2335
  str(ns.confirm).strip().lower())
2336
+ if (meta or {}).get("applied"):
2337
+ # the token names a preview that already ran. "the file changed" is
2338
+ # the wrong story, and sends the agent hunting a difference that is
2339
+ # not there
2340
+ return (f"Token {str(ns.confirm).strip().lower()} was already applied "
2341
+ "— one approval, one write.\nPreview again, then get the new "
2342
+ f"token approved:\n {invocation()} apply {ns.file}")
2343
+ if not row_set:
2344
+ return base
2338
2345
  then = (meta or {}).get("row_set") or {}
2339
2346
  if not then or fingerprint_text(then) == fingerprint_text(row_set):
2340
2347
  return base # the file moved, not the rows
@@ -3467,10 +3474,15 @@ def build_parser():
3467
3474
  def main(argv=None):
3468
3475
  argv = sys.argv[1:] if argv is None else argv
3469
3476
  forbid_write_flag(argv)
3470
- for stream in (sys.stdout, sys.stderr):
3477
+ # stdin too, and before anything reads it: the AskUserQuestion hook
3478
+ # payload is UTF-8, and Windows would decode it as cp1252 — an em dash
3479
+ # arrives as mojibake and no approval card ever matches again.
3480
+ # line_buffering keeps stdout in order with stderr when both are pipes,
3481
+ # so a driver error lands after the preview it belongs to, not above it.
3482
+ for stream in (sys.stdout, sys.stderr, sys.stdin):
3471
3483
  try:
3472
- stream.reconfigure(encoding="utf-8")
3473
- except AttributeError:
3484
+ stream.reconfigure(encoding="utf-8", line_buffering=True)
3485
+ except (AttributeError, ValueError):
3474
3486
  pass
3475
3487
  ns = build_parser().parse_args(argv)
3476
3488
  # refusals that need no database come first — an unreachable or locked
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.11.1
3
+ Version: 0.11.2
4
4
  Summary: PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/thaildhe172591/pythia
@@ -1565,5 +1565,49 @@ def main():
1565
1565
  print("OK")
1566
1566
 
1567
1567
 
1568
+
1569
+
1570
+ def test_hook_matches_a_card_with_a_non_ascii_dash_through_real_stdin():
1571
+ """The whole hook, end to end, the way Claude Code runs it: UTF-8 bytes on
1572
+ a pipe. Windows decodes stdin as cp1252 unless told otherwise, and the em
1573
+ dash in the structural card would arrive as mojibake — no card would ever
1574
+ match again. Subprocess on purpose: an in-process StringIO is already
1575
+ decoded and cannot fail this way."""
1576
+ import subprocess
1577
+ with tempfile.TemporaryDirectory() as td:
1578
+ stmt = "ALTER PROCEDURE PKG_ORDER COMPILE;"
1579
+ pythia.write_journal_entry(
1580
+ td, "STRUCTURAL", "STATEMENT", "", stmt,
1581
+ {"token": "dc8d27", "connection": "DEV", "schema": "APP",
1582
+ "group": "structural", "applied": False})
1583
+ _, _, body = pythia.approval_card(td, "dc8d27")
1584
+ card = "python -m pythia approve dc8d27\n" + "\n".join(body)
1585
+ assert "—" in card, "the structural card is the non-ASCII one"
1586
+ payload = json.dumps(_hook_payload(card, "Approve"), ensure_ascii=False)
1587
+ p = subprocess.run(
1588
+ [sys.executable,
1589
+ str(pathlib.Path(pythia.__file__).resolve()), "approve", "--hook"],
1590
+ input=payload.encode("utf-8"), cwd=td, capture_output=True)
1591
+ assert p.returncode == 0, p.stderr.decode("utf-8", "replace")
1592
+ g = pythia.read_grant(td, "dc8d27")
1593
+ assert g and g["approver"] == "chat", p.stdout.decode("utf-8", "replace")
1594
+
1595
+
1596
+ def test_token_mismatch_names_an_already_applied_preview():
1597
+ """A spent token is not a moved file. Saying 'the file or the object
1598
+ changed' sends the agent looking for a difference that is not there."""
1599
+ import argparse
1600
+ with tempfile.TemporaryDirectory() as td:
1601
+ pythia.write_journal_entry(
1602
+ td, "PACKAGE BODY", "PKG_ORDER", OLD_SRC, NEW_FILE,
1603
+ {"token": "7f3a91", "connection": "DEV", "schema": "APP",
1604
+ "group": "plsql_source", "applied": True})
1605
+ ns = argparse.Namespace(project_root=td, confirm="7f3a91",
1606
+ file="pkg.sql")
1607
+ msg = pythia.token_mismatch_message(ns, None)
1608
+ assert "already applied" in msg and "changed since that preview" not in msg
1609
+
1610
+
1611
+
1568
1612
  if __name__ == "__main__":
1569
1613
  main()
File without changes
File without changes
File without changes