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.
Files changed (24) hide show
  1. {contextzip-0.2.1 → contextzip-0.2.2}/PKG-INFO +21 -4
  2. {contextzip-0.2.1 → contextzip-0.2.2}/README.md +20 -3
  3. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/__init__.py +1 -1
  4. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/cli.py +254 -55
  5. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/PKG-INFO +21 -4
  6. {contextzip-0.2.1 → contextzip-0.2.2}/pyproject.toml +1 -1
  7. {contextzip-0.2.1 → contextzip-0.2.2}/LICENSE +0 -0
  8. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/clipboard.py +0 -0
  9. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/detector.py +0 -0
  10. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/filters.py +0 -0
  11. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/git.py +0 -0
  12. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/packager.py +0 -0
  13. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/__init__.py +0 -0
  14. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/base.py +0 -0
  15. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/go.py +0 -0
  16. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/node.py +0 -0
  17. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/python.py +0 -0
  18. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip/rules/rust.py +0 -0
  19. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/SOURCES.txt +0 -0
  20. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/dependency_links.txt +0 -0
  21. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/entry_points.txt +0 -0
  22. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/requires.txt +0 -0
  23. {contextzip-0.2.1 → contextzip-0.2.2}/contextzip.egg-info/top_level.txt +0 -0
  24. {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.1
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). Space-separated or repeatable: `-e '*.log' file1 file2` or `-e '*.log' -e file1` |
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 -e "*.log" "*.sqlite" "tests/"
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). Space-separated or repeatable: `-e '*.log' file1 file2` or `-e '*.log' -e file1` |
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 -e "*.log" "*.sqlite" "tests/"
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,3 +1,3 @@
1
1
  """contextzip — intelligent codebase packager for AI tools."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
@@ -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
- # CLI
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
- @click.command(context_settings={"help_option_names": ["-h", "--help"]})
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="Only include files under these paths (relative to project root). "
41
- "Repeatable: --include src --include app",
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="Extra exclusion patterns on top of auto-rules (gitignore syntax). "
47
- "Space-separated or repeatable: -e '*.log' file1 file2 OR -e '*.log' -e file1",
48
- )
49
- @click.option(
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=list(include) if include else None,
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
- f" [dim] Use --exclude to drop them if unneeded.[/]"
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]--include[/] to override."
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.1
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). Space-separated or repeatable: `-e '*.log' file1 file2` or `-e '*.log' -e file1` |
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 -e "*.log" "*.sqlite" "tests/"
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "contextzip"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Intelligently package your codebase for AI tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes
File without changes
File without changes