ghosttrap-cli 0.3.17__tar.gz → 0.3.18__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.
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/PKG-INFO +3 -1
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/README.md +2 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli/cli.py +59 -21
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/PKG-INFO +3 -1
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/pyproject.toml +1 -1
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli/__init__.py +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/SOURCES.txt +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/dependency_links.txt +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/entry_points.txt +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/requires.txt +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/ghosttrap_cli.egg-info/top_level.txt +0 -0
- {ghosttrap_cli-0.3.17 → ghosttrap_cli-0.3.18}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghosttrap-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: Watch for errors streaming from ghosttrap.io
|
|
5
5
|
Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -41,6 +41,8 @@ After fixing, Claude restarts peek and waits for the next one. Errors become a r
|
|
|
41
41
|
|
|
42
42
|
Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to report errors. The Claude Code skill handles the integration automatically — it installs the SDK, wires it into your app, and adds Django/Celery hooks if applicable. You shouldn't need to touch the SDK manually.
|
|
43
43
|
|
|
44
|
+
If you want to manually flag a caught exception or a non-exception condition, call `ghosttrap.trap(exc_or_message)` from your app code — see the [SDK README](https://github.com/alex-rowley/ghosttrap-sdk#manually-trap-an-event).
|
|
45
|
+
|
|
44
46
|
## Commands
|
|
45
47
|
|
|
46
48
|
| Command | What it does |
|
|
@@ -32,6 +32,8 @@ After fixing, Claude restarts peek and waits for the next one. Errors become a r
|
|
|
32
32
|
|
|
33
33
|
Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to report errors. The Claude Code skill handles the integration automatically — it installs the SDK, wires it into your app, and adds Django/Celery hooks if applicable. You shouldn't need to touch the SDK manually.
|
|
34
34
|
|
|
35
|
+
If you want to manually flag a caught exception or a non-exception condition, call `ghosttrap.trap(exc_or_message)` from your app code — see the [SDK README](https://github.com/alex-rowley/ghosttrap-sdk#manually-trap-an-event).
|
|
36
|
+
|
|
35
37
|
## Commands
|
|
36
38
|
|
|
37
39
|
| Command | What it does |
|
|
@@ -2,31 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import asyncio
|
|
5
|
-
import hashlib
|
|
6
5
|
import json
|
|
7
6
|
import os
|
|
8
7
|
import subprocess
|
|
9
8
|
import sys
|
|
9
|
+
import tempfile
|
|
10
10
|
import time
|
|
11
11
|
import urllib.error
|
|
12
12
|
import urllib.request
|
|
13
13
|
|
|
14
14
|
import websockets
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
"aeda67bc5971bd8af4d7ebe819ebcce5acead562fa618227a1798b4b5ae7143e", # v0.2.0
|
|
18
|
-
"0f2d2f4105e393fc69084d404d5a8154ba5d97fd23f92810c51345e3dc68e9a0", # v0.3.0
|
|
19
|
-
"8564b65b8ab5c63283cda1706e30ca62bc4e111d33ba8918220f4b556ad01da1", # v0.3.1..v0.3.3
|
|
20
|
-
"5759b2e0dc8ca47c3801915fd688cc8da878a7ab8d405f5183ffd7e8c8df4c55", # v0.3.4..v0.3.7
|
|
21
|
-
"0651bb4247cf5c68960ff5b63d6a5d0c85ff1ce08e7966ab4823601ff02cf1f4", # v0.3.9
|
|
22
|
-
"38810f43867a2a91420cc3dacbc71d2acabd7125596fd5b43f222b49725c9696", # v0.3.10
|
|
23
|
-
"19b67d913dc5214ee4db3610bd8749da67324c174b904b5da71ee6de13e23e63", # v0.3.11
|
|
24
|
-
"bf7768c3de266b7018d5c722c6c9991b487e7897786b3a406c460842cdcde8b5", # v0.3.12
|
|
25
|
-
"d3f594c4c3601a4594c18ebc5e16dfd4abad4ab97d56285ecc20634b919b6731", # v0.3.14
|
|
26
|
-
"1c3a0507fab027abc0f176ce2fd88cad28276bef4fcef46cdb497f53967346e1", # v0.3.16
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
__version__ = "0.3.17"
|
|
16
|
+
__version__ = "0.3.18"
|
|
30
17
|
|
|
31
18
|
GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
|
|
32
19
|
CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
|
|
@@ -76,6 +63,10 @@ Read `~/.ghosttrap/config.json` for state. It contains:
|
|
|
76
63
|
3. If `sdk_installed` is false or missing: install the SDK (`pip install ghosttrap-sdk`), wire `ghosttrap.init("<token>")` into the app startup. For Django projects, also add `"ghosttrap.django.GhostTrapApp"` to INSTALLED_APPS (re-attaches logging handler after Django's dictConfig) and `"ghosttrap.django.GhostTrapMiddleware"` to MIDDLEWARE (catches unhandled view exceptions). The SDK auto-hooks into Celery task_failure if Celery is installed, and attaches a logging handler for logger.exception() calls. Use whatever pattern the project already uses for configuration (env vars, settings files, hardcoded — match the existing style). Then update the config: set `sdk_installed: true`, `sdk_version`, `init_file` to record what you did. Only pass `send_user=True` to `init()` if the user explicitly asks for user context in reports — it's PII and stays off by default.
|
|
77
64
|
4. Run `ghosttrap peek --clear` with `run_in_background: true`. The `--clear` flag skips any stale backlog from prior sessions so you only get fresh errors.
|
|
78
65
|
|
|
66
|
+
## Manual capture
|
|
67
|
+
|
|
68
|
+
For caught exceptions or non-exception conditions the user explicitly wants reported, use `ghosttrap.trap(exc_or_message)` from app code — pass an exception instance or a string. Synthetic string events arrive as type `TrappedEvent` with the caller's stack. Only wire this in when the user asks for it; don't add `trap()` calls speculatively.
|
|
69
|
+
|
|
79
70
|
## When peek returns
|
|
80
71
|
|
|
81
72
|
1. **Immediately restart peek** in the background before doing anything else — this ensures you're listening for the next error while you work on the current one. Use plain `ghosttrap peek` here (no `--clear`) — you only want to skip backlog at session start.
|
|
@@ -306,22 +297,69 @@ def _require_setup():
|
|
|
306
297
|
sys.exit(1)
|
|
307
298
|
|
|
308
299
|
|
|
309
|
-
def _write_skill():
|
|
300
|
+
def _write_skill(config=None):
|
|
310
301
|
os.makedirs(SKILL_DIR, exist_ok=True)
|
|
311
302
|
with open(SKILL_FILE, "w") as f:
|
|
312
303
|
f.write(SKILL_CONTENT)
|
|
304
|
+
if config is None:
|
|
305
|
+
config = _load_config()
|
|
306
|
+
config["skill_baseline"] = SKILL_CONTENT
|
|
307
|
+
_save_config(config)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _merge_skill(base, local, remote):
|
|
311
|
+
"""3-way merge via `git merge-file -p`. Returns (merged_text, clean)."""
|
|
312
|
+
with tempfile.TemporaryDirectory() as d:
|
|
313
|
+
bp = os.path.join(d, "base")
|
|
314
|
+
lp = os.path.join(d, "local")
|
|
315
|
+
rp = os.path.join(d, "remote")
|
|
316
|
+
for path, text in [(bp, base), (lp, local), (rp, remote)]:
|
|
317
|
+
with open(path, "w") as f:
|
|
318
|
+
f.write(text)
|
|
319
|
+
result = subprocess.run(
|
|
320
|
+
["git", "merge-file", "-p",
|
|
321
|
+
"-L", "your edits", "-L", "previous release", "-L", "new release",
|
|
322
|
+
lp, bp, rp],
|
|
323
|
+
capture_output=True, text=True,
|
|
324
|
+
)
|
|
325
|
+
return result.stdout, result.returncode == 0
|
|
313
326
|
|
|
314
327
|
|
|
315
328
|
def _refresh_skill_if_stale():
|
|
316
329
|
if not os.path.exists(SKILL_FILE):
|
|
317
330
|
return
|
|
318
331
|
with open(SKILL_FILE) as f:
|
|
319
|
-
|
|
320
|
-
if
|
|
332
|
+
on_disk = f.read()
|
|
333
|
+
if on_disk == SKILL_CONTENT:
|
|
334
|
+
return
|
|
335
|
+
config = _load_config()
|
|
336
|
+
baseline = config.get("skill_baseline")
|
|
337
|
+
if baseline is None:
|
|
338
|
+
# Pre-baseline install: adopt current on-disk content as the baseline
|
|
339
|
+
# so future releases can 3-way-merge instead of clobbering local edits.
|
|
340
|
+
config["skill_baseline"] = on_disk
|
|
341
|
+
_save_config(config)
|
|
321
342
|
return
|
|
322
|
-
if
|
|
323
|
-
_write_skill()
|
|
343
|
+
if baseline == on_disk:
|
|
344
|
+
_write_skill(config)
|
|
324
345
|
print("ghosttrap skill file updated", file=sys.stderr)
|
|
346
|
+
return
|
|
347
|
+
merged, clean = _merge_skill(baseline, on_disk, SKILL_CONTENT)
|
|
348
|
+
if clean:
|
|
349
|
+
with open(SKILL_FILE, "w") as f:
|
|
350
|
+
f.write(merged)
|
|
351
|
+
config["skill_baseline"] = SKILL_CONTENT
|
|
352
|
+
_save_config(config)
|
|
353
|
+
print("ghosttrap skill file updated (merged with your local edits)", file=sys.stderr)
|
|
354
|
+
return
|
|
355
|
+
new_path = SKILL_FILE + ".new"
|
|
356
|
+
with open(new_path, "w") as f:
|
|
357
|
+
f.write(merged)
|
|
358
|
+
print(
|
|
359
|
+
f"ghosttrap skill update has conflicts with your local edits; "
|
|
360
|
+
f"merged candidate at {new_path} — resolve, copy to {SKILL_FILE}, and rerun.",
|
|
361
|
+
file=sys.stderr,
|
|
362
|
+
)
|
|
325
363
|
|
|
326
364
|
|
|
327
365
|
async def setup(server_url, token):
|
|
@@ -350,7 +388,7 @@ async def setup(server_url, token):
|
|
|
350
388
|
|
|
351
389
|
repos = event.get("repos", [])
|
|
352
390
|
_save_repos(config, repos)
|
|
353
|
-
_write_skill()
|
|
391
|
+
_write_skill(config)
|
|
354
392
|
|
|
355
393
|
target = repos[0] if repos else None
|
|
356
394
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghosttrap-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: Watch for errors streaming from ghosttrap.io
|
|
5
5
|
Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -41,6 +41,8 @@ After fixing, Claude restarts peek and waits for the next one. Errors become a r
|
|
|
41
41
|
|
|
42
42
|
Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to report errors. The Claude Code skill handles the integration automatically — it installs the SDK, wires it into your app, and adds Django/Celery hooks if applicable. You shouldn't need to touch the SDK manually.
|
|
43
43
|
|
|
44
|
+
If you want to manually flag a caught exception or a non-exception condition, call `ghosttrap.trap(exc_or_message)` from your app code — see the [SDK README](https://github.com/alex-rowley/ghosttrap-sdk#manually-trap-an-event).
|
|
45
|
+
|
|
44
46
|
## Commands
|
|
45
47
|
|
|
46
48
|
| Command | What it does |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|