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.
Files changed (3) hide show
  1. package/README.md +57 -0
  2. package/dist/index.js +110 -6
  3. 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 WORKER_URL = 'https://commit.d-code.workers.dev';
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
- const args = rawArgs.filter((arg) => arg !== '--version' && arg !== '-v').join(' ');
204
- if (args) {
205
- console.log(`Hint detected: "${args}"`);
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(WORKER_URL, {
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: args }) // 힌트도 같이 전송
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cmg",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "AI Commit Message Generator",
5
5
  "type": "module",
6
6
  "bin": {