pythia-plsql 0.14.0__tar.gz → 0.14.1__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 (46) hide show
  1. {pythia_plsql-0.14.0/scripts/pythia_plsql.egg-info → pythia_plsql-0.14.1}/PKG-INFO +1 -1
  2. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/pyproject.toml +1 -1
  3. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia.py +34 -50
  4. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1/scripts/pythia_plsql.egg-info}/PKG-INFO +1 -1
  5. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_mcp.py +20 -33
  6. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/LICENSE +0 -0
  7. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/README.md +0 -0
  8. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/compile-errors.sql +0 -0
  9. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/current-schema.sql +0 -0
  10. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/dependencies.sql +0 -0
  11. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/impact.sql +0 -0
  12. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/invalid-objects.sql +0 -0
  13. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/name-occupants.sql +0 -0
  14. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/object-names.sql +0 -0
  15. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/object-source.sql +0 -0
  16. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/plscope-enabled.sql +0 -0
  17. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/plscope-statements.sql +0 -0
  18. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/plscope-usages.sql +0 -0
  19. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/session-privileges.sql +0 -0
  20. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/similar-candidates.sql +0 -0
  21. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/queries/source.sql +0 -0
  22. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
  23. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
  24. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
  25. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
  26. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
  27. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/setup.cfg +0 -0
  28. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-apply/SKILL.md +0 -0
  29. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-comment/SKILL.md +0 -0
  30. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-conventions/SKILL.md +0 -0
  31. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-explore/SKILL.md +0 -0
  32. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-explore/reference/data-dictionary.md +0 -0
  33. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-impact/SKILL.md +0 -0
  34. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-review/SKILL.md +0 -0
  35. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-review/reference/antipatterns.md +0 -0
  36. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-setup/SKILL.md +0 -0
  37. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-skill-author/SKILL.md +0 -0
  38. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-spec/SKILL.md +0 -0
  39. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-write/SKILL.md +0 -0
  40. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/pythia-write/reference/patterns.md +0 -0
  41. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/skills/using-pythia/SKILL.md +0 -0
  42. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_install.py +0 -0
  43. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_phase1.py +0 -0
  44. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_phase2.py +0 -0
  45. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_phase3.py +0 -0
  46. {pythia_plsql-0.14.0 → pythia_plsql-0.14.1}/tests/test_phase5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.14.0
3
+ Version: 0.14.1
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.14.0"
7
+ version = "0.14.1"
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" }
@@ -3484,9 +3484,11 @@ def mcp_approve_decision(root, token, elicit):
3484
3484
  prune_expired_grants(root)
3485
3485
  mint_grant(root, token, meta.get("connection") or "",
3486
3486
  revalidate=fingerprint_text(meta.get("row_set")), approver="mcp")
3487
- return _mcp_text(f"Approved — grant minted for {token}, single use, expires "
3488
- f"in {GRANT_TTL_MINUTES} minutes. Now run: "
3489
- f"{invocation()} apply <file> --confirm {token}")
3487
+ return _mcp_text(f"Approved — grant minted for {token} (single use, "
3488
+ f"{GRANT_TTL_MINUTES} min). Now run apply --confirm on the "
3489
+ "file you previewed YOURSELF — do not hand the developer a "
3490
+ f"command to type: {invocation()} apply <file> --confirm "
3491
+ f"{token}")
3490
3492
 
3491
3493
 
3492
3494
  def mcp_serve(reader, writer, root):
@@ -3558,33 +3560,30 @@ def cmd_mcp(conn, schema, ns):
3558
3560
  # table header inside a string/comment is not worth a parser here.
3559
3561
  CODEX_MCP_BEGIN = "# pythia:begin (managed by `pythia install`)"
3560
3562
  CODEX_MCP_END = "# pythia:end"
3563
+ # Only the server roster — nothing else. An earlier attempt also wrote an
3564
+ # [approval_policy.granular] table, but that table requires several fields
3565
+ # (sandbox_approval, rules, ...) and an incomplete one broke Codex v0.154 at
3566
+ # config-load time. Elicitations surface under the default interactive
3567
+ # approval policy without any of that, and the developer's approval policy is
3568
+ # theirs, not ours to set.
3561
3569
  CODEX_MCP_BLOCK = (f"{CODEX_MCP_BEGIN}\n"
3562
3570
  "[mcp_servers.pythia]\n"
3563
3571
  'command = "python"\n'
3564
- 'args = ["-m", "pythia", "mcp"]\n\n'
3565
- "[approval_policy.granular]\n"
3566
- "mcp_elicitations = true\n"
3572
+ 'args = ["-m", "pythia", "mcp"]\n'
3567
3573
  f"{CODEX_MCP_END}")
3568
- # requirements.toml is a separate admin-enforced file; forbidding these two is
3569
- # what closes the danger-full-access elicitation auto-approve hole.
3570
- CODEX_REQUIREMENTS = (f"{CODEX_MCP_BEGIN}\n"
3571
- "# pythia needs elicitations to reach a human, so the two\n"
3572
- "# modes that would auto-answer or skip them are disallowed.\n"
3573
- 'allowed_approval_policies = ["untrusted", "on-request", "on-failure"]\n'
3574
- 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n'
3575
- f"{CODEX_MCP_END}")
3576
3574
 
3577
3575
 
3578
3576
  def merge_codex_mcp_config(path):
3579
- """Append the MCP block to <project>/.codex/config.toml. Returns
3580
- (path, action): 'created' | 'appended' | 'present' | 'conflict'. Refuses
3581
- (writes nothing) if either table already exists without our marker."""
3577
+ """Append the MCP server registration to <project>/.codex/config.toml.
3578
+ Returns (path, action): 'created' | 'appended' | 'present' | 'conflict'.
3579
+ Refuses (writes nothing) if [mcp_servers.pythia] already exists without our
3580
+ marker — the stdlib cannot safely merge TOML, so a collision is reported,
3581
+ never overwritten."""
3582
3582
  path = pathlib.Path(path)
3583
3583
  old = path.read_text(encoding="utf-8") if path.is_file() else None
3584
3584
  if old is not None and CODEX_MCP_BEGIN in old:
3585
3585
  return path, "present"
3586
- if old is not None and ("[mcp_servers.pythia]" in old
3587
- or "[approval_policy.granular]" in old):
3586
+ if old is not None and "[mcp_servers.pythia]" in old:
3588
3587
  return path, "conflict"
3589
3588
  if old is None:
3590
3589
  new, action = CODEX_MCP_BLOCK + "\n", "created"
@@ -3596,21 +3595,6 @@ def merge_codex_mcp_config(path):
3596
3595
  return path, action
3597
3596
 
3598
3597
 
3599
- def merge_codex_requirements(path):
3600
- """Write <project>/.codex/requirements.toml forbidding danger-full-access
3601
- and never-approval. Returns (path, action): 'created' | 'present' |
3602
- 'conflict'. An existing file without our marker is left untouched."""
3603
- path = pathlib.Path(path)
3604
- old = path.read_text(encoding="utf-8") if path.is_file() else None
3605
- if old is not None and CODEX_MCP_BEGIN in old:
3606
- return path, "present"
3607
- if old is not None:
3608
- return path, "conflict"
3609
- path.parent.mkdir(parents=True, exist_ok=True)
3610
- path.write_text(CODEX_REQUIREMENTS + "\n", encoding="utf-8")
3611
- return path, "created"
3612
-
3613
-
3614
3598
  def install_claude_hooks(base_dir, events=None):
3615
3599
  """Merge the two hooks and the deny rule into <base>/.claude/settings.json
3616
3600
  — the project's, or the home directory's for -g. The file is Claude
@@ -3693,28 +3677,28 @@ def report_codex_hooks(project_root, ns):
3693
3677
 
3694
3678
 
3695
3679
  def report_codex_mcp(project_root, ns):
3696
- """Project scope: register the write-free MCP approver in Codex's config and
3697
- pin the sandbox so elicitations always reach a human. --no-hooks skips it,
3698
- as with the other Codex wiring. Needs Codex >= v0.120; older Codex ignores
3699
- the server and the console approve path still works."""
3680
+ """Project scope: register the write-free MCP approver in Codex's config.
3681
+ --no-hooks skips it, as with the other Codex wiring. Needs Codex >= v0.120
3682
+ for elicitation; older Codex ignores the server and the console approve
3683
+ path still works."""
3700
3684
  if getattr(ns, "no_hooks", False):
3701
3685
  return
3702
- base = pathlib.Path(project_root) / ".codex"
3703
- cfg_path, cfg = merge_codex_mcp_config(base / "config.toml")
3704
- req_path, req = merge_codex_requirements(base / "requirements.toml")
3686
+ cfg_path, cfg = merge_codex_mcp_config(pathlib.Path(project_root)
3687
+ / ".codex" / "config.toml")
3705
3688
  if cfg in ("created", "appended"):
3706
3689
  print(f"\nRegistered the pythia MCP approver in {cfg_path} "
3707
3690
  "(Codex >= v0.120; run /mcp to trust it).")
3708
3691
  elif cfg == "conflict":
3709
- print(f"\n! {cfg_path} already defines [mcp_servers.pythia] or "
3710
- "[approval_policy.granular]; add this yourself:\n"
3711
- + CODEX_MCP_BLOCK)
3712
- if req == "created":
3713
- print(f"Pinned the sandbox in {req_path} (no danger-full-access, so "
3714
- "elicitations always ask a human).")
3715
- elif req == "conflict":
3716
- print(f"\n! {req_path} already exists; add these constraints to it:\n"
3717
- + CODEX_REQUIREMENTS)
3692
+ print(f"\n! {cfg_path} already defines [mcp_servers.pythia]; add this "
3693
+ "yourself:\n" + CODEX_MCP_BLOCK)
3694
+ else:
3695
+ print(f"\npythia MCP approver already registered in {cfg_path}.")
3696
+ # The one bypass of the elicitation is Codex auto-approving it under the
3697
+ # danger-full-access sandbox. pythia does not write requirements.toml to
3698
+ # forbid that — it is enterprise-managed config, and the schema is
3699
+ # version-fragile — so it warns instead.
3700
+ print("Do not run Codex under `danger-full-access` with pythia: that "
3701
+ "sandbox auto-approves elicitations, bypassing your Approve.")
3718
3702
 
3719
3703
 
3720
3704
  def cmd_install(conn, schema, ns):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.14.0
3
+ Version: 0.14.1
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
@@ -79,7 +79,11 @@ def test_mint_only_on_accept_with_approve():
79
79
 
80
80
  res = pythia.mcp_approve_decision(td, token, elicit)
81
81
  assert res["isError"] is False
82
- assert token in res["content"][0]["text"]
82
+ text = res["content"][0]["text"]
83
+ assert token in text
84
+ # the message tells the AGENT to continue itself, not the developer to
85
+ # type a command — 0.14.1 fix
86
+ assert "yourself" in text.lower()
83
87
  assert "PKG_ORDER" in seen["message"] # the card was shown
84
88
  assert seen["schema"]["properties"]["decision"]["enum"] == ["Approve", "Reject"]
85
89
  g = pythia.read_grant(td, token)
@@ -200,7 +204,10 @@ def test_merge_codex_mcp_config_appends_once_when_clean():
200
204
  body = path.read_text(encoding="utf-8")
201
205
  assert "[mcp_servers.pythia]" in body
202
206
  assert 'args = ["-m", "pythia", "mcp"]' in body
203
- assert "mcp_elicitations = true" in body
207
+ # 0.14.1: we do NOT touch approval_policy — an incomplete granular table
208
+ # broke Codex v0.154 config loading, and elicitations work under the
209
+ # default interactive policy anyway.
210
+ assert "approval_policy" not in body
204
211
  assert pythia.merge_codex_mcp_config(path)[1] == "present" # idempotent
205
212
 
206
213
 
@@ -216,43 +223,20 @@ def test_merge_codex_mcp_config_preserves_other_content():
216
223
  assert "[mcp_servers.pythia]" in body
217
224
 
218
225
 
219
- def test_merge_codex_mcp_config_refuses_on_table_collision():
226
+ def test_merge_codex_mcp_config_refuses_on_server_collision():
220
227
  with tempfile.TemporaryDirectory() as td:
221
228
  path = pathlib.Path(td) / ".codex" / "config.toml"
222
229
  path.parent.mkdir(parents=True)
223
- original = "[approval_policy.granular]\nmcp_elicitations = false\n"
230
+ original = '[mcp_servers.pythia]\ncommand = "other"\n'
224
231
  path.write_text(original, encoding="utf-8")
225
232
  p, action = pythia.merge_codex_mcp_config(path)
226
233
  assert action == "conflict"
227
234
  assert path.read_text(encoding="utf-8") == original # untouched
228
235
 
229
236
 
230
- def test_merge_codex_requirements_creates_and_is_idempotent():
231
- with tempfile.TemporaryDirectory() as td:
232
- path = pathlib.Path(td) / ".codex" / "requirements.toml"
233
- p, action = pythia.merge_codex_requirements(path)
234
- assert action == "created"
235
- body = path.read_text(encoding="utf-8")
236
- assert "allowed_approval_policies" in body and "allowed_sandbox_modes" in body
237
- assert "danger-full-access" not in body # the forbidden mode is absent
238
- assert '"never"' not in body # the forbidden policy is absent
239
- assert pythia.merge_codex_requirements(path)[1] == "present"
240
-
241
-
242
- def test_merge_codex_requirements_refuses_an_existing_file():
243
- with tempfile.TemporaryDirectory() as td:
244
- path = pathlib.Path(td) / ".codex" / "requirements.toml"
245
- path.parent.mkdir(parents=True)
246
- original = "# org policy\nallowed_sandbox_modes = []\n"
247
- path.write_text(original, encoding="utf-8")
248
- p, action = pythia.merge_codex_requirements(path)
249
- assert action == "conflict"
250
- assert path.read_text(encoding="utf-8") == original # untouched
251
-
252
-
253
237
  # --- Task 5: wire the MCP install into cmd_install (project scope) ----------
254
238
 
255
- def test_cmd_install_registers_the_mcp_server_and_pins_the_sandbox():
239
+ def test_cmd_install_registers_the_mcp_server_and_warns_off_danger():
256
240
  import argparse
257
241
  import contextlib
258
242
  old = os.environ.get("PATH")
@@ -261,13 +245,16 @@ def test_cmd_install_registers_the_mcp_server_and_pins_the_sandbox():
261
245
  with tempfile.TemporaryDirectory() as td:
262
246
  ns = argparse.Namespace(project_root=td, glob=False, source=None,
263
247
  color=False, json=False, no_hooks=False)
264
- with contextlib.redirect_stdout(io.StringIO()):
248
+ buf = io.StringIO()
249
+ with contextlib.redirect_stdout(buf):
265
250
  pythia.cmd_install(None, None, ns)
266
251
  cfg = pathlib.Path(td) / ".codex" / "config.toml"
267
- req = pathlib.Path(td) / ".codex" / "requirements.toml"
268
- assert "[mcp_servers.pythia]" in cfg.read_text(encoding="utf-8")
269
- assert "mcp_elicitations = true" in cfg.read_text(encoding="utf-8")
270
- assert "allowed_sandbox_modes" in req.read_text(encoding="utf-8")
252
+ body = cfg.read_text(encoding="utf-8")
253
+ assert "[mcp_servers.pythia]" in body
254
+ assert "approval_policy" not in body # 0.14.1: never touched
255
+ # requirements.toml is no longer written; the pin became a warning
256
+ assert not (pathlib.Path(td) / ".codex" / "requirements.toml").exists()
257
+ assert "danger-full-access" in buf.getvalue() # the warning
271
258
  finally:
272
259
  if old is not None:
273
260
  os.environ["PATH"] = old
File without changes
File without changes
File without changes