ai-cmg 0.0.2 → 0.0.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 +57 -0
- package/dist/index.js +110 -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,19 @@ 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}"`);
|
|
306
|
+
}
|
|
307
|
+
if (prompt) {
|
|
308
|
+
console.log(`Prompt detected: "${prompt}"`);
|
|
206
309
|
}
|
|
207
310
|
// 2. 인증 토큰 확인 (없으면 물어봄)
|
|
208
311
|
let authToken = config.get('authToken');
|
|
@@ -246,13 +349,13 @@ async function main() {
|
|
|
246
349
|
}
|
|
247
350
|
const filteredDiff = summarizeDiff(diff);
|
|
248
351
|
// 4. API 요청 (Diff + Hint + Auth)
|
|
249
|
-
const response = await fetch(
|
|
352
|
+
const response = await fetch(getWorkerUrl(), {
|
|
250
353
|
method: 'POST',
|
|
251
354
|
headers: {
|
|
252
355
|
'Content-Type': 'application/json',
|
|
253
356
|
'X-AUTH-TOKEN': authToken
|
|
254
357
|
},
|
|
255
|
-
body: JSON.stringify({ diff: filteredDiff, hint
|
|
358
|
+
body: JSON.stringify({ diff: filteredDiff, hint, prompt }) // 힌트/프롬프트도 같이 전송
|
|
256
359
|
});
|
|
257
360
|
// 인증 실패 처리 (401)
|
|
258
361
|
if (response.status === 401) {
|
|
@@ -283,6 +386,7 @@ async function main() {
|
|
|
283
386
|
type: 'number',
|
|
284
387
|
name: 'choice',
|
|
285
388
|
message: 'Enter a number (1-4):',
|
|
389
|
+
initial: 1,
|
|
286
390
|
min: 1,
|
|
287
391
|
max: actions.length
|
|
288
392
|
});
|