ai-cmg 0.0.8 → 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.
Files changed (2) hide show
  1. package/dist/index.js +59 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { execSync, spawnSync } from 'child_process';
2
+ import { execFileSync, execSync, spawnSync } from 'child_process';
3
3
  import { readFileSync, existsSync } from 'fs';
4
4
  import { dirname, join, resolve } from 'path';
5
5
  import { fileURLToPath } from 'url';
@@ -105,13 +105,62 @@ function getGitEnv() {
105
105
  }
106
106
  function isGitAvailable() {
107
107
  try {
108
- execSync('git --version', { env: getGitEnv(), stdio: 'ignore' });
108
+ execFileSync('git', ['--version'], { env: getGitEnv(), stdio: 'ignore' });
109
109
  return true;
110
110
  }
111
111
  catch {
112
112
  return false;
113
113
  }
114
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);
136
+ }
137
+ catch {
138
+ return null;
139
+ }
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
+ }
115
164
  function findRepoRootByFs(startDir) {
116
165
  let current = resolve(startDir);
117
166
  while (true) {
@@ -174,9 +223,7 @@ function parseMessageArgs(rawArgs) {
174
223
  function getNameStatus(repoRoot) {
175
224
  const map = new Map();
176
225
  try {
177
- const output = execSync(`git -C "${repoRoot}" diff --cached --name-status`, {
178
- env: getGitEnv()
179
- }).toString();
226
+ const output = runGit(['diff', '--cached', '--name-status'], repoRoot);
180
227
  output.split('\n').forEach((line) => {
181
228
  if (!line.trim())
182
229
  return;
@@ -193,9 +240,7 @@ function getNameStatus(repoRoot) {
193
240
  }
194
241
  function getChangedFiles(repoRoot) {
195
242
  try {
196
- const output = execSync(`git -C "${repoRoot}" status --porcelain -z`, {
197
- env: getGitEnv()
198
- }).toString();
243
+ const output = runGit(['status', '--porcelain', '-z'], repoRoot);
199
244
  if (!output)
200
245
  return [];
201
246
  const parts = output.split('\0').filter(Boolean);
@@ -253,7 +298,7 @@ async function promptStageFiles(repoRoot) {
253
298
  if (action === 'cancel')
254
299
  return false;
255
300
  if (action === 'all') {
256
- spawnSync('git', ['-C', repoRoot, 'add', '.'], { stdio: 'inherit', env: getGitEnv() });
301
+ spawnGit(['add', '.'], repoRoot);
257
302
  return true;
258
303
  }
259
304
  const fileChoices = candidates.map((entry) => ({
@@ -270,7 +315,7 @@ async function promptStageFiles(repoRoot) {
270
315
  const files = picked.files;
271
316
  if (!files || files.length === 0)
272
317
  return false;
273
- spawnSync('git', ['-C', repoRoot, 'add', '--', ...files], { stdio: 'inherit', env: getGitEnv() });
318
+ spawnGit(['add', '--', ...files], repoRoot);
274
319
  return true;
275
320
  }
276
321
  function getExtension(filePath) {
@@ -397,7 +442,7 @@ async function main() {
397
442
  // 3. Git Diff 가져오기
398
443
  let diff;
399
444
  try {
400
- diff = execSync(`git -C "${repoRoot}" diff --cached`, { env: getGitEnv() }).toString();
445
+ diff = runGit(['diff', '--cached'], repoRoot);
401
446
  }
402
447
  catch (e) {
403
448
  console.log('Error: not a git repository or git is unavailable.');
@@ -407,7 +452,7 @@ async function main() {
407
452
  const staged = await promptStageFiles(repoRoot);
408
453
  if (!staged)
409
454
  return;
410
- diff = execSync(`git -C "${repoRoot}" diff --cached`, { env: getGitEnv() }).toString();
455
+ diff = runGit(['diff', '--cached'], repoRoot);
411
456
  if (!diff.trim()) {
412
457
  console.log('No staged changes. Nothing to commit.');
413
458
  return;
@@ -466,11 +511,11 @@ async function main() {
466
511
  const action = actions[choice - 1].value;
467
512
  switch (action) {
468
513
  case 'commit':
469
- spawnSync('git', ['-C', repoRoot, 'commit', '-m', message], { stdio: 'inherit', env: getGitEnv() });
514
+ spawnGit(['commit', '-m', message], repoRoot);
470
515
  console.log('Commit complete.');
471
516
  break;
472
517
  case 'edit':
473
- spawnSync('git', ['-C', repoRoot, 'commit', '-e', '-m', message], { stdio: 'inherit', env: getGitEnv() });
518
+ spawnGit(['commit', '-e', '-m', message], repoRoot);
474
519
  console.log('Commit complete.');
475
520
  break;
476
521
  case 'copy':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cmg",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "AI Commit Message Generator",
5
5
  "type": "module",
6
6
  "bin": {