pythia-plsql 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythia-plsql
3
+ Version: 0.1.0
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
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/thaildhe172591/pythia
7
+ Project-URL: Repository, https://github.com/thaildhe172591/pythia
8
+ Keywords: oracle,plsql,database,agent-skills,sql
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: oracledb>=2.0
13
+ Dynamic: license-file
14
+
15
+ # pythia
16
+
17
+ > Oracle's MCP gives your agent a connection. **pythia gives it the judgment to use it.**
18
+
19
+ [![ci](https://github.com/thaildhe172591/pythia/actions/workflows/ci.yml/badge.svg)](https://github.com/thaildhe172591/pythia/actions/workflows/ci.yml)
20
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
21
+ ![python](https://img.shields.io/badge/python-3.9%2B-blue)
22
+
23
+ An Agent Skills + CLI kit for developing PL/SQL on Oracle Database with AI coding
24
+ agents (Claude Code, Codex, Cursor — any of the 76 agents `npx skills` supports).
25
+ Explore schemas too big to dump, measure blast radius **before** touching anything,
26
+ and land changes through a snapshot-verified write path that never lies about rollback.
27
+
28
+ ## Why ask the database instead of reading dumps
29
+
30
+ A real mid-size system, repo export vs live database, audited in 2026:
31
+
32
+ | Object type | In the dump | In the database | Verdict |
33
+ |---|---|---|---|
34
+ | Procedures | 3,827 | 3,827 | matched |
35
+ | Tables | 952 | 952 | matched |
36
+ | **Types** | **0** | **115** | **all missing** |
37
+ | **Packages** | **0** | **9** | **all missing** |
38
+ | **Indexes** | **116** | **1,016** | **~89% missing** |
39
+
40
+ Code that "reads fine" against the dump references types and packages the dump never
41
+ heard of. Every pythia command asks the live data dictionary instead — and every
42
+ truncated output says so, so an agent never mistakes a partial answer for a full one.
43
+
44
+ ## How it works
45
+
46
+ ```
47
+ developer chats with the agent
48
+ │
49
+ skills/ teach the agent when to ask, when to stop, when to ask YOU
50
+ │
51
+ pythia CLI — expert queries, impact analysis, the six-step write path
52
+ │
53
+ Oracle data dictionary: ALL_SOURCE, ALL_DEPENDENCIES, ALL_ERRORS, PL/Scope
54
+ ```
55
+
56
+ The write path is the heart: **snapshot → impact → preview → apply → verify → report**.
57
+ DDL self-commits in Oracle — the snapshot is the only real undo, so it always runs
58
+ first and no flag can turn it off. A 6-hex token binds the write to exactly what was
59
+ previewed; exit codes make honesty machine-readable
60
+ (`0` clean · `1` refused · `3` **written but broken — never reported as success**).
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ npx pythia-plsql # everything: pip install + skills picker + config scaffold
66
+ ```
67
+
68
+ Or the same thing piecewise:
69
+
70
+ ```bash
71
+ pip install pythia-plsql # the CLI (thin driver — no Oracle Instant Client needed)
72
+ pythia install # skills into your agent + .pythia/connections.json scaffold
73
+ pythia check # fill in connections.json first, then verify
74
+ ```
75
+
76
+ The pip package is the whole kit: with Node.js present, `pythia install`
77
+ runs `npx skills add` (77 agents, symlinked updates; `--source <git-url>`
78
+ for internal mirrors) — without Node it copies the bundled skill pack into
79
+ `.claude/skills/` and `.agents/skills/`. Skills alone: `npx skills add
80
+ thaildhe172591/pythia`, or `/plugin marketplace add thaildhe172591/pythia`.
81
+
82
+ Running from a clone works too — `python scripts/pythia.py <command>`; every
83
+ printed follow-up command matches how you invoked it. Windows, macOS, Linux
84
+ and WSL are all CI-tested.
85
+
86
+ ## Commands
87
+
88
+ | Read | Understand | Write |
89
+ |---|---|---|
90
+ | `check` connectivity + counts | `deps` what it depends on | `apply` the six-step write |
91
+ | `ls` find objects | `impact` what depends on it | `journal` list · diff · export · restore |
92
+ | `src` source, compiler line numbers | `errors` compile errors, line:col | `policy` show · set |
93
+ | `args` signatures | `invalid` everything broken | |
94
+ | `ddl` via DBMS_METADATA | `plscope` exact identifier usages | |
95
+ | `cols` columns + types | `similar` programs named like this | |
96
+ | `grep` search all source | | |
97
+ | `sql` free query (SELECT/WITH only) | | |
98
+
99
+ Every command takes `--json` (machine output), `--conn` (pick a connection), and
100
+ caps output with explicit truncation markers so context windows stay intact.
101
+
102
+ **Your house style is config, not folklore**: put naming patterns in
103
+ `.pythia/conventions.json` and apply previews warn when a new object's name
104
+ drifts; put the prose rules in `.pythia/conventions.md` and the skills make
105
+ every agent read them first (`pythia conventions` shows both).
106
+
107
+ ## Security & write policy
108
+
109
+ **The account is the real security layer** — the policy file is an application-side
110
+ fence. Give the agent its own revocable credential with proxy authentication
111
+ (`agent_user[schema_owner]`, no `ANY` privileges, no owner password shared):
112
+ see [`examples/agent-user-setup.example.sql`](examples/agent-user-setup.example.sql).
113
+ `pythia check` warns when the session runs with more power than the task needs.
114
+
115
+ Per-group write policy, `.pythia/policy.json` (defaults shown):
116
+
117
+ | Group | Default | Is rollback real? |
118
+ |---|---|---|
119
+ | `plsql_source` | `confirm` | **Yes — completely.** Source is recoverable from `ALL_SOURCE`. |
120
+ | `data_dml` | `deny` | **No.** After commit only Flashback Query remains, within undo retention. |
121
+ | `structural` | `deny` | **Almost never.** `DROP COLUMN` is permanent; a dropped table may be in the Recycle Bin. |
122
+ | `grants` | `deny` | Yes, but by hand. |
123
+ | `session` | `allow` | Not needed. |
124
+
125
+ The groups that cannot be snapshotted default to `deny` — and the refusal says that,
126
+ instead of "policy forbids it". Anonymous PL/SQL blocks are refused outright.
127
+ Unrecognized statements are refused, never guessed into a group.
128
+
129
+ Reads may flow through Oracle's official SQLcl MCP server (`sql -mcp`, keep `-R 4`);
130
+ it audits every interaction in `DBTOOLS$MCP_LOG`. **Writes never do** — only
131
+ `pythia apply` has the snapshot, preview, verify and journal.
132
+
133
+ ## Skills
134
+
135
+ Seven skills teach the agent the workflow — superpowers-style gates, not suggestions:
136
+
137
+ `plsql-setup` · `plsql-explore` · `plsql-impact` (impact **before** any change) ·
138
+ `plsql-write` (copy the codebase's conventions) · `plsql-apply` (the gate: the
139
+ developer sees the preview and approves in chat before anything is written) ·
140
+ `plsql-review` (seven antipatterns) · `plsql-skill-author` (capture *your team's*
141
+ workflow as a new skill, mined from the live schema).
142
+
143
+ ## Compatibility
144
+
145
+ | | |
146
+ |---|---|
147
+ | OS | Windows, macOS, Linux, WSL — full test matrix in CI |
148
+ | Python | 3.9+ · stdlib + `python-oracledb` (thin mode) only |
149
+ | Oracle | core works broadly; PL/Scope statement capture needs 12.2+; license-safe views only |
150
+ | Agents | any `npx skills` agent (76) · native Claude Code plugin |
151
+
152
+ ## Star History
153
+
154
+ <a href="https://www.star-history.com/?repos=thaildhe172591%2Fpythia&type=date&legend=top-left">
155
+ <picture>
156
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=thaildhe172591/pythia&type=date&theme=dark&legend=top-left&sealed_token=OnPCuXPZZEbpQk5_Eor5ZB0fTeMzMN1nmrsDJ8qqahouiJt4-IoDvjONdD05i2D2PhfDC5kwd6CUQeBsWGNV20gt2-4HSD-RygX3h0Ni0lrbQnRh60EN3A" />
157
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=thaildhe172591/pythia&type=date&legend=top-left&sealed_token=OnPCuXPZZEbpQk5_Eor5ZB0fTeMzMN1nmrsDJ8qqahouiJt4-IoDvjONdD05i2D2PhfDC5kwd6CUQeBsWGNV20gt2-4HSD-RygX3h0Ni0lrbQnRh60EN3A" />
158
+ <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=thaildhe172591/pythia&type=date&legend=top-left&sealed_token=OnPCuXPZZEbpQk5_Eor5ZB0fTeMzMN1nmrsDJ8qqahouiJt4-IoDvjONdD05i2D2PhfDC5kwd6CUQeBsWGNV20gt2-4HSD-RygX3h0Ni0lrbQnRh60EN3A" />
159
+ </picture>
160
+ </a>
161
+
162
+ ## Contributing
163
+
164
+ Tests need **no database** — the fakes prove the safety properties (snapshot before
165
+ write, deny touches nothing, stale tokens refused). See [CONTRIBUTING.md](CONTRIBUTING.md).
166
+
167
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,29 @@
1
+ pythia.py,sha256=odx1LIG-ve0fv5byYkVTNZT7TNQjvqxErdwutjkOoVM,66824
2
+ pythia_plsql-0.1.0.dist-info/licenses/LICENSE,sha256=fY6GEoq3pNfFbCVGoUQulGzhdL5NrJCnoLdc7dOA4dQ,1063
3
+ pythia_queries/compile-errors.sql,sha256=wUZHicAxONcAsltUSgiihYajHzuyjksUOuxeT0oRJ5Q,573
4
+ pythia_queries/dependencies.sql,sha256=tt1c25dOY_340eVvfzKoNB7FJFZd6Y8bCeLYmAkkxNw,1544
5
+ pythia_queries/impact.sql,sha256=s2AH8QW6vaj0V5PHVbvnqmiK-_wVuKI57ZtJGfWciqY,1022
6
+ pythia_queries/invalid-objects.sql,sha256=wa3Cw4nsxnkGa9Ik15QgTHWC1BiYdmSbOCo9mKbV0kk,439
7
+ pythia_queries/name-occupants.sql,sha256=lTx26iWSdG7sobCfxJNWf0tev9JbY86DPvXasFMVuxc,758
8
+ pythia_queries/object-source.sql,sha256=foqe7AOd_wVyLqDZ6nSe7GLK-3ct44FKewlCBS0DlEs,493
9
+ pythia_queries/plscope-enabled.sql,sha256=LOlpd7FFsWJ2mXSAfRBMlR2P2mwaXJZZ7OmYIiAe-Bo,324
10
+ pythia_queries/plscope-statements.sql,sha256=6LATpNm-3WrSHEMIWkJpI3OlUreglUnGC9mg2x0vzVw,732
11
+ pythia_queries/plscope-usages.sql,sha256=THcR4GbPvVA-aTWs9oAzul5L17iekUwkMWt80y_gv2Q,619
12
+ pythia_queries/session-privileges.sql,sha256=q5OUhFHPGaOXsdCSChBw9SUbiyVdf0fWf9dMgG551lM,280
13
+ pythia_queries/similar-candidates.sql,sha256=Q04BnVlJJuV1EIVaot6jkoV0zvzZhYi_1wP-tAvWwps,556
14
+ pythia_queries/source.sql,sha256=Q-nJ58LHAverO07hy9wOHzPsWwfdQ2h19ZyEbn0fUaQ,328
15
+ pythia_skills/plsql-apply/SKILL.md,sha256=sF8NHR8huWkPEXpl0-weIXSjakTnxL9eCH6XvkbxHjQ,6513
16
+ pythia_skills/plsql-explore/SKILL.md,sha256=VuE3QSON29lBQllXG_lKZgviR2BVMo0tzyZ3A1MYS1s,2891
17
+ pythia_skills/plsql-explore/reference/data-dictionary.md,sha256=DnpaPq6ukHnwFMaAuc8vVOz6PKhGIsc3RzRE_KVpyf8,2560
18
+ pythia_skills/plsql-impact/SKILL.md,sha256=85sPgPahXX_YaolZpBjcp5RFtUKWPBLQ8ve7FoebsXg,2468
19
+ pythia_skills/plsql-review/SKILL.md,sha256=w3Cfg4KRI6ziiHz_Es4MHlJAs4p5nZC0a-5Docp_FYc,2356
20
+ pythia_skills/plsql-review/reference/antipatterns.md,sha256=I2s9f6GKiwQ0l0FU8Ifj-CqMT2Z4ZY4GygH62PVWkks,3213
21
+ pythia_skills/plsql-setup/SKILL.md,sha256=rNr3SFw1HaxJAdiFgU0iDJ__lfoobuh9oVYCOWAMGmE,3489
22
+ pythia_skills/plsql-skill-author/SKILL.md,sha256=pG_qRTktVzEfEEniziJtf6w-qMSfi8XVOif221uxGnE,4594
23
+ pythia_skills/plsql-write/SKILL.md,sha256=PNeFTd6UU3c2ni614nN7bdQWTmvmoQduQ4Bf04HC4eU,2644
24
+ pythia_skills/plsql-write/reference/patterns.md,sha256=1p5-GTGpaDeSrHPSEkWf4vCaUGREzQ3F-P67uXj4eBo,2812
25
+ pythia_plsql-0.1.0.dist-info/METADATA,sha256=BeQ9ZdOvdpugKIooUFtuV73CdGLafbM2OtXEkOIQrGQ,8130
26
+ pythia_plsql-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
27
+ pythia_plsql-0.1.0.dist-info/entry_points.txt,sha256=dUdl60jlLUfdd4utUcbQruuHgxron7LMmWvxm_SkGFg,39
28
+ pythia_plsql-0.1.0.dist-info/top_level.txt,sha256=5a6jYMFVXa2nyT6jPP8WAbgKAC2qYfRw1PvAyDzQH-E,36
29
+ pythia_plsql-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pythia = pythia:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thaild
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ pythia
2
+ pythia_queries
3
+ pythia_skills
@@ -0,0 +1,17 @@
1
+ -- Purpose: compilation errors and warnings with the exact line and column
2
+ -- Oracle reports, so a fix can be aimed rather than guessed. This
3
+ -- closes the compile-read-fix loop.
4
+ -- Binds: :s schema (object owner)
5
+ -- :n object name, or NULL for every object in the schema
6
+ -- Returns: NAME, TYPE, SEQUENCE, LINE, POSITION, ATTRIBUTE, TEXT
7
+ select name,
8
+ type,
9
+ sequence,
10
+ line,
11
+ position,
12
+ attribute,
13
+ text
14
+ from all_errors
15
+ where owner = :s
16
+ and (:n is null or name = upper(:n))
17
+ order by name, sequence
@@ -0,0 +1,30 @@
1
+ -- Purpose: what an object depends on, walked downward. NOCYCLE survives
2
+ -- circular references; the depth bound sits in CONNECT BY so the
3
+ -- walk is pruned rather than filtered after the fact.
4
+ -- Oracle's own built-ins (SYS.STANDARD and friends) are excluded
5
+ -- unless asked for: every PL/SQL object depends on them, so they
6
+ -- crowd out the dependencies a developer is actually looking for.
7
+ -- The test is applied in both START WITH and CONNECT BY, so an
8
+ -- excluded object takes its whole subtree with it instead of
9
+ -- leaving its children behind as orphans.
10
+ -- Binds: :s schema (owner of the starting object)
11
+ -- :n starting object name
12
+ -- :depth levels to walk (1 = direct dependencies only)
13
+ -- :with_sys 1 to include SYS/PUBLIC built-ins, 0 to leave them out
14
+ -- Returns: LVL, OWNER, NAME, TYPE, DEPENDENCY_TYPE
15
+ select level lvl,
16
+ d.referenced_owner owner,
17
+ d.referenced_name name,
18
+ d.referenced_type type,
19
+ d.dependency_type
20
+ from all_dependencies d
21
+ start with d.owner = :s
22
+ and d.name = upper(:n)
23
+ and (:with_sys = 1 or d.referenced_owner not in ('SYS', 'PUBLIC'))
24
+ connect by nocycle
25
+ prior d.referenced_owner = d.owner
26
+ and prior d.referenced_name = d.name
27
+ and prior d.referenced_type = d.type
28
+ and level <= :depth
29
+ and (:with_sys = 1 or d.referenced_owner not in ('SYS', 'PUBLIC'))
30
+ order siblings by d.referenced_type, d.referenced_name
@@ -0,0 +1,26 @@
1
+ -- Purpose: what depends on an object — everything a change to it can break.
2
+ -- The reverse of dependencies.sql. Status comes from a scalar
3
+ -- subquery so the hierarchical row order survives.
4
+ -- Binds: :s schema (owner of the starting object)
5
+ -- :n starting object name
6
+ -- :depth levels to walk (1 = direct dependents only)
7
+ -- Returns: LVL, OWNER, NAME, TYPE, STATUS, DEPENDENCY_TYPE
8
+ select level lvl,
9
+ d.owner,
10
+ d.name,
11
+ d.type,
12
+ (select o.status
13
+ from all_objects o
14
+ where o.owner = d.owner
15
+ and o.object_name = d.name
16
+ and o.object_type = d.type) status,
17
+ d.dependency_type
18
+ from all_dependencies d
19
+ start with d.referenced_owner = :s
20
+ and d.referenced_name = upper(:n)
21
+ connect by nocycle
22
+ prior d.owner = d.referenced_owner
23
+ and prior d.name = d.referenced_name
24
+ and prior d.type = d.referenced_type
25
+ and level <= :depth
26
+ order siblings by d.type, d.name
@@ -0,0 +1,11 @@
1
+ -- Purpose: every INVALID object in the schema. Captured before and after a
2
+ -- change, this is what proves a fix did not break something else.
3
+ -- Binds: :s schema (object owner)
4
+ -- Returns: OBJECT_NAME, OBJECT_TYPE, LAST_DDL
5
+ select object_name,
6
+ object_type,
7
+ to_char(last_ddl_time, 'yyyy-mm-dd hh24:mi:ss') last_ddl
8
+ from all_objects
9
+ where owner = :s
10
+ and status = 'INVALID'
11
+ order by object_type, object_name
@@ -0,0 +1,15 @@
1
+ -- Purpose: which object types currently hold a name in the schema's main
2
+ -- namespace. CREATE OR REPLACE cannot change an object's type, so a
3
+ -- name held by a different type must be refused at preview time —
4
+ -- otherwise the preview promises what the database will reject with
5
+ -- ORA-00955. Found by an agent during the first field test.
6
+ -- Binds: :s schema (object owner)
7
+ -- :n object name
8
+ -- Returns: OBJECT_TYPE
9
+ select object_type
10
+ from all_objects
11
+ where owner = :s
12
+ and object_name = upper(:n)
13
+ and object_type in ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY',
14
+ 'TYPE', 'TYPE BODY', 'VIEW', 'TABLE',
15
+ 'MATERIALIZED VIEW', 'SEQUENCE', 'SYNONYM')
@@ -0,0 +1,13 @@
1
+ -- Purpose: source of one specific unit — the snapshot read that runs before
2
+ -- every write. PACKAGE and PACKAGE BODY are distinct objects, so the
3
+ -- type is part of the identity, not a filter convenience.
4
+ -- Binds: :s schema (object owner)
5
+ -- :n object name
6
+ -- :t object type exactly as in ALL_SOURCE (e.g. PACKAGE BODY)
7
+ -- Returns: TEXT
8
+ select text
9
+ from all_source
10
+ where owner = :s
11
+ and name = upper(:n)
12
+ and type = upper(:t)
13
+ order by line
@@ -0,0 +1,9 @@
1
+ -- Purpose: one-row probe telling an empty result apart from a schema that was
2
+ -- never compiled with PL/Scope — the difference between "not found"
3
+ -- and "cannot know".
4
+ -- Binds: :s schema (object owner)
5
+ -- Returns: ENABLED
6
+ select 1 enabled
7
+ from all_identifiers
8
+ where owner = :s
9
+ and rownum = 1
@@ -0,0 +1,21 @@
1
+ -- Purpose: the SQL statements that touch a table, and where they sit. Answers
2
+ -- "which program writes to this table?" exactly. Needs PL/Scope with
3
+ -- STATEMENTS:ALL (Oracle 12.2+).
4
+ -- Binds: :s schema (object owner)
5
+ -- :n table name
6
+ -- Returns: SQL_TYPE, OBJECT_NAME, OBJECT_TYPE, LINE, COL
7
+ select s.type sql_type,
8
+ s.object_name,
9
+ s.object_type,
10
+ s.line,
11
+ s.col
12
+ from all_statements s
13
+ join all_identifiers i
14
+ on i.owner = s.owner
15
+ and i.object_name = s.object_name
16
+ and i.object_type = s.object_type
17
+ and i.usage_context_id = s.usage_id
18
+ where s.owner = :s
19
+ and i.name = upper(:n)
20
+ and i.type = 'TABLE'
21
+ order by s.object_name, s.line
@@ -0,0 +1,18 @@
1
+ -- Purpose: every place an identifier is declared, referenced, assigned or
2
+ -- called. PL/Scope records this at compile time, so it is exact where
3
+ -- grep can only guess. Declarations sort first.
4
+ -- Binds: :s schema (object owner)
5
+ -- :n identifier name
6
+ -- Returns: USAGE, OBJECT_NAME, OBJECT_TYPE, TYPE, LINE, COL, USAGE_ID
7
+ select usage,
8
+ object_name,
9
+ object_type,
10
+ type,
11
+ line,
12
+ col,
13
+ usage_id
14
+ from all_identifiers
15
+ where owner = :s
16
+ and name = upper(:n)
17
+ order by case usage when 'DECLARATION' then 0 else 1 end,
18
+ object_name, line, col
@@ -0,0 +1,8 @@
1
+ -- Purpose: the dangerous ANY privileges this session holds. Feeds the
2
+ -- one-line warning in apply previews and check — warn, never block.
3
+ -- Binds: (none)
4
+ -- Returns: PRIVILEGE
5
+ select privilege
6
+ from session_privs
7
+ where privilege like '%ANY%'
8
+ order by privilege
@@ -0,0 +1,13 @@
1
+ -- Purpose: the pool of programs a new one could be modelled on. Ranking runs
2
+ -- in Python (rank_similar) where it is unit-testable; splitting names
3
+ -- in SQL would need a recursive CTE and make this file unreviewable.
4
+ -- Binds: :s schema (object owner)
5
+ -- Returns: OBJECT_NAME, OBJECT_TYPE, STATUS, LAST_DDL
6
+ select object_name,
7
+ object_type,
8
+ status,
9
+ to_char(last_ddl_time, 'yyyy-mm-dd') last_ddl
10
+ from all_objects
11
+ where owner = :s
12
+ and object_type in ('PROCEDURE', 'FUNCTION', 'PACKAGE')
13
+ order by object_name
@@ -0,0 +1,10 @@
1
+ -- Purpose: full source of a named object, every unit (spec and body), in
2
+ -- compilation order. Backs the src command.
3
+ -- Binds: :s schema (object owner)
4
+ -- :n object name
5
+ -- Returns: TYPE, LINE, TEXT
6
+ select type, line, text
7
+ from all_source
8
+ where owner = :s
9
+ and name = upper(:n)
10
+ order by type, line
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: plsql-apply
3
+ description: Use when a PL/SQL change is ready to reach the database - applying a CREATE OR REPLACE, restoring from the journal, or any other write. Runs the six-step flow through pythia apply and enforces the gate - the developer sees the preview and approves in chat before anything is written, and a broken result is never reported as success.
4
+ ---
5
+
6
+ # Applying PL/SQL Changes
7
+
8
+ **Announce at start:** "Using plsql-apply — I'll preview the change first."
9
+
10
+ DDL in Oracle commits itself. There is no transaction to roll back — the
11
+ snapshot pythia takes before writing is the only undo that exists. This skill
12
+ exists so that safety net is always used, and used honestly.
13
+
14
+ ## The Iron Law
15
+
16
+ ```
17
+ NO WRITE THE DEVELOPER HAS NOT SEEN AND APPROVED
18
+ ```
19
+
20
+ Applies to every write: new objects, fixes, restores, batch runs. No
21
+ exceptions for "trivial" changes — a one-line change can invalidate twelve
22
+ dependents.
23
+
24
+ ## The Workflow
25
+
26
+ **Before step 1:** if this conversation has not yet seen a standalone impact
27
+ analysis for this object, run `plsql-impact` first. The `impact:` line inside
28
+ apply's preview is confirmation of a number you already knew — if it is the
29
+ first time anyone sees it, a step was skipped.
30
+
31
+ 1. **Preview.** Run `pythia apply <file>`. This writes nothing: it snapshots,
32
+ computes impact, prints a diff, a warning block, and a confirm token.
33
+ 2. **Relay the preview to the developer — verbatim.** Show the diff, the
34
+ `impact:` line, and any `!` warning exactly as printed. Do not summarize
35
+ the diff away; the developer approves what they see, not your paraphrase.
36
+ 3. **Wait for an explicit yes.** A yes is an instruction to proceed with THIS
37
+ preview: "yes, apply it", "go ahead", "looks good — do it". Not a yes: a
38
+ compliment without a go-ahead, a question, silence, or an approval that
39
+ was given for an earlier preview. If the developer changes the file
40
+ instead, start over at step 1.
41
+ 4. **Apply** by running the exact `To apply:` command pythia printed (it
42
+ contains the token). If pythia says the token is stale, the file or the
43
+ database changed since the preview — go back to step 1, never "retry".
44
+ If the fresh preview's before-side no longer matches what you last saw,
45
+ say so explicitly: someone else may have changed the object on this
46
+ shared database, and the developer must know that before approving.
47
+ 5. **Read the exit code — it is the verdict:**
48
+
49
+ | Exit | Meaning | What you must do |
50
+ |------|---------|-----------------|
51
+ | 0 | applied, compiled clean, nothing newly INVALID | report done, mention the restore id |
52
+ | 1 | refused (policy, classification, stale token) | relay the printed reason and its fix; do not work around it |
53
+ | 3 | **written but broken** — compile errors or other objects now INVALID | see below |
54
+ 6. **On exit 3, never report success.** Say plainly that the change went in
55
+ and broke something, show the compile errors (line:col) and the list of
56
+ newly INVALID objects, and offer the `To undo:` command pythia printed.
57
+ Fixing forward is allowed only after the developer sees this state.
58
+
59
+ ## Restores
60
+
61
+ `pythia journal restore <id>` is itself a write and goes through the same six
62
+ steps and the same gate: preview the reverse diff to the developer, wait for
63
+ yes, then confirm. Note: restoring an object that did not exist before means
64
+ DROP — policy will refuse it under `structural: deny`, and that refusal is
65
+ correct; relay it instead of forcing a way around.
66
+
67
+ ## Batch mode
68
+
69
+ `--yes` skips the pause, not the preview — output and journal are identical.
70
+ Use it only when the developer explicitly asked for unattended application
71
+ ("apply all of these"). A frustrated "stop asking" grants `--yes` for the
72
+ task at hand, not from now on: confirm the scope once ("this batch, or
73
+ standing?") and default to this-batch-only. A standing `--yes` never extends
74
+ to restores. Rules for a batch:
75
+
76
+ - **Stop at the first exit 3.** Never keep applying onto a broken state.
77
+ - Afterwards report: one line per success, full detail (errors, newly
78
+ INVALID, restore command) for the failure, and the exact list of files
79
+ that were NOT applied because the batch stopped.
80
+
81
+ ## Never bypass the write path
82
+
83
+ When `pythia apply` is available, do not write through anything else — not
84
+ SQLcl MCP `run-sql`, not `sqlplus`, not a driver script. Those paths have no
85
+ snapshot, no impact preview, no verify, no journal. If apply refuses a
86
+ statement, that refusal is information for the developer, not an obstacle to
87
+ route around.
88
+
89
+ ## Is rollback real? Be honest about it
90
+
91
+ | Group | Is rollback real? |
92
+ |---|---|
93
+ | `plsql_source` | **Yes — completely.** The source is recoverable from `ALL_SOURCE`. |
94
+ | `data_dml` | **No.** After commit only Flashback Query remains, and only within undo retention. |
95
+ | `structural` | **Almost never.** `DROP COLUMN` is permanent; a dropped table may be in the Recycle Bin. |
96
+ | `grants` | Yes, but by hand. |
97
+ | `session` | Not needed. |
98
+
99
+ Never promise "we can always roll back" — that sentence is only true for the
100
+ first row, and saying it generally misleads the developer at the exact moment
101
+ the stakes are highest.
102
+
103
+ ## Red Flags — STOP if you catch yourself thinking
104
+
105
+ | Thought | Reality |
106
+ |---------|---------|
107
+ | "It's a tiny change, skip the preview" | Tiny changes invalidate dependents too. Preview. |
108
+ | "The dev approved something like this earlier" | Approval is per-preview, not per-topic. Ask again. |
109
+ | "Exit 3, but my part compiled — report done" | Something is broken that was not. That is not done. |
110
+ | "Token is stale, I'll just take the new one" | The content changed. The developer must see the new preview. |
111
+ | "apply refused it; run-sql will take it" | The refusal is the product working. Relay it. |
112
+ | "I'll restore quietly to clean up my mistake" | Restores are writes. Same gate, same visibility. |
113
+ | "The dev said 'stop asking' once" | That covered that task, not forever. Re-confirm scope on the next one. |
114
+
115
+ ## When NOT to use this skill
116
+
117
+ - Reading or exploring — use `plsql-explore`.
118
+ - Judging blast radius before editing — use `plsql-impact` (always run it
119
+ before proposing a change; apply's preview is confirmation, not discovery).
120
+ - Editing files the developer has not asked to land on the database yet.
121
+
122
+ > Invocation note: examples say `pythia ...`; run it however this project
123
+ > provides it (for example `python scripts/pythia.py ...`). Every pythia
124
+ > output prints follow-up commands in the correct form — prefer pasting those.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: plsql-explore
3
+ description: Use when you need to understand anything in an Oracle schema - finding objects, reading PL/SQL source, signatures, table columns, DDL, searching code, or asking who uses what. The database is the only source of truth; repo dumps and exports drift and lie.
4
+ ---
5
+
6
+ # Exploring the Schema
7
+
8
+ **Announce at start:** "Using plsql-explore — asking the database directly."
9
+
10
+ ## The principle: ask the database, never the dump
11
+
12
+ Repositories of exported `.sql` files go stale the day after export. A real
13
+ mid-size system, audited in 2026, compared its repo dump against the live
14
+ database:
15
+
16
+ | Object type | In the dump | In the database | Verdict |
17
+ |---|---|---|---|
18
+ | Procedures | 3,827 | 3,827 | matched |
19
+ | Tables | 952 | 952 | matched |
20
+ | **Types** | **0** | **115** | **all missing** |
21
+ | **Packages** | **0** | **9** | **all missing** |
22
+ | **Indexes** | **116** | **1,016** | **~89% missing** |
23
+
24
+ Code that "reads fine" against the dump can reference types and packages the
25
+ dump never heard of. Read files only when the database is unreachable, or to
26
+ compare a repo version against the live one.
27
+
28
+ ## What you need → what you run
29
+
30
+ | Need | Command |
31
+ |---|---|
32
+ | Is the connection alive, what schema | `pythia check` |
33
+ | Find objects by name | `pythia ls "PKG_%"` |
34
+ | Read source, with the compiler's line numbers | `pythia src NAME` (`--body`, `--spec`) |
35
+ | A procedure/function signature | `pythia args NAME` |
36
+ | Columns and real data types | `pythia cols TABLE_NAME` |
37
+ | Full DDL | `pythia ddl TABLE NAME` |
38
+ | Search all PL/SQL text | `pythia grep "text"` |
39
+ | What an object depends on | `pythia deps NAME` |
40
+ | What depends on an object | `pythia impact NAME` |
41
+ | Exact identifier usages (beats grep) | `pythia plscope NAME` |
42
+ | Programs named like this one | `pythia similar NAME` |
43
+ | Everything currently broken | `pythia invalid`, `pythia errors` |
44
+ | A free-form question | `pythia sql "select ..."` (SELECT/WITH only) |
45
+
46
+ ## Rules that keep answers honest
47
+
48
+ - **Truncation is always announced.** Outputs end with `-- truncated ...` or
49
+ set `"truncated": true` in `--json`. If you do not see the marker, you saw
50
+ everything; if you do, say so or fetch more (`--limit`, `--offset`,
51
+ `--max-lines`) — never present a truncated list as complete.
52
+ - **`plscope` before `grep`** for "where is X used": PL/Scope is recorded by
53
+ the compiler and exact; grep matches comments and look-alike names. If
54
+ PL/Scope has no data, the command says so and grep is the fallback.
55
+ - **`src` line numbers are the compiler's own** — an error at `line 47`
56
+ means line 47 in `src` output. No offset arithmetic.
57
+ - Default output caps exist to protect your context window; raise them only
58
+ for the object you are actually working on.
59
+
60
+ Deeper reference — which dictionary views back these commands, PL/Scope
61
+ enablement, licensing boundaries: `reference/data-dictionary.md`.