diff --git a/.gitignore b/.gitignore
index e36513b..0b8cdbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,10 @@ src/bindings/python/__pycache__/
/src/AdvChkSys.Benchmarks/bin
/src/AdvChkSys.Benchmarks/obj
build/
+docs/features.md.*
+scripts/__pycache__/
+snyk.exe
+scripts/track_progress/__pycache__/
+scripts/track_progress/dist/
+scripts/track_progress/scripts/__pycache__/
+scripts/track_progress/docs/features.md.bak.*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3333a98..a7b10c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,3 @@
# Changelog
-- 2025-05-11: removed unnecessary ignores in gitignore (79d6716)
-- 2025-05-11: XML comments added Builds cleanly (0ae815c)
-- 2025-05-11: updated track_progress restored accidentally deleted source file (8a6f914)
-- 2025-05-10: fixed a bug of not building (09ee0d8)
-- 2025-05-10: added a build (e59a909)
-- 2025-05-10: updated ChunkMark (f35c612)
-- 2025-05-10: updated memory memory handling to be more safe (becedcc)
-- 2025-05-09: first commit (f1b3868)
-
-- 2025-05-10: added a build (e59a909)
-- 2025-05-10: updated memory memory handling to be more safe (becedcc)
\ No newline at end of file
diff --git a/docs/features.md b/docs/features.md
deleted file mode 100644
index 32b4c86..0000000
--- a/docs/features.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Feature Tracking
-
-## chunk_management
-- Status: completed
-- Description: 2D/3D chunk management with memory efficiency
-- Last Update: 2025-05-10
-
-## memory_efficiency
-- Status: completed
-- Description: LRU caching, array pooling, and air-singleton patterns
-- Last Update: 2025-05-10
-
-## resource_tracking
-- Status: completed
-- Description: Track and manage chunk resources
-- Last Update: 2025-05-10
-
-## async_tasks
-- Status: completed
-- Description: Asynchronous chunk loading and processing
-- Last Update: 2025-05-10
-
-## serialization
-- Status: completed
-- Description: Chunk serialization and deserialization
-- Last Update: 2025-05-10
-
-## event_system
-- Status: completed
-- Description: Events for load/unload/save operations
-- Last Update: 2025-05-10
-
-## interop
-- Status: completed
-- Description: Python and .NET interoperability
-- Last Update: 2025-05-10
-
-## spatial_queries
-- Status: completed
-- Description: Spatial indexing and region queries with 2D/3D support and quadtree optimization
-- Last Update: 2025-05-11
-
-## thread_sync
-- Status: completed
-- Description: Explicit synchronization and concurrent collections
-- Last Update: 2025-05-11
-- Updated By: Stan44
-
-## spatial_interfaces
-- Status: completed
-- Description: Interface-level spatial query methods with support for custom filters
-- Last Update: 2025-05-11
-
-## parallel_processing
-- Status: completed
-- Description: Parallel processing of chunks based on spatial queries and regions
-- Last Update: 2025-05-11
-
-## feature_tracking
-- Status: completed
-- Description: Track feature statuses
-- Last Update: 2025-05-11
-
-## priority_loading
-- Status: in_progress
-- Description: Prioritized chunk loading system (partially implemented)
-- Last Update: 2025-05-10
-
-## dependency_tracking
-- Status: in_progress
-- Description: Dependency-aware disposal logic (Partially Implemented)
-- Last Update: 2025-05-10
-
-## dispose_pattern
-- Status: in_progress
-- Description: Full dispose pattern with finalizers for unmanaged resources (Partially Implemented)
-- Last Update: 2025-05-10
-
-## exception_handling
-- Status: in_progress
-- Description: Better async exception handling with specific types and logging
-- Last Update: 2025-05-10
-
-## performance_metrics
-- Status: in_progress
-- Description: Track load times, cache hit rates, and memory usage (partially implemented)
-- Last Update: 2025-05-10
-
-## progress_tracking
-- Status: in_progress
-- Description: Track progress and auto-update status documents (mostly implemented kinda still manual...)
-- Last Update: 2025-05-11
-
-## git_integration
-- Status: in_progress
-- Description: Parse Git logs for status updates (partially implemented testing with this push.)
-- Last Update: 2025-05-11
-
-## doc_generation
-- Status: in_progress
-- Description: Auto-generate status docs and changelog (partially implemented)
-- Last Update: 2025-05-10
-
-## dependency_interfaces
-- Status: planned
-- Description: Interface-level dependency methods
-- Last Update: 2025-05-10
-
-## runtime_config
-- Status: planned
-- Description: Runtime-adjustable configuration options
-- Last Update: 2025-05-10
-
-## known_issues
-- Status: in_progress
-- Description: Edge chunk unload delay under high concurrency
-- Last Update: 2025-05-10
-
diff --git a/docs/status/ChunkManager-Status.md b/docs/status/ChunkManager-Status.md
deleted file mode 100644
index 185400d..0000000
--- a/docs/status/ChunkManager-Status.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# AdvChkSys Development Status
-
-Last updated: 2025-05-11
-
-## Code Statistics
-
-Total lines of code: **7414**
-
-Number of source files: **34**
-
-### All Files by Line Count
-
-| File | Lines | Path |
-|------|------:|------|
-| SpatialChunkIndex.cs | 1228 | src/AdvChkSys\Spatial\SpatialChunkIndex.cs |
-| ChunkLoadingPriority.cs | 696 | src/AdvChkSys\Loading\ChunkLoadingPriority.cs |
-| ChunkThreadingExtensions.cs | 380 | src/AdvChkSys\Threading\ChunkThreadingExtensions.cs |
-| ChunkDependencyTracker.cs | 325 | src/AdvChkSys\Dependencies\ChunkDependencyTracker.cs |
-| ChunkManager2D.cs | 313 | src/AdvChkSys\Manager\ChunkManager2D.cs |
-| ChunkTaskScheduler.cs | 310 | src/AdvChkSys\Threading\ChunkTaskScheduler.cs |
-| ChunkThreadingManager.cs | 280 | src/AdvChkSys\Threading\ChunkThreadingManager.cs |
-| ChunkOperationQueue.cs | 274 | src/AdvChkSys\Threading\ChunkOperationQueue.cs |
-| ChunkThreadingDiagnostics.cs | 273 | src/AdvChkSys\Threading\ChunkThreadingDiagnostics.cs |
-| ChunkThreadingPerformanceMonitor.cs | 265 | src/AdvChkSys\Threading\ChunkThreadingPerformanceMonitor.cs |
-| ChunkParallelProcessor.cs | 252 | src/AdvChkSys\Threading\ChunkParallelProcessor.cs |
-| ChunkManager3D.cs | 244 | src/AdvChkSys\Manager\ChunkManager3D.cs |
-| ChunkThreadSafetyManager.cs | 237 | src/AdvChkSys\Threading\ChunkThreadSafetyManager.cs |
-| ChunkThreadingExtensions2.cs | 223 | src/AdvChkSys\Threading\ChunkThreadingExtensions2.cs |
-| Chunk3D.cs | 215 | src/AdvChkSys\Chunk\Chunk3D.cs |
-| ChunkSerializer.cs | 194 | src/AdvChkSys\Serialization\ChunkSerializer.cs |
-| Chunk2D.cs | 174 | src/AdvChkSys\Chunk\Chunk2D.cs |
-| LimitedConcurrencyTaskScheduler.cs | 170 | src/AdvChkSys\Threading\LimitedConcurrencyTaskScheduler.cs |
-| ChunkAsyncLock.cs | 167 | src/AdvChkSys\Threading\ChunkAsyncLock.cs |
-| AdvChkSys.cs | 162 | src/AdvChkSys\AdvChkSys.cs |
-| MemoryHelper.cs | 143 | src/AdvChkSys\Util\MemoryHelper.cs |
-| MemoryUsageReporter.cs | 140 | src/AdvChkSys\Diagnostics\MemoryUsageReporter.cs |
-| LRUCache.cs | 135 | src/AdvChkSys\Util\LRUCache.cs |
-| ChunkEvents.cs | 126 | src/AdvChkSys\Events\ChunkEvents.cs |
-| ChunkThreadingConfiguration.cs | 98 | src/AdvChkSys\Threading\ChunkThreadingConfiguration.cs |
-| ChunkResourceManager.cs | 72 | src/AdvChkSys\Resources\ChunkResourceManager.cs |
-| WorldConstraints.cs | 67 | src/AdvChkSys\Constraints\WorldConstraints.cs |
-| ChunkTaskSchedulerExtensions.cs | 63 | src/AdvChkSys\Threading\ChunkTaskSchedulerExtensions.cs |
-| ChunkExtensions.cs | 40 | src/AdvChkSys\Spatial\ChunkExtensions.cs |
-| IChunkManager.cs | 38 | src/AdvChkSys\Interfaces\IChunkManager.cs |
-| IChunk.cs | 36 | src/AdvChkSys\Interfaces\IChunk.cs |
-| CacheCapacityHelper.cs | 30 | src/AdvChkSys\Util\CacheCapacityHelper.cs |
-| AdvChkSys.AssemblyInfo.cs | 22 | src/AdvChkSys\obj\Debug\netstandard2.1\AdvChkSys.AssemblyInfo.cs |
-| AdvChkSys.AssemblyInfo.cs | 22 | src/AdvChkSys\obj\Release\netstandard2.1\AdvChkSys.AssemblyInfo.cs |
-
-## Feature Status
-
-| Feature | Status | Description | Last Update |
-|---------|--------|-------------|-------------|
-| Chunk Management | [COMPLETED] | 2D/3D chunk management with memory efficiency | 2025-05-10 |
-| Memory Efficiency | [COMPLETED] | LRU caching, array pooling, and air-singleton patterns | 2025-05-10 |
-| Resource Tracking | [COMPLETED] | Track and manage chunk resources | 2025-05-10 |
-| Async Tasks | [COMPLETED] | Asynchronous chunk loading and processing | 2025-05-10 |
-| Serialization | [COMPLETED] | Chunk serialization and deserialization | 2025-05-10 |
-| Event System | [COMPLETED] | Events for load/unload/save operations | 2025-05-10 |
-| Interop | [COMPLETED] | Python and .NET interoperability | 2025-05-10 |
-| Spatial Queries | [COMPLETED] | Spatial indexing and region queries with 2D/3D support and quadtree optimization | 2025-05-11 |
-| Thread Sync | [COMPLETED] | Explicit synchronization and concurrent collections | 2025-05-11 |
-| Spatial Interfaces | [COMPLETED] | Interface-level spatial query methods with support for custom filters | 2025-05-11 |
-| Parallel Processing | [COMPLETED] | Parallel processing of chunks based on spatial queries and regions | 2025-05-11 |
-| Feature Tracking | [COMPLETED] | Track feature statuses | 2025-05-11 |
-| Priority Loading | [IN PROGRESS] | Prioritized chunk loading system (partially implemented) | 2025-05-10 |
-| Dependency Tracking | [IN PROGRESS] | Dependency-aware disposal logic (Partially Implemented) | 2025-05-10 |
-| Dispose Pattern | [IN PROGRESS] | Full dispose pattern with finalizers for unmanaged resources (Partially Implemented) | 2025-05-10 |
-| Exception Handling | [IN PROGRESS] | Better async exception handling with specific types and logging | 2025-05-10 |
-| Performance Metrics | [IN PROGRESS] | Track load times, cache hit rates, and memory usage (partially implemented) | 2025-05-10 |
-| Progress Tracking | [IN PROGRESS] | Track progress and auto-update status documents (mostly implemented kinda still manual...) | 2025-05-11 |
-| Git Integration | [IN PROGRESS] | Parse Git logs for status updates (partially implemented testing with this push.) | 2025-05-11 |
-| Doc Generation | [IN PROGRESS] | Auto-generate status docs and changelog (partially implemented) | 2025-05-10 |
-| Dependency Interfaces | [PLANNED] | Interface-level dependency methods | 2025-05-10 |
-| Runtime Config | [PLANNED] | Runtime-adjustable configuration options | 2025-05-10 |
-| Known Issues | [IN PROGRESS] | Edge chunk unload delay under high concurrency | 2025-05-10 |
-
-## Recent Updates
-
-- 2025-05-11: removed unnecessary ignores in gitignore (79d6716)
-- 2025-05-11: XML comments added Builds cleanly (0ae815c)
-- 2025-05-11: updated track_progress restored accidentally deleted source file (8a6f914)
-- 2025-05-10: fixed a bug of not building (09ee0d8)
-- 2025-05-10: added a build (e59a909)
-- 2025-05-10: updated ChunkMark (f35c612)
-- 2025-05-10: updated memory memory handling to be more safe (becedcc)
-- 2025-05-09: first commit (f1b3868)
diff --git a/scripts/README.md b/scripts/README.md
deleted file mode 100644
index a25c9b5..0000000
--- a/scripts/README.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# AdvChkSys Development Scripts
-
-## Track Progress Script
-
-The `track_progress.py` script automates feature tracking, status updates, and changelog management for the AdvChkSys project.
-
-### Overview
-
-This script:
-- Tracks features and their status in a human-readable Markdown file
-- Generates a status document with current development progress
-- Updates the changelog with tagged entries from commit messages
-- Automatically detects new features from specially formatted commit messages
-
-### Usage
-
-Run the script periodically to update documentation:
-
-```bash
-python scripts/track_progress.py
-```
-
-### Commit Message Tags
-
-The script recognizes special tags in commit messages:
-
-#### 1. Feature Status Updates
-
-```
-git commit -m "Implemented spatial queries [feature:spatial_queries]"
-```
-
-This marks the `spatial_queries` feature as completed and records the date and author.
-
-#### 2. New Features
-
-```
-git commit -m "Initial setup [new-feature:custom_serialization:Support for custom serialization formats]"
-```
-
-This adds a new feature called `custom_serialization` with the provided description and sets its status to "planned".
-
-#### 3. Changelog Entries
-
-```
-git commit -m "Fixed bug in chunk loading [changelog:Fixed race condition in async chunk loading]"
-```
-
-This adds an entry to the changelog with the date, message, and commit hash.
-
-#### 4. Status Updates
-
-```
-git commit -m "Completed milestone 1 [status:milestone1]"
-```
-
-This records a general status update.
-
-### Output Files
-
-The script generates and updates the following files:
-
-- `docs/features.md`: Human-readable feature tracking in Markdown format
-- `docs/status/ChunkManager-Status.md`: Status document with feature progress and recent updates
-- `CHANGELOG.md`: Project changelog with tagged entries from commits
-
-### Feature Statuses
-
-Features can have the following statuses:
-- `planned`: Feature is planned but not yet started
-- `in_progress`: Feature is currently being implemented
-- `completed`: Feature has been implemented
-
-### Manual Editing
-
-You can manually edit the `docs/features.md` file to update feature descriptions, statuses, or add new features. The script will preserve your changes when it runs.
-
-### Requirements
-
-- Python 3.6+
-- Git repository
\ No newline at end of file
diff --git a/scripts/track_progress.py b/scripts/track_progress.py
deleted file mode 100644
index 50174c6..0000000
--- a/scripts/track_progress.py
+++ /dev/null
@@ -1,291 +0,0 @@
-import os
-import re
-import sys
-from datetime import datetime
-from subprocess import check_output
-
-# Try to set UTF-8 encoding for better emoji support
-sys.stderr = open(
- sys.stderr.fileno(), mode="w", encoding="utf-8", errors="replace"
-) # Configuration
-PROGRESS_FILE = "docs/progress.json"
-STATUS_DOC = "docs/status/ChunkManager-Status.md"
-CHANGELOG_FILE = "CHANGELOG.md"
-FEATURES_FILE = "docs/features.md"
-GIT_LOG_LIMIT = 100 # number of commits to parse
-UNTAGGED_COMMIT_LIMIT = 100 # number of untagged commits to include
-
-# Regular expression to match tags in commit messages
-STATUS_TAG_RE = re.compile(r"\[status:(\w+)\]")
-CHANGELOG_TAG_RE = re.compile(r"\[changelog:(.+?)\]")
-FEATURE_TAG_RE = re.compile(r"\[feature:(\w+)\]")
-NEW_FEATURE_RE = re.compile(r"\[new-feature:(\w+):(.+?)\]")
-
-
-# Ensure directories exist
-os.makedirs(os.path.dirname(STATUS_DOC), exist_ok=True)
-os.makedirs(os.path.dirname(FEATURES_FILE), exist_ok=True)
-
-
-def count_lines_of_code():
- """Count lines of code in the project and return statistics."""
- import os
- import glob
-
- stats = {}
- total_lines = 0
- file_stats = []
-
- # Find all .cs files in the src/AdvChkSys directory and subdirectories
- cs_files = glob.glob("src/AdvChkSys/**/*.cs", recursive=True)
-
- for file_path in cs_files:
- with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
- lines = len(f.readlines())
- total_lines += lines
- file_name = os.path.basename(file_path)
- file_stats.append((file_name, lines, file_path))
-
- # Sort by line count in descending order
- file_stats.sort(key=lambda x: x[1], reverse=True)
-
- stats["total_lines"] = total_lines
- stats["file_count"] = len(cs_files)
- stats["files"] = file_stats
-
- return stats
-
-
-# Load features from Markdown file if it exists
-features = {}
-if os.path.exists(FEATURES_FILE):
- with open(FEATURES_FILE, "r", encoding="utf-8") as f:
- content = f.read()
- # Parse the markdown file
- sections = re.split(r"## (\w+)", content)[1:] # Skip the header
- for i in range(0, len(sections), 2):
- if i + 1 < len(sections):
- feature_key = sections[i]
- feature_content = sections[i + 1]
-
- # Extract status, description, and date
- status_match = re.search(r"- Status: (\w+)", feature_content)
- desc_match = re.search(
- r"- Description: (.+?)$", feature_content, re.MULTILINE
- )
- date_match = re.search(
- r"- Last Update: (.+?)$", feature_content, re.MULTILINE
- )
-
- status = status_match.group(1) if status_match else "planned"
- description = desc_match.group(1) if desc_match else ""
- date = date_match.group(1) if date_match else ""
-
- features[feature_key] = {
- "status": status,
- "description": description,
- "date": date,
- }
-else:
- # Default features
- features = {
- "spatial_queries": {
- "status": "planned",
- "description": "Methods to efficiently find chunks within regions or distances", # noqa: E501
- },
- "priority_loading": {
- "status": "planned",
- "description": "API to specify which chunks should be loaded first", # noqa: E501
- },
- "serialization_optimization": {
- "status": "in_progress",
- "description": "Further improvements to chunk saving/loading",
- },
- "chunk_dependency": {
- "status": "planned",
- "description": "For cases where chunks need to reference neighbors", # noqa: E501
- },
- }
-
-# Get recent git log entries
-git_log = check_output(
- [
- "git",
- "log",
- f"-n{GIT_LOG_LIMIT}",
- "--pretty=format:%h|%s|%ad|%an",
- "--date=short",
- ]
-).decode()
-
-# Parse and collect updates
-status_updates = {}
-changelog_entries = []
-feature_updates = {}
-new_features = {}
-untagged_count = 0 # Initialize the counter for untagged commits
-
-for line in git_log.splitlines():
- parts = line.split("|")
- if len(parts) < 4:
- continue
-
- commit_hash, subject, date, author = parts
-
- status_match = STATUS_TAG_RE.search(subject)
- changelog_match = CHANGELOG_TAG_RE.search(subject)
- feature_match = FEATURE_TAG_RE.search(subject)
- new_feature_match = NEW_FEATURE_RE.search(subject)
-
- if status_match:
- status_key = status_match.group(1)
- status_updates[status_key] = {
- "status": "done",
- "commit": commit_hash,
- "date": date,
- "message": subject,
- }
-
- if changelog_match:
- changelog_entries.append(
- f"- {date}: {changelog_match.group(1)} ({commit_hash})"
- )
-
- if feature_match:
- feature_key = feature_match.group(1)
- if feature_key in features:
- feature_updates[feature_key] = {
- "status": "completed",
- "date": date,
- "author": author,
- }
-
- if new_feature_match:
- feature_key = new_feature_match.group(1)
- feature_desc = new_feature_match.group(2)
- if feature_key not in features:
- new_features[feature_key] = {
- "status": "planned",
- "description": feature_desc,
- "date": date,
- "author": author,
- }
-
- # Also add regular commits without tags to changelog entries
- if not (status_match or changelog_match or feature_match or new_feature_match):
- # Skip if we've reached the limit for untagged commits
- if untagged_count >= UNTAGGED_COMMIT_LIMIT:
- continue
-
- # Skip merge commits and very short messages
- if not subject.startswith('Merge ') and len(subject) > 5:
- # Extract the first sentence or up to 100 chars
- commit_desc = subject.split('.')[0]
- if len(commit_desc) > 100:
- commit_desc = commit_desc[:97] + "..."
-
- # Add to changelog entries with a different format to distinguish from tagged entries
- changelog_entries.append(
- f"- {date}: {commit_desc} ({commit_hash})"
- )
- untagged_count += 1
-
-# Update features with new information
-for feature_key, update in feature_updates.items():
- features[feature_key].update(update)
-
-# Add new features
-for feature_key, feature_data in new_features.items():
- features[feature_key] = feature_data
-
-# Save features to Markdown file
-with open(FEATURES_FILE, "w", encoding="utf-8") as f:
- f.write("# Feature Tracking\n\n")
-
- for feature_key, feature_data in features.items():
- status = feature_data.get("status", "planned")
- description = feature_data.get("description", "")
- date = feature_data.get("date", "")
- author = feature_data.get("author", "")
-
- f.write(f"## {feature_key}\n")
- f.write(f"- Status: {status}\n")
- f.write(f"- Description: {description}\n")
- f.write(f"- Last Update: {date}\n")
- if author:
- f.write(f"- Updated By: {author}\n")
- f.write("\n")
-
-# Generate status document
-with open(STATUS_DOC, "w", encoding="utf-8") as f:
- f.write("# AdvChkSys Development Status\n\n")
- f.write(f"Last updated: {datetime.now().strftime('%Y-%m-%d')}\n\n")
-
- # Add code statistics section
- f.write("## Code Statistics\n\n")
- code_stats = count_lines_of_code()
- f.write(f"Total lines of code: **{code_stats['total_lines']}**\n\n")
- f.write(f"Number of source files: **{code_stats['file_count']}**\n\n")
-
- f.write("### All Files by Line Count\n\n")
- f.write("| File | Lines | Path |\n")
- f.write("|------|------:|------|\n")
-
- for file_name, lines, file_path in code_stats["files"]:
- f.write(f"| {file_name} | {lines} | {file_path} |\n")
-
- f.write("\n")
-
- f.write("## Feature Status\n\n")
- f.write("| Feature | Status | Description | Last Update |\n")
- f.write("|---------|--------|-------------|-------------|\n")
-
- for feature_key, feature_data in features.items():
- status = feature_data.get("status", "unknown")
- description = feature_data.get("description", "")
- date = feature_data.get("date", "")
-
- # Use text indicators instead of emojis
- status_indicator = {
- "planned": "[PLANNED]",
- "in_progress": "[IN PROGRESS]",
- "completed": "[COMPLETED]",
- "done": "[DONE]",
- }.get(status, "[UNKNOWN]")
-
- f.write(
- f"| {feature_key.replace('_', ' ').title()} | {status_indicator} | {description} | {date} |\n" # noqa: E501
- )
-
- f.write("\n## Recent Updates\n\n")
- for entry in changelog_entries[
- :100
- ]:
- f.write(f"{entry}\n")
-
-# Update changelog if there are new entries
-if changelog_entries and os.path.exists(CHANGELOG_FILE):
- with open(CHANGELOG_FILE, "r", encoding="utf-8") as f:
- existing_changelog = f.read()
-
- with open(CHANGELOG_FILE, "w", encoding="utf-8") as f:
- # Add new entries at the top, under the first heading
- lines = existing_changelog.splitlines()
- insertion_point = next(
- (i for i, line in enumerate(lines) if line.startswith("##")), 2
- )
-
- updated_changelog = "\n".join(lines[:insertion_point]) + "\n\n"
- updated_changelog += "\n".join(changelog_entries) + "\n\n"
- updated_changelog += "\n".join(lines[insertion_point:])
-
- f.write(updated_changelog)
-elif changelog_entries:
- # Create new changelog file
- with open(CHANGELOG_FILE, "w", encoding="utf-8") as f:
- f.write("# Changelog\n\n")
- f.write("\n".join(changelog_entries))
- f.write("\n")
-
-print(f"Progress tracking updated. Status document generated at {STATUS_DOC}")
-print(f"Features updated at {FEATURES_FILE}")
diff --git a/scripts/track_progress/CHANGELOG.md b/scripts/track_progress/CHANGELOG.md
new file mode 100644
index 0000000..4dc68c6
--- /dev/null
+++ b/scripts/track_progress/CHANGELOG.md
@@ -0,0 +1,2 @@
+# Changelog
+
diff --git a/scripts/track_progress/README.md b/scripts/track_progress/README.md
new file mode 100644
index 0000000..2733efc
--- /dev/null
+++ b/scripts/track_progress/README.md
@@ -0,0 +1,207 @@
+# Progress Tracker
+
+A comprehensive tool for tracking project progress, feature status, and generating documentation from Git history.
+
+## Overview
+
+The Progress Tracker analyzes your Git repository and codebase to automatically generate:
+
+- Feature tracking documentation
+- Project status reports
+- Changelog based on commit messages
+- Code statistics and metrics
+
+## Installation
+
+### Prerequisites
+
+- Python 3.8 or higher
+- Git
+
+### Dependencies
+
+Install the required dependencies:
+
+```bash
+pip install -r scripts/track_progress/requirements.txt
+```
+
+For development or building the executable, install all dependencies:
+
+```bash
+pip install -r scripts/track_progress/requirements.txt[all]
+```
+
+### Quick Install
+
+Run the setup script to install the tracker to your project:
+
+```bash
+python scripts/track_progress/scripts/setup_install.py --project "Your Project Name" --dest path/to/destination
+```
+
+Options:
+- `--project name`: Set your project name
+- `--dest folder`: Set destination directory (default: "scripts")
+- `--verbose` or `-v`: Enable verbose output
+- `--force` or `-f`: Force overwrite existing files
+
+### Manual Installation
+
+1. Copy the `track_progress` directory to your project
+2. Create a `docs/status` directory in your project root
+3. Create a `docs/features.md` file if it doesn't exist
+4. Create a `CHANGELOG.md` file in your project root
+
+## Usage
+
+### Basic Usage
+
+Run the tracker with default settings:
+
+```bash
+# Using the Python script
+python scripts/track_progress/track_progress.py
+
+# Using the executable (if built)
+scripts/track_progress/track_progress
+```
+
+### Command-Line Options
+
+The tracker supports the following command-line options:
+
+```
+--config PATH Path to configuration file (default: scripts/track_progress/progress_config.json)
+--project NAME Project name (overrides config)
+--output-dir DIR Output directory for status document (overrides config)
+--repo PATH Repository path to analyze (default: current directory)
+--verbose, -v Enable verbose output
+```
+
+Examples:
+
+```bash
+# Specify a custom configuration file
+python scripts/track_progress/track_progress.py --config path/to/config.json
+
+# Override project name
+python scripts/track_progress/track_progress.py --project "My Project"
+
+# Specify output directory
+python scripts/track_progress/track_progress.py --output-dir docs/my-status
+
+# Analyze a different repository
+python scripts/track_progress/track_progress.py --repo path/to/repo
+
+# Enable verbose output
+python scripts/track_progress/track_progress.py --verbose
+```
+
+## Commit Message Tags
+
+The tracker recognizes special tags in commit messages to update feature status and documentation:
+
+| Tag | Description | Example |
+|-----|-------------|---------|
+| `[status:key]` | Update status of a component | `[status:database]` |
+| `[feature:key]` | Mark a feature as completed | `[feature:login]` |
+| `[new-feature:key:description]` | Add a new feature | `[new-feature:search:Implement search functionality]` |
+| `[changelog:message]` | Add an entry to the changelog | `[changelog:Added dark mode]` |
+| `[fix:description]` | Document a bug fix | `[fix:Fixed login issue]` |
+| `[issue:id]` | Reference an issue | `[issue:42]` |
+| `[breaking]` | Mark a breaking change | `[breaking]` |
+| `[roadmap:milestone:item]` | Add an item to the roadmap | `[roadmap:v2:Add OAuth support]` |
+| `[milestone:key]` | Reference a milestone | `[milestone:v1.0]` |
+| `[priority:level]` | Set priority level | `[priority:high]` |
+| `[owner:name]` | Assign an owner | `[owner:John]` |
+| `[tag:name]` | Add a custom tag | `[tag:security]` |
+
+Example commit message:
+```
+Implement search functionality [feature:search] [changelog:Added search with filtering]
+```
+
+## Configuration
+
+The tracker uses a JSON configuration file (`progress_config.json`) with the following settings:
+
+```json
+{
+ "project_name": "Your Project",
+ "output_dir": "docs/status",
+ "status_doc": "status.md",
+ "features_file": "docs/features.md",
+ "changelog_file": "CHANGELOG.md",
+ "git_log_limit": 100,
+ "untagged_commit_limit": 50,
+ "top_files_limit": 20,
+ "exclude_dirs": ["node_modules", "venv", ".git"],
+ "source_extensions": [".py", ".cs", ".js"],
+ "templates": {
+ "status": "status_template.md",
+ "features": "feature_template.md",
+ "changelog": "changelog_template.md"
+ }
+}
+```
+
+## Output Files
+
+The tracker generates the following files:
+
+- `docs/features.md`: Feature tracking document
+- `docs/status/status.md`: Project status document (or path specified in config)
+- `CHANGELOG.md`: Project changelog
+
+## Components
+
+The tracker consists of the following components:
+
+- `track_progress.py`: Main script that orchestrates the tracking process
+- `config.py`: Configuration management
+- `git_analyzer.py`: Git history analysis
+- `code_stats.py`: Code statistics and metrics
+- `feature_markdown.py`: Feature tracking utilities
+- `template_engine.py`: Template rendering engine
+- `changelog_generator.py`: Changelog generation
+- `roadmap_generator.py`: Roadmap generation
+
+## Building an Executable
+
+You can build a standalone executable using Nuitka:
+
+```bash
+python scripts/track_progress/scripts/build_with_nuitka.py
+```
+
+This creates an executable in the `scripts/track_progress/dist` directory.
+
+## Customizing Templates
+
+The tracker uses Markdown templates to generate documentation. You can customize these templates in the `scripts/track_progress/templates` directory:
+
+- `status_template.md`: Template for the status document
+- `feature_template.md`: Template for the feature tracking document
+- `changelog_template.md`: Template for the changelog
+
+The templates use a simple syntax:
+- `{{ variable }}`: Insert a variable
+- `{% if condition %}...{% endif %}`: Conditional block
+- `{% for item in items %}...{% endfor %}`: Loop over items
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Missing Git Repository**: Ensure you're running the tracker in a Git repository.
+2. **No Features Found**: Create a basic `docs/features.md` file with at least one feature.
+3. **Template Not Found**: Check that templates exist in the templates directory.
+
+### Debug Mode
+
+Run with `--verbose` to see detailed output:
+
+```bash
+python scripts/track_progress/track_progress.py --verbose
+```
diff --git a/scripts/track_progress/__init__.py b/scripts/track_progress/__init__.py
new file mode 100644
index 0000000..c2d4cd1
--- /dev/null
+++ b/scripts/track_progress/__init__.py
@@ -0,0 +1 @@
+# This file makes the directory a Python package
\ No newline at end of file
diff --git a/scripts/track_progress/changelog_generator.py b/scripts/track_progress/changelog_generator.py
new file mode 100644
index 0000000..7c012b3
--- /dev/null
+++ b/scripts/track_progress/changelog_generator.py
@@ -0,0 +1,103 @@
+"""
+Changelog generator for project documentation.
+"""
+
+import os
+from datetime import datetime
+from typing import List, Dict, Any
+
+
+def update_changelog_file(
+ changelog_entries: List[Dict[str, Any]],
+ changelog_file: str,
+ max_entries: int = 100,
+ verbose: bool = False,
+) -> bool:
+ """Update the changelog file with new entries."""
+ if not changelog_file:
+ if verbose:
+ print("No changelog file specified, skipping update")
+ return False
+
+ if verbose:
+ print(f"Updating changelog file: {changelog_file}")
+ print(f"Found {len(changelog_entries)} entries to add")
+
+ # Ensure the directory exists if the file is not in the current directory
+ changelog_dir = os.path.dirname(changelog_file)
+ if (
+ changelog_dir
+ ): # Only create directory if there is one (not empty string)
+ os.makedirs(changelog_dir, exist_ok=True)
+
+ # Create the file if it doesn't exist
+ if not os.path.exists(changelog_file):
+ with open(changelog_file, "w", encoding="utf-8") as f:
+ f.write("# Changelog\n\n")
+ if verbose:
+ print(f"Created new changelog file: {changelog_file}")
+
+ # Read existing content
+ with open(changelog_file, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ # Extract header (everything before the first entry)
+ header_end = content.find("## ")
+ if header_end == -1:
+ header = "# Changelog\n\n"
+ else:
+ header = content[:header_end]
+
+ # Format new entries
+ new_entries = []
+
+ # Group entries by date
+ entries_by_date = {}
+ for entry in changelog_entries:
+ date = entry.get("date", "")
+ if not date:
+ continue
+
+ if date not in entries_by_date:
+ entries_by_date[date] = []
+
+ entries_by_date[date].append(entry)
+
+ # Sort dates in reverse chronological order
+ for date in sorted(entries_by_date.keys(), reverse=True):
+ entries = entries_by_date[date]
+
+ # Format date as a section header
+ new_entries.append(f"## {date}\n")
+
+ # Add each entry
+ for entry in entries:
+ message = entry.get("message", "").strip()
+ commit = entry.get("commit", "")
+
+ if message:
+ # Clean up the message
+ if message.startswith("- "):
+ message = message[2:]
+
+ new_entries.append(f"- {message} ({commit})")
+
+ new_entries.append("") # Add an empty line after each date section
+
+ # If no new entries, just return
+ if not new_entries:
+ if verbose:
+ print("No new entries to add to changelog")
+ return False
+
+ # Combine header and new entries
+ new_content = header + "\n".join(new_entries)
+
+ # Write the updated content
+ with open(changelog_file, "w", encoding="utf-8") as f:
+ f.write(new_content)
+
+ if verbose:
+ print(f"Updated changelog with {len(changelog_entries)} new entries")
+
+ return True
diff --git a/scripts/track_progress/code_stats.py b/scripts/track_progress/code_stats.py
new file mode 100644
index 0000000..1fc03da
--- /dev/null
+++ b/scripts/track_progress/code_stats.py
@@ -0,0 +1,121 @@
+import os
+import glob
+from typing import Dict, Any, List, Optional
+
+
+def count_lines(file_path: str) -> int:
+ """Count lines in a file."""
+ try:
+ with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
+ return len(f.readlines())
+ except Exception:
+ return 0
+
+
+def get_language(file_path: str) -> str:
+ """Determine language from file extension."""
+ ext = os.path.splitext(file_path)[1].lower()
+ language_map = {
+ ".py": "Python",
+ ".cs": "C#",
+ ".js": "JavaScript",
+ ".ts": "TypeScript",
+ ".html": "HTML",
+ ".css": "CSS",
+ ".md": "Markdown",
+ ".json": "JSON",
+ ".xml": "XML",
+ ".java": "Java",
+ ".cpp": "C++",
+ ".c": "C",
+ ".h": "C/C++ Header",
+ ".go": "Go",
+ ".rs": "Rust",
+ ".php": "PHP",
+ ".rb": "Ruby",
+ ".sh": "Shell",
+ ".bat": "Batch",
+ ".ps1": "PowerShell",
+ ".sql": "SQL",
+ ".yaml": "YAML",
+ ".yml": "YAML",
+ }
+ return language_map.get(ext, "Other")
+
+
+def analyze_code_stats(
+ root_dir: str = ".",
+ exclude_dirs: List[str] = None,
+ source_extensions: List[str] = None,
+ top_files_limit: int = 20,
+) -> Dict[str, Any]:
+ """Analyze code statistics for the project."""
+ if exclude_dirs is None:
+ exclude_dirs = ["node_modules", "venv", ".git", ".vs", "bin", "obj"]
+
+ if source_extensions is None:
+ source_extensions = [
+ ".py",
+ ".cs",
+ ".js",
+ ".ts",
+ ".html",
+ ".css",
+ ".md",
+ ]
+
+ stats = {
+ "total_lines": 0,
+ "file_count": 0,
+ "languages": {},
+ "top_files": [],
+ }
+
+ file_stats = []
+
+ # Convert exclude_dirs to absolute paths
+ exclude_paths = [os.path.join(root_dir, d) for d in exclude_dirs]
+
+ # Find all source files
+ for ext in source_extensions:
+ pattern = os.path.join(root_dir, "**", f"*{ext}")
+ for file_path in glob.glob(pattern, recursive=True):
+ # Skip excluded directories
+ if any(
+ file_path.startswith(exclude_path)
+ for exclude_path in exclude_paths
+ ):
+ continue
+
+ # Count lines
+ lines = count_lines(file_path)
+
+ # Get language
+ language = get_language(file_path)
+
+ # Update statistics
+ stats["total_lines"] += lines
+ stats["file_count"] += 1
+
+ # Update language count
+ if language not in stats["languages"]:
+ stats["languages"][language] = 0
+ stats["languages"][language] += 1
+
+ # Add to file stats
+ file_stats.append(
+ {
+ "path": file_path,
+ "name": os.path.basename(file_path),
+ "lines": lines,
+ "language": language,
+ }
+ )
+
+ # Sort file stats by line count
+ file_stats.sort(key=lambda x: x["lines"], reverse=True)
+
+ # Get top files
+ stats["top_files"] = file_stats[:top_files_limit]
+
+ return stats
diff --git a/scripts/track_progress/config.py b/scripts/track_progress/config.py
new file mode 100644
index 0000000..bec408d
--- /dev/null
+++ b/scripts/track_progress/config.py
@@ -0,0 +1,87 @@
+"""
+Configuration management for project status documentation.
+"""
+
+import os
+import json
+from typing import Dict, Any, List, Optional
+
+
+class Config:
+ """Configuration manager for project status documentation."""
+
+ def __init__(self, config_file: str = "scripts/progress_config.json"):
+ """Initialize with configuration file."""
+ self.config_file = config_file
+ self.config = self._load_config()
+
+ def _load_config(self) -> Dict[str, Any]:
+ """Load configuration from file."""
+ default_config = {
+ "project_name": "Project",
+ "output_dir": "docs/status",
+ "status_doc": "status.md",
+ "features_file": "docs/features.md",
+ "changelog_file": "CHANGELOG.md",
+ "git_log_limit": 100,
+ "untagged_commit_limit": 50,
+ "top_files_limit": 20,
+ "exclude_dirs": [
+ "node_modules",
+ "venv",
+ ".git",
+ ".vs",
+ "bin",
+ "obj",
+ ],
+ "source_extensions": [".py", ".cs", ".js", ".ts", ".html", ".css"],
+ "templates": {
+ "status": "status_template.md",
+ "features": "feature_template.md",
+ "changelog": "changelog_template.md",
+ },
+ }
+
+ if not os.path.exists(self.config_file):
+ return default_config
+
+ try:
+ with open(self.config_file, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ # Merge with default config to ensure all keys exist
+ merged_config = default_config.copy()
+ merged_config.update(config)
+
+ return merged_config
+ except Exception:
+ return default_config
+
+ def get(self, key: str, default: Any = None) -> Any:
+ """Get a configuration value."""
+ if "." in key:
+ # Handle nested keys like "templates.status"
+ parts = key.split(".")
+ value = self.config
+ for part in parts:
+ if isinstance(value, dict) and part in value:
+ value = value[part]
+ else:
+ return default
+ return value
+
+ return self.config.get(key, default)
+
+ def set(self, key: str, value: Any) -> None:
+ """Set a configuration value."""
+ if "." in key:
+ # Handle nested keys
+ parts = key.split(".")
+ config = self.config
+ for part in parts[:-1]:
+ if part not in config:
+ config[part] = {}
+ config = config[part]
+ config[parts[-1]] = value
+ else:
+ self.config[key] = value
diff --git a/scripts/track_progress/docs/features.md b/scripts/track_progress/docs/features.md
new file mode 100644
index 0000000..8ef5244
--- /dev/null
+++ b/scripts/track_progress/docs/features.md
@@ -0,0 +1,218 @@
+# Feature Tracking
+
+## Overview
+
+This document tracks the implementation status of all features in the Project project.
+
+## Feature Status Summary
+
+| Status | Description |
+|--------|-------------|
+| โ
Completed | Features that are fully implemented and tested |
+| ๐ In Progress | Features currently being implemented |
+| ๐ Planned | Features planned for future implementation |
+
+## Features
+
+### 2d3d_chunk_management
+
+**Status:** โ
completed
+**Description:** 2D/3D chunk management with memory efficiency
+**Last Update:** 2025-05-10
+
+### asynchronous_chunk_loading
+
+**Status:** โ
completed
+**Description:** Asynchronous chunk loading and processing
+**Last Update:** 2025-05-10
+
+### autogenerate_status_docs
+
+**Status:** ๐ in_progress
+**Description:** Auto-generate status docs and changelog (partially implemented)
+**Last Update:** 2025-05-10
+
+### better_async_exception
+
+**Status:** ๐ in_progress
+**Description:** Better async exception handling with specific types and logging
+**Last Update:** 2025-05-10
+
+### chunk_serialization_and
+
+**Status:** โ
completed
+**Description:** Chunk serialization and deserialization
+**Last Update:** 2025-05-10
+
+### dependencyaware_disposal_logic
+
+**Status:** ๐ in_progress
+**Description:** Dependency-aware disposal logic (Partially Implemented)
+**Last Update:** 2025-05-10
+
+### edge_chunk_unload
+
+**Status:** ๐ in_progress
+**Description:** Edge chunk unload delay under high concurrency
+**Last Update:** 2025-05-10
+
+### events_for_loadunloadsave
+
+**Status:** โ
completed
+**Description:** Events for load/unload/save operations
+**Last Update:** 2025-05-10
+
+### explicit_synchronization_and
+
+**Status:** โ
completed
+**Description:** Explicit synchronization and concurrent collections
+**Last Update:** 2025-05-11
+
+### full_dispose_pattern
+
+**Status:** ๐ in_progress
+**Description:** Full dispose pattern with finalizers for unmanaged resources (Partially Implemented)
+**Last Update:** 2025-05-10
+
+### interfacelevel_dependency_methods
+
+**Status:** ๐ planned
+**Description:** Interface-level dependency methods
+**Last Update:** 2025-05-10
+
+### interfacelevel_spatial_query
+
+**Status:** โ
completed
+**Description:** Interface-level spatial query methods with support for custom filters
+**Last Update:** 2025-05-11
+
+### lru_caching_array
+
+**Status:** โ
completed
+**Description:** LRU caching, array pooling, and air-singleton patterns
+**Last Update:** 2025-05-10
+
+### parallel_processing_of
+
+**Status:** โ
completed
+**Description:** Parallel processing of chunks based on spatial queries and regions
+**Last Update:** 2025-05-11
+
+### parse_git_logs
+
+**Status:** โ
completed
+**Description:** Parse Git logs for status updates (partially implemented testing with this push.)
+**Last Update:** 2025-05-12
+
+### prioritized_chunk_loading
+
+**Status:** ๐ in_progress
+**Description:** Prioritized chunk loading system (partially implemented)
+**Last Update:** 2025-05-10
+
+### python_and_net
+
+**Status:** โ
completed
+**Description:** Python and .NET interoperability
+**Last Update:** 2025-05-10
+
+### runtimeadjustable_configuration_options
+
+**Status:** ๐ planned
+**Description:** Runtime-adjustable configuration options
+**Last Update:** 2025-05-10
+
+### spatial_indexing_and
+
+**Status:** โ
completed
+**Description:** Spatial indexing and region queries with 2D/3D support and quadtree optimization
+**Last Update:** 2025-05-11
+
+### track_and_manage
+
+**Status:** โ
completed
+**Description:** Track and manage chunk resources
+**Last Update:** 2025-05-10
+
+### track_load_times
+
+**Status:** ๐ in_progress
+**Description:** Track load times, cache hit rates, and memory usage (partially implemented)
+**Last Update:** 2025-05-10
+
+### track_progress_and feature_statuses
+
+**Status:** โ
completed
+**Description:** Track progress and auto-update status documents (you can use cron or something or a vs commit script that requires running this after committing)
+**Last Update:** 2025-05-12
+
+## Feature Categories
+
+### Completed Features
+
+- **2d3d_chunk_management**: 2D/3D chunk management with memory efficiency
+- **asynchronous_chunk_loading**: Asynchronous chunk loading and processing
+- **chunk_serialization_and**: Chunk serialization and deserialization
+- **events_for_loadunloadsave**: Events for load/unload/save operations
+- **explicit_synchronization_and**: Explicit synchronization and concurrent collections
+- **interfacelevel_spatial_query**: Interface-level spatial query methods with support for custom filters
+- **lru_caching_array**: LRU caching, array pooling, and air-singleton patterns
+- **parallel_processing_of**: Parallel processing of chunks based on spatial queries and regions
+- **python_and_net**: Python and .NET interoperability
+- **spatial_indexing_and**: Spatial indexing and region queries with 2D/3D support and quadtree optimization
+
+### In Progress Features
+
+- **autogenerate_status_docs**: Auto-generate status docs and changelog (partially implemented)
+- **better_async_exception**: Better async exception handling with specific types and logging
+- **dependencyaware_disposal_logic**: Dependency-aware disposal logic (Partially Implemented)
+- **edge_chunk_unload**: Edge chunk unload delay under high concurrency
+- **full_dispose_pattern**: Full dispose pattern with finalizers for unmanaged resources (Partially Implemented)
+- **parse_git_logs**: Parse Git logs for status updates (partially implemented testing with this push.)
+- **prioritized_chunk_loading**: Prioritized chunk loading system (partially implemented)
+- **track_load_times**: Track load times, cache hit rates, and memory usage (partially implemented)
+
+### Planned Features
+
+- **interfacelevel_dependency_methods**: Interface-level dependency methods
+- **runtimeadjustable_configuration_options**: Runtime-adjustable configuration options
+
+
\ No newline at end of file
diff --git a/scripts/track_progress/docs/status/status.md b/scripts/track_progress/docs/status/status.md
new file mode 100644
index 0000000..102ce57
--- /dev/null
+++ b/scripts/track_progress/docs/status/status.md
@@ -0,0 +1,100 @@
+# Project Development Status
+
+Last updated: 2025-05-12
+
+## Code Statistics
+
+
+Total lines of code: **2151**
+
+Number of source files: **15**
+
+### Files by Language
+
+- **Python**: 15 files
+
+
+### Top Files by Line Count
+
+| File | Lines | Language | Path |
+|------|------:|----------|------|
+
+| 2D3D Chunk Management | โ
completed | 2D/3D chunk management with memory efficiency | 2025-05-10 | |
+
+| Asynchronous Chunk Loading | โ
completed | Asynchronous chunk loading and processing | 2025-05-10 | |
+
+| Autogenerate Status Docs | ๐ in_progress | Auto-generate status docs and changelog (partially implemented) | 2025-05-10 | |
+
+| Better Async Exception | ๐ in_progress | Better async exception handling with specific types and logging | 2025-05-10 | |
+
+| Chunk Serialization And | โ
completed | Chunk serialization and deserialization | 2025-05-10 | |
+
+| Dependencyaware Disposal Logic | ๐ in_progress | Dependency-aware disposal logic (Partially Implemented) | 2025-05-10 | |
+
+| Edge Chunk Unload | ๐ in_progress | Edge chunk unload delay under high concurrency | 2025-05-10 | |
+
+| Events For Loadunloadsave | โ
completed | Events for load/unload/save operations | 2025-05-10 | |
+
+| Explicit Synchronization And | โ
completed | Explicit synchronization and concurrent collections | 2025-05-11 | |
+
+| Full Dispose Pattern | ๐ in_progress | Full dispose pattern with finalizers for unmanaged resources (Partially Implemented) | 2025-05-10 | |
+
+| Interfacelevel Dependency Methods | ๐ planned | Interface-level dependency methods | 2025-05-10 | |
+
+| Interfacelevel Spatial Query | โ
completed | Interface-level spatial query methods with support for custom filters | 2025-05-11 | |
+
+| Lru Caching Array | โ
completed | LRU caching, array pooling, and air-singleton patterns | 2025-05-10 | |
+
+| Parallel Processing Of | โ
completed | Parallel processing of chunks based on spatial queries and regions | 2025-05-11 | |
+
+| Parse Git Logs | ๐ in_progress | Parse Git logs for status updates (partially implemented testing with this push.) | 2025-05-11 | |
+
+| Prioritized Chunk Loading | ๐ in_progress | Prioritized chunk loading system (partially implemented) | 2025-05-10 | |
+
+| Python And Net | โ
completed | Python and .NET interoperability | 2025-05-10 | |
+
+| Runtimeadjustable Configuration Options | ๐ planned | Runtime-adjustable configuration options | 2025-05-10 | |
+
+| Spatial Indexing And | โ
completed | Spatial indexing and region queries with 2D/3D support and quadtree optimization | 2025-05-11 | |
+
+| Track And Manage | โ
completed | Track and manage chunk resources | 2025-05-10 | |
+
+| Track Feature Statuses | โ
completed | Track feature statuses | 2025-05-11 | |
+
+| Track Load Times | ๐ in_progress | Track load times, cache hit rates, and memory usage (partially implemented) | 2025-05-10 | |
+
+| Track Progress And | ๐ in_progress | Track progress and auto-update status documents (mostly implemented kinda still manual...) | 2025-05-11 | |
+
+
+## Recent Updates
+
+
+- 2025-05-11: part of the code formatter fix and minor bug fixes (5352e90)
+
+- 2025-05-11: fixed code formatter (01d2cbf)
+
+- 2025-05-11: fixed track_progress not getting git history it was not getting the data correctly (657e19f)
+
+- 2025-05-11: removed unnecessary ignores in gitignore (79d6716)
+
+- 2025-05-11: XML comments added Builds cleanly (0ae815c)
+
+- 2025-05-11: updated track_progress restored accidentally deleted source file (8a6f914)
+
+- 2025-05-10: fixed a bug of not building (09ee0d8)
+
+- 2025-05-10: added a build (e59a909)
+
+- 2025-05-10: updated ChunkMark (f35c612)
+
+- 2025-05-10: updated memory memory handling to be more safe (becedcc)
+
+- 2025-05-09: first commit (f1b3868)
+
+
+{% if known_issues %}
+## Known Issues
+
+
+
+
diff --git a/scripts/track_progress/feature_markdown.py b/scripts/track_progress/feature_markdown.py
new file mode 100644
index 0000000..4b74fda
--- /dev/null
+++ b/scripts/track_progress/feature_markdown.py
@@ -0,0 +1,317 @@
+"""
+Feature markdown utilities for project status documentation.
+"""
+
+import os
+from typing import Dict, Any, List, Optional
+import re
+from datetime import datetime
+
+# Mapping of status to emojis
+STATUS_EMOJIS = {
+ "completed": "โ
",
+ "in_progress": "๐",
+ "planned": "๐",
+ "error": "โฃ๏ธ",
+ "blocked": "โ",
+ "failed": "โ",
+ "warning": "โ ๏ธ",
+ "unknown": "โ",
+ "done": "โ
",
+ "testing": "๐งช",
+ "review": "๐",
+ "design": "๐จ",
+ "research": "๐",
+ "deprecated": "๐๏ธ",
+ "postponed": "โณ",
+}
+
+
+def emoji_status(status: str) -> str:
+ """Return emoji for a status."""
+ return STATUS_EMOJIS.get(status.lower(), STATUS_EMOJIS["unknown"])
+
+
+def format_feature_md(feature_name: str, data: dict) -> str:
+ """Format a single feature into Markdown."""
+ status = data.get("status", "unknown")
+ status_emoji = emoji_status(status)
+
+ lines = [f"## {feature_name}"]
+ lines.append(f"- Status: {status_emoji} {status}")
+
+ if "description" in data:
+ lines.append(f"- Description: {data['description']}")
+
+ if "date" in data:
+ lines.append(f"- Last Update: {data['date']}")
+
+ if "author" in data:
+ lines.append(f"- Updated By: {data['author']}")
+
+ if "details" in data:
+ lines.append(f"- Details: {data['details']}")
+
+ return "\n".join(lines)
+
+
+def build_feature_md(features: dict, title: str = "# Feature Tracking") -> str:
+ """Build the full Markdown document from features dictionary."""
+ lines = [title, ""]
+
+ for feature_name, data in features.items():
+ lines.append(format_feature_md(feature_name, data))
+ lines.append("") # Add an empty line between features
+
+ return "\n".join(lines)
+
+
+def extract_features(
+ features_file: str, verbose: bool = False
+) -> Dict[str, Dict[str, Any]]:
+ """Extract all valid features from the file, focusing on descriptions."""
+ features = {}
+ seen_descriptions = set()
+
+ if not os.path.exists(features_file):
+ if verbose:
+ print(f"File not found: {features_file}")
+ return features
+
+ with open(features_file, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ # First, try to extract features from the specific format
+ # This format has ### feature_name followed by status, description, date
+ status_blocks = re.findall(
+ r"### ([^\n]+)\s+\*\*Status:\*\* ([^\n]+)\s+\*\*Description:\*\* ([^\n]+)\s+\*\*Last Update:\*\* ([^\n]+)",
+ content,
+ )
+
+ for feature_key, status_line, desc, date in status_blocks:
+ desc = desc.strip()
+ date = date.strip()
+
+ if desc and len(desc) > 2 and desc not in seen_descriptions:
+ seen_descriptions.add(desc)
+
+ # Extract status without emoji
+ status_match = re.search(
+ r"[^a-zA-Z]*([a-zA-Z_]+)", status_line
+ )
+ status = (
+ status_match.group(1).lower()
+ if status_match
+ else "planned"
+ )
+
+ # Clean up the feature key
+ clean_key = re.sub(
+ r"[^a-zA-Z0-9_]", "", feature_key.replace(" ", "_").lower()
+ )
+ if not clean_key or len(clean_key) < 2:
+ # Generate key from description
+ words = desc.split()[:3]
+ clean_key = "_".join(words).lower()
+ clean_key = re.sub(r"[^a-zA-Z0-9_]", "", clean_key)
+
+ features[clean_key] = {
+ "status": status,
+ "description": desc,
+ "date": date,
+ "author": "",
+ "details": "",
+ }
+
+ # Also try the format: ### **Status:** status \n **Description:** desc \n **Last Update:** date
+ alt_blocks = re.findall(
+ r"### \*\*Status:\*\* ([^\n]+)\s+\*\*Description:\*\* ([^\n]+)\s+\*\*Last Update:\*\* ([^\n]+)",
+ content,
+ )
+
+ for status_line, desc, date in alt_blocks:
+ desc = desc.strip()
+ date = date.strip()
+
+ if desc and len(desc) > 2 and desc not in seen_descriptions:
+ seen_descriptions.add(desc)
+
+ # Extract status without emoji
+ status_match = re.search(
+ r"[^a-zA-Z]*([a-zA-Z_]+)", status_line
+ )
+ status = (
+ status_match.group(1).lower()
+ if status_match
+ else "planned"
+ )
+
+ # Generate key from description
+ words = desc.split()[:3]
+ clean_key = "_".join(words).lower()
+ clean_key = re.sub(r"[^a-zA-Z0-9_]", "", clean_key)
+
+ features[clean_key] = {
+ "status": status,
+ "description": desc,
+ "date": date,
+ "author": "",
+ "details": "",
+ }
+
+ return features
+
+
+def enrich_features(
+ features: Dict[str, Dict[str, Any]]
+) -> Dict[str, Dict[str, Any]]:
+ """Add additional information to features for display purposes."""
+ enriched = {}
+
+ for key, data in features.items():
+ enriched[key] = data.copy()
+ status = data.get("status", "unknown")
+ enriched[key]["status_emoji"] = emoji_status(status)
+
+ # Add title-cased key for display
+ enriched[key]["display_name"] = key.replace("_", " ").title()
+
+ return enriched
+
+
+def generate_features_md(
+ features: Dict[str, Dict[str, Any]], project_name: str
+) -> str:
+ """Generate a clean features markdown from the features dictionary."""
+ lines = [
+ "# Feature Tracking",
+ "",
+ "## Overview",
+ "",
+ f"This document tracks the implementation status of all features in the {project_name} project.",
+ "",
+ "## Feature Status Summary",
+ "",
+ "| Status | Description |",
+ "|--------|-------------|",
+ "| โ
Completed | Features that are fully implemented and tested |",
+ "| ๐ In Progress | Features currently being implemented |",
+ "| ๐ Planned | Features planned for future implementation |",
+ "",
+ "## Features",
+ "",
+ ]
+
+ # Add each feature
+ for feature_key, feature in sorted(features.items()):
+ status = feature.get("status", "planned").lower()
+ status_emoji = emoji_status(status)
+ description = feature.get("description", "")
+ date = feature.get("date", "") or datetime.now().strftime("%Y-%m-%d")
+ author = feature.get("author", "")
+ details = feature.get("details", "")
+
+ lines.append(f"### {feature_key}")
+ lines.append("")
+ lines.append(f"**Status:** {status_emoji} {status}")
+ lines.append(f"**Description:** {description}")
+ lines.append(f"**Last Update:** {date}")
+
+ if author:
+ lines.append(f"**Owner:** {author}")
+
+ if details:
+ lines.append(f"**Details:** {details}")
+
+ lines.append("")
+
+ # Add categorized lists
+ lines.append("## Feature Categories")
+ lines.append("")
+
+ # Completed features
+ lines.append("### Completed Features")
+ lines.append("")
+ completed_count = 0
+ for feature_key, feature in sorted(features.items()):
+ if feature.get("status", "").lower() in ["completed", "done"]:
+ lines.append(
+ f"- **{feature_key}**: {feature.get('description', '')}"
+ )
+ completed_count += 1
+ if completed_count == 0:
+ lines.append("*No completed features yet.*")
+ lines.append("")
+
+ # In progress features
+ lines.append("### In Progress Features")
+ lines.append("")
+ in_progress_count = 0
+ for feature_key, feature in sorted(features.items()):
+ if feature.get("status", "").lower() == "in_progress":
+ lines.append(
+ f"- **{feature_key}**: {feature.get('description', '')}"
+ )
+ in_progress_count += 1
+ if in_progress_count == 0:
+ lines.append("*No features currently in progress.*")
+ lines.append("")
+
+ # Planned features
+ lines.append("### Planned Features")
+ lines.append("")
+ planned_count = 0
+ for feature_key, feature in sorted(features.items()):
+ if feature.get("status", "").lower() == "planned":
+ lines.append(
+ f"- **{feature_key}**: {feature.get('description', '')}"
+ )
+ planned_count += 1
+ if planned_count == 0:
+ lines.append("*No planned features yet.*")
+ lines.append("")
+
+ # Add CSS styling
+ lines.append(
+ """"""
+ )
+
+ return "\n".join(lines)
diff --git a/scripts/track_progress/git_analyzer.py b/scripts/track_progress/git_analyzer.py
new file mode 100644
index 0000000..04b2283
--- /dev/null
+++ b/scripts/track_progress/git_analyzer.py
@@ -0,0 +1,238 @@
+import os
+import re
+import subprocess
+from datetime import datetime
+from typing import Dict, Any, List, Optional, Tuple
+
+
+class GitAnalyzer:
+ """Analyze Git repository for project status information."""
+
+ def __init__(self, repo_path: str = "."):
+ """Initialize with repository path."""
+ self.repo_path = repo_path
+
+ def _run_git_command(self, command: List[str]) -> str:
+ """Run a Git command and return the output."""
+ try:
+ result = subprocess.run(
+ ["git"] + command,
+ cwd=self.repo_path,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ return result.stdout.strip()
+ except subprocess.CalledProcessError:
+ return ""
+
+ def get_repo_info(self) -> Dict[str, Any]:
+ """Get basic repository information."""
+ info = {}
+
+ # Get remote URL
+ remote_url = self._run_git_command(["remote", "get-url", "origin"])
+ info["remote_url"] = remote_url
+
+ # Get current branch
+ branch = self._run_git_command(["branch", "--show-current"])
+ info["branch"] = branch
+
+ # Get last commit
+ last_commit = self._run_git_command(
+ ["log", "-1", "--pretty=format:%h|%s|%ad|%an", "--date=short"]
+ )
+ if last_commit:
+ parts = last_commit.split("|")
+ if len(parts) >= 4:
+ info["last_commit"] = {
+ "hash": parts[0],
+ "message": parts[1],
+ "date": parts[2],
+ "author": parts[3],
+ }
+
+ return info
+
+ def analyze_commits(
+ self, limit: int = 100, untagged_limit: int = 50
+ ) -> Dict[str, Any]:
+ """Analyze Git commits for feature updates, changelog entries, etc."""
+ result = {
+ "feature_updates": {},
+ "new_features": {},
+ "changelog_entries": [],
+ "fixes": [],
+ "issues": [],
+ "untagged_commits": [],
+ "milestones": {},
+ "roadmap_items": {},
+ }
+
+ # Regular expressions for parsing commit messages
+ status_re = re.compile(r"\[status:(\w+)\]")
+ feature_re = re.compile(r"\[feature:(\w+)\]")
+ new_feature_re = re.compile(r"\[new-feature:(\w+):(.+?)\]")
+ changelog_re = re.compile(r"\[changelog:(.+?)\]")
+ fix_re = re.compile(r"\[fix:(.+?)\]")
+ issue_re = re.compile(r"\[issue:(.+?)\]")
+ milestone_re = re.compile(r"\[milestone:(\w+)\]")
+ roadmap_re = re.compile(r"\[roadmap:(\w+):(.+?)\]")
+
+ # Get Git log
+ git_log = self._run_git_command(
+ [
+ "log",
+ f"-n{limit}",
+ "--pretty=format:%h|%s|%ad|%an",
+ "--date=short",
+ ]
+ )
+
+ untagged_count = 0
+
+ # Parse commits
+ for line in git_log.splitlines():
+ parts = line.split("|")
+ if len(parts) < 4:
+ continue
+
+ commit_hash, subject, date, author = parts
+
+ # Check for tags
+ status_match = status_re.search(subject)
+ feature_match = feature_re.search(subject)
+ new_feature_match = new_feature_re.search(subject)
+ changelog_match = changelog_re.search(subject)
+ fix_match = fix_re.search(subject)
+ issue_match = issue_re.search(subject)
+ milestone_match = milestone_re.search(subject)
+ roadmap_match = roadmap_re.search(subject)
+
+ # Process status updates
+ if status_match:
+ status_key = status_match.group(1)
+ result["feature_updates"][status_key] = {
+ "status": "completed",
+ "date": date,
+ "author": author,
+ }
+
+ # Process feature updates
+ if feature_match:
+ feature_key = feature_match.group(1)
+ result["feature_updates"][feature_key] = {
+ "status": "completed",
+ "date": date,
+ "author": author,
+ }
+
+ # Process new features
+ if new_feature_match:
+ feature_key = new_feature_match.group(1)
+ feature_desc = new_feature_match.group(2)
+ result["new_features"][feature_key] = {
+ "status": "planned",
+ "description": feature_desc,
+ "date": date,
+ "author": author,
+ }
+
+ # Process changelog entries
+ if changelog_match:
+ result["changelog_entries"].append(
+ {
+ "message": changelog_match.group(1),
+ "date": date,
+ "commit": commit_hash,
+ "author": author,
+ }
+ )
+
+ # Process fixes
+ if fix_match:
+ result["fixes"].append(
+ {
+ "message": fix_match.group(1),
+ "date": date,
+ "commit": commit_hash,
+ "author": author,
+ }
+ )
+
+ # Process issues
+ if issue_match:
+ result["issues"].append(
+ {
+ "message": issue_match.group(1),
+ "date": date,
+ "commit": commit_hash,
+ "author": author,
+ }
+ )
+
+ # Process milestones
+ if milestone_match:
+ milestone_key = milestone_match.group(1)
+ if milestone_key not in result["milestones"]:
+ result["milestones"][milestone_key] = {
+ "first_date": date,
+ "last_date": date,
+ "commits": [],
+ }
+ else:
+ result["milestones"][milestone_key]["last_date"] = date
+
+ result["milestones"][milestone_key]["commits"].append(
+ commit_hash
+ )
+
+ # Process roadmap items
+ if roadmap_match:
+ milestone_key = roadmap_match.group(1)
+ item_desc = roadmap_match.group(2)
+
+ if milestone_key not in result["roadmap_items"]:
+ result["roadmap_items"][milestone_key] = []
+
+ result["roadmap_items"][milestone_key].append(
+ {
+ "description": item_desc,
+ "date": date,
+ "commit": commit_hash,
+ "author": author,
+ }
+ )
+
+ # Process untagged commits
+ if not any(
+ [
+ status_match,
+ feature_match,
+ new_feature_match,
+ changelog_match,
+ fix_match,
+ issue_match,
+ milestone_match,
+ roadmap_match,
+ ]
+ ):
+ if untagged_count < untagged_limit:
+ # Skip merge commits and very short messages
+ if not subject.startswith("Merge ") and len(subject) > 5:
+ # Extract the first sentence or up to 100 chars
+ commit_desc = subject.split(".")[0]
+ if len(commit_desc) > 100:
+ commit_desc = commit_desc[:97] + "..."
+
+ result["untagged_commits"].append(
+ {
+ "message": commit_desc,
+ "date": date,
+ "commit": commit_hash,
+ "author": author,
+ }
+ )
+ untagged_count += 1
+
+ return result
diff --git a/scripts/track_progress/progress_config.json b/scripts/track_progress/progress_config.json
new file mode 100644
index 0000000..dc927a9
--- /dev/null
+++ b/scripts/track_progress/progress_config.json
@@ -0,0 +1,37 @@
+{
+ "project_name": "AdvChkSys",
+ "output_dir": "docs/status",
+ "status_doc": "ChunkManager-Status.md",
+ "features_file": "docs/features.md",
+ "changelog_file": "CHANGELOG.md",
+ "git_log_limit": 100,
+ "untagged_commit_limit": 50,
+ "top_files_limit": 20,
+ "exclude_dirs": [
+ "node_modules",
+ "venv",
+ ".git",
+ ".vs",
+ "bin",
+ "obj",
+ "dist",
+ "build"
+ ],
+ "source_extensions": [
+ ".py",
+ ".cs",
+ ".js",
+ ".ts",
+ ".html",
+ ".css",
+ ".rs",
+ ".go",
+ ".java",
+ ".md"
+ ],
+ "templates": {
+ "status": "status_template.md",
+ "features": "feature_template.md",
+ "changelog": "changelog_template.md"
+ }
+}
\ No newline at end of file
diff --git a/scripts/track_progress/requirements.txt b/scripts/track_progress/requirements.txt
new file mode 100644
index 0000000..6540481
--- /dev/null
+++ b/scripts/track_progress/requirements.txt
@@ -0,0 +1,16 @@
+# Core dependencies
+argparse # For parsing command-line arguments
+shutil # For file and directory operations
+GitPython # For Git repository analysis
+Jinja2 # Alternative template engine (optional)
+Markdown # For Markdown processing (optional)
+
+# Build dependencies (only needed for building the executable)
+nuitka # For compiling Python to executable
+ordered-set # Required by Nuitka
+zstandard # For better compression in Nuitka builds
+
+# Development dependencies (optional)
+black # Code formatting
+pylint # Code linting
+pytest # Testing
\ No newline at end of file
diff --git a/scripts/track_progress/roadmap_generator.py b/scripts/track_progress/roadmap_generator.py
new file mode 100644
index 0000000..b56d766
--- /dev/null
+++ b/scripts/track_progress/roadmap_generator.py
@@ -0,0 +1,33 @@
+"""
+Roadmap generator for project status documentation.
+"""
+
+from typing import Dict, Any, List, Optional
+
+
+def generate_roadmap(
+ milestones: Dict[str, Dict[str, Any]],
+ roadmap_items: Dict[str, List[Dict[str, Any]]],
+) -> List[Dict[str, Any]]:
+ """Generate roadmap from milestones and roadmap items."""
+ roadmap = []
+
+ # Process each milestone
+ for milestone_key, milestone_data in milestones.items():
+ # Create milestone entry
+ milestone = {
+ "name": milestone_key.replace("_", " ").title(),
+ "target_date": milestone_data.get("last_date", "TBD"),
+ "items": [],
+ }
+
+ # Add roadmap items for this milestone
+ if milestone_key in roadmap_items:
+ for item in roadmap_items[milestone_key]:
+ milestone["items"].append(
+ {"description": item["description"], "status": "planned"}
+ )
+
+ roadmap.append(milestone)
+
+ return roadmap
diff --git a/scripts/track_progress/scripts/SCRIPTS.md b/scripts/track_progress/scripts/SCRIPTS.md
new file mode 100644
index 0000000..cf6bbee
--- /dev/null
+++ b/scripts/track_progress/scripts/SCRIPTS.md
@@ -0,0 +1,55 @@
+# Progress Tracker Scripts
+
+This directory contains utility scripts for building, installing, and running the progress tracker.
+
+## Available Scripts
+
+### setup_install.py
+
+Installs the progress tracker to your project.
+
+```bash
+python setup_install.py --dest scripts --project "Your Project Name"
+```
+
+Options:
+- `--dest folder`: Set destination directory (default: "scripts")
+- `--project name`: Set project name (default: "Project")
+- `--verbose` or `-v`: Enable verbose output
+- `--force` or `-f`: Force overwrite existing files
+
+### build_with_nuitka.py
+
+Builds the progress tracker into a standalone executable using Nuitka.
+
+```bash
+python build_with_nuitka.py
+```
+
+Options:
+- `--output-dir`: Output directory for the build (default: "dist")
+- `--verbose` or `-v`: Enable verbose output
+
+### create_launcher_scripts.py
+
+Creates launcher scripts for running the progress tracker. This is usually called by setup_install.py.
+
+## Launcher Scripts
+
+The following launcher scripts are created during installation:
+
+- `run_progress_tracker.py`: Python launcher script
+- `run_progress_tracker.sh`: Shell launcher script (Linux/macOS)
+- `run_progress_tracker.bat`: Batch launcher script (Windows)
+
+## Usage Example
+
+1. Install the tracker:
+ ```bash
+ python setup_install.py --project "My Project" --verbose
+ ```
+
+2. Run the tracker using one of the launcher scripts:
+ - Windows: `run_progress_tracker.bat`
+ - Linux/macOS: `./run_progress_tracker.sh`
+ - Any platform: `python run_progress_tracker.py`
\ No newline at end of file
diff --git a/scripts/track_progress/scripts/build_with_nuitka.py b/scripts/track_progress/scripts/build_with_nuitka.py
new file mode 100644
index 0000000..f920509
--- /dev/null
+++ b/scripts/track_progress/scripts/build_with_nuitka.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+"""
+Build the progress tracker with Nuitka.
+"""
+import os
+import sys
+import shutil
+import subprocess
+import argparse
+
+
+def main():
+ """Build the progress tracker with Nuitka."""
+ parser = argparse.ArgumentParser(
+ description="Build the progress tracker with Nuitka"
+ )
+ parser.add_argument(
+ "--output-dir", default="dist", help="Output directory for the build"
+ )
+ parser.add_argument(
+ "--verbose", "-v", action="store_true", help="Enable verbose output"
+ )
+ parser.add_argument(
+ "--move-exe",
+ action="store_true",
+ help="Move executable to parent directory after build",
+ )
+ args = parser.parse_args()
+
+ verbose = args.verbose
+ output_dir = args.output_dir
+ move_exe = args.move_exe
+
+ # Get the directory of this script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ parent_dir = os.path.dirname(script_dir) # scripts/track_progress
+
+ # Check if Nuitka is installed
+ try:
+ subprocess.check_call(
+ [sys.executable, "-m", "pip", "show", "nuitka"],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ except subprocess.CalledProcessError:
+ print("Nuitka is not installed. Installing...")
+ try:
+ subprocess.check_call(
+ [sys.executable, "-m", "pip", "install", "nuitka"]
+ )
+ except subprocess.CalledProcessError:
+ print(
+ "Failed to install Nuitka. Please install it manually with 'pip install nuitka'."
+ )
+ return 1
+
+ # Create output directory
+ dist_dir = os.path.join(parent_dir, output_dir)
+ os.makedirs(dist_dir, exist_ok=True)
+
+ # Path to the main script
+ main_script = os.path.join(parent_dir, "track_progress.py")
+
+ if not os.path.exists(main_script):
+ print(f"Error: Main script not found at {main_script}")
+ return 1
+
+ # Build command
+ cmd = [
+ sys.executable,
+ "-m",
+ "nuitka",
+ "--standalone",
+ "--onefile",
+ "--lto=auto",
+ "--jobs=4",
+ "--include-package=track_progress",
+ "--include-data-dir="
+ + os.path.join(parent_dir, "templates")
+ + "=templates",
+ "--output-dir=" + dist_dir,
+ main_script,
+ ]
+
+ # Try to enable UPX if available
+ try:
+ subprocess.check_call(
+ ["upx", "--version"],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ cmd.append("--enable-plugin=upx")
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ if verbose:
+ print("UPX not found, continuing without compression")
+
+ if verbose:
+ print(f"Building with command: {' '.join(cmd)}")
+ else:
+ cmd.append("--quiet")
+
+ # Run Nuitka
+ try:
+ subprocess.check_call(cmd)
+ print(f"Build successful! Executable is in {dist_dir}")
+
+ # Move the executable to the parent directory if requested
+ if move_exe:
+ exe_ext = ".exe" if sys.platform == "win32" else ""
+ exe_name = f"track_progress{exe_ext}"
+ src_exe = os.path.join(dist_dir, exe_name)
+ dst_exe = os.path.join(parent_dir, exe_name)
+
+ if os.path.exists(src_exe):
+ # Remove existing executable if it exists
+ if os.path.exists(dst_exe):
+ os.remove(dst_exe)
+
+ # Move the executable
+ shutil.move(src_exe, dst_exe)
+ if verbose:
+ print(f"Moved executable from {src_exe} to {dst_exe}")
+ else:
+ print(f"Warning: Built executable not found at {src_exe}")
+
+ return 0
+ except subprocess.CalledProcessError as e:
+ print(f"Build failed: {e}")
+ return 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/scripts/track_progress/scripts/create_launcher_scripts.py b/scripts/track_progress/scripts/create_launcher_scripts.py
new file mode 100644
index 0000000..89c1e47
--- /dev/null
+++ b/scripts/track_progress/scripts/create_launcher_scripts.py
@@ -0,0 +1,217 @@
+"""
+Create launcher scripts for the progress tracker.
+"""
+
+import os
+import sys
+from typing import List, Tuple
+
+
+def create_python_launcher(
+ output_path: str, dest_dir: str, project_name: str
+) -> bool:
+ """Create a Python launcher script."""
+ content = f"""#!/usr/bin/env python3
+\"\"\"
+Launcher for the progress tracker.
+Tries to use the executable version first, then falls back to the Python version.
+\"\"\"
+import os
+import sys
+import subprocess
+
+def main():
+ \"\"\"Main function to run the tracker.\"\"\"
+ # Get the directory of this script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ tracker_dir = os.path.dirname(script_dir)
+
+ # Paths to executable and Python script
+ exe_ext = ".exe" if sys.platform == "win32" else ""
+ exe_path = os.path.join(tracker_dir, f"track_progress{{exe_ext}}")
+ py_path = os.path.join(tracker_dir, "track_progress.py")
+
+ # Command-line arguments to pass through
+ args = sys.argv[1:]
+
+ # Try executable first
+ if os.path.exists(exe_path) and (sys.platform == "win32" or os.access(exe_path, os.X_OK)):
+ print("Running Progress Tracker (executable version)...")
+ try:
+ return subprocess.call([exe_path] + args)
+ except Exception as e:
+ print(f"Error running executable: {{e}}")
+ print("Falling back to Python version...")
+ else:
+ print("Executable not found, trying Python version...")
+
+ # Fall back to Python script
+ if os.path.exists(py_path):
+ print("Running Progress Tracker (Python version)...")
+ try:
+ # Add the tracker directory to the Python path
+ sys.path.insert(0, tracker_dir)
+
+ # Change to the tracker directory
+ os.chdir(tracker_dir)
+
+ # Run the Python script
+ return subprocess.call([sys.executable, py_path] + args)
+ except Exception as e:
+ print(f"Error running Python script: {{e}}")
+ return 1
+ else:
+ print("Error: Neither executable nor Python script found at:")
+ print(f"- {{exe_path}}")
+ print(f"- {{py_path}}")
+ return 1
+
+if __name__ == "__main__":
+ exit_code = main()
+ if exit_code == 0:
+ print("Progress tracking completed successfully!")
+ else:
+ print(f"Error running progress tracker! (Exit code: {{exit_code}})")
+
+ # On Windows, pause to see the output
+ if sys.platform == "win32":
+ input("Press Enter to continue...")
+
+ sys.exit(exit_code)
+"""
+
+ try:
+ with open(output_path, "w", encoding="utf-8", newline="\n") as f:
+ f.write(content)
+ return True
+ except Exception as e:
+ print(f"Error creating Python launcher: {e}")
+ return False
+
+
+def create_shell_launcher(output_path: str) -> bool:
+ """Create a shell launcher script."""
+ content = """#!/bin/bash
+
+# Get the directory of this script
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+TRACKER_DIR="$(dirname "$SCRIPT_DIR")"
+EXE_PATH="$TRACKER_DIR/track_progress"
+PY_PATH="$TRACKER_DIR/track_progress.py"
+
+# Check if executable exists and is executable
+if [ -x "$EXE_PATH" ]; then
+ echo "Running Progress Tracker (executable version)..."
+ "$EXE_PATH" "$@"
+elif [ -f "$PY_PATH" ]; then
+ echo "Executable not found, trying Python version..."
+ echo "Running Progress Tracker (Python version)..."
+ python "$PY_PATH" "$@"
+else
+ echo "Error: Neither executable nor Python script found at:"
+ echo "- $EXE_PATH"
+ echo "- $PY_PATH"
+ exit 1
+fi
+
+EXIT_CODE=$?
+if [ $EXIT_CODE -eq 0 ]; then
+ echo "Progress tracking completed successfully!"
+else
+ echo "Error running progress tracker! (Exit code: $EXIT_CODE)"
+fi
+"""
+
+ try:
+ with open(output_path, "w", encoding="utf-8", newline="\n") as f:
+ f.write(content)
+
+ # Make the script executable on Unix-like systems
+ if sys.platform != "win32":
+ os.chmod(output_path, 0o755)
+
+ return True
+ except Exception as e:
+ print(f"Error creating shell launcher: {e}")
+ return False
+
+
+def create_batch_launcher(output_path: str) -> bool:
+ """Create a batch launcher script."""
+ content = """@echo off
+setlocal enabledelayedexpansion
+
+REM Get the directory of this script
+set "SCRIPT_DIR=%~dp0"
+set "TRACKER_DIR=%SCRIPT_DIR%.."
+set "EXE_PATH=%TRACKER_DIR%\\track_progress.exe"
+set "PY_PATH=%TRACKER_DIR%\\track_progress.py"
+
+REM Check if executable exists
+if exist "%EXE_PATH%" (
+ echo Running Progress Tracker (executable version)...
+ "%EXE_PATH%" %*
+) else (
+ echo Executable not found, trying Python version...
+ if exist "%PY_PATH%" (
+ echo Running Progress Tracker (Python version)...
+ python "%PY_PATH%" %*
+ ) else (
+ echo Error: Neither executable nor Python script found at:
+ echo - %EXE_PATH%
+ echo - %PY_PATH%
+ exit /b 1
+ )
+)
+
+if %ERRORLEVEL% EQU 0 (
+ echo Progress tracking completed successfully!
+) else (
+ echo Error running progress tracker! (Exit code: %ERRORLEVEL%)
+)
+pause
+"""
+
+ try:
+ with open(output_path, "w", encoding="utf-8", newline="\n") as f:
+ f.write(content)
+ return True
+ except Exception as e:
+ print(f"Error creating batch launcher: {e}")
+ return False
+
+
+def ensure_launcher_scripts(
+ script_dir: str,
+ dest_dir: str,
+ project_name: str,
+ verbose: bool = False,
+ force: bool = False,
+) -> List[str]:
+ """Ensure all launcher scripts exist in the script directory."""
+ created_scripts = []
+
+ # Define the scripts to check/create
+ scripts_to_ensure = [
+ ("run_progress_tracker.py", create_python_launcher),
+ ("run_progress_tracker.sh", create_shell_launcher),
+ ("run_progress_tracker.bat", create_batch_launcher),
+ ]
+
+ for script_name, create_func in scripts_to_ensure:
+ script_path = os.path.join(script_dir, script_name)
+
+ if not os.path.exists(script_path) or force:
+ if verbose:
+ print(f"Creating launcher script: {script_path}")
+
+ if create_func(script_path, dest_dir, project_name):
+ created_scripts.append(script_path)
+ if verbose:
+ print(f"Created {script_path}")
+ else:
+ print(f"Failed to create {script_path}")
+ elif verbose:
+ print(f"Launcher script already exists: {script_path}")
+
+ return created_scripts
diff --git a/scripts/track_progress/scripts/run_progress_tracker.bat b/scripts/track_progress/scripts/run_progress_tracker.bat
new file mode 100644
index 0000000..6bfcef8
--- /dev/null
+++ b/scripts/track_progress/scripts/run_progress_tracker.bat
@@ -0,0 +1,32 @@
+@echo off
+setlocal enabledelayedexpansion
+
+REM Get the directory of this script
+set "SCRIPT_DIR=%~dp0"
+set "TRACKER_DIR=%SCRIPT_DIR%.."
+set "EXE_PATH=%TRACKER_DIR%\track_progress.exe"
+set "PY_PATH=%TRACKER_DIR%\track_progress.py"
+
+REM Check if executable exists
+if exist "%EXE_PATH%" (
+ echo Running Progress Tracker (executable version)...
+ "%EXE_PATH%" %*
+) else (
+ echo Executable not found, trying Python version...
+ if exist "%PY_PATH%" (
+ echo Running Progress Tracker (Python version)...
+ python "%PY_PATH%" %*
+ ) else (
+ echo Error: Neither executable nor Python script found at:
+ echo - %EXE_PATH%
+ echo - %PY_PATH%
+ exit /b 1
+ )
+)
+
+if %ERRORLEVEL% EQU 0 (
+ echo Progress tracking completed successfully!
+) else (
+ echo Error running progress tracker! (Exit code: %ERRORLEVEL%)
+)
+pause
\ No newline at end of file
diff --git a/scripts/track_progress/scripts/run_progress_tracker.py b/scripts/track_progress/scripts/run_progress_tracker.py
new file mode 100644
index 0000000..b96f27e
--- /dev/null
+++ b/scripts/track_progress/scripts/run_progress_tracker.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+"""
+Launcher for the progress tracker.
+Tries to use the executable version first, then falls back to the Python version.
+"""
+import os
+import sys
+import subprocess
+
+
+def main():
+ """Main function to run the tracker."""
+ # Get the directory of this script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ tracker_dir = os.path.dirname(script_dir)
+
+ # Paths to executable and Python script
+ exe_ext = ".exe" if sys.platform == "win32" else ""
+ exe_path = os.path.join(tracker_dir, f"track_progress{exe_ext}")
+ py_path = os.path.join(tracker_dir, "track_progress.py")
+
+ # Command-line arguments to pass through
+ args = sys.argv[1:]
+
+ # Try executable first
+ if os.path.exists(exe_path) and (
+ sys.platform == "win32" or os.access(exe_path, os.X_OK)
+ ):
+ print("Running Progress Tracker (executable version)...")
+ try:
+ return subprocess.call([exe_path] + args)
+ except Exception as e:
+ print(f"Error running executable: {e}")
+ print("Falling back to Python version...")
+ else:
+ print("Executable not found, trying Python version...")
+
+ # Fall back to Python script
+ if os.path.exists(py_path):
+ print("Running Progress Tracker (Python version)...")
+ try:
+ # Add the tracker directory to the Python path
+ sys.path.insert(0, tracker_dir)
+
+ # Change to the tracker directory
+ os.chdir(tracker_dir)
+
+ # Run the Python script
+ return subprocess.call([sys.executable, py_path] + args)
+ except Exception as e:
+ print(f"Error running Python script: {e}")
+ return 1
+ else:
+ print("Error: Neither executable nor Python script found at:")
+ print(f"- {exe_path}")
+ print(f"- {py_path}")
+ return 1
+
+
+if __name__ == "__main__":
+ exit_code = main()
+ if exit_code == 0:
+ print("Progress tracking completed successfully!")
+ else:
+ print(f"Error running progress tracker! (Exit code: {exit_code})")
+
+ # On Windows, pause to see the output
+ if sys.platform == "win32":
+ input("Press Enter to continue...")
+
+ sys.exit(exit_code)
diff --git a/scripts/track_progress/scripts/run_progress_tracker.sh b/scripts/track_progress/scripts/run_progress_tracker.sh
new file mode 100644
index 0000000..5dc513b
--- /dev/null
+++ b/scripts/track_progress/scripts/run_progress_tracker.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Get the directory of this script
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+TRACKER_DIR="$(dirname "$SCRIPT_DIR")"
+EXE_PATH="$TRACKER_DIR/track_progress"
+PY_PATH="$TRACKER_DIR/track_progress.py"
+
+# Check if executable exists and is executable
+if [ -x "$EXE_PATH" ]; then
+ echo "Running Progress Tracker (executable version)..."
+ "$EXE_PATH" "$@"
+elif [ -f "$PY_PATH" ]; then
+ echo "Executable not found, trying Python version..."
+ echo "Running Progress Tracker (Python version)..."
+ python "$PY_PATH" "$@"
+else
+ echo "Error: Neither executable nor Python script found at:"
+ echo "- $EXE_PATH"
+ echo "- $PY_PATH"
+ exit 1
+fi
+
+EXIT_CODE=$?
+if [ $EXIT_CODE -eq 0 ]; then
+ echo "Progress tracking completed successfully!"
+else
+ echo "Error running progress tracker! (Exit code: $EXIT_CODE)"
+fi
diff --git a/scripts/track_progress/scripts/setup_install.py b/scripts/track_progress/scripts/setup_install.py
new file mode 100644
index 0000000..edbd619
--- /dev/null
+++ b/scripts/track_progress/scripts/setup_install.py
@@ -0,0 +1,285 @@
+""" Run this to install to your projects
+ | "--dest folder" |to set destination dir
+ | "--project name" |to set project name
+ | "--verbose" or "-v"|to enable verbose output
+ | "--force" or "-f"|to force overwrite existing files
+"""
+
+import os
+import sys
+import shutil
+import argparse
+import subprocess
+from pathlib import Path
+
+# Try to import our script creation module
+try:
+ from create_launcher_scripts import ensure_launcher_scripts
+except ImportError:
+ # If we can't import it, we'll define a simple version here
+ def ensure_launcher_scripts(
+ script_dir, dest_dir, project_name, verbose=False, force=False
+ ):
+ """Fallback function if the module can't be imported."""
+ print(
+ "Warning: create_launcher_scripts module not found. Using fallback."
+ )
+ return []
+
+
+def main():
+ """Main installation function."""
+ parser = argparse.ArgumentParser(
+ description="Install the progress tracking system."
+ )
+ parser.add_argument(
+ "--dest", default="scripts", help="Destination directory"
+ )
+ parser.add_argument("--project", default="Project", help="Project name")
+ parser.add_argument(
+ "--verbose", "-v", action="store_true", help="Enable verbose output"
+ )
+ parser.add_argument(
+ "--force",
+ "-f",
+ action="store_true",
+ help="Force overwrite existing files",
+ )
+ args = parser.parse_args()
+
+ verbose = args.verbose
+ force = args.force
+
+ if verbose:
+ print(
+ f"Installing progress tracking system to {args.dest} for project {args.project}"
+ )
+
+ # Create destination directories
+ os.makedirs(
+ os.path.join(args.dest, "track_progress", "templates"), exist_ok=True
+ )
+ os.makedirs("docs/status", exist_ok=True)
+
+ # Get the directory of this script
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ parent_dir = os.path.dirname(script_dir) # scripts/track_progress
+
+ # Look for compiled executable
+ exe_ext = ".exe" if sys.platform == "win32" else ""
+ compiled_exe = os.path.join(parent_dir, f"track_progress{exe_ext}")
+ dist_exe = os.path.join(parent_dir, "dist", f"track_progress{exe_ext}")
+
+ # Check if we have the executable in either location
+ if not os.path.exists(compiled_exe) and not os.path.exists(dist_exe):
+ # Try to build it
+ if verbose:
+ print(f"Compiled executable not found, trying to build it...")
+
+ build_script = os.path.join(script_dir, "build_with_nuitka.py")
+ if os.path.exists(build_script):
+ build_cmd = [sys.executable, build_script]
+ if verbose:
+ build_cmd.append("--verbose")
+
+ try:
+ subprocess.check_call(build_cmd)
+ # The executable should now be in the dist directory
+
+ # Move the executable from dist to parent directory
+ if os.path.exists(dist_exe):
+ if os.path.exists(compiled_exe):
+ os.remove(compiled_exe)
+ shutil.move(dist_exe, compiled_exe)
+ if verbose:
+ print(
+ f"Moved executable from {dist_exe} to {compiled_exe}"
+ )
+ except subprocess.CalledProcessError:
+ print(
+ "Failed to build the executable. Falling back to Python files."
+ )
+ else:
+ print("Build script not found. Falling back to Python files.")
+
+ # Check if we have the executable now
+ have_executable = os.path.exists(compiled_exe)
+
+ if have_executable:
+ # Copy the compiled executable
+ dst_exe = os.path.join(
+ args.dest, "track_progress", f"track_progress{exe_ext}"
+ )
+ if not os.path.exists(dst_exe) or force:
+ shutil.copy2(compiled_exe, dst_exe)
+ if verbose:
+ print(
+ f"Copied compiled executable from {compiled_exe} to {dst_exe}"
+ )
+ elif verbose:
+ print(f"Skipping existing executable: {dst_exe}")
+ else:
+ # Copy individual Python files
+ python_files = [
+ "track_progress.py",
+ "config.py",
+ "git_analyzer.py",
+ "code_stats.py",
+ "feature_markdown.py",
+ "template_engine.py",
+ "changelog_generator.py",
+ "roadmap_generator.py",
+ "__init__.py", # Add this to make it a proper package
+ "README.md",
+ ]
+
+ for file in python_files:
+ src = os.path.join(parent_dir, file)
+ dst = os.path.join(args.dest, "track_progress", file)
+
+ # Skip existing files unless force is specified
+ if os.path.exists(dst) and not force:
+ if verbose:
+ print(f"Skipping existing file: {dst}")
+ continue
+
+ if os.path.exists(src):
+ shutil.copy2(src, dst)
+ if verbose:
+ print(f"Copied {file} to {dst}")
+ elif file == "__init__.py":
+ # Create empty __init__.py if it doesn't exist
+ with open(dst, "w", encoding="utf-8") as f:
+ f.write(
+ "# This file makes the directory a Python package\n"
+ )
+ if verbose:
+ print(f"Created {dst}")
+
+ # Copy templates (always copy templates, they should remain exposed)
+ template_files = [
+ "status_template.md",
+ "feature_template.md",
+ "changelog_template.md",
+ ]
+
+ for file in template_files:
+ src = os.path.join(parent_dir, "templates", file)
+ dst = os.path.join(args.dest, "track_progress", "templates", file)
+
+ # Skip existing files unless force is specified
+ if os.path.exists(dst) and not force:
+ if verbose:
+ print(f"Skipping existing template: {dst}")
+ continue
+
+ if os.path.exists(src):
+ shutil.copy2(src, dst)
+ if verbose:
+ print(f"Copied template {file} to {dst}")
+
+ # Copy and customize configuration
+ config_src = os.path.join(parent_dir, "progress_config.json")
+ config_dst = os.path.join(
+ args.dest, "track_progress", "progress_config.json"
+ )
+
+ # Skip existing config unless force is specified
+ if not os.path.exists(config_dst) or force:
+ if os.path.exists(config_src):
+ # Read the config file
+ with open(config_src, "r", encoding="utf-8") as f:
+ config_content = f.read()
+
+ # Replace project name
+ config_content = config_content.replace(
+ '"project_name": "AdvChkSys"',
+ f'"project_name": "{args.project}"',
+ )
+
+ # Ensure paths are relative to the project root
+ config_content = config_content.replace(
+ '"features_file": "docs/features.md"',
+ '"features_file": "docs/features.md"',
+ )
+ config_content = config_content.replace(
+ '"output_dir": "docs/status"',
+ '"output_dir": "docs/status"',
+ )
+ config_content = config_content.replace(
+ '"changelog_file": "CHANGELOG.md"',
+ '"changelog_file": "CHANGELOG.md"',
+ )
+
+ # Write the customized config
+ with open(config_dst, "w", encoding="utf-8") as f:
+ f.write(config_content)
+
+ if verbose:
+ print(f"Created customized configuration at {config_dst}")
+ elif verbose:
+ print(f"Skipping existing configuration: {config_dst}")
+
+ # Ensure launcher scripts exist in the scripts directory
+ created_scripts = ensure_launcher_scripts(
+ script_dir, args.dest, args.project, verbose, force
+ )
+ if created_scripts and verbose:
+ print(
+ f"Created {len(created_scripts)} launcher scripts in {script_dir}"
+ )
+
+ # Create an empty CHANGELOG.md if it doesn't exist
+ if not os.path.exists("CHANGELOG.md"):
+ with open("CHANGELOG.md", "w", encoding="utf-8") as f:
+ f.write("# Changelog\n\n")
+ if verbose:
+ print("Created empty CHANGELOG.md")
+
+ # Copy the scripts directory to the destination if it doesn't exist
+ scripts_dst = os.path.join(args.dest, "track_progress", "scripts")
+ if not os.path.exists(scripts_dst) or force:
+ os.makedirs(scripts_dst, exist_ok=True)
+
+ # Copy script files
+ script_files = [
+ "build_with_nuitka.py",
+ "create_launcher_scripts.py",
+ "setup_install.py",
+ ]
+
+ for file in script_files:
+ src = os.path.join(script_dir, file)
+ dst = os.path.join(scripts_dst, file)
+
+ if os.path.exists(src) and (not os.path.exists(dst) or force):
+ shutil.copy2(src, dst)
+ if verbose:
+ print(f"Copied script {file} to {dst}")
+
+ print("\nInstallation complete!")
+ if have_executable:
+ print(
+ f"Compiled tracker installed to: {os.path.join(args.dest, 'track_progress')}"
+ )
+ else:
+ print(
+ f"Python tracker installed to: {os.path.join(args.dest, 'track_progress')}"
+ )
+
+ # Check which launcher scripts exist
+ py_launcher = os.path.join(script_dir, "run_progress_tracker.py")
+ sh_launcher = os.path.join(script_dir, "run_progress_tracker.sh")
+ bat_launcher = os.path.join(script_dir, "run_progress_tracker.bat")
+
+ print("\nTo run the tracker, use one of these commands:")
+ if os.path.exists(py_launcher):
+ print(f"- Python: python {py_launcher}")
+ if os.path.exists(sh_launcher) and sys.platform != "win32":
+ print(f"- Shell: {sh_launcher}")
+ if os.path.exists(bat_launcher) and sys.platform == "win32":
+ print(f"- Batch: {bat_launcher}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/track_progress/template_engine.py b/scripts/track_progress/template_engine.py
new file mode 100644
index 0000000..b83849a
--- /dev/null
+++ b/scripts/track_progress/template_engine.py
@@ -0,0 +1,239 @@
+"""
+Template engine for generating Markdown documentation from templates.
+"""
+
+import os
+import re
+import sys
+from typing import Dict, Any, List, Optional
+
+
+class TemplateEngine:
+ """Simple template engine for generating Markdown files."""
+
+ def __init__(
+ self, templates_dir: str = "scripts/track_progress/templates"
+ ):
+ """Initialize the template engine with the templates directory."""
+ self.templates_dir = templates_dir
+
+ def _load_template(self, template_name: str) -> str:
+ """Load a template file."""
+ # Try multiple possible template locations
+ possible_paths = [
+ # Original path
+ os.path.join(self.templates_dir, template_name),
+ # Path relative to current working directory
+ os.path.join("templates", template_name),
+ # Path relative to executable directory
+ os.path.join(
+ os.path.dirname(sys.executable), "templates", template_name
+ ),
+ # Path relative to script directory
+ os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "templates",
+ template_name,
+ ),
+ # Path with 'scripts/track_progress' removed (for compiled version)
+ template_name.replace("scripts/track_progress/", ""),
+ # Just the filename
+ template_name,
+ ]
+
+ # Try each path
+ for template_path in possible_paths:
+ if os.path.exists(template_path):
+ with open(template_path, "r", encoding="utf-8") as f:
+ return f.read()
+
+ # If we get here, none of the paths worked
+ error_message = f"Template not found: {template_name}\nTried paths: {possible_paths}"
+ raise FileNotFoundError(error_message)
+
+ def _replace_variables(
+ self, template: str, context: Dict[str, Any]
+ ) -> str:
+ """Replace {{ variable }} with values from context."""
+
+ def replace_var(match):
+ var_name = match.group(1).strip()
+
+ # Handle nested variables with dot notation
+ if "." in var_name:
+ parts = var_name.split(".")
+ value = context
+ for part in parts:
+ if isinstance(value, dict) and part in value:
+ value = value[part]
+ else:
+ return match.group(0) # Return original if not found
+ return str(value) if value is not None else ""
+
+ # Handle simple variables
+ if var_name in context:
+ return (
+ str(context[var_name])
+ if context[var_name] is not None
+ else ""
+ )
+
+ return match.group(0) # Return original if not found
+
+ # Replace {{ variable }}
+ pattern = r"{{(.*?)}}"
+ return re.sub(pattern, replace_var, template)
+
+ def _process_conditionals(
+ self, template: str, context: Dict[str, Any]
+ ) -> str:
+ """Process {% if condition %} ... {% endif %} blocks."""
+
+ def replace_conditional(match):
+ condition_var = match.group(1).strip()
+ content = match.group(2)
+
+ # Handle nested conditions with dot notation
+ if "." in condition_var:
+ parts = condition_var.split(".")
+ value = context
+ for part in parts:
+ if isinstance(value, dict) and part in value:
+ value = value[part]
+ else:
+ return "" # Condition not met
+
+ if value:
+ return content
+ return ""
+
+ # Handle simple conditions
+ if condition_var in context and context[condition_var]:
+ return content
+ return ""
+
+ # Process {% if condition %} ... {% endif %}
+ pattern = r"{%\s*if\s+(.*?)\s*%}(.*?){%\s*endif\s*%}"
+ return re.sub(pattern, replace_conditional, template, flags=re.DOTALL)
+
+ def _process_loops(self, template: str, context: Dict[str, Any]) -> str:
+ """Process {% for item_var, item in items.items() %} ... {% endfor %} blocks."""
+
+ # First, process dictionary iteration with items()
+ def replace_dict_loop(match):
+ item_key_var = match.group(1).strip()
+ item_val_var = match.group(2).strip()
+ collection_var = match.group(3).strip()
+ content_template = match.group(4)
+
+ # Handle nested collections with dot notation
+ if "." in collection_var:
+ parts = collection_var.split(".")
+ collection = context
+ for part in parts:
+ if isinstance(collection, dict) and part in collection:
+ collection = collection[part]
+ else:
+ return "" # Collection not found
+ else:
+ collection = context.get(collection_var, {})
+
+ if not isinstance(collection, dict):
+ return "" # Not a dictionary
+
+ result = []
+ for key, value in collection.items():
+ # Create a new context for each iteration
+ loop_context = context.copy()
+ loop_context[item_key_var] = key
+ loop_context[item_val_var] = value
+
+ # Process the content template with the loop context
+ item_content = self._replace_variables(
+ content_template, loop_context
+ )
+ item_content = self._process_conditionals(
+ item_content, loop_context
+ )
+ result.append(item_content)
+
+ return "".join(result)
+
+ # Process {% for key, value in dict.items() %} ... {% endfor %}
+ dict_pattern = r"{%\s*for\s+(.*?),\s*(.*?)\s+in\s+(.*?)\.items\(\)\s*%}(.*?){%\s*endfor\s*%}"
+ template = re.sub(
+ dict_pattern, replace_dict_loop, template, flags=re.DOTALL
+ )
+
+ # Then, process regular list iteration
+ def replace_list_loop(match):
+ item_var = match.group(1).strip()
+ collection_var = match.group(2).strip()
+ content_template = match.group(3)
+
+ # Handle nested collections with dot notation
+ if "." in collection_var:
+ parts = collection_var.split(".")
+ collection = context
+ for part in parts:
+ if isinstance(collection, dict) and part in collection:
+ collection = collection[part]
+ else:
+ return "" # Collection not found
+ else:
+ collection = context.get(collection_var, [])
+
+ if not isinstance(collection, (list, dict)):
+ return "" # Not a collection
+
+ result = []
+ if isinstance(collection, dict):
+ # If it's a dict, iterate over keys
+ for key in collection:
+ # Create a new context for each iteration
+ loop_context = context.copy()
+ loop_context[item_var] = key
+
+ # Process the content template with the loop context
+ item_content = self._replace_variables(
+ content_template, loop_context
+ )
+ item_content = self._process_conditionals(
+ item_content, loop_context
+ )
+ result.append(item_content)
+ else: # list
+ for item in collection:
+ # Create a new context for each iteration
+ loop_context = context.copy()
+ loop_context[item_var] = item
+
+ # Process the content template with the loop context
+ item_content = self._replace_variables(
+ content_template, loop_context
+ )
+ item_content = self._process_conditionals(
+ item_content, loop_context
+ )
+ result.append(item_content)
+
+ return "".join(result)
+
+ # Process {% for item in items %} ... {% endfor %}
+ list_pattern = (
+ r"{%\s*for\s+(.*?)\s+in\s+(.*?)\s*%}(.*?){%\s*endfor\s*%}"
+ )
+ return re.sub(
+ list_pattern, replace_list_loop, template, flags=re.DOTALL
+ )
+
+ def render(self, template_name: str, context: Dict[str, Any]) -> str:
+ """Render a template with the given context."""
+ template = self._load_template(template_name)
+
+ # Process template directives in order
+ template = self._process_loops(template, context)
+ template = self._process_conditionals(template, context)
+ template = self._replace_variables(template, context)
+
+ return template
diff --git a/scripts/track_progress/templates/changelog_template.md b/scripts/track_progress/templates/changelog_template.md
new file mode 100644
index 0000000..ce46bc3
--- /dev/null
+++ b/scripts/track_progress/templates/changelog_template.md
@@ -0,0 +1,7 @@
+# {{ project_name }} Changelog
+
+Last updated: {{ current_date }}
+
+{% for entry in changelog_entries %}
+{{ entry }}
+{% endfor %}
\ No newline at end of file
diff --git a/scripts/track_progress/templates/feature_template.md b/scripts/track_progress/templates/feature_template.md
new file mode 100644
index 0000000..31ada23
--- /dev/null
+++ b/scripts/track_progress/templates/feature_template.md
@@ -0,0 +1,92 @@
+# {{ title }}
+
+## Overview
+
+This document tracks the implementation status of all features in the {{ project_name }} project.
+
+## Feature Status Summary
+
+| Status | Description |
+|--------|-------------|
+| โ
Completed | Features that are fully implemented and tested |
+| ๐ In Progress | Features currently being implemented |
+| ๐ Planned | Features planned for future implementation |
+
+## Features
+
+{% for feature_key, feature in features.items() %}
+### {{ feature_key }}
+
+**Status:** {{ feature.status_emoji }} {{ feature.status }}
+**Description:** {{ feature.description }}
+**Last Update:** {{ feature.date }}
+{% if feature.author %}**Owner:** {{ feature.author }}{% endif %}
+{% if feature.details %}**Details:** {{ feature.details }}{% endif %}
+
+{% endfor %}
+
+## Feature Categories
+
+### Completed Features
+
+{% for feature_key, feature in features.items() %}
+{% if feature.status == 'completed' %}
+- **{{ feature_key }}**: {{ feature.description }}
+{% endif %}
+{% endfor %}
+
+### In Progress Features
+
+{% for feature_key, feature in features.items() %}
+{% if feature.status == 'in_progress' %}
+- **{{ feature_key }}**: {{ feature.description }}
+{% endif %}
+{% endfor %}
+
+### Planned Features
+
+{% for feature_key, feature in features.items() %}
+{% if feature.status == 'planned' %}
+- **{{ feature_key }}**: {{ feature.description }}
+{% endif %}
+{% endfor %}
+
+
\ No newline at end of file
diff --git a/scripts/track_progress/templates/status_template.md b/scripts/track_progress/templates/status_template.md
new file mode 100644
index 0000000..5d8f1d3
--- /dev/null
+++ b/scripts/track_progress/templates/status_template.md
@@ -0,0 +1,57 @@
+# {{ project_name }} Development Status
+
+Last updated: {{ current_date }}
+
+## Code Statistics
+
+{% if stats %}
+Total lines of code: **{{ stats.total_lines }}**
+
+Number of source files: **{{ stats.file_count }}**
+
+### Files by Language
+{% for lang, count in stats.languages.items() %}
+- **{{ lang }}**: {{ count }} files
+{% endfor %}
+
+### Top Files by Line Count
+
+| File | Lines | Language | Path |
+|------|------:|----------|------|
+{% for file in stats.top_files %}
+| {{ file.name }} | {{ file.lines }} | {{ file.language }} | {{ file.path }} |
+{% endfor %}
+{% endif %}
+
+## Feature Status
+
+| Feature | Status | Description | Last Update | Owner |
+|---------|--------|-------------|-------------|-------|
+{% for feature_key, feature in features.items() %}
+| {{ feature.display_name }} | {{ feature.status_emoji }} {{ feature.status }} | {{ feature.description }} | {{ feature.date }} | {{ feature.author }} |
+{% endfor %}
+
+## Recent Updates
+
+{% for entry in changelog_entries %}
+{{ entry }}
+{% endfor %}
+
+{% if known_issues %}
+## Known Issues
+
+{% for issue in known_issues %}
+- {{ issue.description }} {% if issue.status %}({{ issue.status }}){% endif %}
+{% endfor %}
+{% endif %}
+
+{% if roadmap %}
+## Roadmap
+
+{% for milestone in roadmap %}
+### {{ milestone.name }} ({{ milestone.target_date }})
+{% for item in milestone.items %}
+- {{ item.description }} {% if item.status %}[{{ item.status }}]{% endif %}
+{% endfor %}
+{% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/scripts/track_progress/track_progress.exe b/scripts/track_progress/track_progress.exe
new file mode 100644
index 0000000..0d8ed96
Binary files /dev/null and b/scripts/track_progress/track_progress.exe differ
diff --git a/scripts/track_progress/track_progress.py b/scripts/track_progress/track_progress.py
new file mode 100644
index 0000000..55deeea
--- /dev/null
+++ b/scripts/track_progress/track_progress.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Project progress tracker that generates status documentation from Git history.
+"""
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Dict, Any, List, Optional
+
+# Add the current directory to the Python path
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+
+# Import our modules
+from config import Config
+from code_stats import analyze_code_stats
+from git_analyzer import GitAnalyzer
+from feature_markdown import (
+ enrich_features,
+ emoji_status,
+ extract_features,
+ generate_features_md,
+)
+from template_engine import TemplateEngine
+from changelog_generator import update_changelog_file
+from roadmap_generator import generate_roadmap
+
+
+def load_features(features_file: str) -> Dict[str, Dict[str, Any]]:
+ """Load features from a Markdown file."""
+ # Use the improved extraction function
+ return extract_features(features_file)
+
+
+def update_features(
+ features: Dict[str, Dict[str, Any]],
+ feature_updates: Dict[str, Dict[str, Any]],
+ new_features: Dict[str, Dict[str, Any]],
+) -> Dict[str, Dict[str, Any]]:
+ """Update features with new information from Git history."""
+ updated_features = features.copy()
+
+ # Update existing features
+ for feature_key, update in feature_updates.items():
+ if feature_key in updated_features:
+ updated_features[feature_key].update(update)
+
+ # Add new features
+ for feature_key, feature_data in new_features.items():
+ if feature_key not in updated_features:
+ updated_features[feature_key] = feature_data
+
+ return updated_features
+
+
+def format_changelog_entries(git_data: Dict[str, Any]) -> List[str]:
+ """Format changelog entries from Git data."""
+ entries = []
+
+ # Add changelog entries
+ for entry in git_data.get("changelog_entries", []):
+ entries.append(
+ f"- {entry['date']}: {entry['message']} ({entry['commit']})"
+ )
+
+ # Add fix entries
+ for fix in git_data.get("fixes", []):
+ entries.append(
+ f"- {fix['date']}: Fixed: {fix['message']} ({fix['commit']})"
+ )
+
+ # Add untagged commits
+ for commit in git_data.get("untagged_commits", []):
+ entries.append(
+ f"- {commit['date']}: {commit['message']} ({commit['commit']})"
+ )
+
+ return entries
+
+
+def extract_known_issues(git_data: Dict[str, Any]) -> List[Dict[str, Any]]:
+ """Extract known issues from Git data."""
+ issues = []
+
+ # Look for issues in the Git data
+ for issue in git_data.get("issues", []):
+ issues.append(
+ {"description": issue.get("message", ""), "status": "open"}
+ )
+
+ return issues
+
+
+def enrich_features(
+ features: Dict[str, Dict[str, Any]]
+) -> Dict[str, Dict[str, Any]]:
+ """Add additional information to features for display purposes."""
+ enriched = {}
+
+ for key, data in features.items():
+ enriched[key] = data.copy()
+ status = data.get("status", "unknown")
+ enriched[key]["status_emoji"] = emoji_status(status)
+
+ # Add title-cased key for display
+ enriched[key]["display_name"] = key.replace("_", " ").title()
+
+ return enriched
+
+
+def main():
+ """Main function to run the progress tracker."""
+ parser = argparse.ArgumentParser(
+ description="Track project progress and generate status documentation."
+ )
+ parser.add_argument(
+ "--config",
+ default="scripts/track_progress/progress_config.json",
+ help="Path to configuration file",
+ )
+ parser.add_argument("--project", help="Project name (overrides config)")
+ parser.add_argument(
+ "--output-dir", help="Output directory (overrides config)"
+ )
+ parser.add_argument("--repo", default=".", help="Repository path")
+ parser.add_argument(
+ "--verbose", "-v", action="store_true", help="Enable verbose output"
+ )
+ args = parser.parse_args()
+
+ verbose = args.verbose
+
+ # Load configuration
+ config = Config(args.config)
+
+ # Override config with command-line arguments
+ if args.project:
+ config.set("project_name", args.project)
+ if args.output_dir:
+ config.set("output_dir", args.output_dir)
+
+ # Ensure output directory exists
+ output_dir = config.get("output_dir")
+ os.makedirs(output_dir, exist_ok=True)
+
+ # Initialize components
+ git_analyzer = GitAnalyzer(args.repo)
+ template_engine = TemplateEngine()
+
+ # Get repository information
+ repo_info = git_analyzer.get_repo_info()
+
+ # Analyze Git history
+ git_data = git_analyzer.analyze_commits(
+ limit=config.get("git_log_limit"),
+ untagged_limit=config.get("untagged_commit_limit"),
+ )
+
+ # Analyze code statistics
+ code_stats = analyze_code_stats(
+ root_dir=args.repo,
+ exclude_dirs=config.get("exclude_dirs"),
+ source_extensions=config.get("source_extensions"),
+ top_files_limit=config.get("top_files_limit"),
+ )
+
+ # Load existing features
+ features_file = config.get("features_file")
+ if not os.path.isabs(features_file):
+ # If it's not an absolute path, make it relative to the repo root, not the output dir
+ features_file = os.path.join(args.repo, features_file)
+
+ if verbose:
+ print(f"Loading features from: {features_file}")
+
+ features = load_features(features_file)
+
+ if verbose:
+ print(f"Loaded {len(features)} features")
+
+ # Update features with Git data
+ features = update_features(
+ features,
+ git_data.get("feature_updates", {}),
+ git_data.get("new_features", {}),
+ )
+
+ if verbose:
+ print(f"Updated features count: {len(features)}")
+
+ # Enrich features with emojis and other display information
+ enriched_features = enrich_features(features)
+
+ # Format changelog entries
+ changelog_entries = format_changelog_entries(git_data)
+
+ # Extract known issues
+ known_issues = extract_known_issues(git_data)
+
+ # Generate roadmap
+ roadmap = generate_roadmap(
+ git_data.get("milestones", {}), git_data.get("roadmap_items", {})
+ )
+
+ # Prepare template context
+ context = {
+ "project_name": config.get("project_name"),
+ "current_date": datetime.now().strftime("%Y-%m-%d"),
+ "repo_info": repo_info,
+ "stats": code_stats,
+ "features": enriched_features,
+ "changelog_entries": changelog_entries,
+ "known_issues": known_issues,
+ "roadmap": roadmap,
+ }
+
+ # Generate status document
+ status_template = config.get("templates", {}).get(
+ "status", "status_template.md"
+ )
+ status_content = template_engine.render(status_template, context)
+
+ status_file = os.path.join(args.repo, output_dir, config.get("status_doc"))
+ os.makedirs(os.path.dirname(status_file), exist_ok=True)
+ with open(status_file, "w", encoding="utf-8") as f:
+ f.write(status_content)
+
+ # Generate features document using the improved function
+ features_content = generate_features_md(
+ features, config.get("project_name")
+ )
+
+ # Ensure directory exists for features file
+ os.makedirs(os.path.dirname(features_file), exist_ok=True)
+
+ # Create backup of original features file
+ if os.path.exists(features_file):
+ backup_file = (
+ f"{features_file}.bak.{datetime.now().strftime('%Y%m%d%H%M%S')}"
+ )
+ with open(features_file, "r", encoding="utf-8") as src:
+ with open(backup_file, "w", encoding="utf-8") as dst:
+ dst.write(src.read())
+ if verbose:
+ print(f"Created backup: {backup_file}")
+
+ # Write the new features file
+ with open(features_file, "w", encoding="utf-8") as f:
+ f.write(features_content)
+
+ # Update changelog
+ changelog_file = config.get(
+ "changelog_file", "CHANGELOG.md"
+ ) # Default to CHANGELOG.md
+
+ if not os.path.isabs(changelog_file):
+ # If it's a relative path, make it relative to the repo root, not the output dir
+ changelog_file = os.path.join(args.repo, changelog_file)
+
+ if verbose:
+ print(f"Updating changelog file: {changelog_file}")
+
+ # Ensure the directory exists
+ changelog_dir = os.path.dirname(changelog_file)
+ if changelog_dir: # Only create directory if there is one
+ os.makedirs(changelog_dir, exist_ok=True)
+
+ update_changelog_file(
+ git_data.get("changelog_entries", []),
+ changelog_file,
+ max_entries=config.get("git_log_limit"),
+ verbose=verbose,
+ )
+
+ print(f"Progress tracking updated:")
+ print(f"- Status document: {status_file}")
+ print(f"- Features document: {features_file}")
+ print(f"- Changelog: {changelog_file}")
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())