RepoPackPy 0.1.7__tar.gz → 0.1.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RepoPackPy
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Pack/unpack any source workspace into portable JSON, with MCP server support.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "RepoPackPy"
7
- version = "0.1.7"
7
+ version = "0.1.8"
8
8
  description = "Pack/unpack any source workspace into portable JSON, with MCP server support."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1 @@
1
+ __version__ = "0.1.8"
@@ -60,14 +60,22 @@ def pack(
60
60
  output: str = typer.Option(None, "-o", "--output", help="Output JSON file path."),
61
61
  include_binary: bool = typer.Option(False, "--include-binary", help="Include binary files."),
62
62
  custom_ignore: str = typer.Option(None, "--custom-ignore", help="Comma-separated extra ignore patterns."),
63
+ dry_run: bool = typer.Option(False, "--dry-run", help="Report files that would be packed without writing output."),
63
64
  ):
64
65
  try:
65
- result = pack_workspace(Path(directory), include_binary, custom_ignore)
66
+ result = pack_workspace(Path(directory), include_binary, custom_ignore, dry_run=dry_run)
67
+ if dry_run:
68
+ typer.echo(f"Dry run — would pack {result['total_files']} files ({result['total_bytes']} bytes)")
69
+ typer.echo(f"Root : {result['root_directory']}")
70
+ typer.echo(f"Stack : {', '.join(result['detected_stack']) or 'unknown'}")
71
+ for f in result["files"]:
72
+ typer.echo(f" {f['encoding']:6} {f['size']:>10} {f['path']}")
73
+ return
66
74
  if output:
67
75
  with open(output, "w", encoding="utf-8") as f:
68
76
  json.dump(result, f, indent=2)
69
- total_files = result.get("total_files", 0)
70
- total_bytes = result.get("total_bytes", 0)
77
+ total_files = result.get("metadata", {}).get("total_files", 0)
78
+ total_bytes = result.get("metadata", {}).get("total_bytes", 0)
71
79
  typer.echo(f"Packed {total_files} files ({total_bytes} bytes) -> {output}", err=True)
72
80
  else:
73
81
  print(json.dumps(result, indent=2))
@@ -81,9 +89,17 @@ def unpack(
81
89
  input: str = typer.Argument(..., help="File path or raw JSON string to unpack."),
82
90
  target: str = typer.Option(".", "-t", "--target", help="Output directory."),
83
91
  force: bool = typer.Option(False, "--force/--no-force", help="Overwrite existing files."),
92
+ dry_run: bool = typer.Option(False, "--dry-run", help="Report files that would be unpacked without writing anything."),
84
93
  ):
85
94
  try:
86
- result = unpack_workspace(input, Path(target), overwrite=force)
95
+ result = unpack_workspace(input, Path(target), overwrite=force, dry_run=dry_run)
96
+ if dry_run:
97
+ typer.echo(f"Dry run — destination: {result['destination']}")
98
+ typer.echo(f" Would extract : {result['would_extract']}")
99
+ typer.echo(f" Would skip : {result['would_skip']}")
100
+ for f in result["files"]:
101
+ typer.echo(f" {f['action']:20} {f['path']}")
102
+ return
87
103
  extracted = result.get("extracted", 0)
88
104
  destination = result.get("destination", target)
89
105
  skipped = result.get("skipped", 0)
@@ -14,8 +14,16 @@ def export_workspace(
14
14
  workspace_path: str = ".",
15
15
  output_json_path: str | None = None,
16
16
  include_binary: bool = False,
17
+ dry_run: bool = False,
17
18
  ) -> str:
18
- result = pack_workspace(Path(workspace_path), include_binary=include_binary)
19
+ result = pack_workspace(Path(workspace_path), include_binary=include_binary, dry_run=dry_run)
20
+ if dry_run:
21
+ lines = [
22
+ f"Dry run — would pack {result['total_files']} files ({result['total_bytes']} bytes)",
23
+ f"Root : {result['root_directory']}",
24
+ f"Stack : {', '.join(result['detected_stack']) or 'unknown'}",
25
+ ] + [f" {f['encoding']:6} {f['size']:>10} {f['path']}" for f in result["files"]]
26
+ return "\n".join(lines)
19
27
  if output_json_path is not None:
20
28
  with open(output_json_path, "w", encoding="utf-8") as f:
21
29
  json.dump(result, f, indent=2)
@@ -28,8 +36,16 @@ def import_workspace(
28
36
  json_input: str,
29
37
  destination_path: str,
30
38
  overwrite: bool = False,
39
+ dry_run: bool = False,
31
40
  ) -> str:
32
- summary = unpack_workspace(json_input, Path(destination_path), overwrite=overwrite)
41
+ summary = unpack_workspace(json_input, Path(destination_path), overwrite=overwrite, dry_run=dry_run)
42
+ if dry_run:
43
+ lines = [
44
+ f"Dry run — destination: {summary['destination']}",
45
+ f" Would extract : {summary['would_extract']}",
46
+ f" Would skip : {summary['would_skip']}",
47
+ ] + [f" {f['action']:20} {f['path']}" for f in summary["files"]]
48
+ return "\n".join(lines)
33
49
  return f"Extracted {summary['extracted']} files to {summary['destination']} ({summary['skipped']} skipped)"
34
50
 
35
51
 
@@ -13,6 +13,7 @@ def pack_workspace(
13
13
  directory: Path,
14
14
  include_binary: bool = False,
15
15
  custom_ignore: str | None = None,
16
+ dry_run: bool = False,
16
17
  ) -> dict:
17
18
  directory = directory.resolve()
18
19
 
@@ -58,21 +59,25 @@ def pack_workspace(
58
59
  if binary and not include_binary:
59
60
  continue
60
61
 
62
+ encoding = "base64" if binary else "utf-8"
63
+ total_bytes += file_size
64
+
65
+ if dry_run:
66
+ files.append({"path": rel_path.as_posix(), "encoding": encoding, "size": file_size})
67
+ continue
68
+
61
69
  if binary:
62
70
  try:
63
71
  raw = file_path.read_bytes()
64
72
  except OSError:
65
73
  continue
66
74
  content = base64.b64encode(raw).decode("ascii")
67
- encoding = "base64"
68
75
  else:
69
76
  try:
70
77
  content = file_path.read_text(encoding="utf-8", errors="replace")
71
78
  except OSError:
72
79
  continue
73
- encoding = "utf-8"
74
80
 
75
- total_bytes += file_size
76
81
  files.append(
77
82
  {
78
83
  "path": rel_path.as_posix(),
@@ -83,6 +88,16 @@ def pack_workspace(
83
88
 
84
89
  detected_stack = detect_stack(directory)
85
90
 
91
+ if dry_run:
92
+ return {
93
+ "dry_run": True,
94
+ "root_directory": str(directory),
95
+ "detected_stack": detected_stack,
96
+ "total_files": len(files),
97
+ "total_bytes": total_bytes,
98
+ "files": files,
99
+ }
100
+
86
101
  return {
87
102
  "version": "1.0",
88
103
  "metadata": {
@@ -11,6 +11,7 @@ def unpack_workspace(
11
11
  destination_path: Path,
12
12
  overwrite: bool = False,
13
13
  force: bool = False,
14
+ dry_run: bool = False,
14
15
  ) -> dict:
15
16
  if Path(json_input).is_file():
16
17
  with open(json_input, "r", encoding="utf-8") as f:
@@ -25,6 +26,7 @@ def unpack_workspace(
25
26
 
26
27
  extracted = 0
27
28
  skipped = 0
29
+ dry_run_files: list[dict] = []
28
30
 
29
31
  for entry in data["files"]:
30
32
  rel = entry.get("path", "")
@@ -40,8 +42,20 @@ def unpack_workspace(
40
42
  raise ValueError(f"Path traversal detected: {rel}")
41
43
 
42
44
  output_path = destination_path / rel_path
45
+ would_skip = output_path.exists() and not overwrite
46
+
47
+ if dry_run:
48
+ dry_run_files.append({
49
+ "path": rel,
50
+ "action": "skip (exists)" if would_skip else "create",
51
+ })
52
+ if would_skip:
53
+ skipped += 1
54
+ else:
55
+ extracted += 1
56
+ continue
43
57
 
44
- if output_path.exists() and not overwrite:
58
+ if would_skip:
45
59
  skipped += 1
46
60
  continue
47
61
 
@@ -57,4 +71,13 @@ def unpack_workspace(
57
71
 
58
72
  extracted += 1
59
73
 
74
+ if dry_run:
75
+ return {
76
+ "dry_run": True,
77
+ "destination": str(destination_path),
78
+ "would_extract": extracted,
79
+ "would_skip": skipped,
80
+ "files": dry_run_files,
81
+ }
82
+
60
83
  return {"extracted": extracted, "skipped": skipped, "destination": str(destination_path)}
@@ -1 +0,0 @@
1
- __version__ = "0.1.7"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes