`contextzip include .env` or `include dist/` now includes paths the rules
exclude instead of matching nothing. Nested excluded folders still apply,
and naming an ordinary folder doesn't pull in secret files inside it.
Include paths also accept a leading ./ or .\. The CLI and report list
files brought back this way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.env files keep keys and comments but every value becomes [REDACTED];
other secret files get the pattern scan; binary ones are left out.
Applies regardless of limits.redact_secrets, including watch's debug ZIP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fallback to opening the folder used to print nothing, so a failed clipboard copy was invisible. It now prints a warning and, when known, how to fix it.
Windows had no clipboard step (it went straight to Explorer). Linux wrote the ZIP's raw bytes as application/zip, which browsers ignore, and only if xclip was installed.
Windows: set CF_HDROP via ctypes plus Preferred DropEffect, read it back with DragQueryFileW to verify; fall back to PowerShell Set-Clipboard -LiteralPath (path passed via env var); then Explorer. Linux: wl-copy (if present on Wayland) -> built-in X11 owner -> xclip (if present), all setting text/uri-list and read back before reporting success. xclip/wl-copy output is no longer captured, so they can't block the CLI.
Also: Explorer /select is now a single quoted command line (paths with spaces/commas), headless/SSH Linux sessions print the path instead of launching xdg-open, and macOS escapes quotes in the path. ClipboardResult gains an optional hint field.
Owns the CLIPBOARD selection through the raw X protocol (stdlib only), so pasting a file works without xclip or any other system package.
Serves text/uri-list (read by browsers on paste), x-special/gnome-copied-files and application/x-kde-cutselection, answers TARGETS, and exits when another app takes the clipboard. Reads MIT-MAGIC-COOKIE from XAUTHORITY / ~/.Xauthority. Signals 'ready' once it owns the selection so the launcher can verify the copy.
Manifest-based match-rate comparison ties at 0% (both stripped and
unstripped) whenever the freshest manifest is scoped to a subset of the
project — most commonly a git-diff manifest from output/git-changes/,
which only ever tracked files touched in one prior change. A zip of
otherwise-legitimate new files then looks equally unmatched either way,
so the previous logic played it safe and left the wrapper folder in
place, dumping it verbatim into the project.
Now, when the manifest signal ties, fall back to comparing top-level
path segments against the project's real, current directory layout
(app/, components/, lib/, etc.) instead. Confirmed against a
reproduction of the exact reported case.
The success overlay already told the user they could close the tab,
but nothing ever tried to. Calls window.close() shortly after the save
+ shutdown round-trip completes. Browsers permit scripted close on
tabs whose entire session history is a single page, which the config
UI always is, so this closes cleanly and drops the user back to their
terminal; if a browser still blocks it, the existing on-screen message
remains as the manual fallback.
Queries PyPI for the latest published version and compares it against
the running __version__. When a newer release exists, re-invokes
whichever installer matches how contextzip was installed (pip, pipx,
or uv tool), always against the currently running interpreter rather
than a bare 'pip' off PATH.
cz update # check, then confirm before updating
cz update --yes # update immediately if available
cz update --check # report versions only, install nothing
Never raises on network failure — a bad connection or PyPI hiccup
degrades to a clear 'couldn't check' message instead of a traceback.
apply-zip's wrapper-folder detection refused to strip a zip's outer
folder whenever the project already had a real directory of that same
name — which backfires on the single most common shape of AI-returned
zip: the whole project wrapped in a folder named after the project (or
its own package dir), e.g. 'project/' or 'contextzip/contextzip/...'.
Since the project's real package dir almost always shares that name,
the guard treated the wrapper as legitimate structure and dumped a
nested copy at the project root instead of updating the real files.
Now, whenever a manifest is available, the decision is made by
comparing manifest match-rate with vs. without stripping (ground
truth about what paths this project actually has) instead of
inferring intent from what merely exists on disk. Falls back to the
old on-disk heuristic only when no manifest exists to check against.
Fixes the case where apply-zip silently mis-applies a returned zip.
.contextzip/.gitignore now ignores the entire workspace with no
exceptions (previously carved out !config.json so it stayed
trackable by default). config.json is local by default like
everything else in the workspace; a team that wants to share it now
has to explicitly \`git add -f .contextzip/config.json\`, rather than
that happening automatically on a plain \`git add\`.
The core of this change (the .gitignore contents themselves, and the
docstrings in packager.py / project_config.py) landed in the previous
commit since those files were already being touched for redact_secrets.
This commit is the remaining two files whose only change is this.
Previously persisted by config.py / the config UI but never actually
enforced (flagged as a known limitation as of 0.4.0). Text files
already going into the archive are now scanned for secret-shaped
values and matched values are replaced with [REDACTED] before
writing.
- New redact.py: curated high-precision patterns (AWS/Google/GitHub/
Slack/Stripe/Anthropic/OpenAI keys, JWTs, private-key blocks, and a
conservative generic key/secret/token/password assignment pattern)
with placeholder-value filtering (changeme, xxxxxxxx, <placeholder>,
${ENV_VAR} interpolation are left alone).
- packager.py: new _write_member() helper shared by create_zip() and
create_zip_silent(); only scans files already known (via
ResolveResult) to be text and within limits.max_file_size_mb —
binary and oversized files are never touched. PackageResult gains a
redacted field.
- cli.py: passes limits.redact_secrets into create_zip(), prints a
one-line redaction summary, forwards redacted entries to the report.
- cli_display.py: print_redaction_summary() — same 'surface it, don't
hide it' treatment as large-file warnings, silent when nothing was
redacted.
- report.py: itemizes every redacted file + which pattern matched.
- project_config.py: also touches the workspace-gitignore docstrings
as part of the same pass — see the next commit for the rest of that
change (config.py, webui/persist.py), which landed separately
because those files have no redaction-related content.
- New cleanup.py: after every successful command, prunes
.contextzip/output/<mode>/ zip sets, backups/, and inbox/applied/
down to the most recent cleanup.keep_recent (default 1) — no
confirmation, no separate command. Deliberately brutal rather than
cautious, since every zip is trivially reproducible.
- New redact.py: curated high-precision patterns (AWS/Google/GitHub/
Slack/Stripe/Anthropic/OpenAI keys, JWTs, private-key blocks, and a
conservative generic key/secret/token/password assignment pattern)
with placeholder-value filtering.
- project_config.py: new CleanupConfig (enabled, keep_recent); updated
the redact_secrets docstring to describe actual enforced behavior
instead of 'reserved for a future...not yet enforced.
- Main run, apply-zip, watch, and config converted from Panel-based
messaging to ok/warn/err/info.
- Wire write_scan_report()/write_apply_report() into the main
packaging flow and apply-zip; print a dim 'Full report: ...' line.
- Compute run mode (standard/git-changes/prompt) once and pass it
through to create_zip() and the report, replacing the old
git_changes boolean.
- Pass limits.redact_secrets into create_zip() and print a one-line
redaction summary when anything was caught.
- Auto-run .contextzip/ cleanup after every successful command
(main run and apply-zip) per the new cleanup.* project settings.
- Drop the '.gitignore patterns applied' line entirely (dead weight,
no signal) and remove the now-unused used_gitignore variable.
- Clipboard step no longer wraps in a status spinner; folder-opened
no longer prints anything.
- .contextzip/output/ now branches by run mode: output/codebase/,
output/git-changes/, output/prompt/ (vibe.zip), output/watch/ —
named after the flag that produced it, so it's unambiguous which
zip is which without guessing.
- output_subdir_for_mode()/zip_filename_for_mode() are the single
source of truth other modules (watcher.py, cleanup.py) build on.
- apply-zip's manifest auto-detection (find_latest_manifest) now
searches recursively across the new per-mode subfolders instead of
a flat glob.
- Implements limits.redact_secrets (previously persisted but never
enforced, per the 0.4.0 changelog): text files already going into
the archive get scanned for secret-shaped values and matched values
are replaced with [REDACTED] before writing. Only ever scans files
already known to be text and within max_file_size_mb, reusing the
existing binary/large-file classification — never re-scans, never
touches binaries or oversized files.
- Workspace-creation fallback warning converted from panel to a
single warn()-style line.
- Convert 'Watching <command>' line, error prompts, exit summary, and
the debug-context success/failure banner from panels to plain lines.
- debug-context.zip now writes to .contextzip/output/watch/ instead of
the flat output/ folder, matching the same per-mode split applied to
the main packaging command (see the packager.py commit).
- Introduce ok()/warn()/err()/info() as the four output primitives,
replacing all rich.Panel/rich.Table usage.
- Merge the old 'Scanned N files' + 'Packed N files' steps into one
print_scan_and_pack() line.
- Drop the contextzip vX.X.X / project-path banner from every command.
- Full file listings and exclusion breakdowns move to the report file
(report.py) and behind -v/--verbose; the terminal shows one line
per step.
- Add print_auto_cleanup() and print_redaction_summary() for the two
newer background steps (workspace cleanup, secret redaction).