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/__init__.py +6 -0
- autopush/ai_commit.py +483 -0
- autopush/cli.py +316 -0
- autopush/commit_gen.py +144 -0
- autopush/detector.py +342 -0
- autopush/git_ops.py +256 -0
- autopush/watcher.py +159 -0
- pushpilot-1.1.0.dist-info/METADATA +155 -0
- pushpilot-1.1.0.dist-info/RECORD +13 -0
- pushpilot-1.1.0.dist-info/WHEEL +5 -0
- pushpilot-1.1.0.dist-info/entry_points.txt +6 -0
- pushpilot-1.1.0.dist-info/licenses/LICENSE +23 -0
- pushpilot-1.1.0.dist-info/top_level.txt +1 -0
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
|
+
[](https://github.com/rhamifydev/PushPilot)
|
|
24
|
+
[](https://opensource.org/licenses/MIT)
|
|
25
|
+
[](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,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
|