diff --git a/.specstory/history/2025-11-10_16-59Z-change-compression-method-to-7z.md b/.specstory/history/2025-11-10_16-59Z-change-compression-method-to-7z.md
new file mode 100644
index 0000000..33778d3
--- /dev/null
+++ b/.specstory/history/2025-11-10_16-59Z-change-compression-method-to-7z.md
@@ -0,0 +1,702 @@
+
+
+
+
+# Change compression method to 7z (2025-11-10 16:59Z)
+
+_**User (2025-11-10 16:59Z)**_
+
+@zip_sequences.py (1-334) let's have it use 7z at compression lvl 9 instead of zip at 0 (store) I don't mind it taking a few minutes per zip if they're more compressed.
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
+
+---
+
+
+Switching to 7z with compression level 9. Updating the code to use 7z instead of Python's zipfile.
+
+
+---
+
+
+Tool use: **read_file** • Read file: zip_sequences.py
+
+
+
+
+---
+
+
+
+---
+
+
+Updating the code to use 7z with compression level 9 instead of Python's zipfile with no compression.
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 1, lines removed: undefined
+
+```diff
+@@ -14,4 +14,5 @@
+ import os
+ import shutil
++ import subprocess
+ import sys
+ from concurrent.futures import ThreadPoolExecutor, as_completed
+```
+
+
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 1, lines removed: 1
+
+```diff
+@@ -127,5 +127,5 @@
+ def archive_path_for(seq_dir: Path) -> Path:
+ rel = seq_dir.relative_to(RENDER_ROOT)
+- return (ARCHIVE_ROOT / rel).with_suffix(".zip")
++ return (ARCHIVE_ROOT / rel).with_suffix(".7z")
+
+
+```
+
+
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 30, lines removed: 5
+
+```diff
+@@ -140,10 +140,35 @@
+
+ def zip_sequence(seq_dir: Path, zip_path: Path) -> None:
+- from zipfile import ZIP_STORED, ZipFile
+-
+ zip_path.parent.mkdir(parents=True, exist_ok=True)
+- with ZipFile(zip_path, "w", compression=ZIP_STORED) as archive:
+- for file_path in iter_sequence_files(seq_dir):
+- archive.write(file_path, arcname=file_path.relative_to(seq_dir).as_posix())
++
++ # Build list of files to archive with relative paths
++ file_list = []
++ for file_path in iter_sequence_files(seq_dir):
++ rel_path = file_path.relative_to(seq_dir).as_posix()
++ file_list.append(rel_path)
++
++ # Use 7z with maximum compression (level 9)
++ # -mx=9: maximum compression
++ # -mmt=on: multi-threading
++ # -y: assume yes to all prompts
++ cmd = [
++ "7z", "a",
++ "-mx=9",
++ "-mmt=on",
++ "-y",
++ str(zip_path),
++ ]
++
++ # Change to sequence directory and add files
++ result = subprocess.run(
++ cmd + file_list,
++ cwd=seq_dir,
++ capture_output=True,
++ text=True,
++ check=False,
++ )
++
++ if result.returncode != 0:
++ raise RuntimeError(f"7z compression failed: {result.stderr}")
+
+
+```
+
+
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 19, lines removed: 4
+
+```diff
+@@ -174,13 +174,28 @@
+
+ def expand_sequence(zip_path: Path, seq_state: dict) -> None:
+- from zipfile import ZipFile
+-
+ target_dir = sequence_dir_for(zip_path)
+ if target_dir.exists():
+ shutil.rmtree(target_dir)
+ target_dir.mkdir(parents=True, exist_ok=True)
+
+- with ZipFile(zip_path, "r") as archive:
+- archive.extractall(target_dir)
++ # Use 7z to extract archive
++ # -y: assume yes to all prompts
++ # -o: output directory
++ cmd = [
++ "7z", "x",
++ "-y",
++ f"-o{target_dir}",
++ str(zip_path),
++ ]
++
++ result = subprocess.run(
++ cmd,
++ capture_output=True,
++ text=True,
++ check=False,
++ )
++
++ if result.returncode != 0:
++ raise RuntimeError(f"7z extraction failed: {result.stderr}")
+
+ for entry in seq_state.get("files", []):
+```
+
+
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 1, lines removed: 1
+
+```diff
+@@ -284,5 +284,5 @@
+ work_items: list[tuple[Path, dict]] = []
+
+- for zip_path in ARCHIVE_ROOT.rglob("*.zip"):
++ for zip_path in ARCHIVE_ROOT.rglob("*.7z"):
+ state_path = state_path_for(zip_path)
+ seq_state = load_state(state_path)
+```
+
+**Chunk 2**
+Lines added: 1, lines removed: 1
+
+```diff
+@@ -327,5 +327,5 @@
+ removed: list[Path] = []
+
+- for zip_path in ARCHIVE_ROOT.rglob("*.zip"):
++ for zip_path in ARCHIVE_ROOT.rglob("*.7z"):
+ seq_dir = sequence_dir_for(zip_path)
+ if seq_dir.exists():
+```
+
+
+
+
+
+---
+
+
+
+---
+
+
+Updating the docstring to reflect 7z usage and checking for lint errors:
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 5, lines removed: 5
+
+```diff
+@@ -1,9 +1,9 @@
+ #!/usr/bin/env python3
+- """Maintain zipped render sequences for Git hooks.
++ """Maintain compressed render sequences for Git hooks using 7z.
+
+- Default mode scans `Renders/`, produces ZIP archives under `Renders/_zipped/`,
+- and stages any updated archives so commits only track compact files. Switch to
+- `--mode expand` to inflate the tracked archives back into the ignored working
+- directories after checkouts or pulls.
++ Default mode scans `Renders/`, produces 7z archives (compression level 9) under
++ `Renders/_zipped/`, and stages any updated archives so commits only track compact
++ files. Switch to `--mode expand` to inflate the tracked archives back into the
++ ignored working directories after checkouts or pulls.
+ """
+
+```
+
+
+
+
+
+---
+
+
+
+ Tool use: **read_lints** • Read lints for 1 file
+
+Lint paths:
+
+- `zip_sequences.py`
+
+Lint results
+
+
+**No lint errors found**
+
+
+
+---
+
+
+
+---
+
+
+Updated the script to use 7z with compression level 9 instead of Python's zipfile with no compression. Changes:
+
+1. **Added subprocess import** for calling 7z
+2. **Changed archive format** from `.zip` to `.7z`
+3. **Replaced `zip_sequence` function** to use `7z a` with:
+ - `-mx=9`: maximum compression
+ - `-mmt=on`: multi-threading
+ - `-y`: assume yes to prompts
+4. **Replaced `expand_sequence` function** to use `7z x` for extraction
+5. **Updated all file pattern searches** from `*.zip` to `*.7z`
+6. **Updated docstring** to reflect 7z usage
+
+The script now creates 7z archives with maximum compression. Compression may take longer, but archives will be smaller. Ensure 7z is installed and available in your PATH (typically `7z.exe` on Windows).
+
+---
+
+_**User (2025-11-10 16:59Z)**_
+
+Will this remove old zip versions, or will I have to delete the _zipped folder in repo and re-compress?
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
+
+---
+
+
+Checking the cleanup logic to see if it handles old .zip files:
+
+
+---
+
+
+Tool use: **read_file** • Read file: zip_sequences.py
+
+
+
+
+---
+
+
+
+---
+
+
+Tool use: **read_file** • Read file: zip_sequences.py
+
+
+
+
+---
+
+
+
+
+
+---
+
+
+
+---
+
+
+The cleanup function only looks for `.7z` files, so old `.zip` files won't be removed. Updating it to also remove obsolete `.zip` files:
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 11, lines removed: undefined
+
+```diff
+@@ -327,4 +327,15 @@
+ removed: list[Path] = []
+
++ # Remove obsolete .zip files (migrated to .7z)
++ for old_zip in ARCHIVE_ROOT.rglob("*.zip"):
++ rel = old_zip.relative_to(ARCHIVE_ROOT)
++ log("zip", f"Removing obsolete .zip archive {rel}", verbose_only=True, verbose=verbose)
++ old_zip.unlink(missing_ok=True)
++ state_path = state_path_for(old_zip)
++ if state_path.exists():
++ state_path.unlink()
++ removed.append(old_zip)
++
++ # Remove orphan .7z archives (sequence directory no longer exists)
+ for zip_path in ARCHIVE_ROOT.rglob("*.7z"):
+ seq_dir = sequence_dir_for(zip_path)
+```
+
+
+
+
+
+---
+
+
+
+---
+
+
+
+ Tool use: **read_lints** • Read lints for 1 file
+
+Lint paths:
+
+- `zip_sequences.py`
+
+Lint results
+
+
+**No lint errors found**
+
+
+
+---
+
+
+
+
+
+---
+
+
+
+---
+
+
+Updated the cleanup function to automatically remove old `.zip` files. When you run the script, it will:
+
+1. Remove obsolete `.zip` files (migrated to `.7z`)
+2. Remove orphan `.7z` archives (when the sequence directory no longer exists)
+3. Clean up associated `.meta.json` state files
+
+No need to manually delete the `_zipped` folder. The next run will remove old `.zip` files and create new `.7z` archives with maximum compression. The cleanup runs automatically after the zip operation completes.
+
+---
+
+_**User (2025-11-10 16:59Z)**_
+
+I just pulled from HOME. Let's change DailyFormat from a true/false bool to a string so I can just put the format in (daily_YYMMDD vs YYYY-MM-DD) and the same thing for zipper, "7z" or "zip"
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
+
+---
+
+
+Checking `config.json`, then updating the code to use string values for `DailyFormat` and `zipper`.
+
+
+---
+
+
+Tool use: **read_file** • Read file: config.json
+
+
+
+
+---
+
+
+Tool use: **read_file** • Read file: zip_sequences.py
+
+
+
+
+---
+
+
+
+---
+
+
+Searching for `dailyFormat` usage in the file:
+
+
+---
+
+
+
+Tool use: **grep** • Grep for "dailyFormat|daily_format|DailyFormat" in "zip_sequences.py" • 0 matches
+
+Output mode: content
+
+
+_No matches found_
+
+
+
+
+---
+
+
+
+---
+
+
+Updating the code to use string values for both settings:
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 2, lines removed: 1
+
+```diff
+@@ -34,6 +34,7 @@
+ CONFIG_PATH = Path(__file__).resolve().with_name("config.json")
+ DEFAULT_CONFIG = {
+- "zipper": True,
++ "zipper": "7z",
+ "compression": 9,
++ "dailyFormat": "daily_YYMMDD",
+ }
+
+```
+
+
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 2, lines removed: 1
+
+```diff
+@@ -62,5 +62,6 @@
+
+ CONFIG = load_config()
+- USE_7Z = bool(CONFIG.get("zipper", True))
++ ZIPPER_TYPE = str(CONFIG.get("zipper", "7z")).lower()
++ USE_7Z = ZIPPER_TYPE == "7z"
+ COMPRESSION_LEVEL = CONFIG.get("compression", 9)
+ if isinstance(COMPRESSION_LEVEL, str):
+```
+
+
+
+
+
+---
+
+
+
+ Tool use: **read_lints** • Read lints for 1 file
+
+Lint paths:
+
+- `zip_sequences.py`
+
+Lint results
+
+
+**No lint errors found**
+
+
+
+---
+
+
+
+---
+
+
+Adding backward compatibility for boolean values in the config:
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: zip_sequences.py
+
+**Chunk 1**
+Lines added: 6, lines removed: 1
+
+```diff
+@@ -62,5 +62,10 @@
+
+ CONFIG = load_config()
+- ZIPPER_TYPE = str(CONFIG.get("zipper", "7z")).lower()
++ zipper_val = CONFIG.get("zipper", "7z")
++ # Handle both old boolean format and new string format
++ if isinstance(zipper_val, bool):
++ ZIPPER_TYPE = "7z" if zipper_val else "zip"
++ else:
++ ZIPPER_TYPE = str(zipper_val).lower()
+ USE_7Z = ZIPPER_TYPE == "7z"
+ COMPRESSION_LEVEL = CONFIG.get("compression", 9)
+```
+
+
+
+
+
+---
+
+
+
+ Tool use: **read_lints** • Read lints for 1 file
+
+Lint paths:
+
+- `zip_sequences.py`
+
+Lint results
+
+
+**No lint errors found**
+
+
+
+---
+
+
+
+---
+
+
+Updating `config.json` to use the new string format:
+
+
+---
+
+
+Tool use: **read_file** • Read file: config.json
+
+
+
+
+---
+
+
+
+---
+
+
+
+Tool use: **code_edit** • Edit file: config.json
+
+**Chunk 1**
+Lines added: 2, lines removed: 2
+
+```diff
+@@ -1,6 +1,6 @@
+ {
+- "dailyFormat": true,
++ "dailyFormat": "daily_YYMMDD",
+ "structDir": "D:\\ProjectStructure",
+- "zipper": true,
++ "zipper": "7z",
+ "compression": 9
+ }
+```
+
+
+
+
+
+---
+
+
+
+
+
+---
+
+
+
+---
+
+
+Updated both files:
+
+1. **`zip_sequences.py`**:
+ - Changed `DEFAULT_CONFIG` to use string values: `"zipper": "7z"` and `"dailyFormat": "daily_YYMMDD"`
+ - Updated the zipper logic to accept string values (`"7z"` or `"zip"`) with backward compatibility for boolean values
+ - The code now checks if `zipper` is `"7z"` (case-insensitive) to determine whether to use 7z compression
+
+2. **`config.json`**:
+ - Updated `"zipper": true` → `"zipper": "7z"`
+ - Updated `"dailyFormat": true` → `"dailyFormat": "daily_YYMMDD"`
+
+You can now set:
+- `"zipper": "7z"` or `"zipper": "zip"` to choose the compression tool
+- `"dailyFormat": "daily_YYMMDD"` or `"dailyFormat": "YYYY-MM-DD"` or any other format string
+
+The code still supports old boolean values for backward compatibility, so existing configs will continue to work.
+
+---
+
diff --git a/config.json b/config.json
index 5b3d71b..003b2b6 100644
--- a/config.json
+++ b/config.json
@@ -1,7 +1,7 @@
{
- "dailyFormat": true,
+ "dailyFormat": "daily_YYMMDD",
"structDir": "D:\\ProjectStructure",
- "zipper": true,
+ "zipper": "7z",
"compression": 9
}
diff --git a/zip_sequences.py b/zip_sequences.py
index 1715b1b..fb45229 100644
--- a/zip_sequences.py
+++ b/zip_sequences.py
@@ -33,8 +33,9 @@ SEQUENCE_EXTENSIONS = {
STATE_SUFFIX = ".meta.json"
CONFIG_PATH = Path(__file__).resolve().with_name("config.json")
DEFAULT_CONFIG = {
- "zipper": True,
+ "zipper": "7z",
"compression": 9,
+ "dailyFormat": "daily_YYMMDD",
}
@@ -60,7 +61,13 @@ def load_config() -> dict:
CONFIG = load_config()
-USE_7Z = bool(CONFIG.get("zipper", True))
+zipper_val = CONFIG.get("zipper", "7z")
+# Handle both old boolean format and new string format
+if isinstance(zipper_val, bool):
+ ZIPPER_TYPE = "7z" if zipper_val else "zip"
+else:
+ ZIPPER_TYPE = str(zipper_val).lower()
+USE_7Z = ZIPPER_TYPE == "7z"
COMPRESSION_LEVEL = CONFIG.get("compression", 9)
if isinstance(COMPRESSION_LEVEL, str):
try: