ai-cmg 0.0.7 → 0.0.9
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/dist/index.js +94 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execSync, spawnSync } from 'child_process';
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { dirname, join } from 'path';
|
|
2
|
+
import { execFileSync, execSync, spawnSync } from 'child_process';
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { dirname, join, resolve } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import prompts from 'prompts';
|
|
7
7
|
import clipboardy from 'clipboardy';
|
|
@@ -103,15 +103,90 @@ function getGitEnv() {
|
|
|
103
103
|
delete env.GIT_CEILING_DIRECTORIES;
|
|
104
104
|
return env;
|
|
105
105
|
}
|
|
106
|
-
function
|
|
106
|
+
function isGitAvailable() {
|
|
107
107
|
try {
|
|
108
|
-
|
|
109
|
-
return
|
|
108
|
+
execFileSync('git', ['--version'], { env: getGitEnv(), stdio: 'ignore' });
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function resolveGitDir(repoRoot) {
|
|
116
|
+
const gitPath = join(repoRoot, '.git');
|
|
117
|
+
if (!existsSync(gitPath))
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
const stat = execSync(`test -d "${gitPath}" && echo "dir" || echo "file"`).toString().trim();
|
|
121
|
+
if (stat === 'dir')
|
|
122
|
+
return gitPath;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// fallthrough to file parsing
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const content = readFileSync(gitPath, 'utf8').trim();
|
|
129
|
+
const match = content.match(/^gitdir:\s*(.+)$/i);
|
|
130
|
+
if (!match)
|
|
131
|
+
return null;
|
|
132
|
+
const gitDirPath = match[1].trim();
|
|
133
|
+
return gitDirPath.startsWith('/')
|
|
134
|
+
? gitDirPath
|
|
135
|
+
: resolve(repoRoot, gitDirPath);
|
|
110
136
|
}
|
|
111
137
|
catch {
|
|
112
138
|
return null;
|
|
113
139
|
}
|
|
114
140
|
}
|
|
141
|
+
function runGit(args, repoRoot) {
|
|
142
|
+
try {
|
|
143
|
+
return execFileSync('git', ['-C', repoRoot, ...args], { env: getGitEnv() }).toString();
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
147
|
+
if (!gitDir)
|
|
148
|
+
throw new Error('git failed');
|
|
149
|
+
return execFileSync('git', ['--git-dir', gitDir, '--work-tree', repoRoot, ...args], {
|
|
150
|
+
env: getGitEnv()
|
|
151
|
+
}).toString();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function spawnGit(args, repoRoot) {
|
|
155
|
+
const first = spawnSync('git', ['-C', repoRoot, ...args], { stdio: 'inherit', env: getGitEnv() });
|
|
156
|
+
if (first.status === 0)
|
|
157
|
+
return 0;
|
|
158
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
159
|
+
if (!gitDir)
|
|
160
|
+
return first.status ?? 1;
|
|
161
|
+
const second = spawnSync('git', ['--git-dir', gitDir, '--work-tree', repoRoot, ...args], { stdio: 'inherit', env: getGitEnv() });
|
|
162
|
+
return second.status ?? 1;
|
|
163
|
+
}
|
|
164
|
+
function findRepoRootByFs(startDir) {
|
|
165
|
+
let current = resolve(startDir);
|
|
166
|
+
while (true) {
|
|
167
|
+
const gitPath = join(current, '.git');
|
|
168
|
+
if (existsSync(gitPath))
|
|
169
|
+
return current;
|
|
170
|
+
const parent = dirname(current);
|
|
171
|
+
if (parent === current)
|
|
172
|
+
return null;
|
|
173
|
+
current = parent;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function resolveRepoRoot(baseDir) {
|
|
177
|
+
try {
|
|
178
|
+
const root = execSync('git rev-parse --show-toplevel', {
|
|
179
|
+
env: getGitEnv(),
|
|
180
|
+
cwd: baseDir
|
|
181
|
+
}).toString().trim();
|
|
182
|
+
return root || null;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
if (!baseDir)
|
|
186
|
+
return null;
|
|
187
|
+
return findRepoRootByFs(baseDir);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
115
190
|
function parseMessageArgs(rawArgs) {
|
|
116
191
|
const hintParts = [];
|
|
117
192
|
let hint;
|
|
@@ -148,10 +223,7 @@ function parseMessageArgs(rawArgs) {
|
|
|
148
223
|
function getNameStatus(repoRoot) {
|
|
149
224
|
const map = new Map();
|
|
150
225
|
try {
|
|
151
|
-
const output =
|
|
152
|
-
cwd: repoRoot,
|
|
153
|
-
env: getGitEnv()
|
|
154
|
-
}).toString();
|
|
226
|
+
const output = runGit(['diff', '--cached', '--name-status'], repoRoot);
|
|
155
227
|
output.split('\n').forEach((line) => {
|
|
156
228
|
if (!line.trim())
|
|
157
229
|
return;
|
|
@@ -168,10 +240,7 @@ function getNameStatus(repoRoot) {
|
|
|
168
240
|
}
|
|
169
241
|
function getChangedFiles(repoRoot) {
|
|
170
242
|
try {
|
|
171
|
-
const output =
|
|
172
|
-
cwd: repoRoot,
|
|
173
|
-
env: getGitEnv()
|
|
174
|
-
}).toString();
|
|
243
|
+
const output = runGit(['status', '--porcelain', '-z'], repoRoot);
|
|
175
244
|
if (!output)
|
|
176
245
|
return [];
|
|
177
246
|
const parts = output.split('\0').filter(Boolean);
|
|
@@ -229,7 +298,7 @@ async function promptStageFiles(repoRoot) {
|
|
|
229
298
|
if (action === 'cancel')
|
|
230
299
|
return false;
|
|
231
300
|
if (action === 'all') {
|
|
232
|
-
|
|
301
|
+
spawnGit(['add', '.'], repoRoot);
|
|
233
302
|
return true;
|
|
234
303
|
}
|
|
235
304
|
const fileChoices = candidates.map((entry) => ({
|
|
@@ -246,7 +315,7 @@ async function promptStageFiles(repoRoot) {
|
|
|
246
315
|
const files = picked.files;
|
|
247
316
|
if (!files || files.length === 0)
|
|
248
317
|
return false;
|
|
249
|
-
|
|
318
|
+
spawnGit(['add', '--', ...files], repoRoot);
|
|
250
319
|
return true;
|
|
251
320
|
}
|
|
252
321
|
function getExtension(filePath) {
|
|
@@ -361,7 +430,11 @@ async function main() {
|
|
|
361
430
|
return;
|
|
362
431
|
console.log('Analyzing staged changes...');
|
|
363
432
|
try {
|
|
364
|
-
|
|
433
|
+
if (!isGitAvailable()) {
|
|
434
|
+
console.log('Error: git is not available in PATH.');
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const repoRoot = resolveRepoRoot(process.cwd());
|
|
365
438
|
if (!repoRoot) {
|
|
366
439
|
console.log('Error: not a git repository.');
|
|
367
440
|
return;
|
|
@@ -369,7 +442,7 @@ async function main() {
|
|
|
369
442
|
// 3. Git Diff 가져오기
|
|
370
443
|
let diff;
|
|
371
444
|
try {
|
|
372
|
-
diff =
|
|
445
|
+
diff = runGit(['diff', '--cached'], repoRoot);
|
|
373
446
|
}
|
|
374
447
|
catch (e) {
|
|
375
448
|
console.log('Error: not a git repository or git is unavailable.');
|
|
@@ -379,7 +452,7 @@ async function main() {
|
|
|
379
452
|
const staged = await promptStageFiles(repoRoot);
|
|
380
453
|
if (!staged)
|
|
381
454
|
return;
|
|
382
|
-
diff =
|
|
455
|
+
diff = runGit(['diff', '--cached'], repoRoot);
|
|
383
456
|
if (!diff.trim()) {
|
|
384
457
|
console.log('No staged changes. Nothing to commit.');
|
|
385
458
|
return;
|
|
@@ -438,11 +511,11 @@ async function main() {
|
|
|
438
511
|
const action = actions[choice - 1].value;
|
|
439
512
|
switch (action) {
|
|
440
513
|
case 'commit':
|
|
441
|
-
|
|
514
|
+
spawnGit(['commit', '-m', message], repoRoot);
|
|
442
515
|
console.log('Commit complete.');
|
|
443
516
|
break;
|
|
444
517
|
case 'edit':
|
|
445
|
-
|
|
518
|
+
spawnGit(['commit', '-e', '-m', message], repoRoot);
|
|
446
519
|
console.log('Commit complete.');
|
|
447
520
|
break;
|
|
448
521
|
case 'copy':
|