@ictechgy/context-guard 0.4.9 → 0.4.11
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.
- package/CHANGELOG.md +28 -0
- package/README.ko.md +59 -31
- package/README.md +85 -36
- package/docs/benchmark-fixtures/token-savings-12task-baseline.prompt.example.md +7 -0
- package/docs/benchmark-fixtures/token-savings-12task-contextguard.prompt.example.md +7 -0
- package/docs/benchmark-fixtures/token-savings-12task.evidence.example.jsonl +24 -0
- package/docs/benchmark-fixtures/token-savings-12task.tasks.example.json +182 -0
- package/docs/benchmark-fixtures/token-savings-12task.variants.example.json +10 -0
- package/docs/benchmark-workflow-examples.md +3 -0
- package/docs/benchmark-workflows/context-pack-byte-proxy.example.json +278 -137
- package/docs/benchmark-workflows/measured-token-workflow.example.json +279 -138
- package/docs/benchmark-workflows/provider-cache-telemetry.example.json +279 -138
- package/docs/distribution.md +10 -7
- package/docs/experimental-benchmark-fixtures.md +30 -6
- package/package.json +4 -6
- package/packaging/homebrew/context-guard.rb.template +1 -1
- package/plugins/context-guard/.claude-plugin/plugin.json +1 -1
- package/plugins/context-guard/README.ko.md +20 -14
- package/plugins/context-guard/README.md +26 -17
- package/plugins/context-guard/bin/context-guard +147 -25
- package/plugins/context-guard/bin/context-guard-artifact +884 -79
- package/plugins/context-guard/bin/context-guard-audit +33 -2
- package/plugins/context-guard/bin/context-guard-bench +1542 -31
- package/plugins/context-guard/bin/context-guard-cache-score +665 -0
- package/plugins/context-guard/bin/context-guard-compress +146 -1
- package/plugins/context-guard/bin/context-guard-cost +790 -6
- package/plugins/context-guard/bin/context-guard-experiments +463 -26
- package/plugins/context-guard/bin/context-guard-failed-nudge +9 -2
- package/plugins/context-guard/bin/context-guard-filter +163 -7
- package/plugins/context-guard/bin/context-guard-guard-read +3 -0
- package/plugins/context-guard/bin/context-guard-pack +892 -49
- package/plugins/context-guard/bin/context-guard-rewrite-bash +3 -0
- package/plugins/context-guard/bin/context-guard-sanitize-output +76 -12
- package/plugins/context-guard/bin/context-guard-setup +165 -31
- package/plugins/context-guard/bin/context-guard-statusline +490 -283
- package/plugins/context-guard/bin/context-guard-statusline-merged +5 -0
- package/plugins/context-guard/bin/context-guard-tool-prune +480 -53
- package/plugins/context-guard/bin/context-guard-trim-output +288 -41
- package/plugins/context-guard/brief/README.md +5 -5
- package/plugins/context-guard/lib/context_guard_commands.py +230 -0
- package/plugins/context-guard/skills/setup/SKILL.md +1 -0
- package/context-guard-kit/README.md +0 -91
- package/context-guard-kit/benchmark_runner.py +0 -2401
- package/context-guard-kit/claude_transcript_cost_audit.py +0 -2346
- package/context-guard-kit/context_compress.py +0 -695
- package/context-guard-kit/context_escrow.py +0 -935
- package/context-guard-kit/context_filter.py +0 -637
- package/context-guard-kit/context_guard_cli.py +0 -325
- package/context-guard-kit/context_guard_diet.py +0 -1711
- package/context-guard-kit/context_pack.py +0 -2713
- package/context-guard-kit/cost_guard.py +0 -2349
- package/context-guard-kit/experimental_registry.py +0 -4348
- package/context-guard-kit/failed_attempt_nudge.py +0 -567
- package/context-guard-kit/guard_large_read.py +0 -690
- package/context-guard-kit/hook_secret_patterns.py +0 -43
- package/context-guard-kit/read_symbol.py +0 -483
- package/context-guard-kit/rewrite_bash_for_token_budget.py +0 -501
- package/context-guard-kit/sanitize_output.py +0 -725
- package/context-guard-kit/settings.example.json +0 -67
- package/context-guard-kit/setup_wizard.py +0 -2515
- package/context-guard-kit/statusline.sh +0 -362
- package/context-guard-kit/statusline_merged.sh +0 -157
- package/context-guard-kit/tool_schema_pruner.py +0 -837
- package/context-guard-kit/trim_command_output.py +0 -1449
|
@@ -56,8 +56,10 @@ JSON_PARSE_RECURSION_LIMIT = 10_000
|
|
|
56
56
|
READ_CHUNK_BYTES = 64 * 1024
|
|
57
57
|
DEFAULT_MAX_FILE_BYTES = 50 * 1024 * 1024
|
|
58
58
|
DEFAULT_MAX_LINE_BYTES = 2 * 1024 * 1024
|
|
59
|
+
DEFAULT_MAX_SCAN_FILES = 100_000
|
|
59
60
|
MAX_FILE_BYTES_LIMIT = 2 * 1024 * 1024 * 1024
|
|
60
61
|
MAX_LINE_BYTES_LIMIT = 128 * 1024 * 1024
|
|
62
|
+
MAX_SCAN_FILES_LIMIT = 1_000_000
|
|
61
63
|
SECRET_VALUE_RE = re.compile(
|
|
62
64
|
r"(?i)(gh[pousr]_[A-Za-z0-9_]{8,}|github_pat_[A-Za-z0-9_]{20,}|"
|
|
63
65
|
r"xox[abprs]-[A-Za-z0-9-]{8,}|(?:AKIA|ASIA)[0-9A-Z]{8,}|"
|
|
@@ -169,6 +171,8 @@ class UsageSummary:
|
|
|
169
171
|
files: int = 0
|
|
170
172
|
records: int = 0
|
|
171
173
|
skipped_files: int = 0
|
|
174
|
+
unscanned_files_lower_bound: int = 0
|
|
175
|
+
scan_truncated: bool = False
|
|
172
176
|
skipped_records: int = 0
|
|
173
177
|
parse_errors: list[str] = field(default_factory=list)
|
|
174
178
|
tokens: Counter[str] = field(default_factory=Counter)
|
|
@@ -618,6 +622,7 @@ def os_error_summary(exc: OSError) -> str:
|
|
|
618
622
|
class ScanLimits:
|
|
619
623
|
max_file_bytes: int = DEFAULT_MAX_FILE_BYTES
|
|
620
624
|
max_line_bytes: int = DEFAULT_MAX_LINE_BYTES
|
|
625
|
+
max_files: int = DEFAULT_MAX_SCAN_FILES
|
|
621
626
|
|
|
622
627
|
|
|
623
628
|
def open_regular_no_symlink(file: Path):
|
|
@@ -809,6 +814,15 @@ def scan(
|
|
|
809
814
|
limits = limits or ScanLimits()
|
|
810
815
|
summary = UsageSummary()
|
|
811
816
|
for file in iter_jsonl_files(paths):
|
|
817
|
+
if summary.files >= limits.max_files:
|
|
818
|
+
summary.skipped_files += 1
|
|
819
|
+
summary.unscanned_files_lower_bound += 1
|
|
820
|
+
summary.scan_truncated = True
|
|
821
|
+
summary.note_error(
|
|
822
|
+
f"transcript scan file limit reached ({limits.max_files}); "
|
|
823
|
+
"rerun with narrower paths or --max-files if more evidence is required"
|
|
824
|
+
)
|
|
825
|
+
break
|
|
812
826
|
summary.files += 1
|
|
813
827
|
try:
|
|
814
828
|
with open_regular_no_symlink(file) as handle:
|
|
@@ -925,6 +939,8 @@ def scan_integrity(summary: UsageSummary) -> dict[str, Any]:
|
|
|
925
939
|
"files_scanned": summary.files,
|
|
926
940
|
"records_scanned": summary.records,
|
|
927
941
|
"skipped_files": summary.skipped_files,
|
|
942
|
+
"unscanned_files_lower_bound": summary.unscanned_files_lower_bound,
|
|
943
|
+
"scan_truncated": summary.scan_truncated,
|
|
928
944
|
"skipped_records": summary.skipped_records,
|
|
929
945
|
"parse_error_count": len(summary.parse_errors),
|
|
930
946
|
"complete": complete,
|
|
@@ -2151,11 +2167,14 @@ def summary_json(
|
|
|
2151
2167
|
"files": summary.files,
|
|
2152
2168
|
"records": summary.records,
|
|
2153
2169
|
"skipped_files": summary.skipped_files,
|
|
2170
|
+
"unscanned_files_lower_bound": summary.unscanned_files_lower_bound,
|
|
2171
|
+
"scan_truncated": summary.scan_truncated,
|
|
2154
2172
|
"skipped_records": summary.skipped_records,
|
|
2155
2173
|
"parse_errors": summary.parse_errors,
|
|
2156
2174
|
"scan_limits": {
|
|
2157
2175
|
"max_file_bytes": limits.max_file_bytes,
|
|
2158
2176
|
"max_line_bytes": limits.max_line_bytes,
|
|
2177
|
+
"max_files": limits.max_files,
|
|
2159
2178
|
},
|
|
2160
2179
|
"total_tokens": summary.total_tokens,
|
|
2161
2180
|
"tokens": dict(summary.tokens),
|
|
@@ -2221,10 +2240,17 @@ def main() -> int:
|
|
|
2221
2240
|
default=DEFAULT_MAX_LINE_BYTES,
|
|
2222
2241
|
help="skip individual JSONL records larger than this many bytes (default: 2 MiB)",
|
|
2223
2242
|
)
|
|
2243
|
+
parser.add_argument(
|
|
2244
|
+
"--max-files",
|
|
2245
|
+
type=int,
|
|
2246
|
+
default=DEFAULT_MAX_SCAN_FILES,
|
|
2247
|
+
help=f"stop after this many transcript files (default: {DEFAULT_MAX_SCAN_FILES})",
|
|
2248
|
+
)
|
|
2224
2249
|
args = parser.parse_args()
|
|
2225
2250
|
limits = ScanLimits(
|
|
2226
2251
|
max_file_bytes=require_scan_limit(parser, "--max-file-bytes", args.max_file_bytes, MAX_FILE_BYTES_LIMIT),
|
|
2227
2252
|
max_line_bytes=require_scan_limit(parser, "--max-line-bytes", args.max_line_bytes, MAX_LINE_BYTES_LIMIT),
|
|
2253
|
+
max_files=require_scan_limit(parser, "--max-files", args.max_files, MAX_SCAN_FILES_LIMIT),
|
|
2228
2254
|
)
|
|
2229
2255
|
|
|
2230
2256
|
summary = scan(args.paths, show_paths=args.show_paths, show_commands=args.show_commands, limits=limits)
|
|
@@ -2248,9 +2274,14 @@ def main() -> int:
|
|
|
2248
2274
|
print("Claude Code transcript usage audit")
|
|
2249
2275
|
print(
|
|
2250
2276
|
f"files_scanned={summary.files} records={summary.records} "
|
|
2251
|
-
f"skipped_files={summary.skipped_files} skipped_records={summary.skipped_records}"
|
|
2277
|
+
f"skipped_files={summary.skipped_files} skipped_records={summary.skipped_records} "
|
|
2278
|
+
f"scan_truncated={str(summary.scan_truncated).lower()} "
|
|
2279
|
+
f"unscanned_files_lower_bound={summary.unscanned_files_lower_bound}"
|
|
2280
|
+
)
|
|
2281
|
+
print(
|
|
2282
|
+
f"scan_limits=max_file_bytes:{limits.max_file_bytes} "
|
|
2283
|
+
f"max_line_bytes:{limits.max_line_bytes} max_files:{limits.max_files}"
|
|
2252
2284
|
)
|
|
2253
|
-
print(f"scan_limits=max_file_bytes:{limits.max_file_bytes} max_line_bytes:{limits.max_line_bytes}")
|
|
2254
2285
|
print(f"observed_total_tokens={summary.total_tokens}")
|
|
2255
2286
|
if summary.cost_usd:
|
|
2256
2287
|
print(f"observed_cost_usd={summary.cost_usd:.4f}")
|