pythia-plsql 0.5.0__tar.gz → 0.6.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 (41) hide show
  1. {pythia_plsql-0.5.0/scripts/pythia_plsql.egg-info → pythia_plsql-0.6.1}/PKG-INFO +66 -20
  2. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/README.md +65 -19
  3. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/pyproject.toml +1 -1
  4. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia.py +119 -2
  5. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1/scripts/pythia_plsql.egg-info}/PKG-INFO +66 -20
  6. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-apply/SKILL.md +2 -0
  7. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-conventions/SKILL.md +2 -0
  8. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-explore/SKILL.md +2 -0
  9. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-impact/SKILL.md +2 -0
  10. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-review/SKILL.md +2 -0
  11. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-setup/SKILL.md +3 -1
  12. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-skill-author/SKILL.md +2 -0
  13. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-write/SKILL.md +2 -0
  14. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/tests/test_install.py +13 -0
  15. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/tests/test_phase1.py +40 -0
  16. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/tests/test_phase5.py +50 -5
  17. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/LICENSE +0 -0
  18. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/compile-errors.sql +0 -0
  19. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/dependencies.sql +0 -0
  20. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/impact.sql +0 -0
  21. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/invalid-objects.sql +0 -0
  22. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/name-occupants.sql +0 -0
  23. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/object-names.sql +0 -0
  24. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/object-source.sql +0 -0
  25. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/plscope-enabled.sql +0 -0
  26. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/plscope-statements.sql +0 -0
  27. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/plscope-usages.sql +0 -0
  28. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/session-privileges.sql +0 -0
  29. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/similar-candidates.sql +0 -0
  30. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/queries/source.sql +0 -0
  31. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
  32. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
  33. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
  34. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
  35. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
  36. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/setup.cfg +0 -0
  37. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-explore/reference/data-dictionary.md +0 -0
  38. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-review/reference/antipatterns.md +0 -0
  39. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/skills/pythia-write/reference/patterns.md +0 -0
  40. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/tests/test_phase2.py +0 -0
  41. {pythia_plsql-0.5.0 → pythia_plsql-0.6.1}/tests/test_phase3.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.5.0
3
+ Version: 0.6.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
@@ -26,10 +26,16 @@ Dynamic: license-file
26
26
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
27
27
  ![python](https://img.shields.io/badge/python-3.9%2B-blue)
28
28
 
29
- An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI coding
30
- agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills` supports).
31
- Explore schemas too big to dump, measure blast radius **before** touching anything,
32
- and land changes through a snapshot-verified write path that never lies about rollback.
29
+ An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI
30
+ coding agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills`
31
+ supports).
32
+
33
+ pythia is a **harness**: a book of working rules an agent studies and follows
34
+ when it sits next to a developer. Not a chatbot, not an autopilot — a
35
+ disciplined assistant with three properties a good one has: **knowledge** (it
36
+ asks the live schema, never its memory of one), **judgment** (it measures
37
+ before it proposes), and **obedience to rules it can recite** (gates it will
38
+ quote back to you rather than quietly skip).
33
39
 
34
40
  ## Why ask the database instead of reading dumps
35
41
 
@@ -47,23 +53,63 @@ Code that "reads fine" against the dump references types and packages the dump n
47
53
  heard of. Every pythia command asks the live data dictionary instead — and every
48
54
  truncated output says so, so an agent never mistakes a partial answer for a full one.
49
55
 
50
- ## How it works
56
+ ## The operating model: Learn → Ask → Do
51
57
 
52
- ```
53
- developer chats with the agent
54
- │
55
- skills/ teach the agent when to ask, when to stop, when to ask YOU
56
- │
57
- pythia CLI — expert queries, impact analysis, the six-step write path
58
- │
59
- Oracle data dictionary: ALL_SOURCE, ALL_DEPENDENCIES, ALL_ERRORS, PL/Scope
60
- ```
58
+ Give the agent a problem — "add a column and every procedure that maintains
59
+ it", "why does this report double rows", "port this fix" — and the kit walks
60
+ it through the same three movements a careful senior developer makes.
61
61
 
62
- The write path is the heart: **snapshot → impact → preview → apply → verify → report**.
63
- DDL self-commits in Oracle — the snapshot is the only real undo, so it always runs
64
- first and no flag can turn it off. A 6-hex token binds the write to exactly what was
65
- previewed; exit codes make honesty machine-readable
66
- (`0` clean · `1` refused · `3` **written but broken — never reported as success**).
62
+ ### 1 · Learn — understand before proposing
63
+
64
+ The agent studies four things, in order, with tools instead of guesses:
65
+
66
+ | It learns | How | Instead of |
67
+ |---|---|---|
68
+ | the problem's real shape | `deps`, `impact`, `plscope` — the exact dependency graph and usage sites | skimming code and hoping |
69
+ | the schema's ground truth | `src`, `cols`, `args`, `ddl`, `errors` against the live database | trusting a dump that drifted |
70
+ | the house style | `.pythia/conventions.md` + `conventions --scan/--check` — rules measured against real names | inventing a style per session |
71
+ | how this codebase already solves it | `similar` — the neighbours to imitate | writing the first thing that compiles |
72
+
73
+ Nothing in this phase writes. Reading is free, so the bar is: **no proposal
74
+ before the blast radius is known** (`pythia-impact`'s iron law) and **no line
75
+ written before the neighbours have been read** (`pythia-write`'s).
76
+
77
+ ### 2 · Ask — the questions are part of the method, not an interruption
78
+
79
+ The kit makes the agent stop at exactly the moments where a human's judgment
80
+ is the missing input, and forbids it to guess past them:
81
+
82
+ - **Before any write**: the full preview — diff, dependents, warnings — is
83
+ relayed verbatim, and the agent waits for a real yes. A compliment is not a
84
+ yes. Silence is not a yes.
85
+ - **When the blast radius is large**: ten or more dependents, or anything
86
+ cross-schema, goes to the developer *before code is written*, not after.
87
+ - **When sources of truth disagree**: a standards document says one thing,
88
+ the schema does another — that gap is a question ("rule nobody follows,
89
+ new-code-only, or drift?"), never a silent pick.
90
+ - **When something breaks**: exit 3 means *written but broken*. The agent
91
+ reports it exactly so, with the ready rollback — reporting success here is
92
+ the one sin the whole kit is built to prevent.
93
+ - **When policy refuses**: the refusal is relayed as information, not routed
94
+ around.
95
+
96
+ ### 3 · Do — act inside a pipeline that cannot lie
97
+
98
+ Only after Learn and Ask does anything touch the database, and then only
99
+ through one door: **snapshot → impact → preview → token → apply → verify →
100
+ report**. DDL self-commits in Oracle, so the snapshot is the only real undo —
101
+ it runs first and no flag disables it. A content-bound token guarantees what
102
+ lands is byte-for-byte what was approved. And the CLI enforces the gates
103
+ itself: a headless agent cannot `--yes` its own writes or loosen policy —
104
+ that takes a human at a real terminal.
105
+
106
+ The same discipline holds when the *developer* does the work: `src` and
107
+ `impact` silently snapshot what they read, so even a change made by hand in
108
+ SQL Developer has a rollback file waiting (`history` lists them, drift is
109
+ reported when source moved with no apply behind it).
110
+
111
+ **Học – Hỏi – Làm** — Learn, Ask, Do. If the agent cannot show which phase it
112
+ is in, it is doing none of them.
67
113
 
68
114
  ## Install
69
115
 
@@ -12,10 +12,16 @@
12
12
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
13
13
  ![python](https://img.shields.io/badge/python-3.9%2B-blue)
14
14
 
15
- An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI coding
16
- agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills` supports).
17
- Explore schemas too big to dump, measure blast radius **before** touching anything,
18
- and land changes through a snapshot-verified write path that never lies about rollback.
15
+ An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI
16
+ coding agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills`
17
+ supports).
18
+
19
+ pythia is a **harness**: a book of working rules an agent studies and follows
20
+ when it sits next to a developer. Not a chatbot, not an autopilot — a
21
+ disciplined assistant with three properties a good one has: **knowledge** (it
22
+ asks the live schema, never its memory of one), **judgment** (it measures
23
+ before it proposes), and **obedience to rules it can recite** (gates it will
24
+ quote back to you rather than quietly skip).
19
25
 
20
26
  ## Why ask the database instead of reading dumps
21
27
 
@@ -33,23 +39,63 @@ Code that "reads fine" against the dump references types and packages the dump n
33
39
  heard of. Every pythia command asks the live data dictionary instead — and every
34
40
  truncated output says so, so an agent never mistakes a partial answer for a full one.
35
41
 
36
- ## How it works
42
+ ## The operating model: Learn → Ask → Do
37
43
 
38
- ```
39
- developer chats with the agent
40
- │
41
- skills/ teach the agent when to ask, when to stop, when to ask YOU
42
- │
43
- pythia CLI — expert queries, impact analysis, the six-step write path
44
- │
45
- Oracle data dictionary: ALL_SOURCE, ALL_DEPENDENCIES, ALL_ERRORS, PL/Scope
46
- ```
44
+ Give the agent a problem — "add a column and every procedure that maintains
45
+ it", "why does this report double rows", "port this fix" — and the kit walks
46
+ it through the same three movements a careful senior developer makes.
47
47
 
48
- The write path is the heart: **snapshot → impact → preview → apply → verify → report**.
49
- DDL self-commits in Oracle — the snapshot is the only real undo, so it always runs
50
- first and no flag can turn it off. A 6-hex token binds the write to exactly what was
51
- previewed; exit codes make honesty machine-readable
52
- (`0` clean · `1` refused · `3` **written but broken — never reported as success**).
48
+ ### 1 · Learn — understand before proposing
49
+
50
+ The agent studies four things, in order, with tools instead of guesses:
51
+
52
+ | It learns | How | Instead of |
53
+ |---|---|---|
54
+ | the problem's real shape | `deps`, `impact`, `plscope` — the exact dependency graph and usage sites | skimming code and hoping |
55
+ | the schema's ground truth | `src`, `cols`, `args`, `ddl`, `errors` against the live database | trusting a dump that drifted |
56
+ | the house style | `.pythia/conventions.md` + `conventions --scan/--check` — rules measured against real names | inventing a style per session |
57
+ | how this codebase already solves it | `similar` — the neighbours to imitate | writing the first thing that compiles |
58
+
59
+ Nothing in this phase writes. Reading is free, so the bar is: **no proposal
60
+ before the blast radius is known** (`pythia-impact`'s iron law) and **no line
61
+ written before the neighbours have been read** (`pythia-write`'s).
62
+
63
+ ### 2 · Ask — the questions are part of the method, not an interruption
64
+
65
+ The kit makes the agent stop at exactly the moments where a human's judgment
66
+ is the missing input, and forbids it to guess past them:
67
+
68
+ - **Before any write**: the full preview — diff, dependents, warnings — is
69
+ relayed verbatim, and the agent waits for a real yes. A compliment is not a
70
+ yes. Silence is not a yes.
71
+ - **When the blast radius is large**: ten or more dependents, or anything
72
+ cross-schema, goes to the developer *before code is written*, not after.
73
+ - **When sources of truth disagree**: a standards document says one thing,
74
+ the schema does another — that gap is a question ("rule nobody follows,
75
+ new-code-only, or drift?"), never a silent pick.
76
+ - **When something breaks**: exit 3 means *written but broken*. The agent
77
+ reports it exactly so, with the ready rollback — reporting success here is
78
+ the one sin the whole kit is built to prevent.
79
+ - **When policy refuses**: the refusal is relayed as information, not routed
80
+ around.
81
+
82
+ ### 3 · Do — act inside a pipeline that cannot lie
83
+
84
+ Only after Learn and Ask does anything touch the database, and then only
85
+ through one door: **snapshot → impact → preview → token → apply → verify →
86
+ report**. DDL self-commits in Oracle, so the snapshot is the only real undo —
87
+ it runs first and no flag disables it. A content-bound token guarantees what
88
+ lands is byte-for-byte what was approved. And the CLI enforces the gates
89
+ itself: a headless agent cannot `--yes` its own writes or loosen policy —
90
+ that takes a human at a real terminal.
91
+
92
+ The same discipline holds when the *developer* does the work: `src` and
93
+ `impact` silently snapshot what they read, so even a change made by hand in
94
+ SQL Developer has a rollback file waiting (`history` lists them, drift is
95
+ reported when source moved with no apply behind it).
96
+
97
+ **Học – Hỏi – Làm** — Learn, Ask, Do. If the agent cannot show which phase it
98
+ is in, it is doing none of them.
53
99
 
54
100
  ## Install
55
101
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythia-plsql"
7
- version = "0.5.0"
7
+ version = "0.6.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" }
@@ -1988,6 +1988,117 @@ def _schema_objects(conn, schema):
1988
1988
  return [(r[0], r[1]) for r in rows]
1989
1989
 
1990
1990
 
1991
+ OPERATING_GUIDE = """\
1992
+ THE OPERATING MODEL — Learn, Ask, Do (Hoc - Hoi - Lam)
1993
+
1994
+ pythia is a harness: a book of working rules for an AI agent sitting next to
1995
+ a developer. Every task moves through the same three movements. If you cannot
1996
+ say which movement you are in, you are in none of them.
1997
+
1998
+ === 1. LEARN — understand before proposing ======================
1999
+ Nothing here writes. Reading is free; guessing is not.
2000
+
2001
+ the problem's shape deps · impact · plscope the exact graph, not a skim
2002
+ the schema's truth src · args · cols · ddl · errors · invalid · check · ls · grep · sql
2003
+ what you can reach connections - names, users, targets. NEVER open
2004
+ connections.json yourself: it holds passwords, a
2005
+ permission gate should stop you, and this command
2006
+ is the answer you were reaching for.
2007
+ the house style conventions (--scan / --check) · the project's conventions.md
2008
+ how it is done here similar · history neighbours to imitate, versions that exist
2009
+
2010
+ Iron laws: no proposal before the blast radius is known; no line written
2011
+ before the neighbours have been read.
2012
+
2013
+ === 2. ASK — the questions are the method =======================
2014
+ Stop at exactly these moments; a guess past any of them is a defect.
2015
+
2016
+ before any write relay the full preview (diff, dependents, warnings)
2017
+ verbatim, then wait. A compliment is not a yes.
2018
+ blast radius >= 10 or anything cross-schema: show the developer the
2019
+ list BEFORE writing code.
2020
+ truths disagree document says one thing, schema another - ask which:
2021
+ rule nobody follows, new-code-only, or drift.
2022
+ policy refuses relay the refusal (policy shows the rules). Never
2023
+ route around it.
2024
+ it broke exit 3 = written but broken. Say exactly that, with
2025
+ the rollback line. journal (show/diff) is your evidence.
2026
+
2027
+ === 3. DO — act inside a pipeline that cannot lie ===============
2028
+ One door for writes: snapshot -> impact -> preview -> token -> apply ->
2029
+ verify -> report.
2030
+
2031
+ apply the six-step write; --confirm binds to the preview
2032
+ journal restore undo, through the same six steps
2033
+ unistr exact non-ASCII literals for what you are writing
2034
+ install · agent-user · guide setting the harness itself up
2035
+
2036
+ The CLI enforces the gates: headless --yes is refused, policy cannot be
2037
+ loosened without a human at a terminal, the snapshot cannot be switched off.
2038
+
2039
+ Skills carry the full method (pythia-explore, -impact, -conventions, -write,
2040
+ -apply, -review, -setup, -skill-author). No skill support on this platform?
2041
+ This page is the contract; follow it as written.
2042
+ """
2043
+
2044
+
2045
+ def connection_summary(cfg):
2046
+ """Everything about the configured connections except the secrets.
2047
+
2048
+ An agent has to know which connections exist. Without a sanctioned way to
2049
+ ask, it reads connections.json itself — which is exactly the access a
2050
+ permission classifier should stop, and did. So this exists, and it is
2051
+ built to be provably safe: fields are copied in by name, never by
2052
+ iterating the entry, so a key added to the config later cannot leak
2053
+ through here by accident.
2054
+ """
2055
+ cfg = dict(cfg or {})
2056
+ default = cfg.pop("default", None)
2057
+ rows = []
2058
+ for name, entry in cfg.items():
2059
+ if not isinstance(entry, dict):
2060
+ continue
2061
+ dsn = entry.get("dsn") or ""
2062
+ if not dsn and entry.get("host"):
2063
+ svc = entry.get("service_name") or entry.get("sid") or ""
2064
+ dsn = f"{entry['host']}:{entry.get('port', 1521)}"
2065
+ if svc:
2066
+ dsn += f"/{svc}"
2067
+ user = str(entry.get("user") or "")
2068
+ rows.append({
2069
+ "name": name,
2070
+ "user": user,
2071
+ "target": dsn or "\u2014",
2072
+ "schema": (entry.get("schema") or user.split("[")[-1].rstrip("]")
2073
+ or "\u2014").upper(),
2074
+ "default": isinstance(default, str) and default.upper() == name.upper(),
2075
+ })
2076
+ return rows
2077
+
2078
+
2079
+ def cmd_connections(conn, schema, ns):
2080
+ cfg, _ = find_config(pathlib.Path.cwd(), os.environ)
2081
+ rows = connection_summary(cfg)
2082
+ if ns.json:
2083
+ print(json.dumps(rows, indent=2))
2084
+ return
2085
+ if not rows:
2086
+ print("No connections configured. "
2087
+ f"{invocation()} install scaffolds .pythia/connections.json.")
2088
+ return
2089
+ print(f"{'':1} {'NAME':<16} {'USER':<26} {'SCHEMA':<20} TARGET")
2090
+ for r in rows:
2091
+ mark = "*" if r["default"] else " "
2092
+ print(f"{mark} {r['name']:<16} {r['user']:<26} {r['schema']:<20} "
2093
+ f"{r['target']}")
2094
+ print("\n* default. Passwords are never printed — this command reads the "
2095
+ "config so you\n do not have to open it. Pick one with --conn NAME.")
2096
+
2097
+
2098
+ def cmd_guide(conn, schema, ns):
2099
+ print(OPERATING_GUIDE)
2100
+
2101
+
1991
2102
  def cmd_conventions(conn, schema, ns):
1992
2103
  root = ns.project_root
1993
2104
  if getattr(ns, "init", False):
@@ -2381,11 +2492,11 @@ COMMANDS = {"check": cmd_check, "ls": cmd_ls, "src": cmd_src, "args": cmd_args,
2381
2492
  "invalid": cmd_invalid, "errors": cmd_errors, "deps": cmd_deps,
2382
2493
  "impact": cmd_impact, "similar": cmd_similar, "plscope": cmd_plscope,
2383
2494
  "policy": cmd_policy, "journal": cmd_journal, "apply": cmd_apply,
2384
- "conventions": cmd_conventions, "install": cmd_install,
2495
+ "conventions": cmd_conventions, "guide": cmd_guide, "connections": cmd_connections, "install": cmd_install,
2385
2496
  "unistr": cmd_unistr, "agent-user": cmd_agent_user,
2386
2497
  "history": cmd_history}
2387
2498
 
2388
- NO_DB_COMMANDS = {"policy", "journal", "install", "unistr",
2499
+ NO_DB_COMMANDS = {"policy", "journal", "install", "unistr", "guide", "connections",
2389
2500
  "history"}
2390
2501
 
2391
2502
 
@@ -2483,6 +2594,12 @@ def build_parser():
2483
2594
  help="apply without stopping; the full preview still prints and journals")
2484
2595
  s.add_argument("--depth", type=int, default=3,
2485
2596
  help="impact depth for the preview (default 3)")
2597
+ sub.add_parser("connections", parents=[common()],
2598
+ help="list configured connections — names, users, "
2599
+ "targets; never passwords")
2600
+ sub.add_parser("guide", parents=[common()],
2601
+ help="the operating model: Learn, Ask, Do — the whole "
2602
+ "harness on one page, no database needed")
2486
2603
  s = sub.add_parser("conventions", parents=[common()],
2487
2604
  help="show the project's house-style naming patterns")
2488
2605
  s.add_argument("--init", action="store_true",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythia-plsql
3
- Version: 0.5.0
3
+ Version: 0.6.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
@@ -26,10 +26,16 @@ Dynamic: license-file
26
26
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
27
27
  ![python](https://img.shields.io/badge/python-3.9%2B-blue)
28
28
 
29
- An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI coding
30
- agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills` supports).
31
- Explore schemas too big to dump, measure blast radius **before** touching anything,
32
- and land changes through a snapshot-verified write path that never lies about rollback.
29
+ An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI
30
+ coding agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills`
31
+ supports).
32
+
33
+ pythia is a **harness**: a book of working rules an agent studies and follows
34
+ when it sits next to a developer. Not a chatbot, not an autopilot — a
35
+ disciplined assistant with three properties a good one has: **knowledge** (it
36
+ asks the live schema, never its memory of one), **judgment** (it measures
37
+ before it proposes), and **obedience to rules it can recite** (gates it will
38
+ quote back to you rather than quietly skip).
33
39
 
34
40
  ## Why ask the database instead of reading dumps
35
41
 
@@ -47,23 +53,63 @@ Code that "reads fine" against the dump references types and packages the dump n
47
53
  heard of. Every pythia command asks the live data dictionary instead — and every
48
54
  truncated output says so, so an agent never mistakes a partial answer for a full one.
49
55
 
50
- ## How it works
56
+ ## The operating model: Learn → Ask → Do
51
57
 
52
- ```
53
- developer chats with the agent
54
- │
55
- skills/ teach the agent when to ask, when to stop, when to ask YOU
56
- │
57
- pythia CLI — expert queries, impact analysis, the six-step write path
58
- │
59
- Oracle data dictionary: ALL_SOURCE, ALL_DEPENDENCIES, ALL_ERRORS, PL/Scope
60
- ```
58
+ Give the agent a problem — "add a column and every procedure that maintains
59
+ it", "why does this report double rows", "port this fix" — and the kit walks
60
+ it through the same three movements a careful senior developer makes.
61
61
 
62
- The write path is the heart: **snapshot → impact → preview → apply → verify → report**.
63
- DDL self-commits in Oracle — the snapshot is the only real undo, so it always runs
64
- first and no flag can turn it off. A 6-hex token binds the write to exactly what was
65
- previewed; exit codes make honesty machine-readable
66
- (`0` clean · `1` refused · `3` **written but broken — never reported as success**).
62
+ ### 1 · Learn — understand before proposing
63
+
64
+ The agent studies four things, in order, with tools instead of guesses:
65
+
66
+ | It learns | How | Instead of |
67
+ |---|---|---|
68
+ | the problem's real shape | `deps`, `impact`, `plscope` — the exact dependency graph and usage sites | skimming code and hoping |
69
+ | the schema's ground truth | `src`, `cols`, `args`, `ddl`, `errors` against the live database | trusting a dump that drifted |
70
+ | the house style | `.pythia/conventions.md` + `conventions --scan/--check` — rules measured against real names | inventing a style per session |
71
+ | how this codebase already solves it | `similar` — the neighbours to imitate | writing the first thing that compiles |
72
+
73
+ Nothing in this phase writes. Reading is free, so the bar is: **no proposal
74
+ before the blast radius is known** (`pythia-impact`'s iron law) and **no line
75
+ written before the neighbours have been read** (`pythia-write`'s).
76
+
77
+ ### 2 · Ask — the questions are part of the method, not an interruption
78
+
79
+ The kit makes the agent stop at exactly the moments where a human's judgment
80
+ is the missing input, and forbids it to guess past them:
81
+
82
+ - **Before any write**: the full preview — diff, dependents, warnings — is
83
+ relayed verbatim, and the agent waits for a real yes. A compliment is not a
84
+ yes. Silence is not a yes.
85
+ - **When the blast radius is large**: ten or more dependents, or anything
86
+ cross-schema, goes to the developer *before code is written*, not after.
87
+ - **When sources of truth disagree**: a standards document says one thing,
88
+ the schema does another — that gap is a question ("rule nobody follows,
89
+ new-code-only, or drift?"), never a silent pick.
90
+ - **When something breaks**: exit 3 means *written but broken*. The agent
91
+ reports it exactly so, with the ready rollback — reporting success here is
92
+ the one sin the whole kit is built to prevent.
93
+ - **When policy refuses**: the refusal is relayed as information, not routed
94
+ around.
95
+
96
+ ### 3 · Do — act inside a pipeline that cannot lie
97
+
98
+ Only after Learn and Ask does anything touch the database, and then only
99
+ through one door: **snapshot → impact → preview → token → apply → verify →
100
+ report**. DDL self-commits in Oracle, so the snapshot is the only real undo —
101
+ it runs first and no flag disables it. A content-bound token guarantees what
102
+ lands is byte-for-byte what was approved. And the CLI enforces the gates
103
+ itself: a headless agent cannot `--yes` its own writes or loosen policy —
104
+ that takes a human at a real terminal.
105
+
106
+ The same discipline holds when the *developer* does the work: `src` and
107
+ `impact` silently snapshot what they read, so even a change made by hand in
108
+ SQL Developer has a rollback file waiting (`history` lists them, drift is
109
+ reported when source moved with no apply behind it).
110
+
111
+ **Học – Hỏi – Làm** — Learn, Ask, Do. If the agent cannot show which phase it
112
+ is in, it is doing none of them.
67
113
 
68
114
  ## Install
69
115
 
@@ -7,6 +7,8 @@ description: Use when a PL/SQL change is ready to reach the database - applying
7
7
 
8
8
  **Announce at start:** "Using pythia-apply — I'll preview the change first."
9
9
 
10
+ **Phase:** Ask → Do — the preview is relayed and approved before the one write door opens
11
+
10
12
  DDL in Oracle commits itself. There is no transaction to roll back — the
11
13
  snapshot pythia takes before writing is the only undo that exists. This skill
12
14
  exists so that safety net is always used, and used honestly.
@@ -7,6 +7,8 @@ description: Use when a project's house style already exists somewhere and needs
7
7
 
8
8
  **Announce at start:** "Using pythia-conventions — I'll read the patterns off the schema first."
9
9
 
10
+ **Phase:** Learn → Ask — derive from the schema, ask where the document disagrees, only then write the config
11
+
10
12
  Conventions already exist in every mature schema. They are in the object
11
13
  names, in a standards document, or in a senior developer's head. This turns
12
14
  them into two files: patterns the tool checks on every write, and prose every
@@ -7,6 +7,8 @@ description: Use when you need to understand anything in an Oracle schema - find
7
7
 
8
8
  **Announce at start:** "Using pythia-explore — asking the database directly."
9
9
 
10
+ **Phase:** Learn
11
+
10
12
  ## The principle: ask the database, never the dump
11
13
 
12
14
  Repositories of exported `.sql` files go stale the day after export. A real
@@ -7,6 +7,8 @@ description: Use before proposing or writing any change to an Oracle object - a
7
7
 
8
8
  **Announce at start:** "Using pythia-impact to measure the blast radius first."
9
9
 
10
+ **Phase:** Learn — and the first Ask gate: a large blast radius reaches the developer before any code is written
11
+
10
12
  Changing an Oracle object recompiles or invalidates everything that depends
11
13
  on it — immediately, schema-wide, for every user of a shared database. The
12
14
  cost of knowing first is one command.
@@ -7,6 +7,8 @@ description: Use when reviewing PL/SQL - a changed procedure or package, a propo
7
7
 
8
8
  **Announce at start:** "Using pythia-review — checking the database's signals first."
9
9
 
10
+ **Phase:** Learn → Ask — the database's own signals first, findings back to the developer
11
+
10
12
  Review in two passes: what the database *knows* is wrong, then what the
11
13
  checklist says is *likely* wrong. Machine signals first — they are free and
12
14
  exact.
@@ -7,6 +7,8 @@ description: Use when setting pythia up for a project or a machine - writing con
7
7
 
8
8
  **Announce at start:** "Using pythia-setup to configure the database access."
9
9
 
10
+ **Phase:** Do — one-time harness setup; everything after runs through Learn → Ask → Do
11
+
10
12
  Set up in this order: connection first (everything else needs it), the
11
13
  least-privilege user second (the only protection that cannot be bypassed),
12
14
  SQLcl MCP last (optional, reads only).
@@ -83,7 +85,7 @@ until you want it.
83
85
 
84
86
  | File | Created by | What it does |
85
87
  |---|---|---|
86
- | `connections.json` | `pythia install` | Where to connect. Holds credentials; gitignored. |
88
+ | `connections.json` | `pythia install` | Where to connect. **Holds passwords — never open it**; `pythia connections` lists names, users, schemas and targets with no secrets. Gitignored. |
87
89
  | `journal/` | first write or snapshot | Every captured version, with a runnable rollback per entry. |
88
90
  | `conventions.json` | `pythia conventions --init` | Naming patterns per object type. Every apply preview warns when a new name drifts. |
89
91
  | `conventions.md` | `pythia conventions --init` | The house rules in prose. **`pythia-write` reads this before writing anything**, and it outranks the generic patterns this pack ships with. |
@@ -7,6 +7,8 @@ description: Use when the developer wants their own way of working captured as a
7
7
 
8
8
  **Announce at start:** "Using pythia-skill-author — let's capture how you actually work."
9
9
 
10
+ **Phase:** Learn → Ask → Do — interview, mine the schema, verify triggering
11
+
10
12
  A skill is a decision captured so it never has to be re-argued. Capture what
11
13
  the team *actually does* — with evidence from the database — not what anyone
12
14
  remembers about it.
@@ -7,6 +7,8 @@ description: Use when writing or modifying PL/SQL source - a procedure, function
7
7
 
8
8
  **Announce at start:** "Using pythia-write — mining the codebase's conventions first."
9
9
 
10
+ **Phase:** Learn → Do — mine the neighbours first, then draft; landing it is pythia-apply's job
11
+
10
12
  **Before anything:** if the project has `.pythia/conventions.md`, read it —
11
13
  house rules there outrank every generic pattern below, and
12
14
  `pythia conventions` shows the naming patterns the apply preview will check. If the project has none and the developer
@@ -348,6 +348,19 @@ def test_conventions_init_writes_both_halves_and_never_clobbers():
348
348
  assert (d / "conventions.json").read_text(encoding="utf-8") == '{"naming": {}}'
349
349
 
350
350
 
351
+ def test_guide_places_every_command_in_the_model():
352
+ """`pythia guide` is the book an agent can open on any platform, skills
353
+ support or not. Coherence is enforced: every command the CLI exposes must
354
+ appear somewhere in the guide -- adding a command without giving it a
355
+ place in Learn/Ask/Do fails here."""
356
+ text = pythia.OPERATING_GUIDE
357
+ for phase in ("LEARN", "ASK", "DO"):
358
+ assert phase in text, f"guide missing the {phase} movement"
359
+ for cmd in pythia.COMMANDS:
360
+ assert cmd in text, f"command {cmd!r} has no place in the guide"
361
+ assert "guide" in pythia.NO_DB_COMMANDS # the book opens with no database
362
+
363
+
351
364
  def main():
352
365
  failed = 0
353
366
  for name, fn in sorted(globals().items()):
@@ -327,6 +327,46 @@ def test_oracle_account_taken_from_a_proxy_connect_string():
327
327
  assert pythia.authenticating_account(None) is None
328
328
 
329
329
 
330
+ SECRET = "hunter2-do-not-leak"
331
+
332
+
333
+ def _cfg():
334
+ return {
335
+ "dev": {"host": "db.example.com", "port": 1521,
336
+ "service_name": "orcl", "user": "app_agent[app_owner]",
337
+ "password": SECRET, "schema": "APP_OWNER"},
338
+ "stage": {"dsn": "stage.example.com:1521/orcl", "user": "u2",
339
+ "password": SECRET},
340
+ "default": "dev",
341
+ }
342
+
343
+
344
+ def test_connection_summary_never_carries_a_secret():
345
+ """An agent needs to know which connections exist. Until it could ask,
346
+ it parsed connections.json itself -- which is exactly the read a
347
+ permission classifier should block. This is the sanctioned answer, so it
348
+ must be provably secret-free, in every field and both output modes."""
349
+ import json
350
+ rows = pythia.connection_summary(_cfg())
351
+ blob = json.dumps(rows)
352
+ assert SECRET not in blob
353
+ assert "password" not in blob.lower()
354
+ names = [r["name"] for r in rows]
355
+ assert names == ["dev", "stage"] # the "default" key is not one
356
+ dev = rows[0]
357
+ assert dev["default"] is True and rows[1]["default"] is False
358
+ assert dev["user"] == "app_agent[app_owner]" # proxy shape is safe to show
359
+ assert dev["target"] == "db.example.com:1521/orcl"
360
+ assert rows[1]["target"] == "stage.example.com:1521/orcl" # dsn form
361
+ assert dev["schema"] == "APP_OWNER"
362
+
363
+
364
+ def test_connection_summary_survives_a_thin_entry():
365
+ rows = pythia.connection_summary({"env": {"user": "u"}})
366
+ assert rows[0]["target"] == "—" and rows[0]["schema"] == "U"
367
+ assert pythia.connection_summary(None) == []
368
+
369
+
330
370
  def main():
331
371
  failed = 0
332
372
  for name, fn in sorted(globals().items()):
@@ -109,17 +109,62 @@ def test_reference_files_exist_where_promised():
109
109
 
110
110
 
111
111
  def test_readme_carries_the_required_tables():
112
- """The spec's completion criteria: drift table up top, honest-rollback and
113
- policy tables present, install channels, under 200 lines."""
112
+ """The line cap is gone by the owner's call — the README now explains
113
+ the operating model in full. What stays pinned is content: the drift
114
+ table, the honest-rollback and policy tables, the install channels, and
115
+ the Learn-Ask-Do frame the whole kit is organised around."""
114
116
  text = (ROOT / "README.md").read_text(encoding="utf-8")
115
- for needle in ("1,016", # drift table, index row
117
+ for needle in ("1,016", # drift table
116
118
  "Flashback Query", "Recycle Bin", # rollback honesty
117
119
  "plsql_source", "data_dml", # policy table
118
120
  "npx skills add", # install channel
119
- "star-history.com", # star chart
121
+ "Learn", "Ask", "Do", # the operating model
122
+ "star-history.com",
120
123
  "MIT"):
121
124
  assert needle in text, f"README missing {needle!r}"
122
- assert text.count("\n") + 1 <= 200, "README must stay under 200 lines"
125
+
126
+
127
+ def test_vietnamese_readme_carries_the_operating_model():
128
+ text = (ROOT / "README.vi.md").read_text(encoding="utf-8")
129
+ for needle in ("Học", "Hỏi", "Làm"):
130
+ assert needle in text, f"README.vi missing {needle!r}"
131
+
132
+
133
+ def test_every_skill_declares_its_phase():
134
+ """The kit's operating model is Learn - Ask - Do. A skill that cannot say
135
+ which movement it serves is not part of the method, it is a loose page."""
136
+ for name in sorted(EXPECTED):
137
+ path = SKILLS / name / "SKILL.md"
138
+ if not path.is_file():
139
+ continue
140
+ text = path.read_text(encoding="utf-8")
141
+ m = re.search(r"\*\*Phase:\*\* (.+)", text)
142
+ assert m, f"{name}: no '**Phase:**' declaration"
143
+ words = set(re.findall(r"Learn|Ask|Do", m.group(1)))
144
+ assert words and words <= {"Learn", "Ask", "Do"}, \
145
+ f"{name}: phase must name Learn/Ask/Do, got {m.group(1)!r}"
146
+
147
+
148
+ def test_no_manifest_hardcodes_a_stale_skill_count():
149
+ """The pack grew from seven skills to eight and three shipped manifests
150
+ still said seven. Counting in prose is a contradiction waiting to happen,
151
+ so the check is: no manifest states a number the code can disprove."""
152
+ import json
153
+ n = len(EXPECTED)
154
+ words = {7: "seven", 8: "eight", 9: "nine"}
155
+ stale = [w for k, w in words.items() if k != n]
156
+ for rel in (".claude-plugin/marketplace.json", "npm/package.json",
157
+ "npm/README.md"):
158
+ text = (ROOT / rel).read_text(encoding="utf-8").lower()
159
+ for word in stale:
160
+ assert word + " skill" not in text and word + "-skill" not in text, \
161
+ f"{rel} claims {word} skills; there are {n}"
162
+ mk = json.loads((ROOT / ".claude-plugin/marketplace.json")
163
+ .read_text(encoding="utf-8"))
164
+ declared = {p.rsplit("/", 1)[-1] for p in mk["plugins"][0]["skills"]}
165
+ assert declared == EXPECTED, (
166
+ f"marketplace.json and the pack disagree: "
167
+ f"missing {sorted(EXPECTED - declared)}, extra {sorted(declared - EXPECTED)}")
123
168
 
124
169
 
125
170
  def main():
File without changes
File without changes