@aigencydev/cli 0.3.3 → 0.3.5

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 CHANGED
@@ -201,8 +201,8 @@ Bu alan **git'e commit edilmez** ve senkronize de olmaz — her makinede ayrı.
201
201
 
202
202
  | Paket | Fiyat | Aylık Token | Ek Özellikler |
203
203
  |---|---|---|---|
204
- | **AIGENCY CLI Pro** | 1.500 ₺/ay | 1.000.000 | Standart |
205
- | **AIGENCY CLI Max** | 3.000 ₺/ay | 5.000.000 | Öncelikli kuyruk, Auto mode |
204
+ | **AIGENCY CLI Pro** | 1.500 ₺/ay | 100.000.000 | Standart |
205
+ | **AIGENCY CLI Max** | 3.000 ₺/ay | 500.000.000 | Öncelikli kuyruk, Auto mode |
206
206
 
207
207
  Paketleri [aigency.dev/developer/cli](https://aigency.dev/developer/cli) üzerinden
208
208
  yönetebilir, kullanımınızı canlı izleyebilirsiniz.
@@ -23,6 +23,19 @@ export async function refreshAccessToken(refreshToken) {
23
23
  });
24
24
  return res.data;
25
25
  }
26
+ export async function fetchMe(accessToken) {
27
+ try {
28
+ const res = await apiRequest("/api/cli/me", {
29
+ method: "GET",
30
+ bearer: accessToken,
31
+ maxRetries: 0,
32
+ });
33
+ return res.data;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
26
39
  export async function revokeSession(token, reason) {
27
40
  await apiRequest("/api/cli/auth/revoke", {
28
41
  method: "POST",
@@ -2,11 +2,12 @@ import React from "react";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { render } from "ink";
5
- import { requireActiveSession } from "../auth/session.js";
5
+ import { requireActiveSession, saveSession } from "../auth/session.js";
6
6
  import { loadSettings } from "../config/settings.js";
7
7
  import { loadMemorySnapshot } from "../agent/memory.js";
8
8
  import { loadInstructions } from "../agent/instructions.js";
9
9
  import { ensureProjectDataDirs } from "../config/bootstrap.js";
10
+ import { fetchMe } from "../api-client/auth.js";
10
11
  import { App } from "../ui/App.js";
11
12
  import { log } from "../utils/logger.js";
12
13
  async function findProjectRoot(cwd) {
@@ -37,13 +38,30 @@ async function findProjectRoot(cwd) {
37
38
  return cwd;
38
39
  }
39
40
  export async function runChatCommand(options = {}) {
40
- const session = await requireActiveSession();
41
+ let session = await requireActiveSession();
41
42
  const cwd = process.cwd();
42
43
  const projectRoot = await findProjectRoot(cwd);
43
44
  const settings = await loadSettings({ cwd: projectRoot });
44
45
  await ensureProjectDataDirs(projectRoot);
46
+ if (!session.user.tier) {
47
+ const me = await fetchMe(session.access_token);
48
+ if (me) {
49
+ session = {
50
+ ...session,
51
+ user: {
52
+ ...session.user,
53
+ name: me.name || session.user.name,
54
+ tier: me.tier,
55
+ monthly_tokens: me.monthly_tokens,
56
+ },
57
+ };
58
+ await saveSession(session);
59
+ log.debug(`Tier backend'den alındı: ${me.tier}`);
60
+ }
61
+ }
45
62
  const mode = options.mode || settings.mode;
46
- const model = options.model || settings.model;
63
+ const userTier = session.user.tier === "max" ? "max" : "pro";
64
+ const model = options.model || userTier;
47
65
  const memory = await loadMemorySnapshot(projectRoot);
48
66
  if (memory.entries.length > 0 || memory.indexContent) {
49
67
  log.debug(`memory: ${memory.entries.length} kayıt, index ${memory.indexContent.length} karakter`);
@@ -1,9 +1,101 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { checkCommand, autoModeDecision } from "../security/command-filter.js";
3
3
  import { checkPath } from "../security/sandbox.js";
4
- const MAX_OUTPUT_BYTES = 50_000;
4
+ const MAX_OUTPUT_BYTES = 80_000;
5
+ const HEAD_KEEP_BYTES = 4_000;
6
+ const TAIL_KEEP_BYTES = 36_000;
5
7
  const DEFAULT_TIMEOUT_MS = 300_000;
6
8
  const LIVE_OUTPUT_THROTTLE_MS = 150;
9
+ const NON_INTERACTIVE_ENV = {
10
+ CI: "1",
11
+ CONTINUOUS_INTEGRATION: "true",
12
+ npm_config_yes: "true",
13
+ NPM_CONFIG_YES: "true",
14
+ NPM_CONFIG_FUND: "false",
15
+ NPM_CONFIG_AUDIT: "false",
16
+ NPM_CONFIG_UPDATE_NOTIFIER: "false",
17
+ NPM_CONFIG_LOGLEVEL: "error",
18
+ ADBLOCK: "1",
19
+ DISABLE_OPENCOLLECTIVE: "1",
20
+ NO_COLOR: "1",
21
+ FORCE_COLOR: "0",
22
+ TERM: "dumb",
23
+ HUSKY: "0",
24
+ GIT_TERMINAL_PROMPT: "0",
25
+ DEBIAN_FRONTEND: "noninteractive",
26
+ PIP_DISABLE_PIP_VERSION_CHECK: "1",
27
+ };
28
+ function truncateWithTail(s) {
29
+ if (s.length <= MAX_OUTPUT_BYTES)
30
+ return s;
31
+ const head = s.slice(0, HEAD_KEEP_BYTES);
32
+ const tail = s.slice(-TAIL_KEEP_BYTES);
33
+ const omitted = s.length - HEAD_KEEP_BYTES - TAIL_KEEP_BYTES;
34
+ return `${head}\n\n[... ${omitted} bayt kırpıldı ...]\n\n${tail}`;
35
+ }
36
+ function classifyError(stderr, stdout, exitCode) {
37
+ const combined = (stderr + "\n" + stdout).toLowerCase();
38
+ if (combined.includes("contains files that could conflict") || combined.includes("directory is not empty")) {
39
+ return {
40
+ class: "directory_not_empty",
41
+ hint: "Hedef dizin boş değil. Farklı bir isim seç veya dizini temizle.",
42
+ retryable: true,
43
+ };
44
+ }
45
+ if (combined.includes("eintegrity") || combined.includes("sha512 integrity")) {
46
+ return {
47
+ class: "npm_cache_corrupted",
48
+ hint: "npm cache bozuk. 'npm cache clean --force' ile temizle ve tekrar dene.",
49
+ retryable: true,
50
+ };
51
+ }
52
+ if (combined.includes("etimedout") || combined.includes("enotfound") || combined.includes("network timeout")) {
53
+ return {
54
+ class: "network_error",
55
+ hint: "Ağ hatası. Kısa bir beklemeden sonra tekrar dene.",
56
+ retryable: true,
57
+ };
58
+ }
59
+ if (combined.includes("eacces") || combined.includes("eperm") || combined.includes("permission denied")) {
60
+ return {
61
+ class: "permission_denied",
62
+ hint: "İzin reddedildi. Bu dosya/dizin üzerinde yazma hakkı yok.",
63
+ retryable: false,
64
+ };
65
+ }
66
+ if (combined.includes("the engine \"node\"") || combined.includes("node version")) {
67
+ return {
68
+ class: "node_version_mismatch",
69
+ hint: "Node.js sürümü uyumsuz. Kullanıcıya bildir, node sürümünü değiştirme.",
70
+ retryable: false,
71
+ };
72
+ }
73
+ if (combined.includes("command not found") || combined.includes("not recognized") || combined.includes("enoent")) {
74
+ return {
75
+ class: "command_not_found",
76
+ hint: "Komut bulunamadı. Kontrol et: 'which <komut>' veya paketi yükle.",
77
+ retryable: false,
78
+ };
79
+ }
80
+ if (combined.includes("module not found") || combined.includes("cannot find module")) {
81
+ return {
82
+ class: "missing_dependency",
83
+ hint: "Eksik modül. İlgili paketi 'npm install <pkg>' ile yükle.",
84
+ retryable: true,
85
+ };
86
+ }
87
+ if (combined.includes("syntaxerror") || combined.includes("parse error")) {
88
+ return {
89
+ class: "syntax_error",
90
+ hint: "Sözdizimi hatası — ilgili dosyayı read_file ile oku ve düzelt.",
91
+ retryable: true,
92
+ };
93
+ }
94
+ return {
95
+ class: `exit_${exitCode ?? "unknown"}`,
96
+ retryable: true,
97
+ };
98
+ }
7
99
  const INTERACTIVE_PATTERNS = [
8
100
  {
9
101
  regex: /\bcreate-next-app\b(?![^|]*(?:--ts|--javascript|--yes|-y\b))/i,
@@ -99,17 +191,13 @@ function runShell(command, cwd, timeoutMs, signal, onLive) {
99
191
  stdio: ["ignore", "pipe", "pipe"],
100
192
  env: {
101
193
  ...process.env,
102
- CI: "1",
103
- NO_COLOR: "0",
104
- npm_config_yes: "true",
105
- DEBIAN_FRONTEND: "noninteractive",
194
+ ...NON_INTERACTIVE_ENV,
106
195
  },
107
196
  windowsHide: true,
108
197
  });
109
- let stdout = "";
110
- let stderr = "";
111
- let outBytes = 0;
112
- let errBytes = 0;
198
+ const HARD_CAP = 2 * 1024 * 1024;
199
+ let stdoutFull = "";
200
+ let stderrFull = "";
113
201
  let timedOut = false;
114
202
  let stdoutBuffer = "";
115
203
  let stderrBuffer = "";
@@ -166,11 +254,8 @@ function runShell(command, cwd, timeoutMs, signal, onLive) {
166
254
  }
167
255
  child.stdout.on("data", (chunk) => {
168
256
  const text = chunk.toString("utf8");
169
- if (outBytes < MAX_OUTPUT_BYTES) {
170
- const remaining = MAX_OUTPUT_BYTES - outBytes;
171
- const trimmed = text.slice(0, remaining);
172
- stdout += trimmed;
173
- outBytes += trimmed.length;
257
+ if (stdoutFull.length < HARD_CAP) {
258
+ stdoutFull += text;
174
259
  }
175
260
  if (onLive) {
176
261
  stdoutBuffer += text;
@@ -179,11 +264,8 @@ function runShell(command, cwd, timeoutMs, signal, onLive) {
179
264
  });
180
265
  child.stderr.on("data", (chunk) => {
181
266
  const text = chunk.toString("utf8");
182
- if (errBytes < MAX_OUTPUT_BYTES) {
183
- const remaining = MAX_OUTPUT_BYTES - errBytes;
184
- const trimmed = text.slice(0, remaining);
185
- stderr += trimmed;
186
- errBytes += trimmed.length;
267
+ if (stderrFull.length < HARD_CAP) {
268
+ stderrFull += text;
187
269
  }
188
270
  if (onLive) {
189
271
  stderrBuffer += text;
@@ -194,9 +276,9 @@ function runShell(command, cwd, timeoutMs, signal, onLive) {
194
276
  clearTimeout(timeoutHandle);
195
277
  finalFlush();
196
278
  resolve({
197
- stdout,
198
- stderr: `${stderr}\nSpawn hatası: ${err.message}`,
199
- exitCode: 1,
279
+ stdout: truncateWithTail(stdoutFull),
280
+ stderr: truncateWithTail(stderrFull + `\nSpawn hatası: ${err.message}`),
281
+ exitCode: -1,
200
282
  timedOut,
201
283
  });
202
284
  });
@@ -206,8 +288,8 @@ function runShell(command, cwd, timeoutMs, signal, onLive) {
206
288
  signal.removeEventListener("abort", onAbort);
207
289
  finalFlush();
208
290
  resolve({
209
- stdout,
210
- stderr,
291
+ stdout: truncateWithTail(stdoutFull),
292
+ stderr: truncateWithTail(stderrFull),
211
293
  exitCode: code,
212
294
  timedOut,
213
295
  });
@@ -346,16 +428,24 @@ export const bashTool = async (params, ctx) => {
346
428
  };
347
429
  }
348
430
  let errorMsg;
431
+ let errorClass;
432
+ let errorHint;
349
433
  if (result.exitCode !== 0) {
434
+ const classification = classifyError(result.stderr, result.stdout, result.exitCode);
435
+ errorClass = classification.class;
436
+ errorHint = classification.hint;
350
437
  const stderrTrim = result.stderr.trim();
351
438
  const stdoutTrim = result.stdout.trim();
352
439
  const errorDetail = stderrTrim || stdoutTrim;
353
440
  if (errorDetail) {
354
- const tail = errorDetail.slice(-500);
355
- errorMsg = `Komut başarısız (exit code ${result.exitCode}): ${tail}`;
441
+ const tail = errorDetail.slice(-800);
442
+ errorMsg = `Komut başarısız (exit code ${result.exitCode}, class=${classification.class}): ${tail}`;
443
+ if (classification.hint) {
444
+ errorMsg += `\n\nOneri: ${classification.hint}`;
445
+ }
356
446
  }
357
447
  else {
358
- errorMsg = `Komut çıktı vermeden başarısız oldu (exit code ${result.exitCode}). Komut yanlış olabilir veya binary bulunamamış olabilir.`;
448
+ errorMsg = `Komut çıktı vermeden başarısız oldu (exit code ${result.exitCode}, class=${classification.class}). Komut yanlış olabilir veya binary bulunamamış olabilir.`;
359
449
  }
360
450
  }
361
451
  return {
@@ -366,6 +456,10 @@ export const bashTool = async (params, ctx) => {
366
456
  exit_code: result.exitCode,
367
457
  stdout_length: result.stdout.length,
368
458
  stderr_length: result.stderr.length,
459
+ error_class: errorClass,
460
+ error_hint: errorHint,
461
+ command: command.slice(0, 200),
462
+ working_directory: cwdCheck.realPath,
369
463
  },
370
464
  approvalState,
371
465
  duration: Date.now() - start,
@@ -6,6 +6,7 @@ import { editFileTool } from "./edit-file.js";
6
6
  import { bashTool } from "./bash.js";
7
7
  import { saveMemoryToolHandler, readMemoryToolHandler, deleteMemoryToolHandler, } from "./memory-tools.js";
8
8
  import { writeTasksHandler, readTasksHandler } from "./task-tools.js";
9
+ import { verifyProjectHandler } from "./verify-project.js";
9
10
  const HANDLERS = {
10
11
  read_file: readFileTool,
11
12
  list_files: listFilesTool,
@@ -18,6 +19,7 @@ const HANDLERS = {
18
19
  delete_memory: deleteMemoryToolHandler,
19
20
  write_tasks: writeTasksHandler,
20
21
  read_tasks: readTasksHandler,
22
+ verify_project: verifyProjectHandler,
21
23
  };
22
24
  export function hasLocalTool(name) {
23
25
  return name in HANDLERS;
@@ -0,0 +1,339 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { checkPath } from "../security/sandbox.js";
4
+ async function exists(p) {
5
+ try {
6
+ await fs.access(p);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ async function isDirectory(p) {
14
+ try {
15
+ const stat = await fs.stat(p);
16
+ return stat.isDirectory();
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ async function readJsonSafe(p) {
23
+ try {
24
+ const raw = await fs.readFile(p, "utf8");
25
+ return JSON.parse(raw);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ async function countDirEntries(p, limit = 50) {
32
+ try {
33
+ const entries = await fs.readdir(p);
34
+ return Math.min(entries.length, limit);
35
+ }
36
+ catch {
37
+ return 0;
38
+ }
39
+ }
40
+ async function detectProjectType(projectPath) {
41
+ const pkgPath = path.join(projectPath, "package.json");
42
+ const pkg = await readJsonSafe(pkgPath);
43
+ if (!pkg)
44
+ return "unknown";
45
+ const deps = {
46
+ ...(pkg.dependencies || {}),
47
+ ...(pkg.devDependencies || {}),
48
+ };
49
+ if (deps.next)
50
+ return "next";
51
+ if (deps.vite)
52
+ return "vite";
53
+ if (deps["react-scripts"])
54
+ return "cra";
55
+ if (deps.nuxt)
56
+ return "nuxt";
57
+ if (deps["@remix-run/dev"])
58
+ return "remix";
59
+ if (deps.astro)
60
+ return "astro";
61
+ if (deps.react)
62
+ return "react";
63
+ if (deps.express || deps.fastify || deps.koa)
64
+ return "node-server";
65
+ return "node";
66
+ }
67
+ async function checkNextProject(projectPath) {
68
+ const checks = [];
69
+ const missing = [];
70
+ const dirExists = await isDirectory(projectPath);
71
+ checks.push({
72
+ name: "Proje dizini mevcut",
73
+ ok: dirExists,
74
+ detail: dirExists ? projectPath : "Dizin bulunamadı",
75
+ });
76
+ if (!dirExists) {
77
+ missing.push("Proje dizini hiç oluşmamış");
78
+ return {
79
+ projectPath,
80
+ projectType: "next",
81
+ ok: false,
82
+ checks,
83
+ missing,
84
+ suggestedFix: "Proje oluşturulmamış. create-next-app komutunu yeniden çalıştır ve bu sefer exit code'unu kontrol et.",
85
+ };
86
+ }
87
+ const pkgPath = path.join(projectPath, "package.json");
88
+ const pkgExists = await exists(pkgPath);
89
+ checks.push({ name: "package.json mevcut", ok: pkgExists });
90
+ if (!pkgExists) {
91
+ missing.push("package.json yok");
92
+ return {
93
+ projectPath,
94
+ projectType: "next",
95
+ ok: false,
96
+ checks,
97
+ missing,
98
+ suggestedFix: "package.json hiç oluşmamış, create-next-app başlamadan fail olmuş. Dizini temizle ve yeniden kur.",
99
+ };
100
+ }
101
+ const pkg = await readJsonSafe(pkgPath);
102
+ const pkgValid = pkg !== null;
103
+ checks.push({ name: "package.json geçerli JSON", ok: pkgValid });
104
+ if (!pkg) {
105
+ missing.push("package.json bozuk JSON");
106
+ return {
107
+ projectPath,
108
+ projectType: "next",
109
+ ok: false,
110
+ checks,
111
+ missing,
112
+ suggestedFix: "package.json bozuk. Dosyayı sil ve yeniden kur.",
113
+ };
114
+ }
115
+ const scripts = pkg.scripts || {};
116
+ const hasDev = typeof scripts.dev === "string" && scripts.dev.length > 0;
117
+ const hasBuild = typeof scripts.build === "string" && scripts.build.length > 0;
118
+ const hasStart = typeof scripts.start === "string" && scripts.start.length > 0;
119
+ checks.push({
120
+ name: "scripts.dev mevcut",
121
+ ok: hasDev,
122
+ detail: hasDev ? scripts.dev : "Eksik",
123
+ });
124
+ checks.push({
125
+ name: "scripts.build mevcut",
126
+ ok: hasBuild,
127
+ detail: hasBuild ? scripts.build : "Eksik",
128
+ });
129
+ checks.push({ name: "scripts.start mevcut", ok: hasStart });
130
+ if (!hasDev)
131
+ missing.push("package.json'da 'dev' script yok");
132
+ if (!hasBuild)
133
+ missing.push("package.json'da 'build' script yok");
134
+ const deps = {
135
+ ...(pkg.dependencies || {}),
136
+ ...(pkg.devDependencies || {}),
137
+ };
138
+ const hasNext = typeof deps.next === "string";
139
+ const hasReact = typeof deps.react === "string";
140
+ checks.push({
141
+ name: "next bağımlılığı mevcut",
142
+ ok: hasNext,
143
+ detail: hasNext ? deps.next : "Eksik",
144
+ });
145
+ checks.push({ name: "react bağımlılığı mevcut", ok: hasReact });
146
+ if (!hasNext)
147
+ missing.push("'next' paketi package.json'da yok");
148
+ if (!hasReact)
149
+ missing.push("'react' paketi package.json'da yok");
150
+ const nmPath = path.join(projectPath, "node_modules");
151
+ const nmExists = await isDirectory(nmPath);
152
+ checks.push({ name: "node_modules dizini mevcut", ok: nmExists });
153
+ if (!nmExists) {
154
+ missing.push("node_modules kurulmamış");
155
+ return {
156
+ projectPath,
157
+ projectType: "next",
158
+ ok: false,
159
+ checks,
160
+ missing,
161
+ suggestedFix: `Paketler yüklenmemiş. Çalıştır: cd "${projectPath}" && npm install --no-audit --no-fund --loglevel=error`,
162
+ };
163
+ }
164
+ const nmNextExists = await isDirectory(path.join(nmPath, "next"));
165
+ checks.push({
166
+ name: "node_modules/next mevcut (kurulum tamamlandı mı)",
167
+ ok: nmNextExists,
168
+ });
169
+ if (!nmNextExists) {
170
+ missing.push("node_modules/next yok — npm install yarıda kalmış olabilir");
171
+ }
172
+ const nmCount = await countDirEntries(nmPath, 100);
173
+ const nmPopulated = nmCount >= 50;
174
+ checks.push({
175
+ name: "node_modules yeterince dolu",
176
+ ok: nmPopulated,
177
+ detail: `${nmCount} dizin`,
178
+ });
179
+ if (!nmPopulated)
180
+ missing.push(`node_modules sadece ${nmCount} paket içeriyor, beklenen 50+`);
181
+ const hasLockfile = await exists(path.join(projectPath, "package-lock.json"));
182
+ checks.push({ name: "package-lock.json mevcut", ok: hasLockfile });
183
+ if (!hasLockfile)
184
+ missing.push("package-lock.json yok — install tamamlanmamış");
185
+ const appPageExists = (await exists(path.join(projectPath, "src", "app", "page.tsx"))) ||
186
+ (await exists(path.join(projectPath, "src", "app", "page.jsx"))) ||
187
+ (await exists(path.join(projectPath, "app", "page.tsx"))) ||
188
+ (await exists(path.join(projectPath, "app", "page.jsx")));
189
+ const pagesIndexExists = (await exists(path.join(projectPath, "src", "pages", "index.tsx"))) ||
190
+ (await exists(path.join(projectPath, "src", "pages", "index.jsx"))) ||
191
+ (await exists(path.join(projectPath, "pages", "index.tsx"))) ||
192
+ (await exists(path.join(projectPath, "pages", "index.jsx")));
193
+ const hasEntryPage = appPageExists || pagesIndexExists;
194
+ checks.push({
195
+ name: "Ana sayfa (app/page.tsx veya pages/index.tsx) mevcut",
196
+ ok: hasEntryPage,
197
+ });
198
+ if (!hasEntryPage)
199
+ missing.push("Ana sayfa dosyası yok");
200
+ const tsconfigExists = await exists(path.join(projectPath, "tsconfig.json"));
201
+ checks.push({ name: "tsconfig.json mevcut", ok: tsconfigExists });
202
+ const ok = missing.length === 0;
203
+ let suggestedFix;
204
+ if (!ok) {
205
+ if (missing.some((m) => m.includes("node_modules"))) {
206
+ suggestedFix = `Paketler eksik. Çalıştır: cd "${projectPath}" && npm install --no-audit --no-fund --loglevel=error`;
207
+ }
208
+ else if (missing.some((m) => m.includes("script"))) {
209
+ suggestedFix = "package.json scripts bölümü bozuk. Projeyi sıfırdan yeniden kur.";
210
+ }
211
+ }
212
+ return {
213
+ projectPath,
214
+ projectType: "next",
215
+ ok,
216
+ checks,
217
+ missing,
218
+ suggestedFix,
219
+ };
220
+ }
221
+ async function checkGenericNodeProject(projectPath) {
222
+ const checks = [];
223
+ const missing = [];
224
+ const dirExists = await isDirectory(projectPath);
225
+ checks.push({ name: "Proje dizini mevcut", ok: dirExists });
226
+ if (!dirExists) {
227
+ missing.push("Proje dizini yok");
228
+ return {
229
+ projectPath,
230
+ projectType: "node",
231
+ ok: false,
232
+ checks,
233
+ missing,
234
+ };
235
+ }
236
+ const pkgPath = path.join(projectPath, "package.json");
237
+ const pkg = await readJsonSafe(pkgPath);
238
+ const pkgOk = pkg !== null;
239
+ checks.push({ name: "package.json parse edildi", ok: pkgOk });
240
+ if (!pkg) {
241
+ missing.push("package.json yok veya bozuk");
242
+ return {
243
+ projectPath,
244
+ projectType: "node",
245
+ ok: false,
246
+ checks,
247
+ missing,
248
+ };
249
+ }
250
+ const scripts = pkg.scripts || {};
251
+ const hasStart = typeof scripts.start === "string" || typeof scripts.dev === "string";
252
+ checks.push({ name: "start veya dev script mevcut", ok: hasStart });
253
+ const nmExists = await isDirectory(path.join(projectPath, "node_modules"));
254
+ checks.push({ name: "node_modules mevcut", ok: nmExists });
255
+ if (!nmExists)
256
+ missing.push("node_modules yok — npm install çalıştırılmamış");
257
+ return {
258
+ projectPath,
259
+ projectType: "node",
260
+ ok: missing.length === 0,
261
+ checks,
262
+ missing,
263
+ suggestedFix: nmExists ? undefined : `cd "${projectPath}" && npm install --no-audit --no-fund`,
264
+ };
265
+ }
266
+ export const verifyProjectHandler = async (params, ctx) => {
267
+ const start = Date.now();
268
+ const rawPath = (params.path || ".");
269
+ const requestedType = params.type || "auto";
270
+ const check = checkPath(rawPath, ctx.cwd, { mustExist: false });
271
+ if (!check.ok || !check.realPath) {
272
+ return {
273
+ success: false,
274
+ output: "",
275
+ error: `Proje yolu reddedildi: ${check.message || rawPath}`,
276
+ duration: Date.now() - start,
277
+ };
278
+ }
279
+ const projectPath = check.realPath;
280
+ if (!(await isDirectory(projectPath))) {
281
+ return {
282
+ success: false,
283
+ output: "",
284
+ error: `Dizin bulunamadı: ${projectPath}`,
285
+ metadata: { project_ok: false, missing: ["dizin yok"] },
286
+ duration: Date.now() - start,
287
+ };
288
+ }
289
+ let projectType = requestedType;
290
+ if (projectType === "auto") {
291
+ projectType = await detectProjectType(projectPath);
292
+ }
293
+ let report;
294
+ if (projectType === "next") {
295
+ report = await checkNextProject(projectPath);
296
+ }
297
+ else {
298
+ report = await checkGenericNodeProject(projectPath);
299
+ }
300
+ const lines = [];
301
+ lines.push(`## Proje Sağlık Raporu: ${report.projectType}`);
302
+ lines.push(`Dizin: ${report.projectPath}`);
303
+ lines.push(`Sonuç: ${report.ok ? "✓ SAĞLIKLI" : "✗ EKSİK"}`);
304
+ lines.push("");
305
+ lines.push("### Kontroller");
306
+ for (const c of report.checks) {
307
+ const mark = c.ok ? "[✓]" : "[✗]";
308
+ const detail = c.detail ? ` — ${c.detail}` : "";
309
+ lines.push(`${mark} ${c.name}${detail}`);
310
+ }
311
+ if (report.missing.length > 0) {
312
+ lines.push("");
313
+ lines.push("### Eksikler");
314
+ for (const m of report.missing) {
315
+ lines.push(`- ${m}`);
316
+ }
317
+ }
318
+ if (report.suggestedFix) {
319
+ lines.push("");
320
+ lines.push("### Önerilen Düzeltme");
321
+ lines.push(report.suggestedFix);
322
+ }
323
+ return {
324
+ success: report.ok,
325
+ output: lines.join("\n"),
326
+ error: report.ok ? undefined : `Proje sağlık kontrolü başarısız: ${report.missing.join(", ")}`,
327
+ metadata: {
328
+ project_ok: report.ok,
329
+ project_type: report.projectType,
330
+ project_path: report.projectPath,
331
+ missing: report.missing,
332
+ suggested_fix: report.suggestedFix,
333
+ check_count: report.checks.length,
334
+ passed_count: report.checks.filter((c) => c.ok).length,
335
+ },
336
+ approvalState: 2,
337
+ duration: Date.now() - start,
338
+ };
339
+ };
package/dist/ui/App.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { Box, Text, useApp, useInput } from "ink";
3
+ import Spinner from "ink-spinner";
3
4
  import { MessageList } from "./MessageList.js";
4
5
  import { InputBar } from "./InputBar.js";
5
6
  import { StatusBar } from "./StatusBar.js";
6
7
  import { PermissionPrompt } from "./PermissionPrompt.js";
8
+ import { WelcomeBox } from "./WelcomeBox.js";
7
9
  import { ChatHistory } from "../agent/history.js";
8
10
  import { runAgentLoop, clearSessionApprovalCache } from "../agent/loop.js";
9
11
  import { SessionRecorder, generateSessionId, } from "../agent/session-store.js";
@@ -22,9 +24,10 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
22
24
  const [mode, setMode] = useState(initialMode);
23
25
  const [model] = useState(initialModel);
24
26
  const [tokensUsed, setTokensUsed] = useState(0);
25
- const [tokensLimit, setTokensLimit] = useState(initialModel === "max" ? 5_000_000 : 1_000_000);
27
+ const [tokensLimit, setTokensLimit] = useState(initialModel === "max" ? 500_000_000 : 100_000_000);
26
28
  const [pending, setPending] = useState(null);
27
29
  const [liveOutput, setLiveOutput] = useState("");
30
+ const [thinking, setThinking] = useState("");
28
31
  const [taskList, setTaskList] = useState([]);
29
32
  const historyRef = useRef(new ChatHistory());
30
33
  const abortRef = useRef(null);
@@ -35,10 +38,6 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
35
38
  setMessages((prev) => [...prev, { ...msg, id: newId() }]);
36
39
  }, []);
37
40
  useEffect(() => {
38
- pushMessage({
39
- kind: "info",
40
- content: "Shift+Tab ile mod değiştirin · /help yardım · ESC ile iptal · Ctrl+C çıkış",
41
- });
42
41
  setLogContext({
43
42
  sessionId: sessionIdRef.current,
44
43
  cwd: projectRoot,
@@ -47,7 +46,7 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
47
46
  return () => {
48
47
  void recorderRef.current.end("exit");
49
48
  };
50
- }, [pushMessage, projectRoot]);
49
+ }, [projectRoot]);
51
50
  useInput((input, key) => {
52
51
  if (key.shift && key.tab) {
53
52
  if (busy)
@@ -197,10 +196,11 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
197
196
  const onEvent = (event) => {
198
197
  switch (event.type) {
199
198
  case "agent_think":
200
- pushMessage({ kind: "think", content: event.content });
199
+ setThinking(event.content);
201
200
  void recorderRef.current.think(event.content);
202
201
  break;
203
202
  case "agent_tool_call":
203
+ setThinking("");
204
204
  if (event.toolName === "write_tasks") {
205
205
  const rawTasks = event.params.tasks;
206
206
  if (Array.isArray(rawTasks)) {
@@ -250,6 +250,7 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
250
250
  }
251
251
  case "agent_message":
252
252
  if (!event.delta) {
253
+ setThinking("");
253
254
  pushMessage({ kind: "assistant", content: event.content });
254
255
  void recorderRef.current.assistantMessage(event.content);
255
256
  }
@@ -311,6 +312,7 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
311
312
  }
312
313
  finally {
313
314
  setBusy(false);
315
+ setThinking("");
314
316
  abortRef.current = null;
315
317
  liveBufferRef.current = "";
316
318
  setLiveOutput("");
@@ -336,8 +338,10 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
336
338
  handleSubmit(initialPrompt);
337
339
  }
338
340
  }, [busy, handleSubmit, initialPrompt]);
341
+ const hasUserMessage = messages.some((m) => m.kind === "user");
342
+ const showWelcome = !hasUserMessage && !busy;
339
343
  return (React.createElement(Box, { flexDirection: "column" },
340
- React.createElement(Box, { paddingX: 1, marginBottom: 1 },
344
+ showWelcome ? (React.createElement(WelcomeBox, { userName: session.user.name || "", userEmail: session.user.email, modelLabel: model === "max" ? "CLI Max" : "CLI Pro", cwd: cwd, cliVersion: CLI_VERSION, projectRoot: projectRoot })) : (React.createElement(Box, { paddingX: 1, marginBottom: 1 },
341
345
  React.createElement(Text, { color: palette.brand, bold: true },
342
346
  symbols.sparkle,
343
347
  " AIGENCY CLI"),
@@ -345,7 +349,7 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
345
349
  " v",
346
350
  CLI_VERSION),
347
351
  React.createElement(Text, { color: palette.textFaint }, " \u00B7 "),
348
- React.createElement(Text, { color: palette.textMuted }, session.user.name || session.user.email)),
352
+ React.createElement(Text, { color: palette.textMuted }, session.user.name || session.user.email))),
349
353
  React.createElement(MessageList, { messages: messages }),
350
354
  taskList.length > 0 && (React.createElement(Box, { marginX: 1, marginY: 1, paddingX: 1, flexDirection: "column", borderStyle: "round", borderColor: palette.brand },
351
355
  React.createElement(Box, { marginBottom: 0 },
@@ -383,6 +387,12 @@ export function App({ session, initialMode, initialModel, cwd, projectRoot, init
383
387
  ? palette.textPrimary
384
388
  : palette.textDim, strikethrough: task.status === "completed" }, label)));
385
389
  }))),
390
+ thinking && busy && (React.createElement(Box, { paddingX: 2, marginTop: 1 },
391
+ React.createElement(Text, { color: palette.brandBright },
392
+ React.createElement(Spinner, { type: "dots" })),
393
+ React.createElement(Text, { color: palette.textMuted },
394
+ " ",
395
+ thinking))),
386
396
  liveOutput && (React.createElement(Box, { marginX: 1, marginY: 1, paddingX: 1, flexDirection: "column", borderStyle: "round", borderColor: palette.warning },
387
397
  React.createElement(Box, { marginBottom: 0 },
388
398
  React.createElement(Text, { color: palette.warning, bold: true },
@@ -14,7 +14,7 @@ function pct(used, limit) {
14
14
  export function StatusBar({ tokensUsed, tokensLimit, mode, model, cwd, busy, }) {
15
15
  const percent = pct(tokensUsed, tokensLimit);
16
16
  const projectName = path.basename(cwd) || cwd;
17
- const modelLabel = model === "max" ? "AIGENCY Max" : "AIGENCY Pro";
17
+ const modelLabel = model === "max" ? "CLI Max" : "CLI Pro";
18
18
  const modelColor = model === "max" ? palette.accent : palette.brand;
19
19
  const pctColor = usageColor(percent);
20
20
  return (React.createElement(Box, { borderStyle: "single", borderColor: palette.border, paddingX: 1, justifyContent: "space-between" },
@@ -0,0 +1,116 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import path from "node:path";
4
+ import { palette, symbols } from "./theme.js";
5
+ import { listSessions, readSessionTranscript } from "../agent/session-store.js";
6
+ function relativeTime(date) {
7
+ const diff = Date.now() - date.getTime();
8
+ if (diff < 60_000)
9
+ return "az önce";
10
+ if (diff < 3_600_000)
11
+ return `${Math.floor(diff / 60_000)} dk önce`;
12
+ if (diff < 86_400_000)
13
+ return `${Math.floor(diff / 3_600_000)} saat önce`;
14
+ if (diff < 7 * 86_400_000)
15
+ return `${Math.floor(diff / 86_400_000)} gün önce`;
16
+ return date.toLocaleDateString("tr-TR");
17
+ }
18
+ async function buildActivityPreview(sessionId, cwd) {
19
+ try {
20
+ const events = await readSessionTranscript(sessionId, cwd);
21
+ const firstUser = events.find((e) => e.type === "user");
22
+ if (firstUser && firstUser.type === "user") {
23
+ const clean = firstUser.content.replace(/\s+/g, " ").trim();
24
+ return clean.slice(0, 56) + (clean.length > 56 ? "…" : "");
25
+ }
26
+ }
27
+ catch {
28
+ }
29
+ return "(geçmiş oturum)";
30
+ }
31
+ export function WelcomeBox({ userName, userEmail, modelLabel, cwd, cliVersion, projectRoot, }) {
32
+ const [recent, setRecent] = useState([]);
33
+ useEffect(() => {
34
+ let cancelled = false;
35
+ (async () => {
36
+ try {
37
+ const sessions = await listSessions(projectRoot);
38
+ const prior = sessions.slice(1, 4);
39
+ const previews = [];
40
+ for (const s of prior) {
41
+ const preview = await buildActivityPreview(s.sessionId, projectRoot);
42
+ previews.push({
43
+ sessionId: s.sessionId,
44
+ preview,
45
+ whenLabel: relativeTime(s.lastActivityAt),
46
+ });
47
+ }
48
+ if (!cancelled)
49
+ setRecent(previews);
50
+ }
51
+ catch {
52
+ }
53
+ })();
54
+ return () => {
55
+ cancelled = true;
56
+ };
57
+ }, [projectRoot]);
58
+ const projectName = path.basename(cwd) || cwd;
59
+ const displayName = userName.trim() || userEmail || "Geliştirici";
60
+ const tierColor = modelLabel.includes("Max") ? palette.accent : palette.brand;
61
+ return (React.createElement(Box, { borderStyle: "round", borderColor: palette.brand, flexDirection: "column", paddingX: 1, marginBottom: 1 },
62
+ React.createElement(Box, null,
63
+ React.createElement(Text, { color: palette.brand, bold: true },
64
+ symbols.sparkle,
65
+ " AIGENCY CLI"),
66
+ React.createElement(Text, { color: palette.textFaint },
67
+ " v",
68
+ cliVersion),
69
+ React.createElement(Text, { color: palette.textFaint }, " \u00B7 "),
70
+ React.createElement(Text, { color: tierColor, bold: true }, modelLabel)),
71
+ React.createElement(Box, { marginTop: 1 },
72
+ React.createElement(Text, { color: palette.accent },
73
+ symbols.star,
74
+ " "),
75
+ React.createElement(Text, { color: palette.textPrimary, bold: true },
76
+ "Ho\u015F geldin",
77
+ displayName !== "Geliştirici" ? `, ${displayName}` : "",
78
+ "!")),
79
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" },
80
+ React.createElement(Box, null,
81
+ React.createElement(Text, { color: palette.textDim },
82
+ symbols.bullet,
83
+ " Dizin: "),
84
+ React.createElement(Text, { color: palette.textSecondary }, projectName)),
85
+ userEmail && (React.createElement(Box, null,
86
+ React.createElement(Text, { color: palette.textDim },
87
+ symbols.bullet,
88
+ " Hesap: "),
89
+ React.createElement(Text, { color: palette.textMuted }, userEmail)))),
90
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" },
91
+ React.createElement(Text, { color: palette.accent, bold: true },
92
+ symbols.lightning,
93
+ " Ba\u015Flang\u0131\u00E7 ipu\u00E7lar\u0131"),
94
+ React.createElement(Box, { marginTop: 0 },
95
+ React.createElement(Text, { color: palette.brand }, " /init"),
96
+ React.createElement(Text, { color: palette.textMuted }, " \u2014 proje i\u00E7in AIGENCY.md olu\u015Ftur")),
97
+ React.createElement(Box, null,
98
+ React.createElement(Text, { color: palette.brand }, " /help"),
99
+ React.createElement(Text, { color: palette.textMuted }, " \u2014 komut listesi")),
100
+ React.createElement(Box, null,
101
+ React.createElement(Text, { color: palette.brand }, " Shift+Tab"),
102
+ React.createElement(Text, { color: palette.textMuted }, " \u2014 izin modu de\u011Fi\u015Ftir")),
103
+ React.createElement(Box, null,
104
+ React.createElement(Text, { color: palette.brand }, " ESC"),
105
+ React.createElement(Text, { color: palette.textMuted }, " \u2014 aktif i\u015Flemi iptal et"))),
106
+ recent.length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
107
+ React.createElement(Text, { color: palette.success, bold: true },
108
+ symbols.triangle,
109
+ " Son aktiviteler"),
110
+ recent.map((r) => (React.createElement(Box, { key: r.sessionId },
111
+ React.createElement(Text, { color: palette.textFaint },
112
+ " ",
113
+ r.whenLabel),
114
+ React.createElement(Text, { color: palette.textFaint }, " \u00B7 "),
115
+ React.createElement(Text, { color: palette.textSecondary }, r.preview))))))));
116
+ }
package/dist/ui/theme.js CHANGED
@@ -56,22 +56,22 @@ export const agentRoleColor = {
56
56
  export const modeColors = {
57
57
  default: {
58
58
  color: palette.brand,
59
- label: "VARSAYILAN",
59
+ label: "onay iste",
60
60
  icon: "●",
61
61
  },
62
62
  plan: {
63
63
  color: palette.info,
64
- label: "PLAN",
64
+ label: "plan",
65
65
  icon: "◇",
66
66
  },
67
67
  accept_edits: {
68
68
  color: palette.warning,
69
- label: "OTO.YAZ",
69
+ label: "oto. düzenle",
70
70
  icon: "✎",
71
71
  },
72
72
  auto: {
73
73
  color: palette.accent,
74
- label: "OTOMATİK",
74
+ label: "otomatik",
75
75
  icon: "⚡",
76
76
  },
77
77
  };
@@ -113,4 +113,5 @@ export const symbols = {
113
113
  star: "★",
114
114
  hourglass: "⧗",
115
115
  lightning: "⚡",
116
+ spinner: "◜",
116
117
  };
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const CLI_VERSION = "0.3.3";
1
+ export const CLI_VERSION = "0.3.5";
2
2
  export const CLI_NAME = "aigency";
3
3
  export const CLI_DISPLAY_NAME = "AIGENCY CLI";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigencydev/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "AIGENCY CLI — terminalden yapay zeka destekli kod üretimi, dosya yönetimi ve proje otomasyonu.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,6 +28,7 @@
28
28
  "dependencies": {
29
29
  "commander": "^12.1.0",
30
30
  "ink": "^5.0.1",
31
+ "ink-spinner": "^5.0.0",
31
32
  "ink-text-input": "^6.0.0",
32
33
  "react": "^18.3.1",
33
34
  "zod": "^3.23.8"