chad-code 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.
- chad/__init__.py +7 -0
- chad/agent.py +1093 -0
- chad/base_engine.py +109 -0
- chad/bench.py +243 -0
- chad/cli.py +469 -0
- chad/compaction.py +108 -0
- chad/config.py +63 -0
- chad/diag.py +100 -0
- chad/engine.py +883 -0
- chad/guardrails.py +373 -0
- chad/ignore.py +18 -0
- chad/lsp.py +323 -0
- chad/mcp.py +719 -0
- chad/mcp_oauth.py +329 -0
- chad/openai_engine.py +252 -0
- chad/prompt.py +305 -0
- chad/render.py +462 -0
- chad/repomap.py +767 -0
- chad/session.py +281 -0
- chad/skills.py +407 -0
- chad/symbols.py +360 -0
- chad/syntaxgate.py +105 -0
- chad/toolcall_parse.py +115 -0
- chad/tools.py +962 -0
- chad/tui.py +921 -0
- chad/validate.py +361 -0
- chad_code-0.1.0.dist-info/METADATA +370 -0
- chad_code-0.1.0.dist-info/RECORD +32 -0
- chad_code-0.1.0.dist-info/WHEEL +5 -0
- chad_code-0.1.0.dist-info/entry_points.txt +4 -0
- chad_code-0.1.0.dist-info/licenses/LICENSE +21 -0
- chad_code-0.1.0.dist-info/top_level.txt +1 -0
chad/diag.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Diagnostic session log for chad (extracted from agent.py).
|
|
2
|
+
|
|
3
|
+
Throughput / prefill numbers and a readable trace — the user query, tool-call args
|
|
4
|
+
(bash commands, write/edit content), and result previews — are diagnostics, not UX, so
|
|
5
|
+
they go to ~/.chad/session.log (tail it live) instead of cluttering the transcript.
|
|
6
|
+
Dogfooding showed sessions can "do nothing then stop" with no way to see why: which
|
|
7
|
+
tool args were used, whether a search returned anything, whether the model tried to
|
|
8
|
+
declare done, which cache served the turn. This module is that trace.
|
|
9
|
+
|
|
10
|
+
The log is bounded (5 MB x3 rotation) and previews pass through a best-effort secret
|
|
11
|
+
redactor, but it still records command/file previews in plaintext outside the repo, so
|
|
12
|
+
treat it as sensitive. None of this touches the model-facing transcript or the tool
|
|
13
|
+
results the model sees — it is the diagnostic trace only.
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
from logging.handlers import RotatingFileHandler
|
|
20
|
+
|
|
21
|
+
from . import config
|
|
22
|
+
|
|
23
|
+
_LOG_DIR = os.path.expanduser("~/.chad")
|
|
24
|
+
log = logging.getLogger("chad")
|
|
25
|
+
# Local privacy opt-out: set CHAD_NO_SESSION_LOG (any truthy value) to disable the
|
|
26
|
+
# diagnostic session log entirely, matching the CHAD_NO_VALIDATE convention. When opted
|
|
27
|
+
# out we install a NullHandler (so the many log.info calls stay cheap no-ops and Python
|
|
28
|
+
# never warns about missing handlers) and never create ~/.chad for the log's sake.
|
|
29
|
+
_DISABLED = config.flag("CHAD_NO_SESSION_LOG")
|
|
30
|
+
if _DISABLED:
|
|
31
|
+
log.addHandler(logging.NullHandler())
|
|
32
|
+
log.propagate = False
|
|
33
|
+
else:
|
|
34
|
+
try:
|
|
35
|
+
os.makedirs(_LOG_DIR, exist_ok=True)
|
|
36
|
+
# Bounded rotation (5MB x3) on the NAMED logger, not root, so other libraries'
|
|
37
|
+
# logging isn't redirected into our session file. propagate=False keeps these
|
|
38
|
+
# lines off the root handlers. Nothing else relies on basicConfig/root here.
|
|
39
|
+
_handler = RotatingFileHandler(
|
|
40
|
+
os.path.join(_LOG_DIR, "session.log"), maxBytes=5_000_000, backupCount=3)
|
|
41
|
+
_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
|
42
|
+
log.setLevel(logging.INFO)
|
|
43
|
+
log.addHandler(_handler)
|
|
44
|
+
log.propagate = False
|
|
45
|
+
except OSError:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Coarse, conservative secret masking for what gets WRITTEN TO THE LOG only.
|
|
50
|
+
# Two branches:
|
|
51
|
+
# 1. A known key prefix (Bearer/sk-/ghp_/xox/aws_secret/api_key) followed by a
|
|
52
|
+
# value in a WIDE class that includes base64 punctuation (+ / =). The old
|
|
53
|
+
# single-class regex stopped at the first `/`, so an AWS secret like
|
|
54
|
+
# `wJalrXUtnFEMI/K7MDENG/bPx…` was only half-masked — the wide class fixes that.
|
|
55
|
+
# 2. A bare 32+ high-entropy blob in a NARROW class (no `/` or `+`) so ordinary
|
|
56
|
+
# file paths and URLs are left untouched — over-redaction silently guts the
|
|
57
|
+
# log's diagnostic value, which is worse than the status quo.
|
|
58
|
+
_SECRET_RE = re.compile(
|
|
59
|
+
r"(?i)"
|
|
60
|
+
r"(bearer\s+|sk-|ghp_|xox[baprs]-|aws_secret_access_key\s*=\s*|api[_-]?key\s*[=:]\s*)"
|
|
61
|
+
r"([A-Za-z0-9_\-+/]{16,}={0,2})"
|
|
62
|
+
r"|([A-Za-z0-9_\-]{32,})")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def redact(s: str) -> str:
|
|
66
|
+
"""Mask secrets in a log preview. Known-prefix secrets keep their prefix and mask
|
|
67
|
+
the (wide) value; bare high-entropy blobs are masked whole. Length-preserving so
|
|
68
|
+
`<redacted:NN>` still carries a rough signal of what was there."""
|
|
69
|
+
def _mask(m):
|
|
70
|
+
if m.group(1) is not None: # known-prefix secret (wide value class)
|
|
71
|
+
return m.group(1) + "<redacted:" + str(len(m.group(2))) + ">"
|
|
72
|
+
return "<redacted:" + str(len(m.group(3))) + ">" # bare high-entropy blob
|
|
73
|
+
return _SECRET_RE.sub(_mask, s)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def warn_footer(warnings) -> list:
|
|
77
|
+
"""One-line footer summarizing config/discovery warnings for the `/mcp` and
|
|
78
|
+
`/skills` summaries: `(N warning(s): a; b; c …)`, showing the first three. Returns
|
|
79
|
+
[] when there are none. Shared so the format lives in exactly one place."""
|
|
80
|
+
if not warnings:
|
|
81
|
+
return []
|
|
82
|
+
return [f"({len(warnings)} warning(s): " + "; ".join(warnings[:3])
|
|
83
|
+
+ (" …" if len(warnings) > 3 else "") + ")"]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def args_preview(args, n=160):
|
|
87
|
+
"""One-line, redacted, length-capped preview of tool-call args for the log."""
|
|
88
|
+
try:
|
|
89
|
+
s = json.dumps(args, ensure_ascii=False, sort_keys=True)
|
|
90
|
+
except Exception: # noqa: BLE001
|
|
91
|
+
s = str(args)
|
|
92
|
+
s = s if len(s) <= n else s[:n] + f"…(+{len(s) - n})"
|
|
93
|
+
return redact(s)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def result_preview(result, n=120):
|
|
97
|
+
"""One-line, redacted preview of a tool result, with its full length annotated."""
|
|
98
|
+
s = str(result).replace("\n", "⏎")
|
|
99
|
+
extra = f" [{len(str(result))} chars]"
|
|
100
|
+
return redact((s if len(s) <= n else s[:n] + "…")) + extra
|