pythia-plsql 0.4.0__tar.gz → 0.4.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.
- {pythia_plsql-0.4.0/scripts/pythia_plsql.egg-info → pythia_plsql-0.4.2}/PKG-INFO +1 -1
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/pyproject.toml +1 -1
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia.py +24 -8
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2/scripts/pythia_plsql.egg-info}/PKG-INFO +1 -1
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-apply/SKILL.md +12 -1
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/tests/test_phase3.py +26 -29
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/LICENSE +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/README.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/compile-errors.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/dependencies.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/impact.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/invalid-objects.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/name-occupants.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/object-source.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/plscope-enabled.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/plscope-statements.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/plscope-usages.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/session-privileges.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/similar-candidates.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/queries/source.sql +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/setup.cfg +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-explore/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-explore/reference/data-dictionary.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-impact/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-review/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-review/reference/antipatterns.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-setup/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-skill-author/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-write/SKILL.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-write/reference/patterns.md +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/tests/test_install.py +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/tests/test_phase1.py +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/tests/test_phase2.py +0 -0
- {pythia_plsql-0.4.0 → pythia_plsql-0.4.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.4.
|
|
3
|
+
Version: 0.4.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.4.
|
|
7
|
+
version = "0.4.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" }
|
|
@@ -1337,6 +1337,11 @@ def run_apply(conn, schema, ns, file_text, origin=None):
|
|
|
1337
1337
|
print(f" {paint_diff_line(ln, en)}")
|
|
1338
1338
|
print(paint(f"\n Snapshot saved: {journal_root(ns.project_root) / entry}",
|
|
1339
1339
|
"dim", en))
|
|
1340
|
+
if not confirmed:
|
|
1341
|
+
print(paint(" Rollback file for the current database version — "
|
|
1342
|
+
"use it if this is\n run by hand instead: "
|
|
1343
|
+
f"{journal_root(ns.project_root) / entry / 'restore.sql'}",
|
|
1344
|
+
"dim", en))
|
|
1340
1345
|
if not confirmed:
|
|
1341
1346
|
print(f"\n To apply:\n "
|
|
1342
1347
|
+ paint(f"{invocation()} apply {ns.file} --confirm {token}",
|
|
@@ -1536,18 +1541,29 @@ def cmd_journal(conn, schema, ns):
|
|
|
1536
1541
|
if ns.action == "prune":
|
|
1537
1542
|
import shutil
|
|
1538
1543
|
removed = 0
|
|
1539
|
-
|
|
1540
|
-
|
|
1544
|
+
# A preview wrote nothing to the database, but its restore.sql holds
|
|
1545
|
+
# the version that was live at the time — the only rollback there is
|
|
1546
|
+
# for a change the developer then ran by hand. So drop a preview only
|
|
1547
|
+
# when its rollback is byte-identical to one a newer entry already
|
|
1548
|
+
# keeps: redundant captures go, unique ones never do.
|
|
1549
|
+
kept = {}
|
|
1550
|
+
for eid in list_journal_entries(root): # newest first
|
|
1551
|
+
e = read_journal_entry(root, eid)
|
|
1552
|
+
meta, obj = e["meta"], str(e["meta"].get("object", ""))
|
|
1541
1553
|
if meta.get("applied") or meta.get("snapshot"):
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1554
|
+
kept.setdefault(obj, set()).add(e["restore"])
|
|
1555
|
+
continue
|
|
1556
|
+
if e["restore"] in kept.get(obj, set()):
|
|
1557
|
+
shutil.rmtree(journal_root(root) / eid)
|
|
1558
|
+
removed += 1
|
|
1559
|
+
else:
|
|
1560
|
+
kept.setdefault(obj, set()).add(e["restore"])
|
|
1546
1561
|
if ns.json:
|
|
1547
1562
|
print(json.dumps({"pruned": removed}))
|
|
1548
1563
|
else:
|
|
1549
|
-
print(f"-- pruned {removed}
|
|
1550
|
-
"
|
|
1564
|
+
print(f"-- pruned {removed} redundant previews; applied entries, "
|
|
1565
|
+
"snapshots, and any preview holding a rollback nothing "
|
|
1566
|
+
"else has are all kept")
|
|
1551
1567
|
return
|
|
1552
1568
|
if not ns.id:
|
|
1553
1569
|
sys.exit(f"Usage: {invocation()} journal "
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythia-plsql
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -31,6 +31,15 @@ Three moves are FORBIDDEN for agents, and the CLI enforces the first two
|
|
|
31
31
|
your turn; the token is only used after the developer's approval
|
|
32
32
|
arrives as a NEW message.
|
|
33
33
|
|
|
34
|
+
## When the change will NOT go through apply
|
|
35
|
+
|
|
36
|
+
Sometimes the developer runs the file themselves — a DBA executes it, a
|
|
37
|
+
release process owns it, or policy denies the group. The preview still ran,
|
|
38
|
+
so a rollback file for the version currently live already exists at
|
|
39
|
+
`.pythia/journal/<entry>/restore.sql`. Never let a change leave your hands
|
|
40
|
+
for manual execution without naming that path in the same message as the
|
|
41
|
+
.sql file. `pythia history <OBJECT>` lists every captured version.
|
|
42
|
+
|
|
34
43
|
## The Workflow
|
|
35
44
|
|
|
36
45
|
**Before step 1:** if this conversation has not yet seen a standalone impact
|
|
@@ -54,7 +63,8 @@ first time anyone sees it, a step was skipped.
|
|
|
54
63
|
If the fresh preview's before-side no longer matches what you last saw,
|
|
55
64
|
say so explicitly: someone else may have changed the object on this
|
|
56
65
|
shared database, and the developer must know that before approving.
|
|
57
|
-
5. **Read the exit code — it is the verdict:**
|
|
66
|
+
5. **Read the exit code — it is the verdict. Never through a pipe:**
|
|
67
|
+
`apply … | tail` returns *tail's* code, always 0. Unpiped, or `${PIPESTATUS[0]}`.
|
|
58
68
|
|
|
59
69
|
| Exit | Meaning | What you must do |
|
|
60
70
|
|------|---------|-----------------|
|
|
@@ -120,6 +130,7 @@ the stakes are highest.
|
|
|
120
130
|
| "Token is stale, I'll just take the new one" | The content changed. The developer must see the new preview. |
|
|
121
131
|
| "apply refused it; run-sql will take it" | The refusal is the product working. Relay it. |
|
|
122
132
|
| "I'll restore quietly to clean up my mistake" | Restores are writes. Same gate, same visibility. |
|
|
133
|
+
| "`$?` said 0 after I piped to tail" | That was tail's 0. Read pythia's own words, or its unpiped code. |
|
|
123
134
|
| "The dev said 'stop asking' once" | That covered that task, not forever. Re-confirm scope on the next one. |
|
|
124
135
|
|
|
125
136
|
## When NOT to use this skill
|
|
@@ -540,25 +540,42 @@ def test_privilege_warning_speaks_proxy():
|
|
|
540
540
|
FakeConn(direct), "APP", "APP")
|
|
541
541
|
|
|
542
542
|
|
|
543
|
-
def
|
|
543
|
+
def test_prune_drops_only_redundant_previews():
|
|
544
|
+
"""A preview wrote nothing, but its restore.sql is the live version at
|
|
545
|
+
that moment — the only rollback for a change run by hand. Prune may drop
|
|
546
|
+
it only when a newer entry already keeps the identical rollback."""
|
|
544
547
|
import argparse
|
|
548
|
+
import contextlib
|
|
545
549
|
import datetime
|
|
550
|
+
import io
|
|
546
551
|
with tempfile.TemporaryDirectory() as td:
|
|
547
|
-
base = datetime.datetime(2026, 8,
|
|
548
|
-
|
|
552
|
+
base = datetime.datetime(2026, 8, 27, 9, 0, 0)
|
|
553
|
+
# two previews of the same object, same live version -> one is spare
|
|
554
|
+
pythia.write_journal_entry(td, "PROCEDURE", "P_A", "old", "new1",
|
|
549
555
|
{"applied": False}, now=base)
|
|
550
|
-
|
|
551
|
-
td, "PROCEDURE", "
|
|
556
|
+
keep_dup = pythia.write_journal_entry(
|
|
557
|
+
td, "PROCEDURE", "P_A", "old", "new2", {"applied": False},
|
|
552
558
|
now=base.replace(minute=1))
|
|
559
|
+
# a preview of another object, unique rollback -> must survive
|
|
560
|
+
lone = pythia.write_journal_entry(
|
|
561
|
+
td, "PROCEDURE", "P_B", "only_copy", "x", {"applied": False},
|
|
562
|
+
now=base.replace(minute=2))
|
|
563
|
+
applied = pythia.write_journal_entry(
|
|
564
|
+
td, "PROCEDURE", "P_C", "a", "b", {"applied": True},
|
|
565
|
+
now=base.replace(minute=3))
|
|
566
|
+
snap = pythia.write_journal_entry(
|
|
567
|
+
td, "PROCEDURE", "P_D", "a", "a",
|
|
568
|
+
{"snapshot": True, "applied": False}, now=base.replace(minute=4))
|
|
553
569
|
ns = argparse.Namespace(action="prune", project_root=td, json=False,
|
|
554
570
|
id=None)
|
|
555
|
-
import contextlib
|
|
556
|
-
import io
|
|
557
571
|
buf = io.StringIO()
|
|
558
572
|
with contextlib.redirect_stdout(buf):
|
|
559
573
|
pythia.cmd_journal(None, None, ns)
|
|
560
|
-
|
|
561
|
-
assert
|
|
574
|
+
left = pythia.list_journal_entries(td)
|
|
575
|
+
assert "pruned 1" in buf.getvalue(), buf.getvalue()
|
|
576
|
+
assert applied in left and snap in left # never touched
|
|
577
|
+
assert lone in left # unique rollback survives
|
|
578
|
+
assert keep_dup in left # newest of the pair kept
|
|
562
579
|
|
|
563
580
|
|
|
564
581
|
def test_agent_user_alter_form_and_warning_prediction():
|
|
@@ -742,26 +759,6 @@ def test_auto_snapshot_reports_drift_and_keeps_the_old_rollback():
|
|
|
742
759
|
assert len(pythia.list_journal_entries(td)) == 2
|
|
743
760
|
|
|
744
761
|
|
|
745
|
-
def test_prune_never_eats_a_snapshot():
|
|
746
|
-
import argparse
|
|
747
|
-
import contextlib
|
|
748
|
-
import io
|
|
749
|
-
import datetime
|
|
750
|
-
with tempfile.TemporaryDirectory() as td:
|
|
751
|
-
base = datetime.datetime(2026, 8, 27, 9, 0, 0)
|
|
752
|
-
pythia.write_journal_entry(td, "PROCEDURE", "P_A", "a", "b",
|
|
753
|
-
{"applied": False}, now=base)
|
|
754
|
-
snap = pythia.write_journal_entry(
|
|
755
|
-
td, "PROCEDURE", "P_B", "a", "a", {"snapshot": True,
|
|
756
|
-
"applied": False},
|
|
757
|
-
now=base.replace(minute=1))
|
|
758
|
-
ns = argparse.Namespace(action="prune", project_root=td, json=False,
|
|
759
|
-
id=None)
|
|
760
|
-
with contextlib.redirect_stdout(io.StringIO()):
|
|
761
|
-
pythia.cmd_journal(None, None, ns)
|
|
762
|
-
assert pythia.list_journal_entries(td) == [snap]
|
|
763
|
-
|
|
764
|
-
|
|
765
762
|
def main():
|
|
766
763
|
failed = 0
|
|
767
764
|
for name, fn in sorted(globals().items()):
|
|
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
|
{pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/scripts/pythia_plsql.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythia_plsql-0.4.0 → pythia_plsql-0.4.2}/skills/pythia-explore/reference/data-dictionary.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
|