- 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).
Replace the single always_include/always_exclude pane with five tabs:
Files, AI selection, Workspace, Advanced, and a live Raw JSON preview.
The file tree and pattern list now render as a diff — a solid gutter
and +/- marks on every row — instead of checkboxes, matching the
git-diff mental model the rest of the tool already uses.
Still a single dependency-free HTML string: no build step, no Node.js,
no network calls from the page itself. Typography uses each OS's own
system font stack rather than a bundled or CDN-fetched face, to keep
that property.
Pattern-list and tree updates are now applied optimistically on every
edit instead of waiting on the debounced /api/preview round-trip.
/api/state now returns workspace_location, scan_depth, ai, limits,
applied_zip_retention, and webui alongside the file tree — previously
only always_include/always_exclude. /api/save accepts and validates
the same full shape. save_config_from_ui() merges nested objects
(ai/limits/webui) key-by-key rather than replacing them wholesale, so
a partial save can't wipe fields it doesn't know about.
launch_config_ui() now honors webui.port (falling back to a random
port if the configured one is taken) and webui.auto_open (skips
launching a browser tab when off).
_move_zip_to_applied() and execute_plan() take a retain count instead
of always keeping only 1. cmd_apply_zip now loads the project config
and passes applied_zip_retention through, defaulting to 1 to match
prior behavior.
ai.prompt_template is now prepended to every generated prompt.txt,
ahead of the task description, as a 'Project conventions:' block.
Threaded through ai_select()/build_prompt_only_txt() in selector.py,
run_ai_selection() in cli_ai.py, and the --prompt call site in cli.py.
resolve_files() and resolve_files_from_git() now accept
large_file_warn_bytes instead of a fixed 1 MB constant. cli.py threads
a project's limits.max_file_size_mb through to both the filtering and
the warning text in output.
Add ai.prompt_template, a new LimitsConfig (max_file_size_mb,
redact_secrets), a new WebUIConfig (auto_open, port), and
applied_zip_retention. All new fields are optional with sensible
defaults and defensive parsing — a malformed value falls back to the
default rather than erroring. Foundation for the config UI redesign
and the features that follow in this release.
Drop the unused GitChanges import in api.py. Expand the .gitignore
comment on .contextzip/ to note it's project-local and that the
Gemini API key is never stored there.
Detect when every entry in an AI-returned zip shares one incidental
top-level folder (the shape produced by 'zip -r' or GitHub's Download
ZIP) and strip it automatically when doing so clearly improves the
match against the project manifest, so files land at their real
project-relative paths.
Also warn before applying a zip whose paths barely match the project
at all, instead of silently treating every file as new. The
applied/ folder now keeps only the most recently applied zip.
Route all writes through a single _write_config() helper and re-apply
0600 permissions each time, not just on creation. A pre-existing file
from an older install (or one created under a permissive umask) could
otherwise stay group/world-readable indefinitely, exposing a plaintext
Gemini API key on shared machines.
Adds an 'Applying AI-returned changes' section covering the manifest
mechanism, the new/modified/unchanged/drifted/untracked classification
table, safety guarantees, and inbox/manifest resolution. Also updates
the command table, Python API table, and workspace layout diagram to
include inbox/, backups/, and the manifest sidecar.
Adds apply_zip() alongside get_files/get_git_changes/create_zip,
following the same plain-function, exception-raising style (no Click,
no Rich, no SystemExit). Exposes ApplyResult and ZipNotFoundError
from the package root.
Wires applier.py into the CLI: auto-detects a zip from
.contextzip/inbox/ (or takes an explicit path), prints a plan summary
(new/modified/unchanged/drifted/untracked counts), and only prompts
for confirmation when something looks risky — drift, untracked paths,
or no manifest at all. Clean modifications and new files apply with
no prompt.
Flags: --dry-run/-n, --verbose/-v, --yes/-y, --manifest.
Introduces applier.py: resolves the AI-returned zip (inbox or explicit
path), diffs it against a per-zip manifest, and classifies every file
as new/modified/unchanged/drifted/untracked before writing anything.
Only ever adds or modifies files — no deletions are inferred.
packager.py now writes a <name>.manifest.json sidecar next to every
zip it creates, containing a hash of each included file. It's written
to disk only, never added as a zip entry, so it's never uploaded or
visible to whatever AI tool the zip is pasted into.
Safety: zip entries are validated against path traversal before
extraction; every file about to be overwritten is backed up under
.contextzip/backups/<timestamp>/ first; consumed zips are archived to
.contextzip/inbox/applied/ rather than deleted.
- 'contextzip config --ui' launches the local config UI directly
- First run in a project with no config at all offers to launch it
interactively, gated on TTY + no CI env var + no --prompt/--output
- Declining is remembered per-machine (config.py) so it only asks once
- Accepting and saving mid-run reloads config and continues packaging
immediately in the same invocation
Single-file HTML/CSS/JS, no build step, no CDN calls — colors and
typography matched to the showcase site's design tokens. Live file tree
with checkboxes, one-click exclude suggestions, working-config panel
with raw JSON preview, and a save flow with a success overlay.
Stdlib-only HTTP server bound to 127.0.0.1, with a random per-session
token (same approach as Jupyter Notebook) and no outbound network calls
of its own. Auto-shuts down on save, tab idle, or a hard time ceiling.
Includes:
- suggestions.py: detects PDFs, office docs, fonts, media, and anything
over 1MB that isn't already covered by the default exclusion rules
- persist.py: writes always_include/always_exclude to
.contextzip/config.json, reusing the existing workspace/.gitignore setup
Splits file-tree scanning into a one-time disk walk (scan_all_files) plus
a pure in-memory classifier (classify_scanned_files) that can be run
against different include/exclude patterns without re-touching the
filesystem. resolve_files() is untouched — this is purely additive.
Prunes known-huge, always-irrelevant directories (.venv, node_modules,
.git, __pycache__, build output, etc.) during the walk itself, and
rejects symlinks that resolve outside the project tree (e.g. a venv's
bin/python pointing at the system interpreter).