contextzip 0.3.4__tar.gz → 0.3.5__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.3.4 → contextzip-0.3.5}/PKG-INFO +37 -44
- {contextzip-0.3.4 → contextzip-0.3.5}/README.md +36 -43
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/__init__.py +1 -1
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/cli.py +58 -222
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/cli_display.py +8 -56
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/cli_onboard.py +0 -59
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/clipboard.py +0 -86
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/config.py +24 -19
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/detector.py +122 -12
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/git.py +0 -131
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/packager.py +83 -18
- contextzip-0.3.5/contextzip/project_config.py +93 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/PKG-INFO +37 -44
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/SOURCES.txt +1 -5
- {contextzip-0.3.4 → contextzip-0.3.5}/pyproject.toml +1 -1
- contextzip-0.3.4/contextzip/brief.py +0 -280
- contextzip-0.3.4/contextzip/claude_artifacts.py +0 -150
- contextzip-0.3.4/contextzip/claude_export.py +0 -73
- contextzip-0.3.4/contextzip/code_changes.py +0 -257
- contextzip-0.3.4/contextzip/markers.py +0 -61
- {contextzip-0.3.4 → contextzip-0.3.5}/LICENSE +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/ai/__init__.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/ai/gemini.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/ai/heuristic.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/ai/selector.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/api.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/cli_ai.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/error_parser.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/filters.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/__init__.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/base.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/errors/__init__.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/errors/node.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/errors/python.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/go.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/node.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/python.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/ruby.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/rules/rust.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip/watcher.py +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/dependency_links.txt +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/entry_points.txt +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/requires.txt +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/contextzip.egg-info/top_level.txt +0 -0
- {contextzip-0.3.4 → contextzip-0.3.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextzip
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Intelligently package your codebase for AI tools
|
|
5
5
|
Author-email: Deepesh <akadeepesh@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -47,13 +47,13 @@ contextzip eliminates that entirely. Run it from your project root — it detect
|
|
|
47
47
|
|
|
48
48
|
## Features
|
|
49
49
|
|
|
50
|
-
- **Smart framework detection** — automatically identifies Node.js, Next.js, Python, Django, FastAPI, Rust, Go, and Ruby, applying the right exclusion rules for each
|
|
50
|
+
- **Smart framework detection** — automatically identifies Node.js, Next.js, Python, Django, FastAPI, Rust, Go, and Ruby, applying the right exclusion rules for each. Detection isn't limited to the project root: a shallow, bounded scan of subdirectories means a monorepo (`frontend/` + `backend/`, etc.) gets every ecosystem it contains detected and excluded correctly, not just whatever sits at the top level
|
|
51
51
|
- **Respects `.gitignore`** — your existing ignore patterns are honoured automatically
|
|
52
52
|
- **Git-aware packaging** — use `--git-changes` to package only modified, staged, and untracked files; perfect for incremental debugging and PR review sessions
|
|
53
53
|
- **AI-powered file selection** — describe your task in plain English with `--prompt` and Gemini selects the minimum relevant files automatically, no manual hunting required
|
|
54
54
|
- **Terminal error watcher** — wrap any dev server with `contextzip watch` to auto-detect errors and package a ready-to-upload debug context in one keypress
|
|
55
|
-
- **
|
|
56
|
-
- **Persistent workspace** — all generated ZIPs land in `.contextzip
|
|
55
|
+
- **Configurable workspace location** — `.contextzip/` lives at the git root by default, but you can pin it elsewhere per-machine (`contextzip config --set-workspace`) or for the whole team via a committed `.contextzip.json`
|
|
56
|
+
- **Persistent workspace** — all generated ZIPs land in `.contextzip/`, discoverable, reusable, and git-ignored automatically
|
|
57
57
|
- **Warns before it's a problem** — flags large (≥ 1 MB) and binary files that AI tools can't read, before you waste an upload
|
|
58
58
|
- **Handles edge cases** — dangling symlinks, unreadable files, and paths outside the project tree are caught and reported, never silently dropped
|
|
59
59
|
- **Full CLI control** — `--include`, `--exclude`, `--dry-run`, `--output`, all composable
|
|
@@ -118,7 +118,7 @@ contextzip [OPTIONS]
|
|
|
118
118
|
| `--no-clipboard` | Skip the clipboard / folder-open step |
|
|
119
119
|
| `--no-gitignore` | Ignore the project's `.gitignore` |
|
|
120
120
|
|
|
121
|
-
**Subcommands:** `exclude`, `include`, `watch`, `config
|
|
121
|
+
**Subcommands:** `exclude`, `include`, `watch`, `config` — run `contextzip --help` for full details.
|
|
122
122
|
|
|
123
123
|
---
|
|
124
124
|
|
|
@@ -244,60 +244,53 @@ Press **D** and contextzip immediately writes `.contextzip/debug-context.zip`. Y
|
|
|
244
244
|
|
|
245
245
|
---
|
|
246
246
|
|
|
247
|
-
##
|
|
248
|
-
|
|
249
|
-
If you work through a problem in a Claude or ChatGPT conversation and need to either (a) summarize what you did for an end-of-day report, or (b) continue the same work in a fresh chat after hitting a usage limit, `eod` and `handoff` build the prompt for you — contextzip does no summarizing itself; that's left to whichever AI tool you paste the result into.
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
contextzip eod
|
|
253
|
-
contextzip handoff
|
|
254
|
-
```
|
|
247
|
+
## What gets excluded
|
|
255
248
|
|
|
256
|
-
|
|
249
|
+
contextzip stacks exclusion rules based on your detected stack, on top of your `.gitignore`.
|
|
257
250
|
|
|
258
|
-
**
|
|
251
|
+
**Always excluded:** `.git/`, `.env` files, logs, caches, editor config (`.vscode/`, `.idea/`), OS files (`.DS_Store`, `Thumbs.db`), common binary formats, and contextzip's own `.contextzip/` working folder.
|
|
259
252
|
|
|
260
|
-
|
|
261
|
-
- Whatever code changed, resolved per file in priority order:
|
|
262
|
-
1. **Not pushed** — diffed against your upstream branch, plus the complete current file
|
|
263
|
-
2. **Diverged from Claude** — diffed against the version Claude last produced (if you've set up a session key — see below), plus the complete current codebase file
|
|
264
|
-
3. **Since last run** — diffed against a per-branch checkpoint that `eod`/`handoff` remember automatically, plus the complete current file
|
|
265
|
-
- New, untracked files are included as full content (there's nothing to diff them against)
|
|
266
|
-
- `eod` ends the prompt with a flat instruction to produce a work-log table; `handoff` frames it as continuing the project in a new chat
|
|
253
|
+
**By framework:**
|
|
267
254
|
|
|
268
|
-
|
|
255
|
+
| Stack | Additional exclusions |
|
|
256
|
+
|---|---|
|
|
257
|
+
| Node.js / Next.js | `node_modules/`, `.next/`, `dist/`, `build/`, lock files, `*.min.js`, `*.d.ts` |
|
|
258
|
+
| Python / Django / FastAPI | `__pycache__/`, `.venv/`, `*.pyc`, `migrations/`, `.pytest_cache/`, lock files |
|
|
259
|
+
| Rust | `target/`, `Cargo.lock`, `*.rlib` |
|
|
260
|
+
| Go | `vendor/`, `go.sum`, `bin/` |
|
|
269
261
|
|
|
270
|
-
|
|
262
|
+
Detection is additive — a monorepo with both `package.json` and `pyproject.toml` gets both rule sets applied. Marker files don't have to sit at the project root: contextzip also does a shallow, bounded scan of subdirectories (2 levels deep, skipping `node_modules/`, `.venv/`, `.git/`, and other dependency/build dirs it would never want to look inside anyway), so a layout like
|
|
271
263
|
|
|
272
|
-
```bash
|
|
273
|
-
contextzip config --set-session-key
|
|
274
264
|
```
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
```bash
|
|
279
|
-
contextzip eod --dry-run # preview without advancing the checkpoint
|
|
280
|
-
contextzip handoff --no-fetch # skip the Claude-artifact fetch for this run
|
|
265
|
+
root/
|
|
266
|
+
frontend/package.json
|
|
267
|
+
backend/requirements.txt
|
|
281
268
|
```
|
|
282
269
|
|
|
283
|
-
|
|
270
|
+
detects both Node.js and Python from `root/`, without either marker existing at the top level. Run with default output (not `--dry-run --verbose`) and you'll see which subdirectory each ecosystem came from, e.g. `Next.js (frontend/) + FastAPI (backend/)`.
|
|
284
271
|
|
|
285
|
-
|
|
272
|
+
---
|
|
286
273
|
|
|
287
|
-
|
|
274
|
+
## Workspace location
|
|
288
275
|
|
|
289
|
-
|
|
276
|
+
By default, `.contextzip/` is created at your git root — that's `_find_git_root()` walking up from the current directory until it finds a `.git` folder; outside a repo, it falls back to the current directory. This can be overridden two ways, in order of priority:
|
|
290
277
|
|
|
291
|
-
**
|
|
278
|
+
1. **`CONTEXTZIP_WORKSPACE_LOCATION` environment variable** — for a one-off override on a single run
|
|
279
|
+
2. **A committed `.contextzip.json` at the project root** — team-shared, applies to everyone who clones the repo:
|
|
280
|
+
```json
|
|
281
|
+
{ "workspace_location": "git-root" }
|
|
282
|
+
```
|
|
283
|
+
3. **Your personal config** — a per-machine default that doesn't get committed:
|
|
284
|
+
```bash
|
|
285
|
+
contextzip config --set-workspace cwd # always use ./.contextzip wherever you run it
|
|
286
|
+
contextzip config --set-workspace git-root # back to the default
|
|
287
|
+
contextzip config --set-workspace ~/zips # a fixed custom location, anywhere
|
|
288
|
+
contextzip config --reset-workspace # clear your personal override
|
|
289
|
+
```
|
|
292
290
|
|
|
293
|
-
|
|
294
|
-
|---|---|
|
|
295
|
-
| Node.js / Next.js | `node_modules/`, `.next/`, `dist/`, `build/`, lock files, `*.min.js`, `*.d.ts` |
|
|
296
|
-
| Python / Django / FastAPI | `__pycache__/`, `.venv/`, `*.pyc`, `migrations/`, `.pytest_cache/`, lock files |
|
|
297
|
-
| Rust | `target/`, `Cargo.lock`, `*.rlib` |
|
|
298
|
-
| Go | `vendor/`, `go.sum`, `bin/` |
|
|
291
|
+
Accepted values are `"git-root"`, `"cwd"`, or any path (absolute, or relative to the git root). Run `contextzip config` with no flags to see which value is currently active and where it came from.
|
|
299
292
|
|
|
300
|
-
|
|
293
|
+
This matters most for monorepos where you sometimes run contextzip from a subdirectory (`cd frontend && contextzip`) — with the default `git-root` setting, the workspace still lands at the repo root no matter where you ran it from, so you don't end up with scattered `.contextzip/` folders across `frontend/`, `backend/`, etc. Set `workspace_location: "cwd"` in a project's `.contextzip.json` instead if you'd rather each subproject keep its own.
|
|
301
294
|
|
|
302
295
|
---
|
|
303
296
|
|
|
@@ -18,13 +18,13 @@ contextzip eliminates that entirely. Run it from your project root — it detect
|
|
|
18
18
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
|
-
- **Smart framework detection** — automatically identifies Node.js, Next.js, Python, Django, FastAPI, Rust, Go, and Ruby, applying the right exclusion rules for each
|
|
21
|
+
- **Smart framework detection** — automatically identifies Node.js, Next.js, Python, Django, FastAPI, Rust, Go, and Ruby, applying the right exclusion rules for each. Detection isn't limited to the project root: a shallow, bounded scan of subdirectories means a monorepo (`frontend/` + `backend/`, etc.) gets every ecosystem it contains detected and excluded correctly, not just whatever sits at the top level
|
|
22
22
|
- **Respects `.gitignore`** — your existing ignore patterns are honoured automatically
|
|
23
23
|
- **Git-aware packaging** — use `--git-changes` to package only modified, staged, and untracked files; perfect for incremental debugging and PR review sessions
|
|
24
24
|
- **AI-powered file selection** — describe your task in plain English with `--prompt` and Gemini selects the minimum relevant files automatically, no manual hunting required
|
|
25
25
|
- **Terminal error watcher** — wrap any dev server with `contextzip watch` to auto-detect errors and package a ready-to-upload debug context in one keypress
|
|
26
|
-
- **
|
|
27
|
-
- **Persistent workspace** — all generated ZIPs land in `.contextzip
|
|
26
|
+
- **Configurable workspace location** — `.contextzip/` lives at the git root by default, but you can pin it elsewhere per-machine (`contextzip config --set-workspace`) or for the whole team via a committed `.contextzip.json`
|
|
27
|
+
- **Persistent workspace** — all generated ZIPs land in `.contextzip/`, discoverable, reusable, and git-ignored automatically
|
|
28
28
|
- **Warns before it's a problem** — flags large (≥ 1 MB) and binary files that AI tools can't read, before you waste an upload
|
|
29
29
|
- **Handles edge cases** — dangling symlinks, unreadable files, and paths outside the project tree are caught and reported, never silently dropped
|
|
30
30
|
- **Full CLI control** — `--include`, `--exclude`, `--dry-run`, `--output`, all composable
|
|
@@ -89,7 +89,7 @@ contextzip [OPTIONS]
|
|
|
89
89
|
| `--no-clipboard` | Skip the clipboard / folder-open step |
|
|
90
90
|
| `--no-gitignore` | Ignore the project's `.gitignore` |
|
|
91
91
|
|
|
92
|
-
**Subcommands:** `exclude`, `include`, `watch`, `config
|
|
92
|
+
**Subcommands:** `exclude`, `include`, `watch`, `config` — run `contextzip --help` for full details.
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
@@ -215,60 +215,53 @@ Press **D** and contextzip immediately writes `.contextzip/debug-context.zip`. Y
|
|
|
215
215
|
|
|
216
216
|
---
|
|
217
217
|
|
|
218
|
-
##
|
|
219
|
-
|
|
220
|
-
If you work through a problem in a Claude or ChatGPT conversation and need to either (a) summarize what you did for an end-of-day report, or (b) continue the same work in a fresh chat after hitting a usage limit, `eod` and `handoff` build the prompt for you — contextzip does no summarizing itself; that's left to whichever AI tool you paste the result into.
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
contextzip eod
|
|
224
|
-
contextzip handoff
|
|
225
|
-
```
|
|
218
|
+
## What gets excluded
|
|
226
219
|
|
|
227
|
-
|
|
220
|
+
contextzip stacks exclusion rules based on your detected stack, on top of your `.gitignore`.
|
|
228
221
|
|
|
229
|
-
**
|
|
222
|
+
**Always excluded:** `.git/`, `.env` files, logs, caches, editor config (`.vscode/`, `.idea/`), OS files (`.DS_Store`, `Thumbs.db`), common binary formats, and contextzip's own `.contextzip/` working folder.
|
|
230
223
|
|
|
231
|
-
|
|
232
|
-
- Whatever code changed, resolved per file in priority order:
|
|
233
|
-
1. **Not pushed** — diffed against your upstream branch, plus the complete current file
|
|
234
|
-
2. **Diverged from Claude** — diffed against the version Claude last produced (if you've set up a session key — see below), plus the complete current codebase file
|
|
235
|
-
3. **Since last run** — diffed against a per-branch checkpoint that `eod`/`handoff` remember automatically, plus the complete current file
|
|
236
|
-
- New, untracked files are included as full content (there's nothing to diff them against)
|
|
237
|
-
- `eod` ends the prompt with a flat instruction to produce a work-log table; `handoff` frames it as continuing the project in a new chat
|
|
224
|
+
**By framework:**
|
|
238
225
|
|
|
239
|
-
|
|
226
|
+
| Stack | Additional exclusions |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Node.js / Next.js | `node_modules/`, `.next/`, `dist/`, `build/`, lock files, `*.min.js`, `*.d.ts` |
|
|
229
|
+
| Python / Django / FastAPI | `__pycache__/`, `.venv/`, `*.pyc`, `migrations/`, `.pytest_cache/`, lock files |
|
|
230
|
+
| Rust | `target/`, `Cargo.lock`, `*.rlib` |
|
|
231
|
+
| Go | `vendor/`, `go.sum`, `bin/` |
|
|
240
232
|
|
|
241
|
-
|
|
233
|
+
Detection is additive — a monorepo with both `package.json` and `pyproject.toml` gets both rule sets applied. Marker files don't have to sit at the project root: contextzip also does a shallow, bounded scan of subdirectories (2 levels deep, skipping `node_modules/`, `.venv/`, `.git/`, and other dependency/build dirs it would never want to look inside anyway), so a layout like
|
|
242
234
|
|
|
243
|
-
```bash
|
|
244
|
-
contextzip config --set-session-key
|
|
245
235
|
```
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
contextzip eod --dry-run # preview without advancing the checkpoint
|
|
251
|
-
contextzip handoff --no-fetch # skip the Claude-artifact fetch for this run
|
|
236
|
+
root/
|
|
237
|
+
frontend/package.json
|
|
238
|
+
backend/requirements.txt
|
|
252
239
|
```
|
|
253
240
|
|
|
254
|
-
|
|
241
|
+
detects both Node.js and Python from `root/`, without either marker existing at the top level. Run with default output (not `--dry-run --verbose`) and you'll see which subdirectory each ecosystem came from, e.g. `Next.js (frontend/) + FastAPI (backend/)`.
|
|
255
242
|
|
|
256
|
-
|
|
243
|
+
---
|
|
257
244
|
|
|
258
|
-
|
|
245
|
+
## Workspace location
|
|
259
246
|
|
|
260
|
-
|
|
247
|
+
By default, `.contextzip/` is created at your git root — that's `_find_git_root()` walking up from the current directory until it finds a `.git` folder; outside a repo, it falls back to the current directory. This can be overridden two ways, in order of priority:
|
|
261
248
|
|
|
262
|
-
**
|
|
249
|
+
1. **`CONTEXTZIP_WORKSPACE_LOCATION` environment variable** — for a one-off override on a single run
|
|
250
|
+
2. **A committed `.contextzip.json` at the project root** — team-shared, applies to everyone who clones the repo:
|
|
251
|
+
```json
|
|
252
|
+
{ "workspace_location": "git-root" }
|
|
253
|
+
```
|
|
254
|
+
3. **Your personal config** — a per-machine default that doesn't get committed:
|
|
255
|
+
```bash
|
|
256
|
+
contextzip config --set-workspace cwd # always use ./.contextzip wherever you run it
|
|
257
|
+
contextzip config --set-workspace git-root # back to the default
|
|
258
|
+
contextzip config --set-workspace ~/zips # a fixed custom location, anywhere
|
|
259
|
+
contextzip config --reset-workspace # clear your personal override
|
|
260
|
+
```
|
|
263
261
|
|
|
264
|
-
|
|
265
|
-
|---|---|
|
|
266
|
-
| Node.js / Next.js | `node_modules/`, `.next/`, `dist/`, `build/`, lock files, `*.min.js`, `*.d.ts` |
|
|
267
|
-
| Python / Django / FastAPI | `__pycache__/`, `.venv/`, `*.pyc`, `migrations/`, `.pytest_cache/`, lock files |
|
|
268
|
-
| Rust | `target/`, `Cargo.lock`, `*.rlib` |
|
|
269
|
-
| Go | `vendor/`, `go.sum`, `bin/` |
|
|
262
|
+
Accepted values are `"git-root"`, `"cwd"`, or any path (absolute, or relative to the git root). Run `contextzip config` with no flags to see which value is currently active and where it came from.
|
|
270
263
|
|
|
271
|
-
|
|
264
|
+
This matters most for monorepos where you sometimes run contextzip from a subdirectory (`cd frontend && contextzip`) — with the default `git-root` setting, the workspace still lands at the repo root no matter where you ran it from, so you don't end up with scattered `.contextzip/` folders across `frontend/`, `backend/`, etc. Set `workspace_location: "cwd"` in a project's `.contextzip.json` instead if you'd rather each subproject keep its own.
|
|
272
265
|
|
|
273
266
|
---
|
|
274
267
|
|
|
@@ -15,19 +15,16 @@ from contextzip import __version__
|
|
|
15
15
|
from contextzip.config import (
|
|
16
16
|
get_api_key,
|
|
17
17
|
delete_api_key,
|
|
18
|
-
get_session_key,
|
|
19
|
-
delete_session_key,
|
|
20
18
|
config_path,
|
|
21
19
|
diagnose_api_key,
|
|
20
|
+
save_workspace_location,
|
|
21
|
+
delete_workspace_location,
|
|
22
22
|
)
|
|
23
23
|
from contextzip.detector import detect
|
|
24
24
|
from contextzip.filters import build_spec, resolve_files, resolve_files_from_git
|
|
25
25
|
from contextzip.git import get_changed_files, GitError
|
|
26
26
|
from contextzip.packager import create_zip
|
|
27
|
-
from contextzip.clipboard import
|
|
28
|
-
handle as clipboard_handle,
|
|
29
|
-
handle_text as clipboard_handle_text,
|
|
30
|
-
)
|
|
27
|
+
from contextzip.clipboard import handle as clipboard_handle
|
|
31
28
|
|
|
32
29
|
from contextzip.cli_display import (
|
|
33
30
|
print_detection,
|
|
@@ -37,14 +34,13 @@ from contextzip.cli_display import (
|
|
|
37
34
|
print_package_result,
|
|
38
35
|
print_zip_write_warnings,
|
|
39
36
|
print_clipboard_result,
|
|
40
|
-
print_brief_result,
|
|
41
37
|
)
|
|
42
38
|
from contextzip.cli_ai import (
|
|
43
39
|
normalize_pattern,
|
|
44
40
|
run_ai_selection,
|
|
45
41
|
run_ai_selection_preview,
|
|
46
42
|
)
|
|
47
|
-
from contextzip.cli_onboard import onboard_api_key
|
|
43
|
+
from contextzip.cli_onboard import onboard_api_key
|
|
48
44
|
|
|
49
45
|
console = Console()
|
|
50
46
|
|
|
@@ -377,166 +373,6 @@ def cmd_watch(command: tuple[str, ...]) -> None:
|
|
|
377
373
|
raise SystemExit(exit_code)
|
|
378
374
|
|
|
379
375
|
|
|
380
|
-
@main.command("eod")
|
|
381
|
-
@click.option(
|
|
382
|
-
"--exports-dir",
|
|
383
|
-
default=None,
|
|
384
|
-
metavar="DIR",
|
|
385
|
-
help="Folder containing conversation exports (default: ./exports).",
|
|
386
|
-
)
|
|
387
|
-
@click.option(
|
|
388
|
-
"--no-clipboard",
|
|
389
|
-
is_flag=True,
|
|
390
|
-
default=False,
|
|
391
|
-
help="Skip copying the prompt to the clipboard.",
|
|
392
|
-
)
|
|
393
|
-
@click.option(
|
|
394
|
-
"--no-fetch",
|
|
395
|
-
is_flag=True,
|
|
396
|
-
default=False,
|
|
397
|
-
help="Don't auto-fetch Claude's artifact files even if a session key is configured.",
|
|
398
|
-
)
|
|
399
|
-
@click.option(
|
|
400
|
-
"--dry-run",
|
|
401
|
-
"-n",
|
|
402
|
-
is_flag=True,
|
|
403
|
-
default=False,
|
|
404
|
-
help="Build the prompt without advancing the eod marker.",
|
|
405
|
-
)
|
|
406
|
-
def cmd_eod(
|
|
407
|
-
exports_dir: str | None, no_clipboard: bool, no_fetch: bool, dry_run: bool
|
|
408
|
-
) -> None:
|
|
409
|
-
"""
|
|
410
|
-
Build a paste-ready end-of-day prompt from today's conversation + code changes.
|
|
411
|
-
|
|
412
|
-
\b
|
|
413
|
-
EXAMPLES
|
|
414
|
-
contextzip eod
|
|
415
|
-
contextzip eod --dry-run
|
|
416
|
-
contextzip eod --exports-dir ~/claude-exports
|
|
417
|
-
|
|
418
|
-
Picks the most recently modified .md file in exports/, packages whatever
|
|
419
|
-
code changed since the last time eod ran (diffs for files that already
|
|
420
|
-
existed, full content for new ones), and copies a ready-to-paste prompt
|
|
421
|
-
to your clipboard. contextzip does no summarizing itself — that's the
|
|
422
|
-
job of whatever AI tool you paste the prompt into.
|
|
423
|
-
"""
|
|
424
|
-
_run_brief("eod", exports_dir, no_clipboard, no_fetch, dry_run)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
@main.command("handoff")
|
|
428
|
-
@click.option(
|
|
429
|
-
"--exports-dir",
|
|
430
|
-
default=None,
|
|
431
|
-
metavar="DIR",
|
|
432
|
-
help="Folder containing conversation exports (default: ./exports).",
|
|
433
|
-
)
|
|
434
|
-
@click.option(
|
|
435
|
-
"--no-clipboard",
|
|
436
|
-
is_flag=True,
|
|
437
|
-
default=False,
|
|
438
|
-
help="Skip copying the prompt to the clipboard.",
|
|
439
|
-
)
|
|
440
|
-
@click.option(
|
|
441
|
-
"--no-fetch",
|
|
442
|
-
is_flag=True,
|
|
443
|
-
default=False,
|
|
444
|
-
help="Don't auto-fetch Claude's artifact files even if a session key is configured.",
|
|
445
|
-
)
|
|
446
|
-
@click.option(
|
|
447
|
-
"--dry-run",
|
|
448
|
-
"-n",
|
|
449
|
-
is_flag=True,
|
|
450
|
-
default=False,
|
|
451
|
-
help="Build the prompt without advancing the handoff marker.",
|
|
452
|
-
)
|
|
453
|
-
def cmd_handoff(
|
|
454
|
-
exports_dir: str | None, no_clipboard: bool, no_fetch: bool, dry_run: bool
|
|
455
|
-
) -> None:
|
|
456
|
-
"""
|
|
457
|
-
Build a paste-ready prompt to continue this project in a fresh chat.
|
|
458
|
-
|
|
459
|
-
\b
|
|
460
|
-
EXAMPLES
|
|
461
|
-
contextzip handoff
|
|
462
|
-
contextzip handoff --dry-run
|
|
463
|
-
|
|
464
|
-
Use this when you've hit a usage limit and need to start a new chat
|
|
465
|
-
without losing context: picks the latest conversation export, packages
|
|
466
|
-
the code changed since the last handoff (or eod), and copies a prompt
|
|
467
|
-
ready to paste into the new chat.
|
|
468
|
-
"""
|
|
469
|
-
_run_brief("handoff", exports_dir, no_clipboard, no_fetch, dry_run)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
def _run_brief(
|
|
473
|
-
kind: str,
|
|
474
|
-
exports_dir: str | None,
|
|
475
|
-
no_clipboard: bool,
|
|
476
|
-
no_fetch: bool,
|
|
477
|
-
dry_run: bool,
|
|
478
|
-
) -> None:
|
|
479
|
-
"""Shared implementation for cmd_eod and cmd_handoff — see brief.py for the actual logic."""
|
|
480
|
-
from contextzip.brief import run_brief, BriefError
|
|
481
|
-
|
|
482
|
-
project_dir = Path(os.getcwd()).resolve()
|
|
483
|
-
|
|
484
|
-
console.print()
|
|
485
|
-
console.print(
|
|
486
|
-
Panel.fit(
|
|
487
|
-
f"[bold cyan]contextzip[/] [dim]{kind}[/]\n"
|
|
488
|
-
f"[dim]Project:[/] [white]{project_dir}[/]",
|
|
489
|
-
border_style="cyan",
|
|
490
|
-
padding=(0, 2),
|
|
491
|
-
)
|
|
492
|
-
)
|
|
493
|
-
console.print()
|
|
494
|
-
|
|
495
|
-
exports_path = Path(exports_dir).resolve() if exports_dir else None
|
|
496
|
-
|
|
497
|
-
try:
|
|
498
|
-
with console.status(f"[cyan]Building {kind} prompt…[/]", spinner="dots"):
|
|
499
|
-
result = run_brief(
|
|
500
|
-
kind=kind,
|
|
501
|
-
project_dir=project_dir,
|
|
502
|
-
exports_dir=exports_path,
|
|
503
|
-
auto_fetch_artifacts=not no_fetch,
|
|
504
|
-
dry_run=dry_run,
|
|
505
|
-
)
|
|
506
|
-
except BriefError as exc:
|
|
507
|
-
console.print(
|
|
508
|
-
Panel.fit(
|
|
509
|
-
f"[red]{exc}[/]",
|
|
510
|
-
border_style="red",
|
|
511
|
-
padding=(0, 2),
|
|
512
|
-
)
|
|
513
|
-
)
|
|
514
|
-
raise SystemExit(1)
|
|
515
|
-
|
|
516
|
-
print_brief_result(result)
|
|
517
|
-
|
|
518
|
-
if dry_run:
|
|
519
|
-
console.print()
|
|
520
|
-
console.print(
|
|
521
|
-
Panel.fit(
|
|
522
|
-
"[yellow]Dry run:[/] the prompt and any changes zip above were "
|
|
523
|
-
"still written so you can review them, but the checkpoint was "
|
|
524
|
-
"[bold]not[/] advanced.\n"
|
|
525
|
-
"[dim]Run again without --dry-run once you're happy with this — "
|
|
526
|
-
"it'll pick up the same files.[/]",
|
|
527
|
-
border_style="yellow",
|
|
528
|
-
padding=(0, 2),
|
|
529
|
-
)
|
|
530
|
-
)
|
|
531
|
-
return
|
|
532
|
-
|
|
533
|
-
if not no_clipboard:
|
|
534
|
-
console.print()
|
|
535
|
-
with console.status("[cyan]Preparing clipboard…[/]", spinner="dots"):
|
|
536
|
-
cb = clipboard_handle_text(result.prompt_text, result.prompt_path)
|
|
537
|
-
print_clipboard_result(cb)
|
|
538
|
-
|
|
539
|
-
|
|
540
376
|
@main.command("config")
|
|
541
377
|
@click.option(
|
|
542
378
|
"--reset-key",
|
|
@@ -545,16 +381,17 @@ def _run_brief(
|
|
|
545
381
|
help="Clear the stored Gemini API key and re-run the setup prompt.",
|
|
546
382
|
)
|
|
547
383
|
@click.option(
|
|
548
|
-
"--set-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
help=
|
|
384
|
+
"--set-workspace",
|
|
385
|
+
default=None,
|
|
386
|
+
metavar="LOCATION",
|
|
387
|
+
help='Set your personal default for where .contextzip/ lives: "git-root", '
|
|
388
|
+
'"cwd", or a path.',
|
|
552
389
|
)
|
|
553
390
|
@click.option(
|
|
554
|
-
"--reset-
|
|
391
|
+
"--reset-workspace",
|
|
555
392
|
is_flag=True,
|
|
556
393
|
default=False,
|
|
557
|
-
help="Clear
|
|
394
|
+
help="Clear your personal workspace location override.",
|
|
558
395
|
)
|
|
559
396
|
@click.option(
|
|
560
397
|
"--show-key-path",
|
|
@@ -564,8 +401,8 @@ def _run_brief(
|
|
|
564
401
|
)
|
|
565
402
|
def cmd_config(
|
|
566
403
|
reset_key: bool,
|
|
567
|
-
|
|
568
|
-
|
|
404
|
+
set_workspace: str | None,
|
|
405
|
+
reset_workspace: bool,
|
|
569
406
|
show_key_path: bool,
|
|
570
407
|
) -> None:
|
|
571
408
|
"""
|
|
@@ -573,25 +410,45 @@ def cmd_config(
|
|
|
573
410
|
|
|
574
411
|
\b
|
|
575
412
|
EXAMPLES
|
|
576
|
-
contextzip config --reset-key
|
|
577
|
-
contextzip config --set-
|
|
578
|
-
contextzip config --
|
|
579
|
-
contextzip config --
|
|
413
|
+
contextzip config --reset-key # clear Gemini key, re-onboard
|
|
414
|
+
contextzip config --set-workspace cwd # always use ./.contextzip here
|
|
415
|
+
contextzip config --set-workspace git-root # back to the default
|
|
416
|
+
contextzip config --set-workspace ~/zips # a fixed custom location
|
|
417
|
+
contextzip config --reset-workspace # clear the personal override
|
|
418
|
+
contextzip config --show-key-path # print config file location
|
|
419
|
+
|
|
420
|
+
\b
|
|
421
|
+
A workspace location can also be pinned for the whole team by committing
|
|
422
|
+
a .contextzip.json file at the project root:
|
|
423
|
+
{"workspace_location": "git-root"}
|
|
424
|
+
Project config (if present) takes priority over this personal setting —
|
|
425
|
+
see the README for the full precedence order.
|
|
580
426
|
"""
|
|
581
427
|
if show_key_path:
|
|
582
428
|
console.print(f"\n [dim]Config file:[/] [cyan]{config_path()}[/]\n")
|
|
583
429
|
return
|
|
584
430
|
|
|
585
|
-
if
|
|
586
|
-
if
|
|
587
|
-
removed =
|
|
431
|
+
if set_workspace is not None or reset_workspace:
|
|
432
|
+
if reset_workspace:
|
|
433
|
+
removed = delete_workspace_location()
|
|
588
434
|
if removed:
|
|
589
435
|
console.print(
|
|
590
|
-
f"\n [green]✓[/]
|
|
436
|
+
f"\n [green]✓[/] Workspace override removed from "
|
|
437
|
+
f"[dim]{config_path()}[/]"
|
|
591
438
|
)
|
|
592
439
|
else:
|
|
593
|
-
console.print("\n [dim]No
|
|
594
|
-
|
|
440
|
+
console.print("\n [dim]No personal workspace override was set.[/]")
|
|
441
|
+
if set_workspace is None:
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
if set_workspace is not None:
|
|
445
|
+
save_workspace_location(set_workspace)
|
|
446
|
+
console.print(
|
|
447
|
+
f"\n [green]✓[/] Personal workspace location set to "
|
|
448
|
+
f"[cyan]{set_workspace}[/] in [dim]{config_path()}[/]\n"
|
|
449
|
+
f" [dim]A project-level .contextzip.json, if present, still "
|
|
450
|
+
f"takes priority over this.[/]"
|
|
451
|
+
)
|
|
595
452
|
return
|
|
596
453
|
|
|
597
454
|
if reset_key:
|
|
@@ -645,46 +502,25 @@ def cmd_config(
|
|
|
645
502
|
)
|
|
646
503
|
|
|
647
504
|
console.print()
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
"
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
console.print(
|
|
663
|
-
Panel(
|
|
664
|
-
f" [dim]Claude session key:[/] [green]{masked}[/] {source}\n\n"
|
|
665
|
-
" [dim]Used by [cyan]eod[/]/[cyan]handoff[/] for case-2 fetching. "
|
|
666
|
-
"Run [cyan]contextzip config --reset-session-key[/] to change it.[/]",
|
|
667
|
-
title="[bold]contextzip config[/]",
|
|
668
|
-
border_style="cyan",
|
|
669
|
-
padding=(0, 2),
|
|
670
|
-
)
|
|
671
|
-
)
|
|
672
|
-
else:
|
|
673
|
-
console.print(
|
|
674
|
-
Panel(
|
|
675
|
-
" No Claude session key configured.\n\n"
|
|
676
|
-
" [dim]Optional — only needed for eod/handoff's diverged-from-Claude "
|
|
677
|
-
"check.[/]\n"
|
|
678
|
-
" Run [cyan]contextzip config --set-session-key[/] to set one up.",
|
|
679
|
-
title="[bold]contextzip config[/]",
|
|
680
|
-
border_style="yellow",
|
|
681
|
-
padding=(0, 2),
|
|
682
|
-
)
|
|
505
|
+
|
|
506
|
+
from contextzip.packager import _resolve_workspace_location
|
|
507
|
+
|
|
508
|
+
ws_location, ws_source = _resolve_workspace_location(Path(os.getcwd()).resolve())
|
|
509
|
+
console.print(
|
|
510
|
+
Panel(
|
|
511
|
+
f" [dim]Workspace location:[/] [green]{ws_location}[/] [dim]({ws_source})[/]\n\n"
|
|
512
|
+
" [dim]Run [cyan]contextzip config --set-workspace <location>[/] to set "
|
|
513
|
+
"a personal default,\n"
|
|
514
|
+
" or commit a [cyan].contextzip.json[/] at the project root to share "
|
|
515
|
+
"one with your team.[/]",
|
|
516
|
+
title="[bold]contextzip config[/]",
|
|
517
|
+
border_style="cyan",
|
|
518
|
+
padding=(0, 2),
|
|
683
519
|
)
|
|
520
|
+
)
|
|
684
521
|
console.print()
|
|
685
522
|
|
|
686
523
|
|
|
687
|
-
# ---------------------------------------------------------------------------
|
|
688
524
|
# Core execution logic (shared by main command + all subcommands)
|
|
689
525
|
# ---------------------------------------------------------------------------
|
|
690
526
|
|