pushpilot 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
autopush/watcher.py ADDED
@@ -0,0 +1,159 @@
1
+ """
2
+ Feature-Completion File Watcher for Autopush.
3
+ Monitors directory for changes with a debounced settlement timer.
4
+ When the developer finishes coding a feature, it automatically stages,
5
+ commits with a semantic message, and pushes to GitHub.
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import time
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+ from typing import Callable
14
+
15
+ from autopush.detector import ProjectStack
16
+ from autopush.git_ops import get_git_status_changes
17
+
18
+
19
+ # Folders and extensions to ignore during file watching
20
+ IGNORED_DIRS = {
21
+ ".git", ".svn", ".hg", "node_modules", "vendor", "target", "build",
22
+ "dist", ".next", ".nuxt", ".svelte-kit", "__pycache__", ".venv",
23
+ "venv", "env", ".idea", ".vscode", ".turbo", ".gradle", "bin", "obj",
24
+ "coverage", ".pytest_cache", ".dart_tool"
25
+ }
26
+
27
+ IGNORED_EXTS = {
28
+ ".tmp", ".swp", ".bak", ".log", ".pyc", ".pyo", ".class", ".exe",
29
+ ".dll", ".so", ".dylib", ".DS_Store"
30
+ }
31
+
32
+
33
+ def should_ignore(path: Path) -> bool:
34
+ """Returns True if file/directory should be ignored."""
35
+ for part in path.parts:
36
+ if part in IGNORED_DIRS or part.startswith(".git"):
37
+ return True
38
+ if path.suffix.lower() in IGNORED_EXTS:
39
+ return True
40
+ if path.name.startswith("~") or path.name.endswith("~"):
41
+ return True
42
+ return False
43
+
44
+
45
+ def get_tree_fingerprint(root: Path) -> dict[str, float]:
46
+ """Scans relevant source files and records modification timestamps."""
47
+ fingerprint = {}
48
+ try:
49
+ for entry in root.rglob("*"):
50
+ if entry.is_file():
51
+ try:
52
+ rel = entry.relative_to(root)
53
+ if not should_ignore(rel):
54
+ fingerprint[str(rel)] = entry.stat().st_mtime
55
+ except (OSError, PermissionError):
56
+ continue
57
+ except (OSError, PermissionError):
58
+ pass
59
+ return fingerprint
60
+
61
+
62
+ class FeatureWatcher:
63
+ """Watches the project filesystem and triggers push when a feature is complete."""
64
+
65
+ def __init__(
66
+ self,
67
+ root: Path,
68
+ stack: ProjectStack,
69
+ on_feature_complete: Callable[[list[tuple[str, str]]], None],
70
+ settle_window_sec: float = 8.0,
71
+ poll_interval_sec: float = 1.5,
72
+ ):
73
+ self.root = root
74
+ self.stack = stack
75
+ self.on_feature_complete = on_feature_complete
76
+ self.settle_window = settle_window_sec
77
+ self.poll_interval = poll_interval_sec
78
+ self.last_snapshot = get_tree_fingerprint(root)
79
+ self.changed_batch: set[str] = set()
80
+ self.last_activity_time: float | None = None
81
+
82
+ def start(self):
83
+ """Runs the monitoring loop."""
84
+ print("\n" + "=" * 65)
85
+ print(" AUTOPUSH WATCHER: FEATURE-DRIVEN AUTO-PUSH")
86
+ print("=" * 65)
87
+ print(f"Stack Detected : {self.stack.language} ({self.stack.framework or 'General'})")
88
+ print(f"Watch Directory : {self.root}")
89
+ print(f"Feature Cooldown : {self.settle_window}s settlement window")
90
+ print("Status : [LISTENING] Ready for changes. Press Ctrl+C to stop.\n")
91
+
92
+ try:
93
+ while True:
94
+ current_snapshot = get_tree_fingerprint(self.root)
95
+ now = time.time()
96
+
97
+ # Detect modified, added, or deleted files
98
+ current_keys = set(current_snapshot.keys())
99
+ last_keys = set(self.last_snapshot.keys())
100
+
101
+ new_files = current_keys - last_keys
102
+ deleted_files = last_keys - current_keys
103
+ modified_files = {
104
+ k for k in (current_keys & last_keys)
105
+ if current_snapshot[k] != self.last_snapshot[k]
106
+ }
107
+
108
+ diff = new_files | deleted_files | modified_files
109
+ if diff:
110
+ self.changed_batch.update(diff)
111
+ self.last_activity_time = now
112
+ self.last_snapshot = current_snapshot
113
+
114
+ sample_names = ", ".join(Path(f).name for f in list(self.changed_batch)[:3])
115
+ more = f" (+{len(self.changed_batch) - 3} more)" if len(self.changed_batch) > 3 else ""
116
+ print(
117
+ f"\r[EDITING] {len(self.changed_batch)} file(s) active: {sample_names}{more} ... waiting for edits to settle",
118
+ end="",
119
+ flush=True,
120
+ )
121
+
122
+ # Check if user paused editing (feature settlement window reached)
123
+ if self.last_activity_time is not None:
124
+ elapsed = now - self.last_activity_time
125
+ remaining = self.settle_window - elapsed
126
+
127
+ if remaining > 0 and self.changed_batch:
128
+ # Still counting down
129
+ print(
130
+ f"\r[SETTLING] Feature completion countdown: {int(remaining) + 1}s remaining... ",
131
+ end="",
132
+ flush=True,
133
+ )
134
+ elif remaining <= 0 and self.changed_batch:
135
+ # Feature settlement window passed! Trigger auto commit & push
136
+ print("\n\n" + "-" * 65)
137
+ ts = datetime.now().strftime("%H:%M:%S")
138
+ print(f"[FEATURE COMPLETE] Activity settled at {ts}. Starting auto-push...")
139
+
140
+ # Verify actual git status changes
141
+ git_changes = get_git_status_changes(self.root)
142
+ if git_changes:
143
+ try:
144
+ self.on_feature_complete(git_changes)
145
+ except Exception as e:
146
+ print(f"[ERROR] Auto-push failed: {e}")
147
+ else:
148
+ print("[INFO] Working tree is clean (no git changes staged or untracked).")
149
+
150
+ # Reset batch and update snapshot
151
+ self.changed_batch.clear()
152
+ self.last_activity_time = None
153
+ self.last_snapshot = get_tree_fingerprint(self.root)
154
+ print("\n[LISTENING] Resuming watch mode for next feature...\n")
155
+
156
+ time.sleep(self.poll_interval)
157
+
158
+ except KeyboardInterrupt:
159
+ print("\n\n[INFO] Autopush watcher stopped by user.")
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: pushpilot
3
+ Version: 1.1.0
4
+ Summary: PushPilot - Intelligent multi-framework Git autopilot that watches features, detects AI coding context, and ships to GitHub
5
+ Author: PushPilot Developers
6
+ Project-URL: Homepage, https://github.com/rhamifydev/PushPilot
7
+ Project-URL: Repository, https://github.com/rhamifydev/PushPilot.git
8
+ Project-URL: Issues, https://github.com/rhamifydev/PushPilot/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Software Development :: Version Control :: Git
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # 🚀 PushPilot
19
+
20
+ > **Intelligent Multi-Framework Git & GitHub Autopilot**
21
+ > One command to initialize Git, link your GitHub repository, generate conventional commits, and push. Includes a **Feature-Complete File Watcher** that automatically syncs code to GitHub whenever a feature is completed.
22
+
23
+ [![GitHub](https://img.shields.io/badge/GitHub-rhamifydev%2FPushPilot-blue?logo=github)](https://github.com/rhamifydev/PushPilot)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
25
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
26
+
27
+ ---
28
+
29
+ ## 🌟 What PushPilot Does
30
+
31
+ 1. **Zero-Config One-Command Setup (`pushpilot <github-url>`)**:
32
+ - Give it your GitHub repo URL once: `pushpilot https://github.com/username/repo.git`.
33
+ - If Git is not initialized, PushPilot initializes it with default branch `main`.
34
+ - Links the `origin` remote automatically.
35
+ - Detects if GitHub was initialized with a default README or `.gitignore` and merges/rebases seamlessly without conflicts.
36
+ - Generates a framework-tailored `.gitignore` if none exists.
37
+ - Stages all files, creates a conventional commit, and pushes directly to GitHub!
38
+
39
+ 2. **Subsequent One-Command Push (`pushpilot` or `auto-push`)**:
40
+ - Devs just write `pushpilot` (or `auto-push`).
41
+ - Detects all modified/added/deleted files.
42
+ - Generates a **Conventional Semantic Commit Message** (e.g., `feat(auth): implement login endpoint in FastAPI`).
43
+ - Stages, commits, and pushes to GitHub in 2 seconds.
44
+
45
+ 3. **Internal AI Commit Intelligence (Auto-Detect)**:
46
+ - **Coding with AI?** If you are coding inside an AI-assisted environment (**Google Antigravity IDE**, **Cursor**, **Aider**, **Claude Code**, **Cline**) or have an AI API configured (**Gemini**, **OpenAI**, **Anthropic**, **Groq**, or local **Ollama**), PushPilot detects the active developer intent and git diff to generate **ultra-accurate, laser-focused Conventional Commit messages**.
47
+ - **Not coding with AI?** PushPilot instantly and seamlessly uses its **smart default heuristic engine** (zero dependencies, zero delay, offline-friendly).
48
+ - Use `--no-ai` to disable AI detection or `--ai` to force it.
49
+
50
+ 4. **Multi-Language & Multi-Framework Intelligence**:
51
+ - **JavaScript / TypeScript**: React, Next.js, Vue, Nuxt, Angular, Svelte, Vite, Node.js, Express, NestJS
52
+ - **Python**: Django, FastAPI, Flask, standard Python, AI/ML
53
+ - **Rust**: Cargo projects
54
+ - **Go**: Go modules
55
+ - **Dart / Flutter**: Mobile & Web Flutter apps
56
+ - **Java / Kotlin**: Spring Boot, Maven, Gradle
57
+ - **C# / .NET**: ASP.NET Core, .NET solutions
58
+ - **PHP**: Laravel, Symfony, Composer
59
+ - **Ruby**: Ruby on Rails, Bundler
60
+
61
+ ---
62
+
63
+ ## 🚀 One-Command Installation for Developers
64
+
65
+ Any developer can install and use **PushPilot** globally with a single command:
66
+
67
+ ### Option 1: Install Directly from GitHub (Works right now for anyone!)
68
+ ```bash
69
+ # Recommended for CLI tools (pipx or uv)
70
+ pipx install git+https://github.com/rhamifydev/PushPilot.git
71
+
72
+ # Or via standard pip
73
+ pip install git+https://github.com/rhamifydev/PushPilot.git
74
+ ```
75
+
76
+ ### Option 2: Install from PyPI
77
+ ```bash
78
+ pip install pushpilot
79
+ # or
80
+ pipx install pushpilot
81
+ ```
82
+
83
+ ### Option 3: Local / Development
84
+ Clone the repo and run:
85
+ ```bash
86
+ pip install -e .
87
+ ```
88
+
89
+ Once installed, the commands `pushpilot`, `pilot`, and `push` are available anywhere in your terminal!
90
+
91
+ ---
92
+
93
+ ## 🛠️ Usage Examples
94
+
95
+ ### 1. Initialize & Link GitHub in One Step
96
+ ```bash
97
+ pushpilot https://github.com/rhamifydev/PushPilot.git
98
+ ```
99
+ *Initializes Git if needed, sets default branch `main`, links remote `origin`, stages, commits, and pushes to GitHub.*
100
+
101
+ ### 2. Instant Auto-Push (Subsequent uses)
102
+ ```bash
103
+ pushpilot
104
+ ```
105
+
106
+ ### 3. Continuous Feature-Complete Watcher
107
+ ```bash
108
+ pushpilot --watch
109
+ ```
110
+ *Watches files and pushes to GitHub whenever you finish coding a feature increment!*
111
+
112
+ Customizing the feature settlement window:
113
+ ```bash
114
+ pushpilot -w --cooldown 10
115
+ ```
116
+
117
+ ### 4. Custom Commit Message
118
+ ```bash
119
+ pushpilot -m "feat(payment): integrate Stripe checkout webhook"
120
+ ```
121
+
122
+ ### 5. Inspect Detected Project Stack
123
+ ```bash
124
+ pushpilot --detect
125
+ ```
126
+
127
+ ### 6. Interactive Setup / Update GitHub Remote
128
+ ```bash
129
+ pushpilot --setup
130
+ ```
131
+
132
+ ### 7. Dry-Run (Preview what would happen)
133
+ ```bash
134
+ pushpilot --dry-run
135
+ ```
136
+
137
+ ---
138
+
139
+ ## 📋 CLI Reference
140
+
141
+ | Command / Option | Description |
142
+ | :--- | :--- |
143
+ | `pushpilot [URL]` | Run auto-sync (optionally initialize & link GitHub repository URL) |
144
+ | `-w`, `--watch` | Continuous watcher mode with feature-completion settlement |
145
+ | `--cooldown SEC` | Seconds of inactivity before triggering feature commit (default: 8s) |
146
+ | `-m`, `--message MSG` | Override auto-generated message with a custom commit message |
147
+ | `-b`, `--branch NAME` | Target git branch (defaults to active branch / `main`) |
148
+ | `-r`, `--remote NAME` | Target git remote (default: `origin`) |
149
+ | `--detect` | Print detected language, framework, ecosystem, and git status |
150
+ | `--setup` | Interactively link or update GitHub repository URL |
151
+ | `--init-gitignore` | Create/reset tailored `.gitignore` for the detected stack |
152
+ | `--ai` | Explicitly enable AI-powered commit generation (auto-detected by default) |
153
+ | `--no-ai` | Disable AI detection and force default rule-based conventional commit |
154
+ | `--dry-run` | Simulate actions without staging, committing, or pushing |
155
+ | `-v`, `--version` | Display version information |
@@ -0,0 +1,13 @@
1
+ autopush/__init__.py,sha256=SpOQFYdsQu6vZ7YZgLb8XZgeMaEhuoKxY4euDJaCSW0,145
2
+ autopush/ai_commit.py,sha256=c6HEmiJANjW07uP0N_81t3SpRGZWDSEE3PSO3t0zIwc,17638
3
+ autopush/cli.py,sha256=JHKW7eXorYn7JYxjclJuCiTL3guu-gA6qnCGEQZNfhM,10714
4
+ autopush/commit_gen.py,sha256=annJAlEK_rwMaZUzFR6t4imSknhWEoVtYAEwvy268tE,5647
5
+ autopush/detector.py,sha256=n4nMeYOlzVc4g3J14BMr6FccAgi3yKJX1LEItVEKKYE,10850
6
+ autopush/git_ops.py,sha256=OogEKTFnZ25TrBVXX6Gu3vE8Pk6U38rq4v6q6fINPlE,9884
7
+ autopush/watcher.py,sha256=iBt9R1Es4EciUoJtaOkx8QP7cHsouPCFB4z7rOZD71Y,6548
8
+ pushpilot-1.1.0.dist-info/licenses/LICENSE,sha256=vrjkLp1rQoTgMwTQWoGgdVIAqWX8jQpeCuoehM-AXW4,1361
9
+ pushpilot-1.1.0.dist-info/METADATA,sha256=70wfTpPWHBGBEoTA-Bgw20n9dAOq_Cn1cxTabNVAkys,6357
10
+ pushpilot-1.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
11
+ pushpilot-1.1.0.dist-info/entry_points.txt,sha256=aayTSdbvlMZRK-VMJ9p85QtWTd6Z7sjEPbkUrrGUKbE,158
12
+ pushpilot-1.1.0.dist-info/top_level.txt,sha256=f3ETxgK4ewbR38w1QOu-wIr7qAy_UzfJiowrTJp9bbI,9
13
+ pushpilot-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ auto-push = autopush.cli:main
3
+ autopush = autopush.cli:main
4
+ pilot = autopush.cli:main
5
+ push = autopush.cli:main
6
+ pushpilot = autopush.cli:main
@@ -0,0 +1,23 @@
1
+ Boost Software License - Version 1.0 - August 17th, 2003
2
+
3
+ Permission is hereby granted, free of charge, to any person or organization
4
+ obtaining a copy of the software and accompanying documentation covered by
5
+ this license (the "Software") to use, reproduce, display, distribute,
6
+ execute, and transmit the Software, and to prepare derivative works of the
7
+ Software, and to permit third-parties to whom the Software is furnished to
8
+ do so, all subject to the following:
9
+
10
+ The copyright notices in the Software and this entire statement, including
11
+ the above license grant, this restriction and the following disclaimer,
12
+ must be included in all copies of the Software, in whole or in part, and
13
+ all derivative works of the Software, unless such copies or derivative
14
+ works are solely in the form of machine-executable object code generated by
15
+ a source language processor.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ autopush