contextzip 0.4.7__tar.gz → 0.5.1__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.
- {contextzip-0.4.7 → contextzip-0.5.1}/PKG-INFO +45 -17
- {contextzip-0.4.7 → contextzip-0.5.1}/README.md +42 -15
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/__init__.py +1 -1
- contextzip-0.5.1/contextzip/__main__.py +37 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/api.py +39 -20
- contextzip-0.5.1/contextzip/applier.py +1357 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/cleanup.py +16 -1
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/cli.py +477 -44
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/cli_ai.py +2 -2
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/cli_display.py +133 -50
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/config.py +28 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/detector.py +12 -1
- contextzip-0.5.1/contextzip/editor.py +124 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/error_parser.py +54 -12
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/filters.py +17 -2
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/git.py +102 -36
- contextzip-0.5.1/contextzip/guide.py +134 -0
- contextzip-0.5.1/contextzip/merge3.py +176 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/packager.py +89 -35
- contextzip-0.5.1/contextzip/pathmatch.py +265 -0
- contextzip-0.5.1/contextzip/pathsetup.py +208 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/project_config.py +57 -5
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/redact.py +2 -3
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/report.py +43 -3
- contextzip-0.5.1/contextzip/root.py +331 -0
- contextzip-0.5.1/contextzip/snapshots.py +259 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/watcher.py +11 -1
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/webui/assets.py +17 -2
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/webui/persist.py +3 -0
- contextzip-0.5.1/contextzip/webui/review.py +360 -0
- contextzip-0.5.1/contextzip/webui/review_assets.py +407 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/webui/server.py +7 -0
- contextzip-0.5.1/contextzip/workspaces.py +138 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/PKG-INFO +45 -17
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/SOURCES.txt +26 -1
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/top_level.txt +1 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/pyproject.toml +3 -2
- contextzip-0.5.1/tests/__init__.py +0 -0
- contextzip-0.5.1/tests/helpers.py +77 -0
- contextzip-0.5.1/tests/test_applier.py +283 -0
- contextzip-0.5.1/tests/test_filters.py +84 -0
- contextzip-0.5.1/tests/test_git.py +47 -0
- contextzip-0.5.1/tests/test_guide.py +31 -0
- contextzip-0.5.1/tests/test_merge3.py +114 -0
- contextzip-0.5.1/tests/test_pathmatch.py +85 -0
- contextzip-0.5.1/tests/test_pathsetup.py +92 -0
- contextzip-0.5.1/tests/test_review.py +30 -0
- contextzip-0.5.1/tests/test_root.py +148 -0
- contextzip-0.5.1/tests/test_snapshots.py +82 -0
- contextzip-0.5.1/tests/test_watch_paths.py +28 -0
- contextzip-0.5.1/tests/test_workspaces.py +48 -0
- contextzip-0.4.7/contextzip/applier.py +0 -808
- {contextzip-0.4.7 → contextzip-0.5.1}/LICENSE +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/_x11_clip.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/ai/__init__.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/ai/gemini.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/ai/heuristic.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/ai/selector.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/cli_onboard.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/clipboard.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/__init__.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/base.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/errors/__init__.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/errors/node.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/errors/python.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/go.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/node.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/python.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/ruby.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/rules/rust.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/updater.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/webui/__init__.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip/webui/suggestions.py +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/dependency_links.txt +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/entry_points.txt +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/contextzip.egg-info/requires.txt +0 -0
- {contextzip-0.4.7 → contextzip-0.5.1}/setup.cfg +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextzip
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Intelligently package your codebase for AI tools
|
|
5
5
|
Author-email: Deepesh <akadeepesh@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://
|
|
7
|
+
Project-URL: Homepage, https://www.contextzipcli.com
|
|
8
|
+
Project-URL: Documentation, https://www.contextzipcli.com/getting-started
|
|
8
9
|
Project-URL: Repository, https://github.com/akadeepesh/contextzip
|
|
9
10
|
Project-URL: Issues, https://github.com/akadeepesh/contextzip/issues
|
|
10
11
|
Keywords: ai,cli,developer-tools,context,zip,llm,claude,chatgpt,code-packaging
|
|
@@ -38,7 +39,7 @@ Dynamic: license-file
|
|
|
38
39
|
[](https://pepy.tech/project/contextzip)
|
|
39
40
|
[](https://github.com/akadeepesh/contextzip/blob/main/LICENSE)
|
|
40
41
|
|
|
41
|
-
[Documentation](https://
|
|
42
|
+
[Documentation](https://www.contextzipcli.com) ·
|
|
42
43
|
[CLI reference](https://github.com/akadeepesh/contextzip/blob/main/CLI.md) ·
|
|
43
44
|
[Changelog](https://github.com/akadeepesh/contextzip/blob/main/CHANGELOG.md) ·
|
|
44
45
|
[Report a bug](https://github.com/akadeepesh/contextzip/issues/new/choose)
|
|
@@ -65,6 +66,7 @@ When the AI sends changed files back, `contextzip apply-zip` writes them into yo
|
|
|
65
66
|
## Table of contents
|
|
66
67
|
|
|
67
68
|
- [Installation](#installation)
|
|
69
|
+
- ["`cz` is not recognized"](#cz-is-not-recognized--command-not-found)
|
|
68
70
|
- [Quick start](#quick-start)
|
|
69
71
|
- [Features](#features)
|
|
70
72
|
- [Smart packaging](#smart-packaging)
|
|
@@ -103,6 +105,20 @@ This installs two commands that do the same thing: `contextzip` and the shorter
|
|
|
103
105
|
|
|
104
106
|
To update later, run `contextzip update`. It works out whether you installed with pip, pipx or uv and upgrades the right one.
|
|
105
107
|
|
|
108
|
+
### "`cz` is not recognized" / "command not found"
|
|
109
|
+
|
|
110
|
+
pip installed contextzip, but put the `cz` command in a folder your terminal doesn't search. This is common with Python from the Microsoft Store or the Python install manager on Windows, and with `pip install --user` on macOS and Linux. Look for pip's `WARNING: The scripts cz.exe and contextzip.exe are installed in '…' which is not on PATH.`
|
|
111
|
+
|
|
112
|
+
Python itself always works, so run contextzip through it and let it fix PATH for you:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m contextzip setup-path # macOS / Linux: python3 -m contextzip setup-path
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
On Windows this adds the folder to your user PATH (no admin needed). On macOS and Linux it adds one line to your shell's startup file. Open a new terminal and `cz` works. Until then, `python -m contextzip` does everything `cz` does.
|
|
119
|
+
|
|
120
|
+
If you installed with pipx, run `pipx ensurepath`. With uv, run `uv tool update-shell`.
|
|
121
|
+
|
|
106
122
|
## Quick start
|
|
107
123
|
|
|
108
124
|
```bash
|
|
@@ -120,6 +136,7 @@ contextzip --git-changes # only what you've changed
|
|
|
120
136
|
contextzip --prompt "fix the login redirect bug" # let AI pick the relevant files
|
|
121
137
|
contextzip include src/ app/ # only these folders
|
|
122
138
|
contextzip include src/ .env # .env too, values redacted
|
|
139
|
+
contextzip --here # only the folder you're in (monorepos)
|
|
123
140
|
contextzip exclude tests/ '*.snap' # everything except these
|
|
124
141
|
contextzip apply-zip # write the AI's changes back
|
|
125
142
|
```
|
|
@@ -163,7 +180,7 @@ Describe your task in plain language and [Gemini](https://ai.google.dev/) picks
|
|
|
163
180
|
|
|
164
181
|
- **Setup:** you need a free Gemini API key. contextzip walks you through it the first time you use `--prompt`, or you can set the `GEMINI_API_KEY` environment variable.
|
|
165
182
|
- **What is sent:** only the list of file **paths and sizes** that already passed the exclusion rules, plus your prompt. File contents never leave your machine.
|
|
166
|
-
- **Fallback:** if Gemini is rate-limited
|
|
183
|
+
- **Fallback:** if Gemini is rate-limited, a local keyword-based selector takes over, so `--prompt` keeps working. Other errors (a bad key, no network) stop with a clear message.
|
|
167
184
|
- **Model:** `gemini-2.5-flash-lite` by default.
|
|
168
185
|
|
|
169
186
|
### Applying AI changes back
|
|
@@ -174,17 +191,25 @@ contextzip apply-zip fix.zip # or point at one directly
|
|
|
174
191
|
contextzip apply-zip --dry-run # preview without writing anything
|
|
175
192
|
```
|
|
176
193
|
|
|
177
|
-
|
|
194
|
+
AI tools return ZIPs in every shape: wrapped in an extra folder, relative to a subfolder, flattened, with Windows separators. `apply-zip` matches each file to its real place in your project anyway, and new files follow their siblings.
|
|
195
|
+
|
|
196
|
+
Every ZIP contextzip makes gets a **local snapshot** (a hash of each file, never uploaded). `apply-zip` picks the snapshot that best matches the returned ZIP and compares three versions of each file: what you sent, what's on disk now, and what came back.
|
|
178
197
|
|
|
179
|
-
| Status | Meaning |
|
|
198
|
+
| Status | Meaning | Default |
|
|
180
199
|
|---|---|---|
|
|
181
|
-
| **New** |
|
|
182
|
-
| **
|
|
183
|
-
| **Unchanged** |
|
|
184
|
-
| **
|
|
185
|
-
| **
|
|
200
|
+
| **New** / **Modified** | New file, or only the AI changed it | Written |
|
|
201
|
+
| **Merged** | You both changed it, in different places | Written (three-way merge) |
|
|
202
|
+
| **Unchanged** | Already matches — including changes you already applied | Skipped |
|
|
203
|
+
| **Conflict** | You both changed the same or neighbouring lines | Needs your call |
|
|
204
|
+
| **Partial** | The AI returned a cut-down file (`// ... rest unchanged`) | Needs your call |
|
|
205
|
+
| **Moved** | The AI moved or renamed it | Written at the new path; the old file is kept unless you say otherwise |
|
|
206
|
+
| **Drifted** / **Deleted** / **Untracked** / **Ambiguous** / **Delete requested** | Can't be applied safely without you | Needs your call |
|
|
207
|
+
|
|
208
|
+
When something needs a decision you choose: apply the safe changes now, **review side by side in your browser** (`--review`), **resolve in VS Code's merge editor** (`--editor`), or apply everything. Line endings never count as a change, redacted secrets are never overwritten with placeholders, and **files are only removed with your explicit OK** — never one you've changed since.
|
|
209
|
+
|
|
210
|
+
### Snapshots and CONTEXTZIP.md
|
|
186
211
|
|
|
187
|
-
`
|
|
212
|
+
Every package is kept locally as a numbered **snapshot** (`#14`), so a reply can be merged against exactly what you sent, even after you've packaged again. Each ZIP also carries a short **`CONTEXTZIP.md`** for the AI: the snapshot ID, and how to reply so the answer applies cleanly (complete files, same paths, secrets left as `[REDACTED]`, moves and deletions listed). When the reply echoes it back, `apply-zip` knows the exact snapshot. `contextzip snapshots` lists them; turn the file off with `"zip_guide": false`.
|
|
188
213
|
|
|
189
214
|
### Terminal error watcher
|
|
190
215
|
|
|
@@ -240,7 +265,7 @@ This opens a local browser tab where you can tick files and folders to always in
|
|
|
240
265
|
|
|
241
266
|
Every command supports `-h` / `--help`, and `contextzip --version` prints the installed version.
|
|
242
267
|
|
|
243
|
-
For every flag of every subcommand (`apply-zip`, `watch`, `config`, `update`), see **[CLI.md](https://github.com/akadeepesh/contextzip/blob/main/CLI.md)** or the [online CLI reference](https://
|
|
268
|
+
For every flag of every subcommand (`apply-zip`, `watch`, `config`, `update`), see **[CLI.md](https://github.com/akadeepesh/contextzip/blob/main/CLI.md)** or the [online CLI reference](https://www.contextzipcli.com/cli).
|
|
244
269
|
|
|
245
270
|
## Configuration
|
|
246
271
|
|
|
@@ -266,6 +291,8 @@ This holds per-project settings. Edit it by hand or with `contextzip config --ui
|
|
|
266
291
|
},
|
|
267
292
|
"applied_zip_retention": 1, // past apply-zip archives to keep
|
|
268
293
|
"cleanup": { "enabled": true, "keep_recent": 1 },
|
|
294
|
+
"snapshots": { "keep": 10, "max_age_days": 14 }, // history apply-zip merges against
|
|
295
|
+
"zip_guide": true, // add CONTEXTZIP.md to every ZIP
|
|
269
296
|
"webui": { "auto_open": true, "port": null }
|
|
270
297
|
}
|
|
271
298
|
```
|
|
@@ -288,7 +315,7 @@ contextzip config --set-workspace cwd # put .contextzip/ in the current dir
|
|
|
288
315
|
contextzip config --show-key-path # print the config file location
|
|
289
316
|
```
|
|
290
317
|
|
|
291
|
-
The full option-by-option guide is at [
|
|
318
|
+
The full option-by-option guide is at [contextzipcli.com/configuration](https://www.contextzipcli.com/configuration).
|
|
292
319
|
|
|
293
320
|
## What is always excluded
|
|
294
321
|
|
|
@@ -328,11 +355,12 @@ contextzip keeps everything in a `.contextzip/` folder at your git root (change
|
|
|
328
355
|
│ ├── git-changes/ changes.zip + manifest + report (--git-changes)
|
|
329
356
|
│ ├── prompt/ vibe.zip + manifest + report (--prompt)
|
|
330
357
|
│ └── watch/ debug-context.zip (contextzip watch)
|
|
358
|
+
├── snapshots/ numbered copies of what you sent (contextzip snapshots)
|
|
331
359
|
├── inbox/ drop AI-returned ZIPs here for apply-zip
|
|
332
360
|
└── config.json project config (optional)
|
|
333
361
|
```
|
|
334
362
|
|
|
335
|
-
Old outputs are cleaned up automatically, and
|
|
363
|
+
Old outputs are cleaned up automatically: `output/` keeps the latest ZIP per mode (`cleanup`), and `snapshots/` keeps the newest 10 or anything under 14 days old (`snapshots`) — never one a reply in the inbox is waiting on.
|
|
336
364
|
|
|
337
365
|
## Python API
|
|
338
366
|
|
|
@@ -365,7 +393,7 @@ print(detect_ecosystem("."))
|
|
|
365
393
|
|
|
366
394
|
All exceptions inherit from `ContextzipError`: `NotARepositoryError`, `GitNotFoundError`, `GitCommandError`, `NoFilesError` and `ZipNotFoundError`.
|
|
367
395
|
|
|
368
|
-
For full signatures, see [
|
|
396
|
+
For full signatures, see [contextzipcli.com/cli#python-api](https://www.contextzipcli.com/cli#python-api).
|
|
369
397
|
|
|
370
398
|
## Privacy & security
|
|
371
399
|
|
|
@@ -391,7 +419,7 @@ If the clipboard can't be used, contextzip opens the output folder with the ZIP
|
|
|
391
419
|
|
|
392
420
|
## Contributing
|
|
393
421
|
|
|
394
|
-
Contributions are welcome, especially **new framework rule sets** and edge-case fixes. Not up for writing code? [Request a feature](https://
|
|
422
|
+
Contributions are welcome, especially **new framework rule sets** and edge-case fixes. Not up for writing code? [Request a feature](https://www.contextzipcli.com/request-feature) or [request your language](https://www.contextzipcli.com/request-language). Both turn into GitHub issues ranked by 👍. To set up a local copy:
|
|
395
423
|
|
|
396
424
|
```bash
|
|
397
425
|
git clone https://github.com/akadeepesh/contextzip
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://pepy.tech/project/contextzip)
|
|
10
10
|
[](https://github.com/akadeepesh/contextzip/blob/main/LICENSE)
|
|
11
11
|
|
|
12
|
-
[Documentation](https://
|
|
12
|
+
[Documentation](https://www.contextzipcli.com) ·
|
|
13
13
|
[CLI reference](https://github.com/akadeepesh/contextzip/blob/main/CLI.md) ·
|
|
14
14
|
[Changelog](https://github.com/akadeepesh/contextzip/blob/main/CHANGELOG.md) ·
|
|
15
15
|
[Report a bug](https://github.com/akadeepesh/contextzip/issues/new/choose)
|
|
@@ -36,6 +36,7 @@ When the AI sends changed files back, `contextzip apply-zip` writes them into yo
|
|
|
36
36
|
## Table of contents
|
|
37
37
|
|
|
38
38
|
- [Installation](#installation)
|
|
39
|
+
- ["`cz` is not recognized"](#cz-is-not-recognized--command-not-found)
|
|
39
40
|
- [Quick start](#quick-start)
|
|
40
41
|
- [Features](#features)
|
|
41
42
|
- [Smart packaging](#smart-packaging)
|
|
@@ -74,6 +75,20 @@ This installs two commands that do the same thing: `contextzip` and the shorter
|
|
|
74
75
|
|
|
75
76
|
To update later, run `contextzip update`. It works out whether you installed with pip, pipx or uv and upgrades the right one.
|
|
76
77
|
|
|
78
|
+
### "`cz` is not recognized" / "command not found"
|
|
79
|
+
|
|
80
|
+
pip installed contextzip, but put the `cz` command in a folder your terminal doesn't search. This is common with Python from the Microsoft Store or the Python install manager on Windows, and with `pip install --user` on macOS and Linux. Look for pip's `WARNING: The scripts cz.exe and contextzip.exe are installed in '…' which is not on PATH.`
|
|
81
|
+
|
|
82
|
+
Python itself always works, so run contextzip through it and let it fix PATH for you:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python -m contextzip setup-path # macOS / Linux: python3 -m contextzip setup-path
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
On Windows this adds the folder to your user PATH (no admin needed). On macOS and Linux it adds one line to your shell's startup file. Open a new terminal and `cz` works. Until then, `python -m contextzip` does everything `cz` does.
|
|
89
|
+
|
|
90
|
+
If you installed with pipx, run `pipx ensurepath`. With uv, run `uv tool update-shell`.
|
|
91
|
+
|
|
77
92
|
## Quick start
|
|
78
93
|
|
|
79
94
|
```bash
|
|
@@ -91,6 +106,7 @@ contextzip --git-changes # only what you've changed
|
|
|
91
106
|
contextzip --prompt "fix the login redirect bug" # let AI pick the relevant files
|
|
92
107
|
contextzip include src/ app/ # only these folders
|
|
93
108
|
contextzip include src/ .env # .env too, values redacted
|
|
109
|
+
contextzip --here # only the folder you're in (monorepos)
|
|
94
110
|
contextzip exclude tests/ '*.snap' # everything except these
|
|
95
111
|
contextzip apply-zip # write the AI's changes back
|
|
96
112
|
```
|
|
@@ -134,7 +150,7 @@ Describe your task in plain language and [Gemini](https://ai.google.dev/) picks
|
|
|
134
150
|
|
|
135
151
|
- **Setup:** you need a free Gemini API key. contextzip walks you through it the first time you use `--prompt`, or you can set the `GEMINI_API_KEY` environment variable.
|
|
136
152
|
- **What is sent:** only the list of file **paths and sizes** that already passed the exclusion rules, plus your prompt. File contents never leave your machine.
|
|
137
|
-
- **Fallback:** if Gemini is rate-limited
|
|
153
|
+
- **Fallback:** if Gemini is rate-limited, a local keyword-based selector takes over, so `--prompt` keeps working. Other errors (a bad key, no network) stop with a clear message.
|
|
138
154
|
- **Model:** `gemini-2.5-flash-lite` by default.
|
|
139
155
|
|
|
140
156
|
### Applying AI changes back
|
|
@@ -145,17 +161,25 @@ contextzip apply-zip fix.zip # or point at one directly
|
|
|
145
161
|
contextzip apply-zip --dry-run # preview without writing anything
|
|
146
162
|
```
|
|
147
163
|
|
|
148
|
-
|
|
164
|
+
AI tools return ZIPs in every shape: wrapped in an extra folder, relative to a subfolder, flattened, with Windows separators. `apply-zip` matches each file to its real place in your project anyway, and new files follow their siblings.
|
|
165
|
+
|
|
166
|
+
Every ZIP contextzip makes gets a **local snapshot** (a hash of each file, never uploaded). `apply-zip` picks the snapshot that best matches the returned ZIP and compares three versions of each file: what you sent, what's on disk now, and what came back.
|
|
149
167
|
|
|
150
|
-
| Status | Meaning |
|
|
168
|
+
| Status | Meaning | Default |
|
|
151
169
|
|---|---|---|
|
|
152
|
-
| **New** |
|
|
153
|
-
| **
|
|
154
|
-
| **Unchanged** |
|
|
155
|
-
| **
|
|
156
|
-
| **
|
|
170
|
+
| **New** / **Modified** | New file, or only the AI changed it | Written |
|
|
171
|
+
| **Merged** | You both changed it, in different places | Written (three-way merge) |
|
|
172
|
+
| **Unchanged** | Already matches — including changes you already applied | Skipped |
|
|
173
|
+
| **Conflict** | You both changed the same or neighbouring lines | Needs your call |
|
|
174
|
+
| **Partial** | The AI returned a cut-down file (`// ... rest unchanged`) | Needs your call |
|
|
175
|
+
| **Moved** | The AI moved or renamed it | Written at the new path; the old file is kept unless you say otherwise |
|
|
176
|
+
| **Drifted** / **Deleted** / **Untracked** / **Ambiguous** / **Delete requested** | Can't be applied safely without you | Needs your call |
|
|
177
|
+
|
|
178
|
+
When something needs a decision you choose: apply the safe changes now, **review side by side in your browser** (`--review`), **resolve in VS Code's merge editor** (`--editor`), or apply everything. Line endings never count as a change, redacted secrets are never overwritten with placeholders, and **files are only removed with your explicit OK** — never one you've changed since.
|
|
179
|
+
|
|
180
|
+
### Snapshots and CONTEXTZIP.md
|
|
157
181
|
|
|
158
|
-
`
|
|
182
|
+
Every package is kept locally as a numbered **snapshot** (`#14`), so a reply can be merged against exactly what you sent, even after you've packaged again. Each ZIP also carries a short **`CONTEXTZIP.md`** for the AI: the snapshot ID, and how to reply so the answer applies cleanly (complete files, same paths, secrets left as `[REDACTED]`, moves and deletions listed). When the reply echoes it back, `apply-zip` knows the exact snapshot. `contextzip snapshots` lists them; turn the file off with `"zip_guide": false`.
|
|
159
183
|
|
|
160
184
|
### Terminal error watcher
|
|
161
185
|
|
|
@@ -211,7 +235,7 @@ This opens a local browser tab where you can tick files and folders to always in
|
|
|
211
235
|
|
|
212
236
|
Every command supports `-h` / `--help`, and `contextzip --version` prints the installed version.
|
|
213
237
|
|
|
214
|
-
For every flag of every subcommand (`apply-zip`, `watch`, `config`, `update`), see **[CLI.md](https://github.com/akadeepesh/contextzip/blob/main/CLI.md)** or the [online CLI reference](https://
|
|
238
|
+
For every flag of every subcommand (`apply-zip`, `watch`, `config`, `update`), see **[CLI.md](https://github.com/akadeepesh/contextzip/blob/main/CLI.md)** or the [online CLI reference](https://www.contextzipcli.com/cli).
|
|
215
239
|
|
|
216
240
|
## Configuration
|
|
217
241
|
|
|
@@ -237,6 +261,8 @@ This holds per-project settings. Edit it by hand or with `contextzip config --ui
|
|
|
237
261
|
},
|
|
238
262
|
"applied_zip_retention": 1, // past apply-zip archives to keep
|
|
239
263
|
"cleanup": { "enabled": true, "keep_recent": 1 },
|
|
264
|
+
"snapshots": { "keep": 10, "max_age_days": 14 }, // history apply-zip merges against
|
|
265
|
+
"zip_guide": true, // add CONTEXTZIP.md to every ZIP
|
|
240
266
|
"webui": { "auto_open": true, "port": null }
|
|
241
267
|
}
|
|
242
268
|
```
|
|
@@ -259,7 +285,7 @@ contextzip config --set-workspace cwd # put .contextzip/ in the current dir
|
|
|
259
285
|
contextzip config --show-key-path # print the config file location
|
|
260
286
|
```
|
|
261
287
|
|
|
262
|
-
The full option-by-option guide is at [
|
|
288
|
+
The full option-by-option guide is at [contextzipcli.com/configuration](https://www.contextzipcli.com/configuration).
|
|
263
289
|
|
|
264
290
|
## What is always excluded
|
|
265
291
|
|
|
@@ -299,11 +325,12 @@ contextzip keeps everything in a `.contextzip/` folder at your git root (change
|
|
|
299
325
|
│ ├── git-changes/ changes.zip + manifest + report (--git-changes)
|
|
300
326
|
│ ├── prompt/ vibe.zip + manifest + report (--prompt)
|
|
301
327
|
│ └── watch/ debug-context.zip (contextzip watch)
|
|
328
|
+
├── snapshots/ numbered copies of what you sent (contextzip snapshots)
|
|
302
329
|
├── inbox/ drop AI-returned ZIPs here for apply-zip
|
|
303
330
|
└── config.json project config (optional)
|
|
304
331
|
```
|
|
305
332
|
|
|
306
|
-
Old outputs are cleaned up automatically, and
|
|
333
|
+
Old outputs are cleaned up automatically: `output/` keeps the latest ZIP per mode (`cleanup`), and `snapshots/` keeps the newest 10 or anything under 14 days old (`snapshots`) — never one a reply in the inbox is waiting on.
|
|
307
334
|
|
|
308
335
|
## Python API
|
|
309
336
|
|
|
@@ -336,7 +363,7 @@ print(detect_ecosystem("."))
|
|
|
336
363
|
|
|
337
364
|
All exceptions inherit from `ContextzipError`: `NotARepositoryError`, `GitNotFoundError`, `GitCommandError`, `NoFilesError` and `ZipNotFoundError`.
|
|
338
365
|
|
|
339
|
-
For full signatures, see [
|
|
366
|
+
For full signatures, see [contextzipcli.com/cli#python-api](https://www.contextzipcli.com/cli#python-api).
|
|
340
367
|
|
|
341
368
|
## Privacy & security
|
|
342
369
|
|
|
@@ -362,7 +389,7 @@ If the clipboard can't be used, contextzip opens the output folder with the ZIP
|
|
|
362
389
|
|
|
363
390
|
## Contributing
|
|
364
391
|
|
|
365
|
-
Contributions are welcome, especially **new framework rule sets** and edge-case fixes. Not up for writing code? [Request a feature](https://
|
|
392
|
+
Contributions are welcome, especially **new framework rule sets** and edge-case fixes. Not up for writing code? [Request a feature](https://www.contextzipcli.com/request-feature) or [request your language](https://www.contextzipcli.com/request-language). Both turn into GitHub issues ranked by 👍. To set up a local copy:
|
|
366
393
|
|
|
367
394
|
```bash
|
|
368
395
|
git clone https://github.com/akadeepesh/contextzip
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
`python -m contextzip` — works even when the `cz` command isn't on PATH.
|
|
3
|
+
|
|
4
|
+
When that's why it's being used, a one-line tip points to `setup-path`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _path_tip() -> None:
|
|
13
|
+
if len(sys.argv) > 1 and sys.argv[1] == "setup-path":
|
|
14
|
+
return
|
|
15
|
+
if not sys.stderr.isatty():
|
|
16
|
+
return
|
|
17
|
+
try:
|
|
18
|
+
from contextzip import pathsetup
|
|
19
|
+
|
|
20
|
+
st = pathsetup.status()
|
|
21
|
+
if st.directory is None or st.on_path or st.manager or st.in_venv:
|
|
22
|
+
return
|
|
23
|
+
except Exception: # noqa: BLE001 — a tip must never break the run
|
|
24
|
+
return
|
|
25
|
+
from rich.console import Console
|
|
26
|
+
|
|
27
|
+
Console(stderr=True).print(
|
|
28
|
+
"[dim]Tip: the cz command isn't on your PATH yet — run "
|
|
29
|
+
"[bold]python -m contextzip setup-path[/] to fix that.[/]"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
from contextzip.cli import main
|
|
35
|
+
|
|
36
|
+
_path_tip()
|
|
37
|
+
main()
|
|
@@ -35,7 +35,6 @@ Quickstart
|
|
|
35
35
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
|
-
import os
|
|
39
38
|
from dataclasses import dataclass, field
|
|
40
39
|
from pathlib import Path
|
|
41
40
|
|
|
@@ -158,7 +157,7 @@ def get_git_changes(
|
|
|
158
157
|
Parameters
|
|
159
158
|
----------
|
|
160
159
|
path:
|
|
161
|
-
Directory to start from. Defaults to
|
|
160
|
+
Directory to start from. Defaults to the project root found from the current folder. The actual
|
|
162
161
|
git root may be a parent of this directory.
|
|
163
162
|
use_project_config:
|
|
164
163
|
Apply the project's ``.contextzip/config.json``. Defaults to
|
|
@@ -234,7 +233,7 @@ def get_files(
|
|
|
234
233
|
Parameters
|
|
235
234
|
----------
|
|
236
235
|
path:
|
|
237
|
-
Project root to scan. Defaults to
|
|
236
|
+
Project root to scan. Defaults to the project root found from the current folder.
|
|
238
237
|
include:
|
|
239
238
|
If given, only files under these paths are returned (e.g.
|
|
240
239
|
``["src/", "app/"]``). Matched as exact path prefixes.
|
|
@@ -358,10 +357,14 @@ def create_zip(
|
|
|
358
357
|
|
|
359
358
|
output_path = Path(output).resolve() if output is not None else None
|
|
360
359
|
|
|
360
|
+
project_cfg = load_project_config(collection.project_dir)
|
|
361
361
|
return create_zip_silent(
|
|
362
362
|
resolve_result=resolve_result,
|
|
363
363
|
project_dir=collection.project_dir,
|
|
364
364
|
output_path=output_path,
|
|
365
|
+
redact_secrets_enabled=project_cfg.limits.redact_secrets,
|
|
366
|
+
ecosystem=collection.ecosystem,
|
|
367
|
+
guide=project_cfg.zip_guide,
|
|
365
368
|
)
|
|
366
369
|
|
|
367
370
|
|
|
@@ -370,20 +373,23 @@ def apply_zip(
|
|
|
370
373
|
*,
|
|
371
374
|
project_dir: str | Path | None = None,
|
|
372
375
|
manifest: str | Path | None = None,
|
|
376
|
+
include_risky: bool = False,
|
|
373
377
|
) -> ApplyResult:
|
|
374
378
|
"""
|
|
375
379
|
Apply an AI-returned ZIP back into *project_dir* (defaults to cwd).
|
|
376
380
|
|
|
377
|
-
|
|
378
|
-
to
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
381
|
+
Each file is matched to its place in the project (even when the zip is
|
|
382
|
+
wrapped, relative to a subfolder, or flattened), then compared with the
|
|
383
|
+
snapshot of what was sent and with the file on disk. New, modified and
|
|
384
|
+
cleanly merged files are written. Files that need a decision —
|
|
385
|
+
conflicts, partial files, ones you deleted or that weren't sent, and
|
|
386
|
+
ambiguous ones — are left alone unless *include_risky* is True, in which
|
|
387
|
+
case the AI's version wins. Nothing is removed — moved files keep their
|
|
388
|
+
old copy and requested deletions are skipped; use
|
|
389
|
+
``execute_plan(..., remove_moved=True, allow_delete=True)`` to opt in.
|
|
382
390
|
|
|
383
|
-
|
|
384
|
-
``
|
|
385
|
-
inspect risk before writing (see ``ApplyPlan.is_risky`` /
|
|
386
|
-
``.risky_entries``).
|
|
391
|
+
Doesn't prompt. To inspect first, use ``contextzip.applier.build_plan()``
|
|
392
|
+
and ``execute_plan(..., decisions=...)`` directly.
|
|
387
393
|
|
|
388
394
|
Parameters
|
|
389
395
|
----------
|
|
@@ -391,15 +397,20 @@ def apply_zip(
|
|
|
391
397
|
Path to the returned zip. If omitted, auto-detects from
|
|
392
398
|
``.contextzip/inbox/`` — exactly one zip must be present there.
|
|
393
399
|
project_dir:
|
|
394
|
-
Project root to apply into. Defaults to
|
|
400
|
+
Project root to apply into. Defaults to the project root found from the current folder.
|
|
395
401
|
manifest:
|
|
396
|
-
|
|
402
|
+
Snapshot (manifest) to compare against. By default the one that
|
|
403
|
+
best matches the zip is picked automatically.
|
|
404
|
+
include_risky:
|
|
405
|
+
Also write files that need a decision, with the AI's version
|
|
406
|
+
winning. Defaults to False.
|
|
397
407
|
|
|
398
408
|
Returns
|
|
399
409
|
-------
|
|
400
410
|
ApplyResult
|
|
401
|
-
``written`` (
|
|
402
|
-
``applied_zip_path`` (where the
|
|
411
|
+
``written`` (relative paths written), ``skipped`` (paths left alone)
|
|
412
|
+
and ``applied_zip_path`` (where the zip ended up — it stays in the
|
|
413
|
+
inbox while something still needs a decision).
|
|
403
414
|
|
|
404
415
|
Raises
|
|
405
416
|
------
|
|
@@ -431,9 +442,9 @@ def apply_zip(
|
|
|
431
442
|
except ApplyError as exc:
|
|
432
443
|
raise ZipNotFoundError(str(exc)) from exc
|
|
433
444
|
|
|
434
|
-
manifest_path = find_latest_manifest(pdir, manifest)
|
|
445
|
+
manifest_path = find_latest_manifest(pdir, manifest) if manifest is not None else None
|
|
435
446
|
plan = build_plan(resolved_zip, pdir, manifest_path)
|
|
436
|
-
return execute_plan(plan, pdir)
|
|
447
|
+
return execute_plan(plan, pdir, include_risky=include_risky)
|
|
437
448
|
|
|
438
449
|
|
|
439
450
|
def detect_ecosystem(
|
|
@@ -445,7 +456,7 @@ def detect_ecosystem(
|
|
|
445
456
|
Parameters
|
|
446
457
|
----------
|
|
447
458
|
path:
|
|
448
|
-
Directory to inspect. Defaults to
|
|
459
|
+
Directory to inspect. Defaults to the project root found from the current folder.
|
|
449
460
|
|
|
450
461
|
Returns
|
|
451
462
|
-------
|
|
@@ -498,8 +509,16 @@ def _config_kwargs(cfg, extra_exclude: list[str] | None = None) -> dict:
|
|
|
498
509
|
|
|
499
510
|
|
|
500
511
|
def _resolve_dir(path: str | Path | None) -> Path:
|
|
512
|
+
"""
|
|
513
|
+
An explicit *path* is used exactly as given. Without one, resolve the
|
|
514
|
+
project root from the current folder — the same root the CLI uses (see
|
|
515
|
+
root.find_project_root) — so a script run from a subfolder of a
|
|
516
|
+
monorepo still works on the whole project with root-relative paths.
|
|
517
|
+
"""
|
|
501
518
|
if path is None:
|
|
502
|
-
|
|
519
|
+
from contextzip.root import find_project_root
|
|
520
|
+
|
|
521
|
+
return find_project_root().root
|
|
503
522
|
return Path(path).resolve()
|
|
504
523
|
|
|
505
524
|
|