contextzip 0.2.1__tar.gz → 0.2.2__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.2.1 → contextzip-0.2.2}/PKG-INFO +21 -4
- {contextzip-0.2.1 → contextzip-0.2.2}/README.md +20 -3
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/__init__.py +1 -1
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/cli.py +254 -55
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/PKG-INFO +21 -4
- {contextzip-0.2.1 → contextzip-0.2.2}/pyproject.toml +1 -1
- {contextzip-0.2.1 → contextzip-0.2.2}/LICENSE +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/clipboard.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/detector.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/filters.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/git.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/packager.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/__init__.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/base.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/go.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/node.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/python.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/rust.py +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/SOURCES.txt +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/dependency_links.txt +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/entry_points.txt +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/requires.txt +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/top_level.txt +0 -0
- {contextzip-0.2.1 → contextzip-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextzip
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Intelligently package your codebase for AI tools
|
|
5
5
|
Author-email: Deepesh <akadeepesh@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -111,7 +111,9 @@ contextzip [OPTIONS]
|
|
|
111
111
|
| Option | Description |
|
|
112
112
|
|---|---|
|
|
113
113
|
| `-i`, `--include PATH` | Only include files under this path. Repeatable. |
|
|
114
|
-
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax).
|
|
114
|
+
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax). Repeatable. |
|
|
115
|
+
| `exclude` | Subcommand: exclude specific files/patterns. `contextzip exclude CHANGELOG.md LICENSE .github/` |
|
|
116
|
+
| `include` | Subcommand: include only specific paths. `contextzip include src/ app/` |
|
|
115
117
|
| `--git-changes` | Only include files reported by git as modified, staged, or untracked. |
|
|
116
118
|
| `-n`, `--dry-run` | Preview what would be included, no ZIP created. |
|
|
117
119
|
| `-o`, `--output FILE` | Custom output path for the ZIP file. |
|
|
@@ -137,7 +139,22 @@ contextzip --include src --include app
|
|
|
137
139
|
|
|
138
140
|
**Exclude additional patterns beyond the auto-rules:**
|
|
139
141
|
```bash
|
|
140
|
-
contextzip
|
|
142
|
+
contextzip --exclude "*.log" --exclude "*.sqlite" --exclude "tests/"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Exclude files using the subcommand (space-separated, no repetition):**
|
|
146
|
+
```bash
|
|
147
|
+
contextzip exclude CHANGELOG.md CONTRIBUTING.md LICENSE .github/
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Exclude with flags:**
|
|
151
|
+
```bash
|
|
152
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Include only specific directories using the subcommand:**
|
|
156
|
+
```bash
|
|
157
|
+
contextzip include src/ app/
|
|
141
158
|
```
|
|
142
159
|
|
|
143
160
|
**Only package files changed in git:**
|
|
@@ -234,7 +251,7 @@ contextzip surfaces issues before they waste your time:
|
|
|
234
251
|
contextzip/
|
|
235
252
|
├── contextzip/
|
|
236
253
|
│ ├── __init__.py # version string
|
|
237
|
-
│ ├── cli.py # Click entry point, all flags, rich output
|
|
254
|
+
│ ├── cli.py # Click entry point, all flags, subcommands (exclude, include), rich output
|
|
238
255
|
│ ├── detector.py # framework/language detection engine
|
|
239
256
|
│ ├── filters.py # pathspec-based file filtering + ResolveResult
|
|
240
257
|
│ ├── git.py # git status parsing + changed-file detection
|
|
@@ -83,7 +83,9 @@ contextzip [OPTIONS]
|
|
|
83
83
|
| Option | Description |
|
|
84
84
|
|---|---|
|
|
85
85
|
| `-i`, `--include PATH` | Only include files under this path. Repeatable. |
|
|
86
|
-
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax).
|
|
86
|
+
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax). Repeatable. |
|
|
87
|
+
| `exclude` | Subcommand: exclude specific files/patterns. `contextzip exclude CHANGELOG.md LICENSE .github/` |
|
|
88
|
+
| `include` | Subcommand: include only specific paths. `contextzip include src/ app/` |
|
|
87
89
|
| `--git-changes` | Only include files reported by git as modified, staged, or untracked. |
|
|
88
90
|
| `-n`, `--dry-run` | Preview what would be included, no ZIP created. |
|
|
89
91
|
| `-o`, `--output FILE` | Custom output path for the ZIP file. |
|
|
@@ -109,7 +111,22 @@ contextzip --include src --include app
|
|
|
109
111
|
|
|
110
112
|
**Exclude additional patterns beyond the auto-rules:**
|
|
111
113
|
```bash
|
|
112
|
-
contextzip
|
|
114
|
+
contextzip --exclude "*.log" --exclude "*.sqlite" --exclude "tests/"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Exclude files using the subcommand (space-separated, no repetition):**
|
|
118
|
+
```bash
|
|
119
|
+
contextzip exclude CHANGELOG.md CONTRIBUTING.md LICENSE .github/
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Exclude with flags:**
|
|
123
|
+
```bash
|
|
124
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Include only specific directories using the subcommand:**
|
|
128
|
+
```bash
|
|
129
|
+
contextzip include src/ app/
|
|
113
130
|
```
|
|
114
131
|
|
|
115
132
|
**Only package files changed in git:**
|
|
@@ -206,7 +223,7 @@ contextzip surfaces issues before they waste your time:
|
|
|
206
223
|
contextzip/
|
|
207
224
|
├── contextzip/
|
|
208
225
|
│ ├── __init__.py # version string
|
|
209
|
-
│ ├── cli.py # Click entry point, all flags, rich output
|
|
226
|
+
│ ├── cli.py # Click entry point, all flags, subcommands (exclude, include), rich output
|
|
210
227
|
│ ├── detector.py # framework/language detection engine
|
|
211
228
|
│ ├── filters.py # pathspec-based file filtering + ResolveResult
|
|
212
229
|
│ ├── git.py # git status parsing + changed-file detection
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
"""
|
|
2
2
|
cli.py — contextzip entry point. Phases 1–5 complete.
|
|
3
|
+
|
|
4
|
+
Command surface
|
|
5
|
+
───────────────
|
|
6
|
+
Main command (unchanged, fully backwards-compatible):
|
|
7
|
+
contextzip [OPTIONS]
|
|
8
|
+
-i / --include PATH only include files under these paths (repeatable)
|
|
9
|
+
-e / --exclude PATTERN extra exclusion patterns (repeatable)
|
|
10
|
+
-n / --dry-run preview without creating ZIP
|
|
11
|
+
-o / --output FILE custom output path
|
|
12
|
+
--no-clipboard skip clipboard / folder-open step
|
|
13
|
+
--no-gitignore ignore project .gitignore
|
|
14
|
+
--git-changes only package files changed in git
|
|
15
|
+
-v / --verbose show every file decision
|
|
16
|
+
|
|
17
|
+
Subcommands (new — all modifier flags available on each):
|
|
18
|
+
contextzip exclude PATTERN… [OPTIONS]
|
|
19
|
+
contextzip include PATH… [OPTIONS]
|
|
20
|
+
|
|
21
|
+
Both subcommands accept the same modifier flags as the main command so that
|
|
22
|
+
the flags naturally follow the verb, matching the git / docker / cargo UX:
|
|
23
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
24
|
+
contextzip include src/ app/ --output ~/Desktop/out.zip
|
|
3
25
|
"""
|
|
4
26
|
|
|
5
27
|
from __future__ import annotations
|
|
@@ -30,55 +52,88 @@ console = Console()
|
|
|
30
52
|
|
|
31
53
|
|
|
32
54
|
# ---------------------------------------------------------------------------
|
|
33
|
-
#
|
|
55
|
+
# Shared modifier-flag decorator
|
|
34
56
|
# ---------------------------------------------------------------------------
|
|
57
|
+
# Defined once so the main command and every subcommand declare exactly the
|
|
58
|
+
# same flags without duplicating help strings.
|
|
59
|
+
|
|
60
|
+
def _modifier_options(f):
|
|
61
|
+
"""
|
|
62
|
+
Attach all run-modifier flags to a command.
|
|
63
|
+
|
|
64
|
+
Applied to both the main command and every subcommand so that flags
|
|
65
|
+
always follow the verb — matching the git / docker / cargo convention:
|
|
66
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
67
|
+
"""
|
|
68
|
+
decorators = [
|
|
69
|
+
click.option(
|
|
70
|
+
"--dry-run", "-n",
|
|
71
|
+
is_flag=True, default=False,
|
|
72
|
+
help="Show what would be included without creating the ZIP.",
|
|
73
|
+
),
|
|
74
|
+
click.option(
|
|
75
|
+
"--output", "-o",
|
|
76
|
+
default=None, metavar="FILE",
|
|
77
|
+
help="Output ZIP path. Defaults to <project>_context_<timestamp>.zip in temp dir.",
|
|
78
|
+
),
|
|
79
|
+
click.option(
|
|
80
|
+
"--no-clipboard",
|
|
81
|
+
is_flag=True, default=False,
|
|
82
|
+
help="Skip clipboard / folder-open step after creating the ZIP.",
|
|
83
|
+
),
|
|
84
|
+
click.option(
|
|
85
|
+
"--no-gitignore",
|
|
86
|
+
is_flag=True, default=False,
|
|
87
|
+
help="Ignore the project's .gitignore file (use only built-in rules).",
|
|
88
|
+
),
|
|
89
|
+
click.option(
|
|
90
|
+
"--git-changes",
|
|
91
|
+
is_flag=True, default=False,
|
|
92
|
+
help=(
|
|
93
|
+
"Only include files that git reports as modified, added, or untracked. "
|
|
94
|
+
"Requires the project to be inside a git repository."
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
click.option(
|
|
98
|
+
"--verbose", "-v",
|
|
99
|
+
is_flag=True, default=False,
|
|
100
|
+
help="Show every included and excluded file.",
|
|
101
|
+
),
|
|
102
|
+
]
|
|
103
|
+
for dec in reversed(decorators):
|
|
104
|
+
f = dec(f)
|
|
105
|
+
return f
|
|
106
|
+
|
|
35
107
|
|
|
36
|
-
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# CLI group
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
@click.group(
|
|
113
|
+
invoke_without_command=True,
|
|
114
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
115
|
+
)
|
|
37
116
|
@click.option(
|
|
38
117
|
"--include", "-i",
|
|
39
118
|
multiple=True, metavar="PATH",
|
|
40
|
-
help=
|
|
41
|
-
|
|
119
|
+
help=(
|
|
120
|
+
"Only include files under these paths (relative to project root). "
|
|
121
|
+
"Repeatable: --include src --include app | or use: contextzip include src app"
|
|
122
|
+
),
|
|
42
123
|
)
|
|
43
124
|
@click.option(
|
|
44
125
|
"--exclude", "-e",
|
|
45
|
-
multiple=True, metavar="PATTERN
|
|
46
|
-
help=
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"--dry-run", "-n",
|
|
51
|
-
is_flag=True, default=False,
|
|
52
|
-
help="Show what would be included without creating the ZIP.",
|
|
53
|
-
)
|
|
54
|
-
@click.option(
|
|
55
|
-
"--output", "-o",
|
|
56
|
-
default=None, metavar="FILE",
|
|
57
|
-
help="Output ZIP path. Defaults to <project>_context_<timestamp>.zip in temp dir.",
|
|
58
|
-
)
|
|
59
|
-
@click.option(
|
|
60
|
-
"--no-clipboard",
|
|
61
|
-
is_flag=True, default=False,
|
|
62
|
-
help="Skip clipboard / folder-open step after creating the ZIP.",
|
|
63
|
-
)
|
|
64
|
-
@click.option(
|
|
65
|
-
"--no-gitignore",
|
|
66
|
-
is_flag=True, default=False,
|
|
67
|
-
help="Ignore the project's .gitignore file (use only built-in rules).",
|
|
68
|
-
)
|
|
69
|
-
@click.option(
|
|
70
|
-
"--git-changes",
|
|
71
|
-
is_flag=True, default=False,
|
|
72
|
-
help="Only include files that git reports as modified, added, or untracked. "
|
|
73
|
-
"Requires the project to be inside a git repository.",
|
|
74
|
-
)
|
|
75
|
-
@click.option(
|
|
76
|
-
"--verbose", "-v",
|
|
77
|
-
is_flag=True, default=False,
|
|
78
|
-
help="Show every included and excluded file.",
|
|
126
|
+
multiple=True, metavar="PATTERN",
|
|
127
|
+
help=(
|
|
128
|
+
"Extra exclusion patterns on top of auto-rules (gitignore syntax). "
|
|
129
|
+
"Repeatable: -e '*.log' -e CHANGELOG.md | or use: contextzip exclude CHANGELOG.md *.log"
|
|
130
|
+
),
|
|
79
131
|
)
|
|
132
|
+
@_modifier_options
|
|
80
133
|
@click.version_option(version=__version__, prog_name="contextzip")
|
|
134
|
+
@click.pass_context
|
|
81
135
|
def main(
|
|
136
|
+
ctx: click.Context,
|
|
82
137
|
include: tuple[str, ...],
|
|
83
138
|
exclude: tuple[str, ...],
|
|
84
139
|
dry_run: bool,
|
|
@@ -94,8 +149,132 @@ def main(
|
|
|
94
149
|
|
|
95
150
|
Run from your project root to produce a smart, lightweight ZIP
|
|
96
151
|
ready to paste directly into Claude, ChatGPT, or any AI interface.
|
|
152
|
+
|
|
153
|
+
\b
|
|
154
|
+
SUBCOMMANDS
|
|
155
|
+
contextzip exclude CHANGELOG.md LICENSE .github/
|
|
156
|
+
contextzip include src/ app/
|
|
157
|
+
|
|
158
|
+
Both subcommands accept the same flags as the main command:
|
|
159
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
160
|
+
"""
|
|
161
|
+
# A subcommand was invoked — let it handle everything.
|
|
162
|
+
if ctx.invoked_subcommand is not None:
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
_run(
|
|
166
|
+
extra_exclude=list(exclude),
|
|
167
|
+
include_only=list(include),
|
|
168
|
+
dry_run=dry_run,
|
|
169
|
+
output=output,
|
|
170
|
+
no_clipboard=no_clipboard,
|
|
171
|
+
no_gitignore=no_gitignore,
|
|
172
|
+
git_changes=git_changes,
|
|
173
|
+
verbose=verbose,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# Subcommand: exclude
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
@main.command("exclude")
|
|
182
|
+
@click.argument("patterns", nargs=-1, required=True, metavar="PATTERN…")
|
|
183
|
+
@_modifier_options
|
|
184
|
+
def cmd_exclude(
|
|
185
|
+
patterns: tuple[str, ...],
|
|
186
|
+
dry_run: bool,
|
|
187
|
+
output: str | None,
|
|
188
|
+
no_clipboard: bool,
|
|
189
|
+
no_gitignore: bool,
|
|
190
|
+
git_changes: bool,
|
|
191
|
+
verbose: bool,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Exclude specific files or patterns and package everything else.
|
|
195
|
+
|
|
196
|
+
\b
|
|
197
|
+
EXAMPLES
|
|
198
|
+
contextzip exclude CHANGELOG.md CONTRIBUTING.md LICENSE
|
|
199
|
+
contextzip exclude .github/ tests/ '*.log'
|
|
200
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
201
|
+
contextzip exclude .github/ CHANGELOG.md --output ~/Desktop/out.zip
|
|
202
|
+
|
|
203
|
+
Patterns follow gitignore syntax. Folders are matched with or without
|
|
204
|
+
a trailing slash: both '.github' and '.github/' work.
|
|
205
|
+
"""
|
|
206
|
+
_run(
|
|
207
|
+
extra_exclude=list(patterns),
|
|
208
|
+
include_only=None,
|
|
209
|
+
dry_run=dry_run,
|
|
210
|
+
output=output,
|
|
211
|
+
no_clipboard=no_clipboard,
|
|
212
|
+
no_gitignore=no_gitignore,
|
|
213
|
+
git_changes=git_changes,
|
|
214
|
+
verbose=verbose,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
# Subcommand: include
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
@main.command("include")
|
|
223
|
+
@click.argument("paths", nargs=-1, required=True, metavar="PATH…")
|
|
224
|
+
@_modifier_options
|
|
225
|
+
def cmd_include(
|
|
226
|
+
paths: tuple[str, ...],
|
|
227
|
+
dry_run: bool,
|
|
228
|
+
output: str | None,
|
|
229
|
+
no_clipboard: bool,
|
|
230
|
+
no_gitignore: bool,
|
|
231
|
+
git_changes: bool,
|
|
232
|
+
verbose: bool,
|
|
233
|
+
) -> None:
|
|
97
234
|
"""
|
|
235
|
+
Package only the specified paths and skip everything else.
|
|
236
|
+
|
|
237
|
+
\b
|
|
238
|
+
EXAMPLES
|
|
239
|
+
contextzip include src/ app/
|
|
240
|
+
contextzip include src/ app/ --dry-run
|
|
241
|
+
contextzip include src/ --output ~/Desktop/out.zip --verbose
|
|
98
242
|
|
|
243
|
+
Paths are matched as exact prefixes at directory boundaries:
|
|
244
|
+
'src' matches 'src/index.ts' but not 'src2/index.ts'.
|
|
245
|
+
"""
|
|
246
|
+
_run(
|
|
247
|
+
extra_exclude=None,
|
|
248
|
+
include_only=list(paths),
|
|
249
|
+
dry_run=dry_run,
|
|
250
|
+
output=output,
|
|
251
|
+
no_clipboard=no_clipboard,
|
|
252
|
+
no_gitignore=no_gitignore,
|
|
253
|
+
git_changes=git_changes,
|
|
254
|
+
verbose=verbose,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# ---------------------------------------------------------------------------
|
|
259
|
+
# Core execution logic (shared by main command + all subcommands)
|
|
260
|
+
# ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
def _run(
|
|
263
|
+
*,
|
|
264
|
+
extra_exclude: list[str] | None,
|
|
265
|
+
include_only: list[str] | None,
|
|
266
|
+
dry_run: bool,
|
|
267
|
+
output: str | None,
|
|
268
|
+
no_clipboard: bool,
|
|
269
|
+
no_gitignore: bool,
|
|
270
|
+
git_changes: bool,
|
|
271
|
+
verbose: bool,
|
|
272
|
+
) -> None:
|
|
273
|
+
"""
|
|
274
|
+
All actual work lives here. The main command and every subcommand
|
|
275
|
+
delegate to this function after collecting their arguments/flags,
|
|
276
|
+
keeping the CLI surface thin and the logic testable in isolation.
|
|
277
|
+
"""
|
|
99
278
|
project_dir = Path(os.getcwd()).resolve()
|
|
100
279
|
|
|
101
280
|
# ── Header ───────────────────────────────────────────────────────────────
|
|
@@ -153,20 +332,12 @@ def main(
|
|
|
153
332
|
and gitignore_path.is_file()
|
|
154
333
|
)
|
|
155
334
|
|
|
335
|
+
normalized_exclude = (
|
|
336
|
+
[_normalize_pattern(p) for p in extra_exclude]
|
|
337
|
+
if extra_exclude else []
|
|
338
|
+
)
|
|
339
|
+
|
|
156
340
|
with console.status("[cyan]Building exclusion rules…[/]", spinner="dots"):
|
|
157
|
-
# Flatten: each -e invocation may itself contain space-separated tokens
|
|
158
|
-
raw_exclude = exclude
|
|
159
|
-
flat_exclude: list[str] = []
|
|
160
|
-
for token in raw_exclude:
|
|
161
|
-
flat_exclude.extend(token.split())
|
|
162
|
-
|
|
163
|
-
# Normalize Windows-style paths (.\foo\bar → foo/bar)
|
|
164
|
-
def _normalize_pattern(p: str) -> str:
|
|
165
|
-
p = p.lstrip(".\\/") # strip leading .\ or ./
|
|
166
|
-
p = p.replace("\\", "/") # backslash → forward slash
|
|
167
|
-
return p
|
|
168
|
-
|
|
169
|
-
normalized_exclude = [_normalize_pattern(p) for p in flat_exclude]
|
|
170
341
|
spec = build_spec(
|
|
171
342
|
rule_modules=detection.rule_modules,
|
|
172
343
|
extra_exclude=normalized_exclude if normalized_exclude else None,
|
|
@@ -182,7 +353,7 @@ def main(
|
|
|
182
353
|
resolved = resolve_files(
|
|
183
354
|
project_dir=project_dir,
|
|
184
355
|
spec=spec,
|
|
185
|
-
include_only=
|
|
356
|
+
include_only=include_only if include_only else None,
|
|
186
357
|
)
|
|
187
358
|
|
|
188
359
|
# ── File scan summary ────────────────────────────────────────────────────
|
|
@@ -202,7 +373,8 @@ def main(
|
|
|
202
373
|
if len(resolved.large_files) > 5:
|
|
203
374
|
console.print(f" [dim]… and {len(resolved.large_files) - 5} more[/]")
|
|
204
375
|
console.print(
|
|
205
|
-
|
|
376
|
+
" [dim] Use [cyan]-e PATTERN[/] or [cyan]contextzip exclude PATTERN[/] "
|
|
377
|
+
"to drop them if unneeded.[/]"
|
|
206
378
|
)
|
|
207
379
|
|
|
208
380
|
# ── Warnings: binary files ───────────────────────────────────────────────
|
|
@@ -245,7 +417,7 @@ def main(
|
|
|
245
417
|
if not resolved.included:
|
|
246
418
|
console.print(
|
|
247
419
|
"\n[red]Nothing to package.[/] All files were excluded — "
|
|
248
|
-
"try [cyan]
|
|
420
|
+
"try [cyan]contextzip include PATH[/] or [cyan]-i PATH[/] to override."
|
|
249
421
|
)
|
|
250
422
|
return
|
|
251
423
|
|
|
@@ -285,6 +457,33 @@ def main(
|
|
|
285
457
|
_print_clipboard_result(cb)
|
|
286
458
|
|
|
287
459
|
|
|
460
|
+
# ---------------------------------------------------------------------------
|
|
461
|
+
# Normalisation
|
|
462
|
+
# ---------------------------------------------------------------------------
|
|
463
|
+
|
|
464
|
+
def _normalize_pattern(p: str) -> str:
|
|
465
|
+
"""
|
|
466
|
+
Canonicalise a user-supplied exclusion pattern.
|
|
467
|
+
|
|
468
|
+
Rules applied in order:
|
|
469
|
+
1. Strip a leading ``./`` or ``.\\`` so that ``./CHANGELOG.md``
|
|
470
|
+
and ``CHANGELOG.md`` are treated identically.
|
|
471
|
+
2. Replace every backslash with a forward slash for cross-platform
|
|
472
|
+
consistency (Windows paths entered on the CLI).
|
|
473
|
+
3. Collapse ``folder/*`` → ``folder/`` so that gitignore-style
|
|
474
|
+
directory globs work as expected.
|
|
475
|
+
"""
|
|
476
|
+
# 1. Strip leading ./ or .\
|
|
477
|
+
if p.startswith("./") or p.startswith(".\\"):
|
|
478
|
+
p = p[2:]
|
|
479
|
+
# 2. Normalise path separators
|
|
480
|
+
p = p.replace("\\", "/")
|
|
481
|
+
# 3. folder/* → folder/
|
|
482
|
+
if p.endswith("/*"):
|
|
483
|
+
p = p[:-1]
|
|
484
|
+
return p
|
|
485
|
+
|
|
486
|
+
|
|
288
487
|
# ---------------------------------------------------------------------------
|
|
289
488
|
# Display helpers
|
|
290
489
|
# ---------------------------------------------------------------------------
|
|
@@ -449,4 +648,4 @@ def _human_size(n: int) -> str:
|
|
|
449
648
|
if n < 1024:
|
|
450
649
|
return f"{n:.0f} {unit}"
|
|
451
650
|
n /= 1024
|
|
452
|
-
return f"{n:.1f} TB"
|
|
651
|
+
return f"{n:.1f} TB"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextzip
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Intelligently package your codebase for AI tools
|
|
5
5
|
Author-email: Deepesh <akadeepesh@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -111,7 +111,9 @@ contextzip [OPTIONS]
|
|
|
111
111
|
| Option | Description |
|
|
112
112
|
|---|---|
|
|
113
113
|
| `-i`, `--include PATH` | Only include files under this path. Repeatable. |
|
|
114
|
-
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax).
|
|
114
|
+
| `-e`, `--exclude PATTERN` | Extra exclusion patterns (gitignore syntax). Repeatable. |
|
|
115
|
+
| `exclude` | Subcommand: exclude specific files/patterns. `contextzip exclude CHANGELOG.md LICENSE .github/` |
|
|
116
|
+
| `include` | Subcommand: include only specific paths. `contextzip include src/ app/` |
|
|
115
117
|
| `--git-changes` | Only include files reported by git as modified, staged, or untracked. |
|
|
116
118
|
| `-n`, `--dry-run` | Preview what would be included, no ZIP created. |
|
|
117
119
|
| `-o`, `--output FILE` | Custom output path for the ZIP file. |
|
|
@@ -137,7 +139,22 @@ contextzip --include src --include app
|
|
|
137
139
|
|
|
138
140
|
**Exclude additional patterns beyond the auto-rules:**
|
|
139
141
|
```bash
|
|
140
|
-
contextzip
|
|
142
|
+
contextzip --exclude "*.log" --exclude "*.sqlite" --exclude "tests/"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Exclude files using the subcommand (space-separated, no repetition):**
|
|
146
|
+
```bash
|
|
147
|
+
contextzip exclude CHANGELOG.md CONTRIBUTING.md LICENSE .github/
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Exclude with flags:**
|
|
151
|
+
```bash
|
|
152
|
+
contextzip exclude CHANGELOG.md --dry-run --verbose
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Include only specific directories using the subcommand:**
|
|
156
|
+
```bash
|
|
157
|
+
contextzip include src/ app/
|
|
141
158
|
```
|
|
142
159
|
|
|
143
160
|
**Only package files changed in git:**
|
|
@@ -234,7 +251,7 @@ contextzip surfaces issues before they waste your time:
|
|
|
234
251
|
contextzip/
|
|
235
252
|
├── contextzip/
|
|
236
253
|
│ ├── __init__.py # version string
|
|
237
|
-
│ ├── cli.py # Click entry point, all flags, rich output
|
|
254
|
+
│ ├── cli.py # Click entry point, all flags, subcommands (exclude, include), rich output
|
|
238
255
|
│ ├── detector.py # framework/language detection engine
|
|
239
256
|
│ ├── filters.py # pathspec-based file filtering + ResolveResult
|
|
240
257
|
│ ├── git.py # git status parsing + changed-file detection
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|