xoegit 1.1.1 → 1.1.3
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 +38 -18
- package/dist/cli/analyze.js +16 -0
- package/dist/cli/program.js +5 -2
- package/dist/config/service.js +20 -0
- package/dist/config/version.js +4 -0
- package/dist/utils/ui.js +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/xoegit)
|
|
7
7
|
[](https://github.com/ujangdoubleday/xoegit/blob/main/LICENSE.md)
|
|
8
8
|
|
|
9
|
+
<img src="docs/xoegit-banner.png" alt="xoegit banner" width="100%">
|
|
10
|
+
|
|
9
11
|
**xoegit** is an AI-powered CLI tool that generates concise, semantic, and atomic git commit messages and PR descriptions. It analyzes your `git diff`, `git status`, and `git log` to provide context-aware suggestions powered by Google's Gemini models.
|
|
10
12
|
|
|
11
13
|
> **Philosophy:** "Craft, Don't Code" — `xoegit` suggests commands; YOU execute them. You stay in control.
|
|
@@ -18,11 +20,17 @@
|
|
|
18
20
|
- **Semantic Commits** — Strictly follows [Conventional Commits](https://www.conventionalcommits.org/)
|
|
19
21
|
- **PR Ready** — Generates ready-to-use PR title and description
|
|
20
22
|
|
|
23
|
+
## How It Works
|
|
24
|
+
|
|
25
|
+
> **Important:** `xoegit` **never** stages your files, commits your changes, or modifies your repository in any way. It only analyzes your changes and provides recommendations that you can review and execute yourself.
|
|
26
|
+
|
|
27
|
+
You remain in full control of your git workflow.
|
|
28
|
+
|
|
21
29
|
## Installation
|
|
22
30
|
|
|
23
31
|
### Prerequisites
|
|
24
32
|
|
|
25
|
-
- **Node.js**:
|
|
33
|
+
- **Node.js**: Minimum version 20.19.5 or higher
|
|
26
34
|
- **Git**: Must be installed and available in your PATH
|
|
27
35
|
- **API Key**: A Google Gemini API key ([get one here](https://aistudio.google.com/))
|
|
28
36
|
|
|
@@ -44,10 +52,12 @@ make
|
|
|
44
52
|
|
|
45
53
|
Simply run `xoegit` for the first time. It will prompt you for your API Key securely and save it locally.
|
|
46
54
|
|
|
47
|
-
> **Security Note:** Your API key is stored locally on your device only. We do not collect, store, or have access to your API key.
|
|
55
|
+
> **Security Note:** Your API key is stored locally on your device only. We do not collect, store, or have access to your API key. See [Security Policy](SECURITY.md) for details.
|
|
48
56
|
|
|
49
57
|
## Usage
|
|
50
58
|
|
|
59
|
+
Then, from whatever project you're working on, just run:
|
|
60
|
+
|
|
51
61
|
```bash
|
|
52
62
|
xoegit
|
|
53
63
|
```
|
|
@@ -60,21 +70,40 @@ xoegit
|
|
|
60
70
|
| ---------------------- | --------------------------------------------- |
|
|
61
71
|
| `-k, --api-key <key>` | Use specific API key for this session |
|
|
62
72
|
| `-c, --context <text>` | Provide context for more accurate suggestions |
|
|
73
|
+
| `-s, --set-key <key>` | Save API key to config
|
|
74
|
+
| `-d, --delete-key` | Delete saved API key from config |
|
|
63
75
|
| `-V, --version` | Show version |
|
|
64
76
|
| `-h, --help` | Show help |
|
|
65
77
|
|
|
66
78
|
### Examples
|
|
67
79
|
|
|
80
|
+
**Basic usage:**
|
|
81
|
+
|
|
68
82
|
```bash
|
|
69
|
-
# Basic usage
|
|
70
83
|
xoegit
|
|
84
|
+
```
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
**With context for better commit type detection:**
|
|
87
|
+
|
|
88
|
+
```bash
|
|
73
89
|
xoegit --context "refactoring folder structure"
|
|
74
90
|
xoegit -c "fixing authentication bug"
|
|
75
91
|
xoegit -c "adding new payment feature"
|
|
76
92
|
```
|
|
77
93
|
|
|
94
|
+
**Use API key for this session only (not saved):**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
xoegit --api-key "YOUR_GEMINI_API_KEY"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Manage API key:**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
xoegit --set-key "YOUR_GEMINI_API_KEY"
|
|
104
|
+
xoegit --delete-key
|
|
105
|
+
```
|
|
106
|
+
|
|
78
107
|
### Sample Output
|
|
79
108
|
|
|
80
109
|
```
|
|
@@ -96,18 +125,6 @@ pr description: feat(auth): implement secure login
|
|
|
96
125
|
- refactor(utils): improve error logging
|
|
97
126
|
```
|
|
98
127
|
|
|
99
|
-
## Smart Model Fallback
|
|
100
|
-
|
|
101
|
-
xoegit uses multiple Gemini models with automatic fallback:
|
|
102
|
-
|
|
103
|
-
| Model | Priority |
|
|
104
|
-
| ----------------------- | ------------- |
|
|
105
|
-
| `gemini-2.5-flash-lite` | 1st (default) |
|
|
106
|
-
| `gemini-2.5-flash` | 2nd |
|
|
107
|
-
| `gemini-3-flash` | 3rd |
|
|
108
|
-
|
|
109
|
-
When one model hits its rate limit, xoegit automatically tries the next one.
|
|
110
|
-
|
|
111
128
|
## Troubleshooting
|
|
112
129
|
|
|
113
130
|
### "Current directory is not a git repository"
|
|
@@ -129,9 +146,12 @@ npm run build
|
|
|
129
146
|
|
|
130
147
|
# Run tests
|
|
131
148
|
npm test
|
|
149
|
+
```
|
|
132
150
|
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
**or** use `make`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
make
|
|
135
155
|
```
|
|
136
156
|
|
|
137
157
|
## Project Structure
|
package/dist/cli/analyze.js
CHANGED
|
@@ -16,6 +16,22 @@ export async function analyzeAction() {
|
|
|
16
16
|
const options = program.opts();
|
|
17
17
|
let apiKey = options.apiKey;
|
|
18
18
|
const configService = new ConfigService();
|
|
19
|
+
// Handle --set-key flag (save and exit)
|
|
20
|
+
if (options.setKey) {
|
|
21
|
+
if (!isValidApiKey(options.setKey)) {
|
|
22
|
+
showError('Invalid API Key', 'Please provide a valid API key.');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
await configService.saveApiKey(options.setKey);
|
|
26
|
+
showSuccess('API Key saved successfully!');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Handle --delete-key flag (delete and exit)
|
|
30
|
+
if (options.deleteKey) {
|
|
31
|
+
await configService.deleteApiKey();
|
|
32
|
+
showSuccess('API Key deleted successfully!');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
19
35
|
if (!apiKey) {
|
|
20
36
|
apiKey = await configService.getApiKey();
|
|
21
37
|
}
|
package/dist/cli/program.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { VERSION } from '../config/version.js';
|
|
2
3
|
export const program = new Command();
|
|
3
4
|
program
|
|
4
5
|
.name('xoegit')
|
|
5
6
|
.description('AI-powered git commit generator')
|
|
6
|
-
.version(
|
|
7
|
+
.version(VERSION)
|
|
7
8
|
.option('-k, --api-key <key>', 'Gemini API Key')
|
|
8
|
-
.option('-c, --context <context>', 'Context for the changes (e.g., "refactoring folder structure")')
|
|
9
|
+
.option('-c, --context <context>', 'Context for the changes (e.g., "refactoring folder structure")')
|
|
10
|
+
.option('-s, --set-key <key>', 'Save Gemini API Key to config (overwrites existing)')
|
|
11
|
+
.option('-d, --delete-key', 'Delete saved API Key from config');
|
package/dist/config/service.js
CHANGED
|
@@ -44,4 +44,24 @@ export class ConfigService {
|
|
|
44
44
|
throw new Error(`Failed to save configuration: ${error.message}`);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
async deleteApiKey() {
|
|
48
|
+
try {
|
|
49
|
+
const configStr = await fs.readFile(this.configPath, 'utf-8');
|
|
50
|
+
const config = JSON.parse(configStr);
|
|
51
|
+
delete config.XOEGIT_GEMINI_API_KEY;
|
|
52
|
+
if (Object.keys(config).length === 0) {
|
|
53
|
+
// Delete the file if no other config remains
|
|
54
|
+
await fs.unlink(this.configPath);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await fs.writeFile(this.configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// Ignore if file doesn't exist
|
|
62
|
+
if (error.code !== 'ENOENT') {
|
|
63
|
+
throw new Error(`Failed to delete API key: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
47
67
|
}
|
package/dist/utils/ui.js
CHANGED
|
@@ -16,7 +16,16 @@ const brand = {
|
|
|
16
16
|
* App banner
|
|
17
17
|
*/
|
|
18
18
|
export function showBanner() {
|
|
19
|
-
|
|
19
|
+
const banner = `
|
|
20
|
+
██╗ ██╗ ██╗ ██████╗ ███████╗ ██████╗ ██╗████████╗
|
|
21
|
+
╚██╗ ╚██╗██╔╝██╔═══██╗██╔════╝██╔════╝ ██║╚══██╔══╝
|
|
22
|
+
╚██╗ ╚███╔╝ ██║ ██║█████╗ ██║ ███╗██║ ██║
|
|
23
|
+
██╔╝ ██╔██╗ ██║ ██║██╔══╝ ██║ ██║██║ ██║
|
|
24
|
+
██╔╝ ██╔╝ ██╗╚██████╔╝███████╗╚██████╔╝██║ ██║
|
|
25
|
+
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
|
26
|
+
`;
|
|
27
|
+
console.log(banner);
|
|
28
|
+
console.log(brand.muted('by https://github.com/ujangdoubleday\n'));
|
|
20
29
|
}
|
|
21
30
|
/**
|
|
22
31
|
* Display the AI suggestion
|