gitxelectron 1.1.0
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.
- package/README.md +84 -0
- package/bin/cli.js +213 -0
- package/bin/postinstall.js +57 -0
- package/bin/preuninstall.js +22 -0
- package/out/main/index.js +1450 -0
- package/out/preload/index.js +90 -0
- package/out/renderer/assets/index-BFPlchlf.js +26371 -0
- package/out/renderer/assets/index-DsltCz9R.css +56 -0
- package/out/renderer/assets/logo-5889zCqp.png +0 -0
- package/out/renderer/index.html +31 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# GitElectron (formerly GitQuickPush)
|
|
2
|
+
|
|
3
|
+
GitElectron is a clean, modern, and intelligent desktop application and CLI tool designed to completely automate your Git workflow. Built with the philosophy **"I just write code. GitElectron takes care of Git,"** the tool removes the manual burden of `add`, `commit`, and `push`, silently synchronizing your work with GitHub while you focus on coding.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Universal Installation:** Works seamlessly as a standalone desktop executable (.exe) or as a globally installed NPM package.
|
|
8
|
+
- **Context-Aware CLI Integration:** Run `gitelectron open` from any integrated terminal (VS Code, Cursor, Antigravity) to instantly launch the desktop app automatically hooked into that specific project directory.
|
|
9
|
+
- **Universal Push Wizard:** Drag-and-drop any folder from your computer to push it directly to any GitHub repository or branch, even if the folder isn't a Git repository! (Utilizes a hyper-optimized `--sparse` checkout engine in the background).
|
|
10
|
+
- **Collaboration & Invitations:** Manage GitHub repository collaborators directly from the UI. Accept or decline incoming repository invitations, and see a beautiful overlapping avatar stack of active collaborators in the top bar.
|
|
11
|
+
- **Invisible Auto-Sync Daemon:**
|
|
12
|
+
- Automatically monitors your code folders using an intelligent debounced file watcher.
|
|
13
|
+
- Senses when you stop typing/saving (3-second debounce).
|
|
14
|
+
- Automatically stages modified files, generates clean commit messages, and pushes to GitHub in the background.
|
|
15
|
+
- Silently fetches remote changes so your UI always knows if you are behind/ahead of the remote!
|
|
16
|
+
- **First-Run Authentication & PIN Lock:** Instantly hooks into your GitHub account using secure OAuth Device Flow. Secure your app with a custom Lockscreen PIN and Daily Auth.
|
|
17
|
+
- **Built-in Auto-Updater:** Never run a terminal command to update again. GitElectron checks the NPM registry on the lockscreen and provides a 1-click silent update button.
|
|
18
|
+
- **Beautiful & Dynamic Interface:**
|
|
19
|
+
- **iOS Glassmorphism Toasts:** All system notifications slide in beautifully using iOS-style translucent blur UI.
|
|
20
|
+
- **Theming & Audio:** Multiple themes equipped with authentic sound effects and globally adaptive custom scrollbars.
|
|
21
|
+
- **Terminal Pushing:** Need to manually push a file fast? Use the built-in `gitelectron push -f <file>` CLI command.
|
|
22
|
+
- **Visual Git Management:** If you prefer manual control, the beautiful React UI allows you to visually inspect diffs, stage files, resolve conflicts, and switch branches.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### Method 1: NPM (Recommended for Developers)
|
|
27
|
+
|
|
28
|
+
If you have Node.js installed, GitElectron is built to be a seamless global utility.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g gitelectron
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Upon installation, GitElectron will automatically launch the desktop application, prompt you for GitHub authorization, and initialize your environment.
|
|
35
|
+
|
|
36
|
+
### Method 2: Standalone Executable
|
|
37
|
+
|
|
38
|
+
Download the latest `.exe` installer from the Releases page and run it. The application will guide you through connecting your GitHub account.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Auto-Sync (The Magic)
|
|
43
|
+
|
|
44
|
+
Once logged in, simply open any Git repository in GitElectron. The **Auto-Sync** feature is enabled by default. Minimize the app.
|
|
45
|
+
As you edit and save files in VS Code or your editor of choice, GitElectron will detect the changes, wait 3 seconds for you to finish, and push them directly to GitHub.
|
|
46
|
+
|
|
47
|
+
### CLI Commands
|
|
48
|
+
|
|
49
|
+
- **Open Contextually:** Open your terminal inside your project folder and run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
gitelectron open
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The desktop app will instantly pop up and initialize that exact folder.
|
|
56
|
+
|
|
57
|
+
- **Manual Push:** Push a single file quickly:
|
|
58
|
+
```bash
|
|
59
|
+
gitelectron push -f src/index.js -m "Updated logic"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Development Setup
|
|
63
|
+
|
|
64
|
+
To build and run GitElectron locally:
|
|
65
|
+
|
|
66
|
+
1. Clone the repository and install dependencies:
|
|
67
|
+
```bash
|
|
68
|
+
npm install
|
|
69
|
+
```
|
|
70
|
+
2. Start the development server (Hot Module Replacement enabled):
|
|
71
|
+
```bash
|
|
72
|
+
npm run dev
|
|
73
|
+
```
|
|
74
|
+
3. To build the production binary:
|
|
75
|
+
```bash
|
|
76
|
+
npm run build:win
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Architecture
|
|
80
|
+
|
|
81
|
+
- **Renderer (`src/renderer`):** A modern React + TailwindCSS interface running inside an Electron Window.
|
|
82
|
+
- **Main (`src/main`):** Node.js backend handling system files (`chokidar` for watching), secure credentials, single-instance locking, and Git automation via `simple-git`.
|
|
83
|
+
- **Preload (`src/preload`):** A secure ContextBridge tying the UI strictly to safe IPC handlers.
|
|
84
|
+
- **CLI (`bin/cli.js`):** A Commander-based Node script that transparently spawns the packaged Electron executable, merging NPM utility with Desktop power.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander')
|
|
4
|
+
const { spawn, execSync } = require('child_process')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs')
|
|
7
|
+
const os = require('os')
|
|
8
|
+
const readline = require('readline')
|
|
9
|
+
|
|
10
|
+
function getConfigPath() {
|
|
11
|
+
return path.join(os.homedir(), '.gitelectron-cli-config.json')
|
|
12
|
+
}
|
|
13
|
+
function getAiConfig() {
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(getConfigPath())) {
|
|
16
|
+
return JSON.parse(fs.readFileSync(getConfigPath(), 'utf8'))
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {}
|
|
19
|
+
return { provider: 'gemini', apiKey: '' }
|
|
20
|
+
}
|
|
21
|
+
function setAiConfig(config) {
|
|
22
|
+
fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function promptUser(query) {
|
|
26
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
27
|
+
return new Promise((resolve) =>
|
|
28
|
+
rl.question(query, (ans) => {
|
|
29
|
+
rl.close()
|
|
30
|
+
resolve(ans)
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function analyzeErrorWithAI(errorMsg, gitStatusStr) {
|
|
36
|
+
let config = getAiConfig()
|
|
37
|
+
if (!config.apiKey) {
|
|
38
|
+
config = {
|
|
39
|
+
provider: 'gemini',
|
|
40
|
+
apiKey:
|
|
41
|
+
'AQ.Ab8RN6IVl_GqoM0dDg6VoC6ru59Q5TBFNqRjueF-5eLms0ZqDQ,AQ.Ab8RN6Lvygx3fB2a7G8xIjjTIqjvxHER9rZmtLQaeiUkkUY3UA'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`\nš¤ Analyzing error with ${config.provider}...`)
|
|
46
|
+
|
|
47
|
+
const systemPrompt = `You are an expert Git Auto-Fixer for GitElectron CLI.
|
|
48
|
+
The user encountered a Git error.
|
|
49
|
+
GIT STATUS: ${gitStatusStr}
|
|
50
|
+
ERROR MESSAGE: ${errorMsg}
|
|
51
|
+
|
|
52
|
+
Provide:
|
|
53
|
+
1. Short explanation.
|
|
54
|
+
2. Terminal commands to fix it.
|
|
55
|
+
Return ONLY valid JSON (no markdown): {"explanation":"...", "commandsToRun":["git ..."]}`
|
|
56
|
+
|
|
57
|
+
const keys = config.apiKey
|
|
58
|
+
.split(',')
|
|
59
|
+
.map((k) => k.trim())
|
|
60
|
+
.filter((k) => k)
|
|
61
|
+
let lastError = null
|
|
62
|
+
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
try {
|
|
65
|
+
let responseText = ''
|
|
66
|
+
if (config.provider === 'gemini') {
|
|
67
|
+
const { GoogleGenerativeAI } = require('@google/generative-ai')
|
|
68
|
+
const genAI = new GoogleGenerativeAI(key)
|
|
69
|
+
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' })
|
|
70
|
+
const result = await model.generateContent([systemPrompt])
|
|
71
|
+
responseText = result.response.text()
|
|
72
|
+
} else {
|
|
73
|
+
const OpenAI = require('openai').default
|
|
74
|
+
const openai = new OpenAI({ apiKey: key, baseURL: 'https://router.huggingface.co/v1' })
|
|
75
|
+
const response = await openai.chat.completions.create({
|
|
76
|
+
model: 'deepseek-ai/DeepSeek-V4-Flash:novita',
|
|
77
|
+
messages: [{ role: 'system', content: systemPrompt }],
|
|
78
|
+
response_format: { type: 'json_object' }
|
|
79
|
+
})
|
|
80
|
+
responseText = response.choices[0].message.content || '{}'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const parsed = JSON.parse(
|
|
84
|
+
responseText
|
|
85
|
+
.replace(/```json/g, '')
|
|
86
|
+
.replace(/```/g, '')
|
|
87
|
+
.trim()
|
|
88
|
+
)
|
|
89
|
+
return parsed
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(
|
|
92
|
+
`[AI Service] Failed with key ${key.substring(0, 5)}... Trying next if available.`
|
|
93
|
+
)
|
|
94
|
+
lastError = e
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.error('AI Analysis failed after trying all keys:', lastError?.message)
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const program = new Command()
|
|
103
|
+
|
|
104
|
+
// The Electron executable is provided by the 'electron' package
|
|
105
|
+
let electronPath = ''
|
|
106
|
+
try {
|
|
107
|
+
electronPath = require('electron')
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.error('GitElectron is missing its electron dependency. Run npm install.')
|
|
110
|
+
process.exit(1)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Path to the application root (where package.json and out/ exist)
|
|
114
|
+
const appPath = path.resolve(__dirname, '..')
|
|
115
|
+
|
|
116
|
+
program
|
|
117
|
+
.name('gitelectron')
|
|
118
|
+
.description('GitElectron - Lightweight, flexible Git automation tool')
|
|
119
|
+
.version('1.0.0')
|
|
120
|
+
|
|
121
|
+
program
|
|
122
|
+
.command('open')
|
|
123
|
+
.description('Open GitElectron desktop application')
|
|
124
|
+
.action(() => {
|
|
125
|
+
console.log('Opening GitElectron...')
|
|
126
|
+
|
|
127
|
+
// Spawn the electron process and pass the current working directory
|
|
128
|
+
const child = spawn(electronPath, [appPath, '--cwd', process.cwd()], {
|
|
129
|
+
detached: true,
|
|
130
|
+
stdio: 'ignore'
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// Unref so the CLI exits immediately and leaves the app running
|
|
134
|
+
child.unref()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
program
|
|
138
|
+
.command('push')
|
|
139
|
+
.description('Push a file directly to GitHub from the terminal')
|
|
140
|
+
.requiredOption('-f, --file <path>', 'File or directory to push')
|
|
141
|
+
.option('-m, --message <msg>', 'Commit message')
|
|
142
|
+
.option('-b, --branch <branch>', 'Branch to push to')
|
|
143
|
+
.action(async (options) => {
|
|
144
|
+
// For push command, we could either implement simple-git logic here
|
|
145
|
+
// or we can pass args to the electron app to handle it (like an invisible window).
|
|
146
|
+
// Let's implement basic push right here for speed, matching the old CLI.
|
|
147
|
+
try {
|
|
148
|
+
const simpleGit = require('simple-git')
|
|
149
|
+
const targetPath = path.resolve(process.cwd(), options.file)
|
|
150
|
+
if (!fs.existsSync(targetPath)) {
|
|
151
|
+
console.error(`Error: File or directory ${targetPath} does not exist.`)
|
|
152
|
+
process.exit(1)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const git = simpleGit(process.cwd())
|
|
156
|
+
const isRepo = await git.checkIsRepo()
|
|
157
|
+
if (!isRepo) {
|
|
158
|
+
console.error('Error: Current directory is not a git repository.')
|
|
159
|
+
process.exit(1)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`Staging ${options.file}...`)
|
|
163
|
+
await git.add(targetPath)
|
|
164
|
+
|
|
165
|
+
const commitMsg = options.message || `Update ${path.basename(targetPath)}`
|
|
166
|
+
console.log(`Committing with message: "${commitMsg}"...`)
|
|
167
|
+
await git.commit(commitMsg)
|
|
168
|
+
|
|
169
|
+
let branch = options.branch
|
|
170
|
+
if (!branch) {
|
|
171
|
+
const status = await git.status()
|
|
172
|
+
branch = status.current || 'main'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log(`Pushing to branch ${branch}...`)
|
|
176
|
+
await git.push('origin', branch, { '--set-upstream': null })
|
|
177
|
+
|
|
178
|
+
console.log('Successfully pushed! š')
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log('\nš Git issue detected. Initiating AI Auto-Heal in the background...')
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const simpleGit = require('simple-git')
|
|
184
|
+
const git = simpleGit(process.cwd())
|
|
185
|
+
const status = await git.status()
|
|
186
|
+
|
|
187
|
+
const aiResponse = await analyzeErrorWithAI(error.message, JSON.stringify(status, null, 2))
|
|
188
|
+
|
|
189
|
+
if (aiResponse && aiResponse.commandsToRun.length > 0) {
|
|
190
|
+
for (const cmd of aiResponse.commandsToRun) {
|
|
191
|
+
execSync(cmd, { stdio: 'ignore' })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let branch = options.branch
|
|
195
|
+
if (!branch) {
|
|
196
|
+
branch = status.current || 'main'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(`ā
AI fix applied. Retrying push to branch ${branch}...`)
|
|
200
|
+
await git.push('origin', branch, { '--set-upstream': null })
|
|
201
|
+
console.log('š Successfully pushed after Auto-Healing!')
|
|
202
|
+
} else {
|
|
203
|
+
console.error('\nā An error occurred and AI Auto-Heal failed:', error.message)
|
|
204
|
+
process.exit(1)
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
console.error('\nā An error occurred and AI Auto-Heal failed:', error.message)
|
|
208
|
+
process.exit(1)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
program.parse()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
console.log('Installing GitElectron dependencies...')
|
|
7
|
+
|
|
8
|
+
// Run electron-builder install-app-deps (only fails if not in dev mode, so we wrap it)
|
|
9
|
+
try {
|
|
10
|
+
const isGlobal = process.env.npm_config_global === 'true'
|
|
11
|
+
|
|
12
|
+
if (!isGlobal) {
|
|
13
|
+
const { execSync } = require('child_process')
|
|
14
|
+
execSync('npx electron-builder install-app-deps', { stdio: 'inherit' })
|
|
15
|
+
} else {
|
|
16
|
+
// If installed globally via NPM, spawn the app to prompt for login
|
|
17
|
+
let electronPath
|
|
18
|
+
try {
|
|
19
|
+
electronPath = require('electron')
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error('Electron binary not found. Skipping auto-launch.')
|
|
22
|
+
process.exit(0)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log('First time setup: Launching GitElectron for authentication...')
|
|
26
|
+
const appPath = path.resolve(__dirname, '..')
|
|
27
|
+
const child = spawn(electronPath, [appPath], {
|
|
28
|
+
detached: true,
|
|
29
|
+
stdio: 'ignore'
|
|
30
|
+
})
|
|
31
|
+
child.unref()
|
|
32
|
+
|
|
33
|
+
// Create a Desktop Shortcut for easy access
|
|
34
|
+
if (process.platform === 'win32') {
|
|
35
|
+
try {
|
|
36
|
+
const os = require('os')
|
|
37
|
+
const desktopPath = path.join(os.homedir(), 'Desktop', 'GitElectron.lnk')
|
|
38
|
+
|
|
39
|
+
const psCommand = `
|
|
40
|
+
$WshShell = New-Object -comObject WScript.Shell;
|
|
41
|
+
$Shortcut = $WshShell.CreateShortcut("${desktopPath}");
|
|
42
|
+
$Shortcut.TargetPath = "${electronPath}";
|
|
43
|
+
$Shortcut.Arguments = "\\"${appPath}\\"";
|
|
44
|
+
$Shortcut.Save();
|
|
45
|
+
`;
|
|
46
|
+
const { execSync } = require('child_process');
|
|
47
|
+
execSync(`powershell -NoProfile -Command "${psCommand}"`, { stdio: 'ignore' });
|
|
48
|
+
console.log('ā
Desktop shortcut created successfully!');
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.log('Failed to create Desktop shortcut.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// Ignore errors on postinstall
|
|
56
|
+
console.log('Postinstall step completed.')
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
|
|
4
|
+
console.log('Preparing GitElectron for update... Safely closing any active instances.');
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
if (os.platform() === 'win32') {
|
|
8
|
+
execSync('taskkill /IM GitElectron.exe /F', { stdio: 'ignore' });
|
|
9
|
+
} else {
|
|
10
|
+
execSync('pkill -f GitElectron', { stdio: 'ignore' });
|
|
11
|
+
}
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// Process might not be running, which is completely fine.
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Block for 1.5 seconds to ensure the OS fully releases all file locks
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
while (Date.now() - start < 1500) {
|
|
19
|
+
// Wait
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
process.exit(0);
|