youmna-git-glance 1.1.2 β 1.1.4
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 -12
- package/index.js +154 -38
- package/package.json +11 -6
- package/utils/ui.js +57 -0
package/README.md
CHANGED
|
@@ -1,24 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# π¦ youmna-git (ygit)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The AI-Powered Git Assistant that makes your workflow glide.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/youmna-git)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
+
`youmna-git` is a professional-grade CLI tool that transforms your terminal into a smart dashboard. Powered by **Google Gemini 3.0**, it doesn't just show you your git statusβit helps you write code, review bugs, and understand your history.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
8
12
|
## β¨ Features
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
+
|
|
14
|
+
- π **Smart Dashboard:** A beautiful, color-coded summary of your branch, changes, and latest commits.
|
|
15
|
+
- π€ **Gemini AI Commit:** Instantly generate professional, one-line commit messages by analyzing your file changes.
|
|
16
|
+
- π **AI Code Review:** Get a senior-level review of your current diff to spot bugs before you push.
|
|
17
|
+
- π¬ **Repo Chat:** Ask questions like "What did I change in the last hour?" and get answers based on your git logs.
|
|
18
|
+
- π‘οΈ **Merge Helper:** Solve complex merge conflicts with a step-by-step AI resolution plan.
|
|
19
|
+
- β‘ **Lightweight & Fast:** Built for speed, keeping your hands on the keyboard.
|
|
20
|
+
---
|
|
13
21
|
|
|
14
22
|
## π¦ Installation
|
|
23
|
+
|
|
15
24
|
Install the tool globally using npm:
|
|
16
25
|
|
|
17
26
|
```bash
|
|
18
|
-
npm install -g youmna-git
|
|
19
|
-
|
|
27
|
+
npm install -g youmna-git
|
|
28
|
+
```
|
|
29
|
+
----------
|
|
30
|
+
# π¦ youmna-git (ygit)
|
|
31
|
+
|
|
32
|
+
**The AI-Powered Git Assistant that makes your workflow glide.**
|
|
33
|
+
|
|
34
|
+
[](https://www.npmjs.com/package/youmna-git)
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
|
|
37
|
+
`youmna-git` is a professional-grade CLI tool that transforms your terminal into a smart dashboard. Powered by **Google Gemini 1.5 Flash**, it doesn't just show you your git statusβit helps you write code, review bugs, and understand your history.
|
|
20
38
|
|
|
21
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## π Setup
|
|
42
|
+
|
|
43
|
+
To use the AI features (Commit, Review, Chat), you need to get a free API Key from Google:
|
|
44
|
+
|
|
45
|
+
1. **Get your Key:** Go to [Google AI Studio](https://aistudio.google.com/) and click **"Get API key"**.
|
|
46
|
+
2. **Add to Environment:** Create a file named `.env` in your project root or add it to your shell profile:
|
|
47
|
+
```text
|
|
48
|
+
GEMINI_API_KEY=your_key_here
|
|
49
|
+
```
|
|
50
|
+
3. **Important:** Make sure your `.env` `.npmrc` files are added to your `.gitignore` so your key stays private!
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## π Usage
|
|
55
|
+
|
|
56
|
+
Simply type `ygit` to launch the main dashboard, or use these commands:
|
|
57
|
+
|
|
58
|
+
β **Launch the interactive dashboard**
|
|
22
59
|
```bash
|
|
23
|
-
|
|
24
|
-
```
|
|
60
|
+
ygit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
β **Generate an AI commit message for staged changes**
|
|
64
|
+
```bash
|
|
65
|
+
ygit commit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
β **Analyze current code diff for bugs**
|
|
69
|
+
```bash
|
|
70
|
+
ygit review
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
β **Ask a question about your history**
|
|
74
|
+
```bash
|
|
75
|
+
ygit chat "What features did I add yesterday?"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
β **Get help resolving active merge conflicts**
|
|
79
|
+
```bash
|
|
80
|
+
ygit merge-help
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
π€ Contributing
|
|
85
|
+
|
|
86
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
87
|
+
|
|
88
|
+
1- Fork the Project
|
|
89
|
+
|
|
90
|
+
2- Create your Feature Branch (git checkout -b feature/AmazingFeature)
|
|
91
|
+
|
|
92
|
+
3- Commit your Changes (git commit -m 'Add some AmazingFeature')
|
|
93
|
+
|
|
94
|
+
4- Push to the Branch (git push origin feature/AmazingFeature)
|
|
95
|
+
|
|
96
|
+
5- Open a Pull Request
|
package/index.js
CHANGED
|
@@ -1,50 +1,166 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { program } from 'commander';
|
|
3
3
|
import { simpleGit } from 'simple-git';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
4
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
5
|
+
import 'dotenv/config';
|
|
6
|
+
import { primary, success, accent, error, text, createSpinner, createBox, createProgressBar, log, logError } from './utils/ui.js'; // Updated import
|
|
7
|
+
import fs from 'fs'; // Ensure you have this import at the top
|
|
7
8
|
|
|
8
9
|
const git = simpleGit();
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
// --- Gemini AI Setup ---
|
|
12
|
+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
|
|
13
|
+
const model = genAI.getGenerativeModel({
|
|
14
|
+
model: process.env.GEMINI_MODEL || "gemini-3-flash-preview"
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// --- π THE DASHBOARD (Default) ---
|
|
18
|
+
async function showDashboard() {
|
|
19
|
+
const spinner = createSpinner('Loading Git Dashboard...').start();
|
|
20
|
+
try {
|
|
21
|
+
const isRepo = await git.checkIsRepo();
|
|
22
|
+
if (!isRepo) {
|
|
23
|
+
spinner.fail(error('Error: Not a git repository!'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const status = await git.status();
|
|
28
|
+
const logResult = await git.log({ n: 1 });
|
|
29
|
+
const remote = await git.getRemotes(true);
|
|
30
|
+
spinner.stop();
|
|
31
|
+
|
|
32
|
+
const stats = `
|
|
33
|
+
${primary.bold('Branch:')} ${text(status.current)}
|
|
34
|
+
${success.bold('Latest:')} ${text(logResult.latest.message)}
|
|
35
|
+
${accent.bold('Author:')} ${text(logResult.latest.author_name)}
|
|
36
|
+
${primary.bold('Status:')} ${text(status.files.length + ' files modified')}
|
|
37
|
+
${primary.bold('Remote:')} ${text(remote[0]?.refs.fetch || 'None')}
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
log(createBox(stats, { title: 'π Youmna Git Dashboard (Hacker Mode)', borderColor: primary.toString() })); // Using new createBox
|
|
41
|
+
} catch (err) {
|
|
42
|
+
spinner.fail(error('Failed to load dashboard.'));
|
|
43
|
+
logError(err.message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
12
46
|
|
|
47
|
+
// --- π€ AI COMMANDS ---
|
|
48
|
+
program.name('ygit').version('2.2.0').description('Personal AI Git Assistant powered by Gemini');
|
|
49
|
+
|
|
50
|
+
// 1. AI Commit Suggestion
|
|
51
|
+
program
|
|
52
|
+
.command('commit')
|
|
53
|
+
.description('Suggest a commit message using Gemini AI')
|
|
54
|
+
.action(async () => {
|
|
55
|
+
const spinner = createSpinner('AI is analyzing changes...').start();
|
|
56
|
+
try {
|
|
57
|
+
const diff = await git.diff();
|
|
58
|
+
if (!diff) return spinner.info(text('No changes found to describe.'));
|
|
59
|
+
|
|
60
|
+
const prompt = `Write a professional, concise one-line git commit message for these changes: ${diff.substring(0, 5000)}`;
|
|
61
|
+
const result = await model.generateContent(prompt);
|
|
62
|
+
|
|
63
|
+
spinner.stop();
|
|
64
|
+
log(createBox(success(result.response.text().trim()), { title: 'β¨ Gemini Suggestion', borderColor: 'green' }));
|
|
65
|
+
} catch (err) {
|
|
66
|
+
spinner.stop();
|
|
67
|
+
if (err.message.includes('429')) {
|
|
68
|
+
logError("Quota reached! Please wait 60 seconds before trying again.");
|
|
69
|
+
} else if (err.message.includes('404')) {
|
|
70
|
+
logError("Model not found. Ensure line 12 is: gemini-3-flash-preview");
|
|
71
|
+
} else {
|
|
72
|
+
logError(err.message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// 2. AI Code Review
|
|
78
|
+
program
|
|
79
|
+
.command('review')
|
|
80
|
+
.description('Let Gemini review your code for bugs')
|
|
81
|
+
.action(async () => {
|
|
82
|
+
const spinner = createSpinner('Gemini is inspecting your code...', accent).start();
|
|
13
83
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
84
|
+
const diff = await git.diff();
|
|
85
|
+
const prompt = `You are a senior reviewer. Spot bugs or messy logic in this diff. Be concise: ${diff.substring(0, 5000)}`;
|
|
86
|
+
const result = await model.generateContent(prompt);
|
|
87
|
+
|
|
88
|
+
spinner.stop();
|
|
89
|
+
|
|
90
|
+
log(createBox(text(result.response.text()), {
|
|
91
|
+
title: 'π AI Code Review',
|
|
92
|
+
borderColor: 'magenta'
|
|
93
|
+
}));
|
|
94
|
+
} catch (err) { spinner.fail(error('Review failed.')); logError(err.message); }
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 3. Repo Chat
|
|
98
|
+
program
|
|
99
|
+
.command('chat <question>')
|
|
100
|
+
.description('Ask a question about your git history')
|
|
101
|
+
.action(async (question) => {
|
|
102
|
+
const spinner = createSpinner('Consulting history...').start();
|
|
103
|
+
try {
|
|
104
|
+
const logs = await git.log({ n: 10 });
|
|
105
|
+
const prompt = `Based on these git logs: ${JSON.stringify(logs)}, answer: ${question}`;
|
|
106
|
+
const result = await model.generateContent(prompt);
|
|
107
|
+
|
|
108
|
+
spinner.stop();
|
|
109
|
+
log(`${accent('π€ Gemini Assistant:')} ${text(result.response.text())}`);
|
|
110
|
+
} catch (err) { spinner.fail(error('Chat failed.')); logError(err.message); }
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 4. AI-Powered Merge Conflict Helper (FIXED)
|
|
114
|
+
program
|
|
115
|
+
.command('merge-help')
|
|
116
|
+
.description('Analyze and suggest resolutions for merge conflicts')
|
|
117
|
+
.action(async () => {
|
|
118
|
+
const spinner = createSpinner('Scanning for π conflicts...').start();
|
|
119
|
+
try {
|
|
120
|
+
const status = await git.status();
|
|
121
|
+
const conflictedFiles = status.conflicted; // Get files that are "unmerged"
|
|
122
|
+
|
|
123
|
+
if (conflictedFiles.length === 0) {
|
|
124
|
+
spinner.succeed(success('No merge conflicts detected! Everything is clean.'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const conflictDetails = [];
|
|
129
|
+
for (const file of conflictedFiles) {
|
|
130
|
+
// We read from the disk (worktree) instead of the Git index
|
|
131
|
+
// This avoids the "Stage 0" error
|
|
132
|
+
const content = await fs.promises.readFile(file, 'utf8');
|
|
133
|
+
if (content.includes('<<<<<<<')) {
|
|
134
|
+
conflictDetails.push(`File: ${file}\n${content}`);
|
|
18
135
|
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (conflictDetails.length === 0) {
|
|
139
|
+
spinner.fail(error('Conflicted files found, but no markers (<<<<<<<) detected.'));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
spinner.text = primary('Gemini is resolving the puzzle...');
|
|
144
|
+
const prompt = `You are a Git Expert. Explain the conflict and suggest a solution for:
|
|
145
|
+
${conflictDetails.join('\n\n').substring(0, 8000)}`;
|
|
19
146
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
${chalk.magenta.bold('Changes:')} ${chalk.white(status.files.length + ' files modified')}
|
|
32
|
-
${chalk.blue.bold('Remote:')} ${chalk.white(remote[0]?.refs.fetch || 'None')}
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
console.log(boxen(stats, {
|
|
36
|
-
padding: 1,
|
|
37
|
-
margin: 1,
|
|
38
|
-
borderStyle: 'round',
|
|
39
|
-
title: 'Git Glance Dashboard',
|
|
40
|
-
titleAlignment: 'center',
|
|
41
|
-
borderColor: 'cyan'
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
} catch (error) {
|
|
45
|
-
spinner.fail(chalk.red('Something went wrong!'));
|
|
46
|
-
console.error(error);
|
|
147
|
+
const result = await model.generateContent(prompt);
|
|
148
|
+
spinner.stop();
|
|
149
|
+
|
|
150
|
+
log(createBox(text(result.response.text()), {
|
|
151
|
+
title: 'π‘οΈ AI Merge Resolution',
|
|
152
|
+
borderColor: 'red'
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
} catch (err) {
|
|
156
|
+
spinner.stop();
|
|
157
|
+
logError("Could not analyze conflicts: " + err.message);
|
|
47
158
|
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Default behavior
|
|
162
|
+
if (!process.argv.slice(2).length) {
|
|
163
|
+
showDashboard();
|
|
48
164
|
}
|
|
49
165
|
|
|
50
|
-
|
|
166
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "youmna-git-glance",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
|
+
"description": "AI-powered Git assistant using Google Gemini with advanced features π¦",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
|
-
"
|
|
7
|
+
"ygit": "./index.js"
|
|
7
8
|
},
|
|
8
9
|
"dependencies": {
|
|
9
|
-
"
|
|
10
|
-
"chalk": "^5.3.0",
|
|
10
|
+
"@google/generative-ai": "^0.24.1",
|
|
11
11
|
"boxen": "^7.1.1",
|
|
12
|
-
"
|
|
12
|
+
"chalk": "^5.3.0",
|
|
13
|
+
"cli-progress": "^3.12.0",
|
|
14
|
+
"commander": "^11.1.0",
|
|
15
|
+
"dotenv": "^16.4.2",
|
|
16
|
+
"ora": "^8.0.1",
|
|
17
|
+
"simple-git": "^3.27.0"
|
|
13
18
|
}
|
|
14
|
-
}
|
|
19
|
+
}
|
package/utils/ui.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// utils/ui.js
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import boxen from 'boxen';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import cliProgress from 'cli-progress'; // New dependency for progress bar
|
|
6
|
+
|
|
7
|
+
// --- Theme Colors ---
|
|
8
|
+
export const primary = chalk.hex('#00FFFF'); // Electric Cyan
|
|
9
|
+
export const success = chalk.hex('#00FF00'); // Bright Green
|
|
10
|
+
export const accent = chalk.hex('#FF00FF'); // Magenta
|
|
11
|
+
export const warning = chalk.hex('#FFFF00'); // Yellow
|
|
12
|
+
export const error = chalk.hex('#FF0000'); // Red
|
|
13
|
+
export const text = chalk.whiteBright;
|
|
14
|
+
|
|
15
|
+
// --- CLI Components ---
|
|
16
|
+
// utils/ui.js
|
|
17
|
+
export const createSpinner = (textMsg, color = primary) => {
|
|
18
|
+
// Safe check for hex codes
|
|
19
|
+
const match = color.toString().match(/#(?:[0-9a-fA-F]{3}){1,2}/);
|
|
20
|
+
const spinnerColor = match ? match[0] : 'cyan';
|
|
21
|
+
|
|
22
|
+
return ora({
|
|
23
|
+
text: color(textMsg),
|
|
24
|
+
spinner: { interval: 80, frames: ['β ', 'β ', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ', 'β '] },
|
|
25
|
+
// Don't pass hex codes to 'ora', use 'cyan' as a fallback
|
|
26
|
+
color: spinnerColor.startsWith('#') ? 'cyan' : spinnerColor
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const createProgressBar = (name = "Progress", barSize = 20) => {
|
|
31
|
+
const bar = new cliProgress.SingleBar({
|
|
32
|
+
format: `${accent('{bar}')} ${text('{percentage}%')} | {value}/{total} ${primary(name)}`,
|
|
33
|
+
barCompleteChar: '\u2588', // Full block
|
|
34
|
+
barIncompleteChar: '\u2591', // Light shade
|
|
35
|
+
hideCursor: true,
|
|
36
|
+
barsize: barSize,
|
|
37
|
+
linewrap: false
|
|
38
|
+
}, cliProgress.Presets.shades_classic);
|
|
39
|
+
return bar;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const createBox = (content, options = {}) => {
|
|
43
|
+
const defaultOptions = {
|
|
44
|
+
padding: 1,
|
|
45
|
+
margin: 1,
|
|
46
|
+
borderStyle: 'double', // Hacker Mode border
|
|
47
|
+
borderColor: primary.name,
|
|
48
|
+
titleAlignment: 'center',
|
|
49
|
+
...options
|
|
50
|
+
};
|
|
51
|
+
return boxen(content, defaultOptions);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const log = (message, color = text) => console.log(color(message));
|
|
55
|
+
export const logError = (message) => console.error(error('Error: ') + error(message));
|
|
56
|
+
export const logSuccess = (message) => console.log(success(message));
|
|
57
|
+
export const logWarning = (message) => console.log(warning(message));
|