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.
Files changed (44) hide show
  1. {pythia_plsql-0.10.0/scripts/pythia_plsql.egg-info → pythia_plsql-0.11.0}/PKG-INFO +7 -4
  2. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/README.md +6 -3
  3. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/pyproject.toml +1 -1
  4. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia.py +80 -1
  5. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0/scripts/pythia_plsql.egg-info}/PKG-INFO +7 -4
  6. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_install.py +113 -0
  7. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/LICENSE +0 -0
  8. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/compile-errors.sql +0 -0
  9. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/current-schema.sql +0 -0
  10. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/dependencies.sql +0 -0
  11. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/impact.sql +0 -0
  12. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/invalid-objects.sql +0 -0
  13. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/name-occupants.sql +0 -0
  14. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/object-names.sql +0 -0
  15. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/object-source.sql +0 -0
  16. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-enabled.sql +0 -0
  17. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-statements.sql +0 -0
  18. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/plscope-usages.sql +0 -0
  19. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/session-privileges.sql +0 -0
  20. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/similar-candidates.sql +0 -0
  21. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/queries/source.sql +0 -0
  22. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
  23. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
  24. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
  25. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
  26. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
  27. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/setup.cfg +0 -0
  28. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-apply/SKILL.md +0 -0
  29. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-conventions/SKILL.md +0 -0
  30. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-explore/SKILL.md +0 -0
  31. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-explore/reference/data-dictionary.md +0 -0
  32. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-impact/SKILL.md +0 -0
  33. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-review/SKILL.md +0 -0
  34. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-review/reference/antipatterns.md +0 -0
  35. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-setup/SKILL.md +0 -0
  36. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-skill-author/SKILL.md +0 -0
  37. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-spec/SKILL.md +0 -0
  38. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-write/SKILL.md +0 -0
  39. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/pythia-write/reference/patterns.md +0 -0
  40. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/skills/using-pythia/SKILL.md +0 -0
  41. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase1.py +0 -0
  42. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase2.py +0 -0
  43. {pythia_plsql-0.10.0 → pythia_plsql-0.11.0}/tests/test_phase3.py +0 -0
  44. {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.10.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? [`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
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? [`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
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.10.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 and scaffold .pythia/ config")
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.10.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? [`examples/claude-code-settings.example.json`](examples/claude-code-settings.example.json)
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