ai-cmg 0.0.2 → 0.0.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 +57 -0
- package/dist/index.js +107 -6
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# ai-cmg
|
|
2
|
+
|
|
3
|
+
An AI-powered CLI that analyzes staged changes and generates a commit message. You can commit immediately, edit in your editor, or copy the message.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g ai-cmg
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ai-cmg
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Flow:
|
|
18
|
+
- If there are staged changes, it generates a message right away.
|
|
19
|
+
- If nothing is staged, it offers `git add .` or a file picker to stage selected files.
|
|
20
|
+
- You can choose to commit, edit, or copy the generated message.
|
|
21
|
+
|
|
22
|
+
## Provide a Hint or Prompt
|
|
23
|
+
|
|
24
|
+
Use `-m` for a short hint (commit message guidance), and `-p` for a prompt (instruction).
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ai-cmg -m "Improve login error handling"
|
|
28
|
+
ai-cmg -p "Use a short title and 3 bullet points"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
The Worker URL defaults to the built-in value but can be overridden.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ai-cmg config --show
|
|
37
|
+
ai-cmg config --set https://your-worker.example.com
|
|
38
|
+
ai-cmg config --reset
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Interactive configuration is also available.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
ai-cmg config
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Version
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ai-cmg --version
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Notes
|
|
54
|
+
|
|
55
|
+
- The tool uses the **staged diff** to generate commit messages.
|
|
56
|
+
- Large diffs, binary/media files, and lockfiles are summarized to reduce token usage.
|
|
57
|
+
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import prompts from 'prompts';
|
|
7
7
|
import clipboardy from 'clipboardy';
|
|
8
8
|
import Conf from 'conf';
|
|
9
|
-
const
|
|
9
|
+
const DEFAULT_WORKER_URL = 'https://commit.d-code.workers.dev';
|
|
10
10
|
const config = new Conf({ projectName: 'ai-cmg' });
|
|
11
11
|
const MEDIA_EXTENSIONS = new Set([
|
|
12
12
|
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.bmp', '.tiff',
|
|
@@ -36,6 +36,98 @@ function getPackageVersion() {
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
function getWorkerUrl() {
|
|
40
|
+
return config.get('workerUrl') ?? DEFAULT_WORKER_URL;
|
|
41
|
+
}
|
|
42
|
+
function showConfig() {
|
|
43
|
+
const workerUrl = getWorkerUrl();
|
|
44
|
+
console.log('Current configuration:');
|
|
45
|
+
console.log(`- workerUrl: ${workerUrl}`);
|
|
46
|
+
}
|
|
47
|
+
function showConfigHelp() {
|
|
48
|
+
console.log('Usage: ai-cmg config [--show | --set <url> | --reset]');
|
|
49
|
+
console.log(' --show Show current configuration');
|
|
50
|
+
console.log(' --set <url> Set Worker URL');
|
|
51
|
+
console.log(' --reset Reset Worker URL to default');
|
|
52
|
+
}
|
|
53
|
+
function setWorkerUrl(url) {
|
|
54
|
+
config.set('workerUrl', url);
|
|
55
|
+
console.log('Updated workerUrl.');
|
|
56
|
+
}
|
|
57
|
+
function resetWorkerUrl() {
|
|
58
|
+
config.delete('workerUrl');
|
|
59
|
+
console.log('Reset workerUrl to default.');
|
|
60
|
+
}
|
|
61
|
+
async function runConfigCommand(args) {
|
|
62
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
63
|
+
showConfigHelp();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (args.includes('--show')) {
|
|
67
|
+
showConfig();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const setIndex = args.indexOf('--set');
|
|
71
|
+
if (setIndex >= 0) {
|
|
72
|
+
const nextUrl = args[setIndex + 1]?.trim();
|
|
73
|
+
if (!nextUrl) {
|
|
74
|
+
console.log('Error: --set requires a URL.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setWorkerUrl(nextUrl);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (args.includes('--reset')) {
|
|
81
|
+
resetWorkerUrl();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
showConfig();
|
|
85
|
+
const response = await prompts({
|
|
86
|
+
type: 'text',
|
|
87
|
+
name: 'workerUrl',
|
|
88
|
+
message: 'Enter Worker URL (leave empty to keep current):'
|
|
89
|
+
});
|
|
90
|
+
const nextUrl = response.workerUrl?.trim();
|
|
91
|
+
if (nextUrl) {
|
|
92
|
+
setWorkerUrl(nextUrl);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log('No changes made.');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function parseMessageArgs(rawArgs) {
|
|
99
|
+
const hintParts = [];
|
|
100
|
+
let hint;
|
|
101
|
+
let prompt;
|
|
102
|
+
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
103
|
+
const arg = rawArgs[i];
|
|
104
|
+
if (arg === '-m') {
|
|
105
|
+
const value = rawArgs[i + 1];
|
|
106
|
+
if (!value) {
|
|
107
|
+
console.log('Error: -m requires a value.');
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
hint = value;
|
|
111
|
+
i += 1;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (arg === '-p') {
|
|
115
|
+
const value = rawArgs[i + 1];
|
|
116
|
+
if (!value) {
|
|
117
|
+
console.log('Error: -p requires a value.');
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
prompt = value;
|
|
121
|
+
i += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
hintParts.push(arg);
|
|
125
|
+
}
|
|
126
|
+
if (!hint && hintParts.length > 0) {
|
|
127
|
+
hint = hintParts.join(' ');
|
|
128
|
+
}
|
|
129
|
+
return { hint, prompt };
|
|
130
|
+
}
|
|
39
131
|
function getNameStatus() {
|
|
40
132
|
const map = new Map();
|
|
41
133
|
try {
|
|
@@ -103,6 +195,7 @@ async function promptStageFiles() {
|
|
|
103
195
|
type: 'number',
|
|
104
196
|
name: 'choice',
|
|
105
197
|
message: 'Enter a number (1-3):',
|
|
198
|
+
initial: 1,
|
|
106
199
|
min: 1,
|
|
107
200
|
max: options.length
|
|
108
201
|
});
|
|
@@ -200,9 +293,16 @@ async function main() {
|
|
|
200
293
|
console.log(version ?? 'unknown');
|
|
201
294
|
return;
|
|
202
295
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
296
|
+
if (rawArgs[0] === 'config') {
|
|
297
|
+
await runConfigCommand(rawArgs.slice(1));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const parsed = parseMessageArgs(rawArgs.filter((arg) => arg !== '--version' && arg !== '-v'));
|
|
301
|
+
if (!parsed)
|
|
302
|
+
return;
|
|
303
|
+
const { hint, prompt } = parsed;
|
|
304
|
+
if (hint) {
|
|
305
|
+
console.log(`Hint detected: "${hint}"`);
|
|
206
306
|
}
|
|
207
307
|
// 2. 인증 토큰 확인 (없으면 물어봄)
|
|
208
308
|
let authToken = config.get('authToken');
|
|
@@ -246,13 +346,13 @@ async function main() {
|
|
|
246
346
|
}
|
|
247
347
|
const filteredDiff = summarizeDiff(diff);
|
|
248
348
|
// 4. API 요청 (Diff + Hint + Auth)
|
|
249
|
-
const response = await fetch(
|
|
349
|
+
const response = await fetch(getWorkerUrl(), {
|
|
250
350
|
method: 'POST',
|
|
251
351
|
headers: {
|
|
252
352
|
'Content-Type': 'application/json',
|
|
253
353
|
'X-AUTH-TOKEN': authToken
|
|
254
354
|
},
|
|
255
|
-
body: JSON.stringify({ diff: filteredDiff, hint
|
|
355
|
+
body: JSON.stringify({ diff: filteredDiff, hint, prompt }) // 힌트/프롬프트도 같이 전송
|
|
256
356
|
});
|
|
257
357
|
// 인증 실패 처리 (401)
|
|
258
358
|
if (response.status === 401) {
|
|
@@ -283,6 +383,7 @@ async function main() {
|
|
|
283
383
|
type: 'number',
|
|
284
384
|
name: 'choice',
|
|
285
385
|
message: 'Enter a number (1-4):',
|
|
386
|
+
initial: 1,
|
|
286
387
|
min: 1,
|
|
287
388
|
max: actions.length
|
|
288
389
|
});
|