pythia-plsql 0.10.0__tar.gz → 0.11.0__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.10.0/scripts/pythia_plsql.egg-info → pythia_plsql-0.11.0}/PKG-INFO +7 -4
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/README.md +6 -3
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/pyproject.toml +1 -1
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia.py +80 -1
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0/scripts/pythia_plsql.egg-info}/PKG-INFO +7 -4
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_install.py +113 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/LICENSE +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/compile-errors.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/current-schema.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/dependencies.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/impact.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/invalid-objects.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/name-occupants.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/object-names.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/object-source.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-enabled.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-statements.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-usages.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/session-privileges.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/similar-candidates.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/source.sql +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/setup.cfg +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-apply/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-conventions/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-explore/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-explore/reference/data-dictionary.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-impact/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-review/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-review/reference/antipatterns.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-setup/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-skill-author/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-spec/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-write/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-write/reference/patterns.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/using-pythia/SKILL.md +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase1.py +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase2.py +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase3.py +0 -0
- {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase5.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythia-plsql
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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
|
|
@@ -121,14 +121,14 @@ is in, it is doing none of them.
|
|
|
121
121
|
## Install
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
|
-
npx pythia-plsql # everything: pip install + skills picker + config scaffold
|
|
124
|
+
npx pythia-plsql # everything: pip install + skills picker + config scaffold + hooks
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
Or the same thing piecewise:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
130
|
pip install pythia-plsql # the CLI (thin driver — no Oracle Instant Client needed)
|
|
131
|
-
python -m pythia install # skills into your agent + .pythia/ scaffold
|
|
131
|
+
python -m pythia install # skills into your agent + .pythia/ scaffold + Claude Code hooks
|
|
132
132
|
pythia check # fill in connections.json first, then verify
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -204,7 +204,10 @@ hand with
|
|
|
204
204
|
works just as well.
|
|
205
205
|
`pythia check` warns when the session runs with more power than the task needs.
|
|
206
206
|
|
|
207
|
-
Using Claude Code?
|
|
207
|
+
Using Claude Code? `pythia install` wires the two hooks — the approve-in-chat
|
|
208
|
+
door and the session-start guide — and the deny rule into
|
|
209
|
+
`.claude/settings.json`, merged into whatever is already there. The rest of
|
|
210
|
+
[`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
|
|
208
211
|
stops it prompting for the read-only commands and asks it to pause on
|
|
209
212
|
writes — optional, and yours to install
|
|
210
213
|
([why pythia does not](GUIDE.md#11-optional-claude-code-permission-settings)).
|
|
@@ -107,14 +107,14 @@ is in, it is doing none of them.
|
|
|
107
107
|
## Install
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
|
-
npx pythia-plsql # everything: pip install + skills picker + config scaffold
|
|
110
|
+
npx pythia-plsql # everything: pip install + skills picker + config scaffold + hooks
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
Or the same thing piecewise:
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
pip install pythia-plsql # the CLI (thin driver — no Oracle Instant Client needed)
|
|
117
|
-
python -m pythia install # skills into your agent + .pythia/ scaffold
|
|
117
|
+
python -m pythia install # skills into your agent + .pythia/ scaffold + Claude Code hooks
|
|
118
118
|
pythia check # fill in connections.json first, then verify
|
|
119
119
|
```
|
|
120
120
|
|
|
@@ -190,7 +190,10 @@ hand with
|
|
|
190
190
|
works just as well.
|
|
191
191
|
`pythia check` warns when the session runs with more power than the task needs.
|
|
192
192
|
|
|
193
|
-
Using Claude Code?
|
|
193
|
+
Using Claude Code? `pythia install` wires the two hooks — the approve-in-chat
|
|
194
|
+
door and the session-start guide — and the deny rule into
|
|
195
|
+
`.claude/settings.json`, merged into whatever is already there. The rest of
|
|
196
|
+
[`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
|
|
194
197
|
stops it prompting for the read-only commands and asks it to pause on
|
|
195
198
|
writes — optional, and yours to install
|
|
196
199
|
([why pythia does not](GUIDE.md#11-optional-claude-code-permission-settings)).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythia-plsql"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
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" }
|
|
@@ -3143,6 +3143,78 @@ def clean_legacy_skills(base_dir):
|
|
|
3143
3143
|
_remove_link_first(pathlib.Path(base_dir) / rel / old_name)
|
|
3144
3144
|
|
|
3145
3145
|
|
|
3146
|
+
# What `install` wires into Claude Code's settings.json: the two hooks and
|
|
3147
|
+
# the deny rule, verbatim from examples/claude-code-settings.example.json (a
|
|
3148
|
+
# test keeps them equal). The allow list and autoMode stay in the example —
|
|
3149
|
+
# those are the developer's permission preferences, not pythia's to set.
|
|
3150
|
+
CLAUDE_HOOKS = {
|
|
3151
|
+
"SessionStart": [{"hooks": [
|
|
3152
|
+
{"type": "command", "command": "python -m pythia guide --brief"}]}],
|
|
3153
|
+
"PostToolUse": [{"matcher": "AskUserQuestion", "hooks": [
|
|
3154
|
+
{"type": "command", "command": "python -m pythia approve --hook",
|
|
3155
|
+
"timeout": 15}]}],
|
|
3156
|
+
}
|
|
3157
|
+
CLAUDE_DENY = ["Bash(pythia approve --hook*)",
|
|
3158
|
+
"Bash(python -m pythia approve --hook*)"]
|
|
3159
|
+
# a hook already present in any spelling (python3, the plugin's script path)
|
|
3160
|
+
# is recognised by what it runs, not by how it invokes it
|
|
3161
|
+
HOOK_MARKS = {"SessionStart": "guide --brief", "PostToolUse": "approve --hook"}
|
|
3162
|
+
|
|
3163
|
+
|
|
3164
|
+
def install_claude_hooks(base_dir, events=None):
|
|
3165
|
+
"""Merge the two hooks and the deny rule into <base>/.claude/settings.json
|
|
3166
|
+
— the project's, or the home directory's for -g. The file is Claude
|
|
3167
|
+
Code's, so every other key survives and a pythia hook already there in
|
|
3168
|
+
any spelling counts as present. Returns (path, added): the pieces
|
|
3169
|
+
written, [] when all were there, None when the file could not be parsed
|
|
3170
|
+
and was therefore left alone. `events` narrows which hooks: -g writes
|
|
3171
|
+
only PostToolUse, since ~/.claude/settings.json applies to every project
|
|
3172
|
+
and the session guide would be noise outside Oracle work."""
|
|
3173
|
+
path = pathlib.Path(base_dir) / ".claude" / "settings.json"
|
|
3174
|
+
try:
|
|
3175
|
+
s = json.loads(path.read_text(encoding="utf-8")) if path.is_file() else {}
|
|
3176
|
+
hooks = s.setdefault("hooks", {})
|
|
3177
|
+
deny = s.setdefault("permissions", {}).setdefault("deny", [])
|
|
3178
|
+
added = []
|
|
3179
|
+
for event in events or CLAUDE_HOOKS:
|
|
3180
|
+
entries = CLAUDE_HOOKS[event]
|
|
3181
|
+
present = hooks.setdefault(event, [])
|
|
3182
|
+
cmds = " ".join(h.get("command", "")
|
|
3183
|
+
for e in present for h in e.get("hooks", []))
|
|
3184
|
+
if HOOK_MARKS[event] not in cmds:
|
|
3185
|
+
present.extend(json.loads(json.dumps(entries))) # a copy
|
|
3186
|
+
added.append(f"hooks.{event}")
|
|
3187
|
+
missing = [d for d in CLAUDE_DENY if d not in deny]
|
|
3188
|
+
if missing:
|
|
3189
|
+
deny.extend(missing)
|
|
3190
|
+
added.append("permissions.deny")
|
|
3191
|
+
except (ValueError, TypeError, AttributeError):
|
|
3192
|
+
return path, None
|
|
3193
|
+
if added:
|
|
3194
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
3195
|
+
path.write_text(json.dumps(s, indent=2, ensure_ascii=False) + "\n",
|
|
3196
|
+
encoding="utf-8")
|
|
3197
|
+
return path, added
|
|
3198
|
+
|
|
3199
|
+
|
|
3200
|
+
def report_claude_hooks(base_dir, ns, events=None):
|
|
3201
|
+
"""install's last step, in both scopes: wire the hooks and say what
|
|
3202
|
+
happened. --no-hooks leaves Claude Code's file alone."""
|
|
3203
|
+
if getattr(ns, "no_hooks", False):
|
|
3204
|
+
return
|
|
3205
|
+
path, added = install_claude_hooks(base_dir, events)
|
|
3206
|
+
if added is None:
|
|
3207
|
+
print(f"\n! {path} is not valid JSON, so it was left untouched. Fix "
|
|
3208
|
+
f"it and re-run {invocation()} install, or paste the hooks "
|
|
3209
|
+
"block from GUIDE.md section 11.")
|
|
3210
|
+
elif added:
|
|
3211
|
+
print()
|
|
3212
|
+
print(f"Wired Claude Code hooks into {path}: {', '.join(added)}.")
|
|
3213
|
+
print("Restart the Claude Code session for them to load.")
|
|
3214
|
+
else:
|
|
3215
|
+
print(f"\nClaude Code hooks already in {path}.")
|
|
3216
|
+
|
|
3217
|
+
|
|
3146
3218
|
def cmd_install(conn, schema, ns):
|
|
3147
3219
|
import shutil
|
|
3148
3220
|
en = getattr(ns, "color", False)
|
|
@@ -3158,6 +3230,9 @@ def cmd_install(conn, schema, ns):
|
|
|
3158
3230
|
sys.exit(code)
|
|
3159
3231
|
else:
|
|
3160
3232
|
clean_legacy_skills(pathlib.Path.home())
|
|
3233
|
+
# machine-wide: the approve hook is silent without a pythia token;
|
|
3234
|
+
# the session guide would be noise in every other project
|
|
3235
|
+
report_claude_hooks(pathlib.Path.home(), ns, events=("PostToolUse",))
|
|
3161
3236
|
print("\nGlobal skills serve every project — per-project installs "
|
|
3162
3237
|
"will now skip the skills step automatically.")
|
|
3163
3238
|
return
|
|
@@ -3180,6 +3255,7 @@ def cmd_install(conn, schema, ns):
|
|
|
3180
3255
|
sys.exit(code)
|
|
3181
3256
|
else:
|
|
3182
3257
|
clean_legacy_skills(ns.project_root)
|
|
3258
|
+
report_claude_hooks(ns.project_root, ns)
|
|
3183
3259
|
print(f"\nNext: fill in {path}")
|
|
3184
3260
|
print(f"Then: {invocation()} check")
|
|
3185
3261
|
scripts_dir = installed_scripts_dir()
|
|
@@ -3348,13 +3424,16 @@ def build_parser():
|
|
|
3348
3424
|
s.add_argument("--loi", action="store_true",
|
|
3349
3425
|
help="wrap as 'loi:'||unistr(...)||':loi'")
|
|
3350
3426
|
s = sub.add_parser("install", parents=[common()],
|
|
3351
|
-
help="install the skill pack
|
|
3427
|
+
help="install the skill pack, scaffold .pythia/ config, wire the Claude Code hooks")
|
|
3352
3428
|
s.add_argument("-g", "--global", dest="glob", action="store_true",
|
|
3353
3429
|
help="install the skill pack machine-wide (serves every "
|
|
3354
3430
|
"project; per-project installs then skip skills)")
|
|
3355
3431
|
s.add_argument("--add-to-path", action="store_true", dest="add_to_path",
|
|
3356
3432
|
help="put the pythia command on your PATH without asking "
|
|
3357
3433
|
"(Windows; user PATH only)")
|
|
3434
|
+
s.add_argument("--no-hooks", action="store_true", dest="no_hooks",
|
|
3435
|
+
help="leave Claude Code's .claude/settings.json alone "
|
|
3436
|
+
"(no approve-in-chat hook, no session-start guide)")
|
|
3358
3437
|
s.add_argument("--source", default=DEFAULT_SKILLS_SOURCE,
|
|
3359
3438
|
help="skills repo for `npx skills add` "
|
|
3360
3439
|
f"(default {DEFAULT_SKILLS_SOURCE})")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythia-plsql
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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
|
|
@@ -121,14 +121,14 @@ is in, it is doing none of them.
|
|
|
121
121
|
## Install
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
|
-
npx pythia-plsql # everything: pip install + skills picker + config scaffold
|
|
124
|
+
npx pythia-plsql # everything: pip install + skills picker + config scaffold + hooks
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
Or the same thing piecewise:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
130
|
pip install pythia-plsql # the CLI (thin driver — no Oracle Instant Client needed)
|
|
131
|
-
python -m pythia install # skills into your agent + .pythia/ scaffold
|
|
131
|
+
python -m pythia install # skills into your agent + .pythia/ scaffold + Claude Code hooks
|
|
132
132
|
pythia check # fill in connections.json first, then verify
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -204,7 +204,10 @@ hand with
|
|
|
204
204
|
works just as well.
|
|
205
205
|
`pythia check` warns when the session runs with more power than the task needs.
|
|
206
206
|
|
|
207
|
-
Using Claude Code?
|
|
207
|
+
Using Claude Code? `pythia install` wires the two hooks — the approve-in-chat
|
|
208
|
+
door and the session-start guide — and the deny rule into
|
|
209
|
+
`.claude/settings.json`, merged into whatever is already there. The rest of
|
|
210
|
+
[`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
|
|
208
211
|
stops it prompting for the read-only commands and asks it to pause on
|
|
209
212
|
writes — optional, and yours to install
|
|
210
213
|
([why pythia does not](GUIDE.md#11-optional-claude-code-permission-settings)).
|
|
@@ -237,6 +237,119 @@ def test_cmd_install_runs_end_to_end():
|
|
|
237
237
|
assert env is not None
|
|
238
238
|
|
|
239
239
|
|
|
240
|
+
def test_claude_hook_block_matches_the_example_file():
|
|
241
|
+
"""What install wires into .claude/settings.json is exactly the hooks
|
|
242
|
+
block and the deny rule of the example file — the reviewable source of
|
|
243
|
+
truth, as with the connections template. The allow list and autoMode
|
|
244
|
+
stay in the example: those are the developer's permission preferences."""
|
|
245
|
+
ex = json.loads((ROOT / "examples" / "claude-code-settings.example.json")
|
|
246
|
+
.read_text(encoding="utf-8"))
|
|
247
|
+
assert pythia.CLAUDE_HOOKS == ex["hooks"]
|
|
248
|
+
assert pythia.CLAUDE_DENY == ex["permissions"]["deny"]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def test_install_claude_hooks_creates_settings_and_is_idempotent():
|
|
252
|
+
with tempfile.TemporaryDirectory() as td:
|
|
253
|
+
path, added = pythia.install_claude_hooks(td)
|
|
254
|
+
assert path == pathlib.Path(td) / ".claude" / "settings.json"
|
|
255
|
+
assert added == ["hooks.SessionStart", "hooks.PostToolUse",
|
|
256
|
+
"permissions.deny"]
|
|
257
|
+
s = json.loads(path.read_text(encoding="utf-8"))
|
|
258
|
+
assert s["hooks"] == pythia.CLAUDE_HOOKS
|
|
259
|
+
assert s["permissions"]["deny"] == pythia.CLAUDE_DENY
|
|
260
|
+
before = path.read_text(encoding="utf-8")
|
|
261
|
+
assert pythia.install_claude_hooks(td) == (path, [])
|
|
262
|
+
assert path.read_text(encoding="utf-8") == before
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_install_claude_hooks_merges_into_existing_settings():
|
|
266
|
+
"""The file is Claude Code's, not pythia's: every key the developer put
|
|
267
|
+
there survives, their own hooks stay, and a pythia hook already present
|
|
268
|
+
in another spelling (python3, the plugin's path) is recognised, not
|
|
269
|
+
doubled. Only the missing pieces are added."""
|
|
270
|
+
with tempfile.TemporaryDirectory() as td:
|
|
271
|
+
path = pathlib.Path(td) / ".claude" / "settings.json"
|
|
272
|
+
path.parent.mkdir(parents=True)
|
|
273
|
+
path.write_text(json.dumps({
|
|
274
|
+
"model": "opus",
|
|
275
|
+
"permissions": {"allow": ["Bash(git status)"],
|
|
276
|
+
"deny": ["Bash(rm -rf *)"]},
|
|
277
|
+
"hooks": {
|
|
278
|
+
"SessionStart": [{"hooks": [
|
|
279
|
+
{"type": "command", "command": "echo hi"}]}],
|
|
280
|
+
"PostToolUse": [{"matcher": "AskUserQuestion", "hooks": [
|
|
281
|
+
{"type": "command",
|
|
282
|
+
"command": "python3 -m pythia approve --hook"}]}]}}),
|
|
283
|
+
encoding="utf-8")
|
|
284
|
+
_, added = pythia.install_claude_hooks(td)
|
|
285
|
+
assert added == ["hooks.SessionStart", "permissions.deny"]
|
|
286
|
+
s = json.loads(path.read_text(encoding="utf-8"))
|
|
287
|
+
assert s["model"] == "opus"
|
|
288
|
+
assert s["permissions"]["allow"] == ["Bash(git status)"]
|
|
289
|
+
assert s["permissions"]["deny"][0] == "Bash(rm -rf *)"
|
|
290
|
+
assert set(pythia.CLAUDE_DENY) <= set(s["permissions"]["deny"])
|
|
291
|
+
cmds = [h["command"] for ev in s["hooks"].values()
|
|
292
|
+
for e in ev for h in e["hooks"]]
|
|
293
|
+
assert "echo hi" in cmds
|
|
294
|
+
assert sum("approve --hook" in c for c in cmds) == 1 # not doubled
|
|
295
|
+
assert sum("guide --brief" in c for c in cmds) == 1
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_install_claude_hooks_refuses_a_file_it_cannot_parse():
|
|
299
|
+
"""Broken JSON is still the developer's file. Touching it would destroy
|
|
300
|
+
what they wrote; say so and leave it byte-identical."""
|
|
301
|
+
with tempfile.TemporaryDirectory() as td:
|
|
302
|
+
path = pathlib.Path(td) / ".claude" / "settings.json"
|
|
303
|
+
path.parent.mkdir(parents=True)
|
|
304
|
+
path.write_text("{not json", encoding="utf-8")
|
|
305
|
+
assert pythia.install_claude_hooks(td) == (path, None)
|
|
306
|
+
assert path.read_text(encoding="utf-8") == "{not json"
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_cmd_install_wires_the_claude_hooks_unless_told_not_to():
|
|
310
|
+
"""The chat approval door needs the hook; a pip user who never opened
|
|
311
|
+
GUIDE section 11 had none — seen in the 0.10.0 field test. install wires
|
|
312
|
+
it by default and says so; --no-hooks leaves Claude Code's file alone."""
|
|
313
|
+
import argparse
|
|
314
|
+
import contextlib
|
|
315
|
+
import io
|
|
316
|
+
old = os.environ.get("PATH")
|
|
317
|
+
os.environ["PATH"] = "" # no npx: bundled path, offline
|
|
318
|
+
try:
|
|
319
|
+
for no_hooks in (False, True):
|
|
320
|
+
with tempfile.TemporaryDirectory() as td:
|
|
321
|
+
ns = argparse.Namespace(project_root=td, glob=False,
|
|
322
|
+
source=None, color=False, json=False,
|
|
323
|
+
no_hooks=no_hooks)
|
|
324
|
+
buf = io.StringIO()
|
|
325
|
+
with contextlib.redirect_stdout(buf):
|
|
326
|
+
pythia.cmd_install(None, None, ns)
|
|
327
|
+
settings = pathlib.Path(td) / ".claude" / "settings.json"
|
|
328
|
+
if no_hooks:
|
|
329
|
+
assert not settings.exists()
|
|
330
|
+
continue
|
|
331
|
+
s = json.loads(settings.read_text(encoding="utf-8"))
|
|
332
|
+
assert "approve --hook" in json.dumps(s["hooks"]["PostToolUse"])
|
|
333
|
+
assert set(pythia.CLAUDE_DENY) <= set(s["permissions"]["deny"])
|
|
334
|
+
assert "settings.json" in buf.getvalue()
|
|
335
|
+
finally:
|
|
336
|
+
if old is not None:
|
|
337
|
+
os.environ["PATH"] = old
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def test_global_install_wires_the_approve_hook_only():
|
|
341
|
+
"""~/.claude/settings.json applies to every project on the machine. The
|
|
342
|
+
approve hook is silent unless a question carries a pythia token, so it
|
|
343
|
+
belongs there; the session-start guide would put 15 lines about pythia
|
|
344
|
+
into every session of every project, so that one stays per project."""
|
|
345
|
+
with tempfile.TemporaryDirectory() as td:
|
|
346
|
+
path, added = pythia.install_claude_hooks(td, events=("PostToolUse",))
|
|
347
|
+
assert added == ["hooks.PostToolUse", "permissions.deny"]
|
|
348
|
+
s = json.loads(path.read_text(encoding="utf-8"))
|
|
349
|
+
assert "SessionStart" not in s["hooks"]
|
|
350
|
+
assert s["hooks"]["PostToolUse"] == pythia.CLAUDE_HOOKS["PostToolUse"]
|
|
351
|
+
|
|
352
|
+
|
|
240
353
|
# Native paths: on POSIX ':' is the PATH separator and would cut a
|
|
241
354
|
# Windows path in half, so these are built from os.sep.
|
|
242
355
|
SCRIPTS = os.path.join(os.sep + 'opt', 'a', 'Scripts')
|
|
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
|
|
File without changes
|
{pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/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
|
|
File without changes
|
|
File without changes
|
{pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/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
|
|
File without changes
|
|
File without changes
|