youmna-git-glance 1.1.5 β 1.1.6
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 +64 -27
- package/index.js +45 -21
- package/package.json +1 -1
- package/utils/ui.js +9 -13
package/README.md
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
# π¦ youmna-git (ygit)
|
|
1
|
+
# π¦ youmna-git-glance (ygit)
|
|
2
|
+
**The Git Assistant with Long-Term Memory.**
|
|
3
|
+
[](https://www.npmjs.com/package/youmna-git-glance)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
`youmna-git-glance` is an AI-native CLI that transforms your repository's history into an active knowledge base. Powered by Google Gemini, it uses intelligent keyword extraction to scout your entire commit history, providing AI-powered code reviews, automated professional commits, and context-aware chat that truly understands your codebase.
|
|
4
7
|
|
|
5
|
-
[](https://www.npmjs.com/package/youmna-git)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## π₯ Demo
|
|
12
|
+
|
|
13
|
+
<a href="https://www.youtube.com/watch?v=Jxc45Jc_-dc" target="_blank">
|
|
14
|
+
<img src="https://img.youtube.com/vi/Jxc45Jc_-dc/maxresdefault.jpg" alt="ygit Demo Video" width="600px">
|
|
15
|
+
</a>
|
|
16
|
+
|
|
17
|
+
*Click to watch the full walkthrough (AI Review, Smart Chat, and Merge Resolution)*
|
|
9
18
|
|
|
10
19
|
---
|
|
11
20
|
|
|
@@ -14,9 +23,10 @@
|
|
|
14
23
|
- π **Smart Dashboard:** A beautiful, color-coded summary of your branch, changes, and latest commits.
|
|
15
24
|
- π€ **Gemini AI Commit:** Instantly generate professional, one-line commit messages by analyzing your file changes.
|
|
16
25
|
- π **AI Code Review:** Get a senior-level review of your current diff to spot bugs before you push.
|
|
17
|
-
- π¬ **
|
|
26
|
+
- π¬ **Smart Context Chat:** Ask natural questions about your repository. The AI scans your entire git history using intelligent keyword matching to find relevant commits and deliver context-aware answers.
|
|
18
27
|
- π‘οΈ **Merge Helper:** Solve complex merge conflicts with a step-by-step AI resolution plan.
|
|
19
28
|
- β‘ **Lightweight & Fast:** Built for speed, keeping your hands on the keyboard.
|
|
29
|
+
|
|
20
30
|
---
|
|
21
31
|
|
|
22
32
|
## π¦ Installation
|
|
@@ -24,17 +34,14 @@
|
|
|
24
34
|
Install the tool globally using npm:
|
|
25
35
|
|
|
26
36
|
```bash
|
|
27
|
-
npm install -g youmna-git
|
|
37
|
+
npm install -g youmna-git-glance
|
|
28
38
|
```
|
|
29
|
-
----------
|
|
30
|
-
# π¦ youmna-git (ygit)
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
Then use it anywhere with:
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
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.
|
|
42
|
+
```bash
|
|
43
|
+
ygit
|
|
44
|
+
```
|
|
38
45
|
|
|
39
46
|
---
|
|
40
47
|
|
|
@@ -42,12 +49,12 @@ npm install -g youmna-git
|
|
|
42
49
|
|
|
43
50
|
To use the AI features (Commit, Review, Chat), you need to get a free API Key from Google:
|
|
44
51
|
|
|
45
|
-
1.
|
|
46
|
-
2.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
3.
|
|
52
|
+
1. **Get your Key:** Go to [Google AI Studio](https://aistudio.google.com/) and click **"Get API key"**.
|
|
53
|
+
2. **Add to Environment:** Create a file named `.env` in your project root or add it to your shell profile:
|
|
54
|
+
```text
|
|
55
|
+
GEMINI_API_KEY=your_key_here
|
|
56
|
+
```
|
|
57
|
+
3. **Important:** Make sure your `.env` and `.npmrc` files are added to your `.gitignore` so your key stays private!
|
|
51
58
|
|
|
52
59
|
---
|
|
53
60
|
|
|
@@ -70,27 +77,57 @@ ygit commit
|
|
|
70
77
|
ygit review
|
|
71
78
|
```
|
|
72
79
|
|
|
73
|
-
β **Ask a question about your history**
|
|
80
|
+
β **Ask a question about your repository history**
|
|
74
81
|
```bash
|
|
75
82
|
ygit chat "What features did I add yesterday?"
|
|
76
83
|
```
|
|
77
84
|
|
|
85
|
+
The chat command uses advanced keyword extraction to intelligently search your entire git history. It identifies important keywords from your question (like "features", "login", "bugfix") and searches across all branches to find the most relevant commits. If no matches are found, it falls back to your 10 most recent commits, ensuring you always get contextual answers about your work.
|
|
86
|
+
|
|
87
|
+
**Chat Examples:**
|
|
88
|
+
```bash
|
|
89
|
+
ygit chat "When did I fix the authentication bug?"
|
|
90
|
+
ygit chat "What changes did I make to the database module?"
|
|
91
|
+
ygit chat "Show me commits related to the API refactor"
|
|
92
|
+
```
|
|
93
|
+
|
|
78
94
|
β **Get help resolving active merge conflicts**
|
|
79
95
|
```bash
|
|
80
96
|
ygit merge-help
|
|
81
97
|
```
|
|
82
98
|
|
|
99
|
+
---
|
|
83
100
|
|
|
84
|
-
π€ Contributing
|
|
101
|
+
## π€ Contributing
|
|
85
102
|
|
|
86
103
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
87
104
|
|
|
88
|
-
1
|
|
105
|
+
1. Fork the Project
|
|
106
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
107
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
108
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
109
|
+
5. Open a Pull Request
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## π License
|
|
114
|
+
|
|
115
|
+
This project is licensed under the MIT License. Feel free to use and modify it.
|
|
89
116
|
|
|
90
|
-
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## π Support & Community
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
- **Issues:** If you run into any trouble, please open an [issue on GitHub](https://github.com/YoumnaSalloum/git-glance-cli/issues).
|
|
122
|
+
- **Feature Requests:** Have an idea for a new AI command? Let us know in the issues!
|
|
123
|
+
- **Pull Requests:** We love community contributions. Feel free to fork the repo and submit a PR.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## π Happy Coding! π
|
|
128
|
+
|
|
129
|
+
---
|
|
93
130
|
|
|
94
|
-
|
|
131
|
+
## Keywords
|
|
95
132
|
|
|
96
|
-
|
|
133
|
+
`git` `gemini` `ai` `cli` `automation` `code-review` `commit-messages` `merge-conflicts` `git-assistant` `developer-tools` `terminal` `productivity`
|
package/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import { program } from 'commander';
|
|
3
2
|
import { simpleGit } from 'simple-git';
|
|
4
3
|
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
5
4
|
import 'dotenv/config';
|
|
6
5
|
import { primary, success, accent, error, text, createSpinner, createBox, createProgressBar, log, logError } from './utils/ui.js'; // Updated import
|
|
7
|
-
import fs from 'fs';
|
|
6
|
+
import fs from 'fs';
|
|
8
7
|
|
|
9
8
|
const git = simpleGit();
|
|
10
9
|
|
|
@@ -14,7 +13,6 @@ const model = genAI.getGenerativeModel({
|
|
|
14
13
|
model: process.env.GEMINI_MODEL || "gemini-3-flash-preview"
|
|
15
14
|
});
|
|
16
15
|
|
|
17
|
-
// --- π THE DASHBOARD (Default) ---
|
|
18
16
|
async function showDashboard() {
|
|
19
17
|
const spinner = createSpinner('Loading Git Dashboard...').start();
|
|
20
18
|
try {
|
|
@@ -37,14 +35,13 @@ ${primary.bold('Status:')} ${text(status.files.length + ' files modified')}
|
|
|
37
35
|
${primary.bold('Remote:')} ${text(remote[0]?.refs.fetch || 'None')}
|
|
38
36
|
`;
|
|
39
37
|
|
|
40
|
-
log(createBox(stats, { title: 'π Youmna Git Dashboard (Hacker Mode)', borderColor: primary.toString() }));
|
|
38
|
+
log(createBox(stats, { title: 'π Youmna Git Dashboard (Hacker Mode)', borderColor: primary.toString() }));
|
|
41
39
|
} catch (err) {
|
|
42
40
|
spinner.fail(error('Failed to load dashboard.'));
|
|
43
41
|
logError(err.message);
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
// --- π€ AI COMMANDS ---
|
|
48
45
|
program.name('ygit').version('2.2.0').description('Personal AI Git Assistant powered by Gemini');
|
|
49
46
|
|
|
50
47
|
// 1. AI Commit Suggestion
|
|
@@ -54,8 +51,12 @@ program
|
|
|
54
51
|
.action(async () => {
|
|
55
52
|
const spinner = createSpinner('AI is analyzing changes...').start();
|
|
56
53
|
try {
|
|
57
|
-
const diff = await git.diff();
|
|
58
|
-
|
|
54
|
+
const diff = await git.diff(['--cached']);
|
|
55
|
+
|
|
56
|
+
if (!diff) {
|
|
57
|
+
spinner.info(text('No staged changes found. Use "git add" first!'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
59
60
|
|
|
60
61
|
const prompt = `Write a professional, concise one-line git commit message for these changes: ${diff.substring(0, 5000)}`;
|
|
61
62
|
const result = await model.generateContent(prompt);
|
|
@@ -81,8 +82,14 @@ program
|
|
|
81
82
|
.action(async () => {
|
|
82
83
|
const spinner = createSpinner('Gemini is inspecting your code...', accent).start();
|
|
83
84
|
try {
|
|
84
|
-
const diff = await git.diff();
|
|
85
|
-
|
|
85
|
+
const diff = await git.diff(['HEAD']);
|
|
86
|
+
|
|
87
|
+
if (!diff) {
|
|
88
|
+
spinner.info(text('No changes found to review.'));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const prompt = `You are a senior reviewer. Spot bugs or messy logic in this diff: ${diff.substring(0, 5000)}`;
|
|
86
93
|
const result = await model.generateContent(prompt);
|
|
87
94
|
|
|
88
95
|
spinner.stop();
|
|
@@ -94,23 +101,43 @@ log(createBox(text(result.response.text()), {
|
|
|
94
101
|
} catch (err) { spinner.fail(error('Review failed.')); logError(err.message); }
|
|
95
102
|
});
|
|
96
103
|
|
|
97
|
-
// 3. Repo Chat
|
|
104
|
+
// 3. Repo Chat with Smart Context
|
|
98
105
|
program
|
|
99
106
|
.command('chat <question>')
|
|
100
|
-
.description('Ask a question about your git history')
|
|
107
|
+
.description('Ask a question about your git history (Smart Context)')
|
|
101
108
|
.action(async (question) => {
|
|
102
|
-
const spinner = createSpinner('
|
|
109
|
+
const spinner = createSpinner('Scanning history for context...').start();
|
|
103
110
|
try {
|
|
104
|
-
const
|
|
105
|
-
|
|
111
|
+
const keywords = question.toLowerCase().split(' ').filter(word => word.length > 3);
|
|
112
|
+
|
|
113
|
+
let contextLogs;
|
|
114
|
+
|
|
115
|
+
if (keywords.length > 0) {
|
|
116
|
+
const args = [
|
|
117
|
+
'--all',
|
|
118
|
+
'--grep=' + keywords.join('|'),
|
|
119
|
+
'-i',
|
|
120
|
+
'-n', '15'
|
|
121
|
+
];
|
|
122
|
+
contextLogs = await git.log(args);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!contextLogs || contextLogs.all.length === 0) {
|
|
126
|
+
contextLogs = await git.log(['-n', '10']);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const prompt = `You are a Git Expert. Answer this question based on these logs: ${JSON.stringify(contextLogs.all)}. Question: "${question}"`;
|
|
106
130
|
const result = await model.generateContent(prompt);
|
|
107
131
|
|
|
108
132
|
spinner.stop();
|
|
109
|
-
log(`${accent('π€ Gemini Assistant:')}
|
|
110
|
-
} catch (err) {
|
|
133
|
+
log(`${accent.bold('π€ Gemini Assistant:')}\n${text(result.response.text())}`);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
spinner.fail(error('Chat failed.'));
|
|
136
|
+
logError(err.message);
|
|
137
|
+
}
|
|
111
138
|
});
|
|
112
139
|
|
|
113
|
-
// 4. AI-Powered Merge Conflict
|
|
140
|
+
// 4. AI-Powered Merge Conflict Resolution
|
|
114
141
|
program
|
|
115
142
|
.command('merge-help')
|
|
116
143
|
.description('Analyze and suggest resolutions for merge conflicts')
|
|
@@ -118,7 +145,7 @@ program
|
|
|
118
145
|
const spinner = createSpinner('Scanning for π conflicts...').start();
|
|
119
146
|
try {
|
|
120
147
|
const status = await git.status();
|
|
121
|
-
const conflictedFiles = status.conflicted;
|
|
148
|
+
const conflictedFiles = status.conflicted;
|
|
122
149
|
|
|
123
150
|
if (conflictedFiles.length === 0) {
|
|
124
151
|
spinner.succeed(success('No merge conflicts detected! Everything is clean.'));
|
|
@@ -127,8 +154,6 @@ program
|
|
|
127
154
|
|
|
128
155
|
const conflictDetails = [];
|
|
129
156
|
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
157
|
const content = await fs.promises.readFile(file, 'utf8');
|
|
133
158
|
if (content.includes('<<<<<<<')) {
|
|
134
159
|
conflictDetails.push(`File: ${file}\n${content}`);
|
|
@@ -158,7 +183,6 @@ program
|
|
|
158
183
|
}
|
|
159
184
|
});
|
|
160
185
|
|
|
161
|
-
// Default behavior
|
|
162
186
|
if (!process.argv.slice(2).length) {
|
|
163
187
|
showDashboard();
|
|
164
188
|
}
|
package/package.json
CHANGED
package/utils/ui.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
// utils/ui.js
|
|
2
1
|
import chalk from 'chalk';
|
|
3
2
|
import boxen from 'boxen';
|
|
4
3
|
import ora from 'ora';
|
|
5
|
-
import cliProgress from 'cli-progress';
|
|
4
|
+
import cliProgress from 'cli-progress';
|
|
6
5
|
|
|
7
6
|
// --- Theme Colors ---
|
|
8
|
-
export const primary = chalk.hex('#00FFFF');
|
|
9
|
-
export const success = chalk.hex('#00FF00');
|
|
10
|
-
export const accent = chalk.hex('#FF00FF');
|
|
11
|
-
export const warning = chalk.hex('#FFFF00');
|
|
12
|
-
export const error = chalk.hex('#FF0000');
|
|
7
|
+
export const primary = chalk.hex('#00FFFF');
|
|
8
|
+
export const success = chalk.hex('#00FF00');
|
|
9
|
+
export const accent = chalk.hex('#FF00FF');
|
|
10
|
+
export const warning = chalk.hex('#FFFF00');
|
|
11
|
+
export const error = chalk.hex('#FF0000');
|
|
13
12
|
export const text = chalk.whiteBright;
|
|
14
13
|
|
|
15
14
|
// --- CLI Components ---
|
|
16
|
-
// utils/ui.js
|
|
17
15
|
export const createSpinner = (textMsg, color = primary) => {
|
|
18
|
-
// Safe check for hex codes
|
|
19
16
|
const match = color.toString().match(/#(?:[0-9a-fA-F]{3}){1,2}/);
|
|
20
17
|
const spinnerColor = match ? match[0] : 'cyan';
|
|
21
18
|
|
|
22
19
|
return ora({
|
|
23
20
|
text: color(textMsg),
|
|
24
21
|
spinner: { interval: 80, frames: ['β ', 'β ', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ', 'β '] },
|
|
25
|
-
// Don't pass hex codes to 'ora', use 'cyan' as a fallback
|
|
26
22
|
color: spinnerColor.startsWith('#') ? 'cyan' : spinnerColor
|
|
27
23
|
});
|
|
28
24
|
};
|
|
@@ -30,8 +26,8 @@ export const createSpinner = (textMsg, color = primary) => {
|
|
|
30
26
|
export const createProgressBar = (name = "Progress", barSize = 20) => {
|
|
31
27
|
const bar = new cliProgress.SingleBar({
|
|
32
28
|
format: `${accent('{bar}')} ${text('{percentage}%')} | {value}/{total} ${primary(name)}`,
|
|
33
|
-
barCompleteChar: '\u2588',
|
|
34
|
-
barIncompleteChar: '\u2591',
|
|
29
|
+
barCompleteChar: '\u2588',
|
|
30
|
+
barIncompleteChar: '\u2591',
|
|
35
31
|
hideCursor: true,
|
|
36
32
|
barsize: barSize,
|
|
37
33
|
linewrap: false
|
|
@@ -43,7 +39,7 @@ export const createBox = (content, options = {}) => {
|
|
|
43
39
|
const defaultOptions = {
|
|
44
40
|
padding: 1,
|
|
45
41
|
margin: 1,
|
|
46
|
-
borderStyle: 'double',
|
|
42
|
+
borderStyle: 'double',
|
|
47
43
|
borderColor: primary.name,
|
|
48
44
|
titleAlignment: 'center',
|
|
49
45
|
...options
|