@gurulu/cli 1.2.1 → 1.3.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/dist/bin.js CHANGED
@@ -24529,7 +24529,7 @@ class ApiClient {
24529
24529
  }
24530
24530
 
24531
24531
  // src/lib/config.ts
24532
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24532
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24533
24533
  import { homedir } from "node:os";
24534
24534
  import { dirname, join } from "node:path";
24535
24535
  var DEFAULT_ENDPOINT = process.env.GURULU_ENDPOINT ?? "https://api.gurulu.io";
@@ -24586,10 +24586,9 @@ function writeGlobalCredentials(creds) {
24586
24586
  const path = globalCredentialsPath();
24587
24587
  ensureDir(path);
24588
24588
  writeFileSync(path, `${JSON.stringify(creds, null, 2)}
24589
- `, "utf-8");
24589
+ `, { encoding: "utf-8", mode: 384 });
24590
24590
  try {
24591
- const fs = __require("node:fs");
24592
- fs.chmodSync(path, 384);
24591
+ chmodSync(path, 384);
24593
24592
  } catch {}
24594
24593
  }
24595
24594
  function findCredentialForWorkspace(workspaceId) {
@@ -25119,7 +25118,7 @@ function sleep(ms) {
25119
25118
  // src/wizard/run.ts
25120
25119
  import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync8 } from "node:fs";
25121
25120
  import { dirname as dirname3 } from "node:path";
25122
- import * as p3 from "@clack/prompts";
25121
+ import * as p4 from "@clack/prompts";
25123
25122
 
25124
25123
  // src/commands/pull.ts
25125
25124
  import { writeFileSync as writeFileSync3 } from "node:fs";
@@ -25263,6 +25262,17 @@ var pullCmd = defineCommand({
25263
25262
  // src/lib/detect.ts
25264
25263
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
25265
25264
  import { dirname as dirname2, join as join5, parse } from "node:path";
25265
+ var LLM_DEP_MAP = {
25266
+ openai: "openai",
25267
+ "@anthropic-ai/sdk": "anthropic",
25268
+ "@langchain/core": "langchain",
25269
+ langchain: "langchain",
25270
+ "@google/generative-ai": "google-genai",
25271
+ ai: "vercel-ai",
25272
+ "cohere-ai": "cohere",
25273
+ "@mistralai/mistralai": "mistral",
25274
+ ollama: "ollama"
25275
+ };
25266
25276
  function readPackageJson(dir) {
25267
25277
  const p = join5(dir, "package.json");
25268
25278
  if (!existsSync4(p))
@@ -25348,6 +25358,14 @@ function frameworkRuntime(fw) {
25348
25358
  return "unknown";
25349
25359
  }
25350
25360
  }
25361
+ function detectLlmFrameworks(pkg) {
25362
+ const found = new Set;
25363
+ for (const [dep, fw] of Object.entries(LLM_DEP_MAP)) {
25364
+ if (hasDep(pkg, dep))
25365
+ found.add(fw);
25366
+ }
25367
+ return [...found];
25368
+ }
25351
25369
  function detectProject(dir) {
25352
25370
  const pkg = readPackageJson(dir);
25353
25371
  const framework = detectFramework(pkg, dir);
@@ -25357,7 +25375,8 @@ function detectProject(dir) {
25357
25375
  framework,
25358
25376
  runtime: frameworkRuntime(framework),
25359
25377
  packageManager: detectPackageManager(dir),
25360
- packageJson: pkg
25378
+ packageJson: pkg,
25379
+ llmFrameworks: detectLlmFrameworks(pkg)
25361
25380
  };
25362
25381
  }
25363
25382
 
@@ -26134,8 +26153,77 @@ function gatherContext(opts) {
26134
26153
  return { files, capped, totalBytes };
26135
26154
  }
26136
26155
 
26137
- // src/wizard/plan.ts
26156
+ // src/wizard/features.ts
26138
26157
  import * as p2 from "@clack/prompts";
26158
+ function availableFeatures(detected) {
26159
+ const isBrowser = frameworkRuntime(detected.framework) !== "node";
26160
+ const feats = [];
26161
+ feats.push({
26162
+ key: "error",
26163
+ label: "Hata takibi",
26164
+ detail: "Tarayıcı JS hatalarını otomatik yakala (js_error)",
26165
+ enableHint: isBrowser ? "gurulu.init({ ..., autocapture: { js_error: true } })" : "Sunucu hatalarında: gurulu.track('js_error', { message, error_type })",
26166
+ recommended: isBrowser
26167
+ });
26168
+ if (detected.llmFrameworks.length > 0) {
26169
+ feats.push({
26170
+ key: "llm",
26171
+ label: `LLM analizi (${detected.llmFrameworks.join(", ")} tespit edildi)`,
26172
+ detail: "AI çağrı maliyeti/latency/hata/model performansı",
26173
+ enableHint: "import { wrapOpenAI } from '@gurulu/node'; const client = wrapOpenAI(openai);",
26174
+ recommended: true
26175
+ });
26176
+ }
26177
+ if (isBrowser) {
26178
+ feats.push({
26179
+ key: "activation",
26180
+ label: "Activation (popup · tur · A/B test · kişiselleştirme)",
26181
+ detail: "Dashboard'dan yayınla, SDK render etsin (Action Layer)",
26182
+ enableHint: "import { runActivation } from '@gurulu/web/activate'; runActivation(gurulu);",
26183
+ recommended: false
26184
+ });
26185
+ }
26186
+ return feats;
26187
+ }
26188
+ async function runFeatures(client, detected, opts) {
26189
+ const avail = availableFeatures(detected);
26190
+ if (avail.length === 0 || !opts.authed)
26191
+ return { selected: [], registered: false };
26192
+ let selectedKeys;
26193
+ if (opts.yes) {
26194
+ selectedKeys = avail.filter((f3) => f3.recommended).map((f3) => f3.key);
26195
+ } else {
26196
+ const choice = await p2.multiselect({
26197
+ message: "Hangi özellikleri kuralım? (event'ler registry'ye otomatik eklenir)",
26198
+ options: avail.map((f3) => ({ value: f3.key, label: f3.label, hint: f3.detail })),
26199
+ initialValues: avail.filter((f3) => f3.recommended).map((f3) => f3.key),
26200
+ required: false
26201
+ });
26202
+ if (p2.isCancel(choice))
26203
+ return { selected: [], registered: false };
26204
+ selectedKeys = choice;
26205
+ }
26206
+ if (selectedKeys.length === 0)
26207
+ return { selected: [], registered: false };
26208
+ const selected = avail.filter((f3) => selectedKeys.includes(f3.key));
26209
+ const s2 = p2.spinner();
26210
+ s2.start("Özellikler registry'ye kuruluyor…");
26211
+ try {
26212
+ await client.post("/features/register", { features: selectedKeys });
26213
+ s2.stop(c3.neon(`✓ ${selected.length} özellik kuruldu`));
26214
+ } catch {
26215
+ s2.stop("Özellik kurulumu atlandı (gateway hatası) — sonra: gurulu doctor", 1);
26216
+ return { selected, registered: false };
26217
+ }
26218
+ p2.note(selected.map((f3) => `${c3.bold(f3.label)}
26219
+ ${c3.dim(f3.enableHint)}`).join(`
26220
+
26221
+ `), "Etkinleştirme");
26222
+ return { selected, registered: true };
26223
+ }
26224
+
26225
+ // src/wizard/plan.ts
26226
+ import * as p3 from "@clack/prompts";
26139
26227
  async function fetchPlan(client, context, input) {
26140
26228
  if (context.files.length === 0)
26141
26229
  return null;
@@ -26180,28 +26268,29 @@ function formatPlan(plan) {
26180
26268
  `).trimEnd();
26181
26269
  }
26182
26270
  async function renderPlan(plan) {
26183
- p2.note(formatPlan(plan), plan.summary);
26271
+ p3.note(formatPlan(plan), plan.summary);
26184
26272
  const newCount = plan.events.filter((e2) => !e2.existing).length;
26185
- const ok = await p2.confirm({
26273
+ const ok = await p3.confirm({
26186
26274
  message: `${plan.events.length} event (${newCount} yeni) — bu planı uygula?`
26187
26275
  });
26188
- if (p2.isCancel(ok) || !ok)
26276
+ if (p3.isCancel(ok) || !ok)
26189
26277
  return null;
26190
26278
  return plan.events;
26191
26279
  }
26192
26280
 
26193
26281
  // src/wizard/wire.ts
26194
26282
  import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
26195
- import { join as join10 } from "node:path";
26196
26283
 
26197
26284
  // src/wizard/agent.ts
26198
26285
  import { execFile } from "node:child_process";
26199
26286
  import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
26200
- import { join as join9 } from "node:path";
26201
26287
  import { promisify } from "node:util";
26202
26288
 
26203
26289
  // src/wizard/guard.ts
26204
26290
  import { basename, isAbsolute, relative as relative2, resolve } from "node:path";
26291
+ function resolveInCwd(p4, cwd) {
26292
+ return isAbsolute(p4) ? p4 : resolve(cwd, p4);
26293
+ }
26205
26294
  function isAdditiveEdit(find, replace) {
26206
26295
  if (find.length === 0)
26207
26296
  return { ok: false, reason: "empty find" };
@@ -26212,22 +26301,45 @@ function isAdditiveEdit(find, replace) {
26212
26301
  return { ok: false, reason: "no-op edit (replace === find)" };
26213
26302
  return { ok: true };
26214
26303
  }
26215
- var BASH_ALLOW_BINS = new Set([
26216
- "bun",
26217
- "bunx",
26218
- "npm",
26219
- "npx",
26220
- "pnpm",
26221
- "yarn",
26304
+ var CHECKER_BINS = new Set([
26222
26305
  "tsc",
26223
26306
  "tsgo",
26307
+ "vue-tsc",
26308
+ "svelte-check",
26224
26309
  "biome",
26225
26310
  "eslint",
26226
26311
  "prettier",
26227
- "vue-tsc",
26228
- "svelte-check",
26229
26312
  "astro"
26230
26313
  ]);
26314
+ var RUNNER_BINS = new Set(["bun", "npm", "pnpm", "yarn"]);
26315
+ var DENY_SUBCMDS = new Set([
26316
+ "install",
26317
+ "i",
26318
+ "add",
26319
+ "remove",
26320
+ "rm",
26321
+ "uninstall",
26322
+ "un",
26323
+ "ci",
26324
+ "dlx",
26325
+ "x",
26326
+ "exec",
26327
+ "create",
26328
+ "init",
26329
+ "up",
26330
+ "update",
26331
+ "upgrade",
26332
+ "link",
26333
+ "unlink",
26334
+ "global",
26335
+ "dedupe",
26336
+ "audit",
26337
+ "publish",
26338
+ "pack",
26339
+ "import",
26340
+ "config"
26341
+ ]);
26342
+ var SCRIPT_NAME = /^[a-z0-9][a-z0-9:._-]*$/i;
26231
26343
  var BASH_DENY = /[;&|`$<>]|\.\.\/|\b(rm|curl|wget|sudo|chmod|chown|mv|dd|kill|eval|sh|bash|node|python)\b/;
26232
26344
  function isAllowedBash(cmd) {
26233
26345
  const t2 = cmd.trim();
@@ -26235,13 +26347,27 @@ function isAllowedBash(cmd) {
26235
26347
  return { ok: false, reason: "empty cmd" };
26236
26348
  if (BASH_DENY.test(t2))
26237
26349
  return { ok: false, reason: "yasak operatör/binary" };
26238
- const bin = t2.split(/\s+/)[0] ?? "";
26239
- if (!BASH_ALLOW_BINS.has(bin))
26240
- return { ok: false, reason: `allowlist dışı binary: ${bin}` };
26241
- return { ok: true };
26350
+ const tokens = t2.split(/\s+/);
26351
+ const bin = tokens[0] ?? "";
26352
+ if (CHECKER_BINS.has(bin))
26353
+ return { ok: true };
26354
+ if (RUNNER_BINS.has(bin)) {
26355
+ const sub = tokens[1] ?? "";
26356
+ if (sub === "run") {
26357
+ const script = tokens[2] ?? "";
26358
+ if (!SCRIPT_NAME.test(script))
26359
+ return { ok: false, reason: `geçersiz script adı: ${script}` };
26360
+ return { ok: true };
26361
+ }
26362
+ if (bin === "yarn" && sub && !DENY_SUBCMDS.has(sub) && SCRIPT_NAME.test(sub)) {
26363
+ return { ok: true };
26364
+ }
26365
+ return { ok: false, reason: `yasak alt-komut (sadece 'run' izinli): ${bin} ${sub}` };
26366
+ }
26367
+ return { ok: false, reason: `allowlist dışı binary: ${bin}` };
26242
26368
  }
26243
- function isAllowedPath(p3, cwd) {
26244
- const abs = isAbsolute(p3) ? p3 : resolve(cwd, p3);
26369
+ function isAllowedPath(p4, cwd) {
26370
+ const abs = isAbsolute(p4) ? p4 : resolve(cwd, p4);
26245
26371
  const rel = relative2(cwd, abs);
26246
26372
  if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
26247
26373
  return { ok: false, reason: "cwd dışı yol" };
@@ -26258,6 +26384,31 @@ function hasPromptInjection(content) {
26258
26384
  // src/wizard/agent.ts
26259
26385
  var MAX_OBS = 4000;
26260
26386
  var pexec = promisify(execFile);
26387
+ function safeEnv() {
26388
+ const keep = [
26389
+ "PATH",
26390
+ "HOME",
26391
+ "USERPROFILE",
26392
+ "TMPDIR",
26393
+ "TEMP",
26394
+ "TMP",
26395
+ "LANG",
26396
+ "LC_ALL",
26397
+ "TERM",
26398
+ "SHELL",
26399
+ "NODE_ENV",
26400
+ "PATHEXT",
26401
+ "SystemRoot",
26402
+ "ComSpec"
26403
+ ];
26404
+ const env2 = {};
26405
+ for (const k2 of keep) {
26406
+ const v2 = process.env[k2];
26407
+ if (v2 !== undefined)
26408
+ env2[k2] = v2;
26409
+ }
26410
+ return env2;
26411
+ }
26261
26412
  async function defaultRunBash(cmd, cwd) {
26262
26413
  const parts = cmd.trim().split(/\s+/);
26263
26414
  const bin = parts[0] ?? "";
@@ -26265,7 +26416,8 @@ async function defaultRunBash(cmd, cwd) {
26265
26416
  const { stdout: stdout2, stderr } = await pexec(bin, parts.slice(1), {
26266
26417
  cwd,
26267
26418
  timeout: 120000,
26268
- maxBuffer: 4 * 1024 * 1024
26419
+ maxBuffer: 4 * 1024 * 1024,
26420
+ env: safeEnv()
26269
26421
  });
26270
26422
  return { stdout: stdout2, stderr };
26271
26423
  } catch (e2) {
@@ -26280,7 +26432,7 @@ async function executeTool(action, deps) {
26280
26432
  const g3 = isAllowedPath(action.path, cwd);
26281
26433
  if (!g3.ok)
26282
26434
  return { ok: false, observation: `read reddedildi: ${g3.reason}` };
26283
- const abs = join9(cwd, action.path);
26435
+ const abs = resolveInCwd(action.path, cwd);
26284
26436
  if (!existsSync7(abs))
26285
26437
  return { ok: false, observation: `dosya yok: ${action.path}` };
26286
26438
  let content = readFileSync8(abs, "utf-8");
@@ -26298,7 +26450,7 @@ async function executeTool(action, deps) {
26298
26450
  const ga = isAdditiveEdit(action.find, action.replace);
26299
26451
  if (!ga.ok)
26300
26452
  return { ok: false, observation: `edit reddedildi: ${ga.reason}` };
26301
- const abs = join9(cwd, action.path);
26453
+ const abs = resolveInCwd(action.path, cwd);
26302
26454
  if (!existsSync7(abs))
26303
26455
  return { ok: false, observation: `dosya yok: ${action.path}` };
26304
26456
  const src2 = readFileSync8(abs, "utf-8");
@@ -26334,7 +26486,9 @@ function buildWireSystemPrompt() {
26334
26486
  "- `find` must be an EXACT, UNIQUE snippet copied from a file you have read (read before edit).",
26335
26487
  "- Use the exact provided event_key (snake_case). Wire gurulu.track(...) at the right place and",
26336
26488
  " gurulu.identify(...) at the auth point if given.",
26337
- "- bash only for verification (typecheck/build/lint) no install, no other commands.",
26489
+ "- bash is ONLY for verification: the project's own scripts (`npm run typecheck`, `bun run build`,",
26490
+ " `pnpm run lint`) or a checker binary (`tsc --noEmit`, `biome check`, `eslint .`). Installing or",
26491
+ " fetching packages (`npm install`, `npx`, `bunx`, `pnpm dlx`, `add`, `exec`) is REJECTED by the guard.",
26338
26492
  "- After wiring, run the project typecheck/build to verify; if it breaks, fix additively.",
26339
26493
  "- When all events are wired and verify passes, emit done{summary}. Keep edits minimal."
26340
26494
  ].join(`
@@ -26368,17 +26522,35 @@ async function runWireAgent(client, input, snapshots) {
26368
26522
  try {
26369
26523
  res = await client.agentStep({ messages, first: i2 === 0 });
26370
26524
  } catch {
26371
- return { edits, changedFiles: [...changed], summary: "gateway hata", steps: i2, stoppedReason: "error" };
26525
+ return {
26526
+ edits,
26527
+ changedFiles: [...changed],
26528
+ summary: "gateway hata",
26529
+ steps: i2,
26530
+ stoppedReason: "error"
26531
+ };
26372
26532
  }
26373
26533
  if (res.status === "stub") {
26374
- return { edits, changedFiles: [...changed], summary: "AI kullanılamadı", steps: i2, stoppedReason: "stub" };
26534
+ return {
26535
+ edits,
26536
+ changedFiles: [...changed],
26537
+ summary: "AI kullanılamadı",
26538
+ steps: i2,
26539
+ stoppedReason: "stub"
26540
+ };
26375
26541
  }
26376
26542
  const { action, reasoning } = res.step;
26377
26543
  if (action.tool === "done") {
26378
- return { edits, changedFiles: [...changed], summary: action.summary, steps: i2, stoppedReason: "done" };
26544
+ return {
26545
+ edits,
26546
+ changedFiles: [...changed],
26547
+ summary: action.summary,
26548
+ steps: i2,
26549
+ stoppedReason: "done"
26550
+ };
26379
26551
  }
26380
26552
  if (action.tool === "edit") {
26381
- const abs = join10(input.cwd, action.path);
26553
+ const abs = resolveInCwd(action.path, input.cwd);
26382
26554
  if (!snapshots.has(action.path) && existsSync8(abs)) {
26383
26555
  snapshots.set(action.path, readFileSync9(abs, "utf-8"));
26384
26556
  }
@@ -26391,11 +26563,17 @@ async function runWireAgent(client, input, snapshots) {
26391
26563
  messages.push({ role: "assistant", content: JSON.stringify(res.step) });
26392
26564
  messages.push({ role: "user", content: `[${reasoning}] → ${out.observation}` });
26393
26565
  }
26394
- return { edits, changedFiles: [...changed], summary: "adım limiti", steps: MAX_STEPS, stoppedReason: "cap" };
26566
+ return {
26567
+ edits,
26568
+ changedFiles: [...changed],
26569
+ summary: "adım limiti",
26570
+ steps: MAX_STEPS,
26571
+ stoppedReason: "cap"
26572
+ };
26395
26573
  }
26396
26574
  function restoreSnapshots(cwd, snapshots) {
26397
26575
  for (const [rel, content] of snapshots) {
26398
- writeFileSync7(join10(cwd, rel), content, "utf-8");
26576
+ writeFileSync7(resolveInCwd(rel, cwd), content, "utf-8");
26399
26577
  }
26400
26578
  }
26401
26579
  function unifiedDiff(oldStr, newStr, file, context = 2) {
@@ -26427,7 +26605,7 @@ function unifiedDiff(oldStr, newStr, file, context = 2) {
26427
26605
  function formatWireDiff(cwd, snapshots) {
26428
26606
  const blocks = [];
26429
26607
  for (const [rel, oldContent] of snapshots) {
26430
- const abs = join10(cwd, rel);
26608
+ const abs = resolveInCwd(rel, cwd);
26431
26609
  const cur = existsSync8(abs) ? readFileSync9(abs, "utf-8") : "";
26432
26610
  if (cur !== oldContent)
26433
26611
  blocks.push(unifiedDiff(oldContent, cur, rel));
@@ -26453,16 +26631,16 @@ var FRAMEWORKS = [
26453
26631
  "node-server"
26454
26632
  ];
26455
26633
  function bail() {
26456
- p3.cancel("İptal edildi.");
26634
+ p4.cancel("İptal edildi.");
26457
26635
  process.exit(0);
26458
26636
  }
26459
26637
  async function runWizard(opts) {
26460
26638
  if (process.stdout.isTTY)
26461
26639
  process.stdout.write(banner());
26462
- p3.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26640
+ p4.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26463
26641
  const TOTAL = 6;
26464
26642
  const phase = (n2, label) => {
26465
- p3.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26643
+ p4.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26466
26644
  };
26467
26645
  phase(1, "Kimlik doğrulama");
26468
26646
  const auth = await ensureAuth({
@@ -26479,18 +26657,18 @@ async function runWizard(opts) {
26479
26657
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
26480
26658
  framework = opts.framework;
26481
26659
  } else if (!opts.yes) {
26482
- const ok = await p3.confirm({
26660
+ const ok = await p4.confirm({
26483
26661
  message: `Saptanan: ${detected.framework} (${detected.packageManager}). Doğru mu?`
26484
26662
  });
26485
- if (p3.isCancel(ok))
26663
+ if (p4.isCancel(ok))
26486
26664
  bail();
26487
26665
  if (!ok) {
26488
- const choice = await p3.select({
26666
+ const choice = await p4.select({
26489
26667
  message: "Framework seç",
26490
26668
  options: FRAMEWORKS.map((f3) => ({ value: f3, label: f3 })),
26491
26669
  initialValue: detected.framework
26492
26670
  });
26493
- if (p3.isCancel(choice))
26671
+ if (p4.isCancel(choice))
26494
26672
  bail();
26495
26673
  framework = choice;
26496
26674
  }
@@ -26498,9 +26676,10 @@ async function runWizard(opts) {
26498
26676
  const project = { ...detected, framework };
26499
26677
  const plan = buildInstallPlan(project, { writeKey, workspaceId });
26500
26678
  const isNode = plan.sdk === "@gurulu/node";
26679
+ const authed = Boolean(auth.apiKey);
26501
26680
  let approvedEvents = [];
26502
26681
  let identifyHint = null;
26503
- if (!opts.noAi && detected.hasPackageJson) {
26682
+ if (!opts.noAi && authed && detected.hasPackageJson) {
26504
26683
  const ctx = gatherContext({ cwd: opts.cwd });
26505
26684
  const aiPlan = await fetchPlan(client, ctx, { framework });
26506
26685
  if (aiPlan) {
@@ -26509,17 +26688,21 @@ async function runWizard(opts) {
26509
26688
  if (approved)
26510
26689
  approvedEvents = approved;
26511
26690
  } else {
26512
- p3.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26691
+ p4.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26513
26692
  }
26514
26693
  }
26694
+ let featuresResult = { selected: [], registered: false };
26695
+ if (authed) {
26696
+ featuresResult = await runFeatures(client, project, { yes: opts.yes, authed });
26697
+ }
26515
26698
  phase(4, "SDK kurulumu");
26516
26699
  let installed = false;
26517
26700
  if (opts.noInstall) {
26518
- p3.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26701
+ p4.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26519
26702
  } else if (!detected.hasPackageJson) {
26520
- p3.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26703
+ p4.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26521
26704
  } else {
26522
- const s2 = p3.spinner();
26705
+ const s2 = p4.spinner();
26523
26706
  s2.start(`${plan.sdk} kuruluyor (${plan.installCommand})…`);
26524
26707
  const res = await execInstall(plan, { cwd: opts.cwd });
26525
26708
  if (res.ok) {
@@ -26540,10 +26723,15 @@ async function runWizard(opts) {
26540
26723
  const envFile = isNode ? ".env" : ".env.local";
26541
26724
  const vars = [];
26542
26725
  for (const k2 of plan.envKeys) {
26543
- if (k2.key.endsWith("_WORKSPACE"))
26726
+ if (k2.key.endsWith("_WORKSPACE")) {
26544
26727
  vars.push({ key: k2.key, value: writeKey });
26545
- else if (k2.key === "GURULU_SECRET_KEY")
26546
- vars.push({ key: k2.key, value: auth.apiKey });
26728
+ } else if (k2.key === "GURULU_SECRET_KEY") {
26729
+ if (auth.apiKey.startsWith("sk_")) {
26730
+ vars.push({ key: k2.key, value: auth.apiKey });
26731
+ } else {
26732
+ p4.log.warn("GURULU_SECRET_KEY atlandı (login token workspace sk_ anahtarı değil). Dashboard → Settings → API Keys'ten server key oluşturup .env'e elle ekle.");
26733
+ }
26734
+ }
26547
26735
  }
26548
26736
  const envRes = vars.length > 0 ? writeEnvFile({ cwd: opts.cwd, file: envFile, vars }) : null;
26549
26737
  writeProjectScaffold(opts.cwd, {
@@ -26561,7 +26749,7 @@ async function runWizard(opts) {
26561
26749
  }
26562
26750
  let registeredCount = 0;
26563
26751
  if (approvedEvents.length > 0) {
26564
- const s2 = p3.spinner();
26752
+ const s2 = p4.spinner();
26565
26753
  s2.start("Yeni eventler registry kuyruguna oneriliyor…");
26566
26754
  const res = await registerNewEvents(client, approvedEvents);
26567
26755
  registeredCount = res.registered.length;
@@ -26570,29 +26758,29 @@ async function runWizard(opts) {
26570
26758
  let wiredCount = 0;
26571
26759
  if (approvedEvents.length > 0) {
26572
26760
  if (opts.noAi) {
26573
- p3.log.message(`Capture — şu çağrıları ekle:
26761
+ p4.log.message(`Capture — şu çağrıları ekle:
26574
26762
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26575
26763
  } else {
26576
26764
  const wireFiles = gatherContext({ cwd: opts.cwd }).files.map((f3) => f3.path);
26577
26765
  const snapshots = new Map;
26578
- const s2 = p3.spinner();
26766
+ const s2 = p4.spinner();
26579
26767
  s2.start("AI capture wiring (kod düzenleniyor)…");
26580
26768
  const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles }, snapshots);
26581
26769
  s2.stop(`wire: ${outcome.changedFiles.length} dosya / ${outcome.steps} adım (${outcome.stoppedReason})`);
26582
26770
  if (outcome.changedFiles.length > 0) {
26583
- p3.log.message(formatWireDiff(opts.cwd, snapshots));
26584
- const keep = opts.yes ? true : await p3.confirm({
26771
+ p4.log.message(formatWireDiff(opts.cwd, snapshots));
26772
+ const keep = opts.yes ? true : await p4.confirm({
26585
26773
  message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?`
26586
26774
  });
26587
- if (p3.isCancel(keep) || !keep) {
26775
+ if (p4.isCancel(keep) || !keep) {
26588
26776
  restoreSnapshots(opts.cwd, snapshots);
26589
- p3.log.info("Wire geri alındı — capture snippet rehberi:");
26590
- p3.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26777
+ p4.log.info("Wire geri alındı — capture snippet rehberi:");
26778
+ p4.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26591
26779
  } else {
26592
26780
  wiredCount = outcome.changedFiles.length;
26593
26781
  }
26594
26782
  } else {
26595
- p3.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26783
+ p4.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26596
26784
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26597
26785
  }
26598
26786
  }
@@ -26611,15 +26799,18 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26611
26799
  if (wiredCount > 0)
26612
26800
  lines.push(`✓ ${wiredCount} dosya AI ile capture wire edildi`);
26613
26801
  }
26802
+ if (featuresResult.registered && featuresResult.selected.length > 0) {
26803
+ lines.push(`✓ özellikler: ${featuresResult.selected.map((f3) => f3.key).join(", ")} kuruldu`);
26804
+ }
26614
26805
  lines.push(`✓ .gurulu/config.json${pulled ? " + registry pull" : ""}`);
26615
- p3.note(lines.join(`
26806
+ p4.note(lines.join(`
26616
26807
  `), c3.neon("✦ Değişiklikler"));
26617
26808
  if (inj.strategy !== "prepend-entry" || inj.reason === "no-entry") {
26618
- p3.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26809
+ p4.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26619
26810
  if (inj.strategy === "manual")
26620
- p3.log.message(plan.initSnippet);
26811
+ p4.log.message(plan.initSnippet);
26621
26812
  }
26622
- p3.outro(`${c3.neon("\uD83E\uDD89 Hazır!")} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
26813
+ p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
26623
26814
  }
26624
26815
  async function resolveWorkspace(client, authWorkspaceId, opts) {
26625
26816
  if (opts.writeKey) {
@@ -26642,7 +26833,7 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26642
26833
  selected = CREATE;
26643
26834
  } else {
26644
26835
  const initial = list.find((w2) => w2.workspace_id === authWorkspaceId)?.workspace_id ?? list[0]?.workspace_id ?? CREATE;
26645
- const choice = await p3.select({
26836
+ const choice = await p4.select({
26646
26837
  message: "Workspace seç",
26647
26838
  options: [
26648
26839
  ...list.map((w2) => ({ value: w2.workspace_id, label: w2.name, hint: w2.slug })),
@@ -26650,26 +26841,26 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26650
26841
  ],
26651
26842
  initialValue: initial
26652
26843
  });
26653
- if (p3.isCancel(choice))
26844
+ if (p4.isCancel(choice))
26654
26845
  bail();
26655
26846
  selected = choice;
26656
26847
  }
26657
26848
  if (selected === CREATE) {
26658
- const name = await p3.text({
26849
+ const name = await p4.text({
26659
26850
  message: "Workspace adı",
26660
26851
  placeholder: "My App",
26661
26852
  validate: (v2) => v2.trim() ? undefined : "gerekli"
26662
26853
  });
26663
- if (p3.isCancel(name))
26854
+ if (p4.isCancel(name))
26664
26855
  bail();
26665
- const domain = await p3.text({
26856
+ const domain = await p4.text({
26666
26857
  message: "Domain",
26667
26858
  placeholder: "example.com",
26668
26859
  validate: (v2) => v2.trim().length >= 3 ? undefined : "geçerli domain gerekli"
26669
26860
  });
26670
- if (p3.isCancel(domain))
26861
+ if (p4.isCancel(domain))
26671
26862
  bail();
26672
- const s2 = p3.spinner();
26863
+ const s2 = p4.spinner();
26673
26864
  s2.start("Workspace oluşturuluyor…");
26674
26865
  const created = await client.createWorkspace({
26675
26866
  name: String(name).trim(),
@@ -26729,11 +26920,19 @@ var wizardArgs = {
26729
26920
  framework: { type: "string", description: "Framework override (auto-detect yerine)" },
26730
26921
  install: { type: "boolean", description: "SDK install (--no-install ile atla)", default: true },
26731
26922
  pull: { type: "boolean", description: "İlk registry pull (--no-pull ile atla)", default: true },
26732
- ai: { type: "boolean", description: "AI Plan/wire fazı (--no-ai ile atla → floor)", default: true },
26923
+ ai: {
26924
+ type: "boolean",
26925
+ description: "AI Plan/wire fazı (--no-ai ile atla → floor)",
26926
+ default: true
26927
+ },
26733
26928
  yes: { type: "boolean", description: "Onayları otomatik geç" },
26734
26929
  ci: { type: "boolean", description: "Non-interaktif (api-key + workspace zorunlu)" }
26735
26930
  };
26736
26931
  async function runWizardFromArgs(args) {
26932
+ if (args.ci && (!args["api-key"] || !args.workspace)) {
26933
+ console.error("[gurulu] --ci için --api-key <sk_...> ve --workspace <uuid> zorunlu (non-interaktif kurulum).");
26934
+ process.exit(1);
26935
+ }
26737
26936
  const opts = {
26738
26937
  cwd: process.cwd(),
26739
26938
  noInstall: args.install === false,
@@ -26767,7 +26966,7 @@ var initCmd = defineCommand({
26767
26966
  // src/lib/editor-mcp.ts
26768
26967
  import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
26769
26968
  import { homedir as homedir2 } from "node:os";
26770
- import { dirname as dirname4, join as join11 } from "node:path";
26969
+ import { dirname as dirname4, join as join9 } from "node:path";
26771
26970
  var SERVER_NAME = "gurulu";
26772
26971
  function buildMcpServerConfig(creds) {
26773
26972
  return {
@@ -26781,14 +26980,14 @@ function buildMcpServerConfig(creds) {
26781
26980
  };
26782
26981
  }
26783
26982
  var EDITORS = {
26784
- cursor: { path: () => join11(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26785
- claude: { path: () => join11(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26983
+ cursor: { path: () => join9(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26984
+ claude: { path: () => join9(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26786
26985
  windsurf: {
26787
- path: () => join11(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26986
+ path: () => join9(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26788
26987
  key: "mcpServers",
26789
26988
  label: "Windsurf"
26790
26989
  },
26791
- vscode: { path: (cwd) => join11(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26990
+ vscode: { path: (cwd) => join9(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26792
26991
  };
26793
26992
  function mergeMcpConfig(existing, serverConfig, key) {
26794
26993
  const servers = existing[key] ?? {};
@@ -26984,9 +27183,9 @@ var pushCmd = defineCommand({
26984
27183
  // src/commands/uninstall.ts
26985
27184
  import { execFile as execFile2 } from "node:child_process";
26986
27185
  import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
26987
- import { join as join12 } from "node:path";
27186
+ import { join as join10 } from "node:path";
26988
27187
  import { promisify as promisify2 } from "node:util";
26989
- import * as p4 from "@clack/prompts";
27188
+ import * as p5 from "@clack/prompts";
26990
27189
  var pexec2 = promisify2(execFile2);
26991
27190
  var ENV_FILES = [".env.local", ".env"];
26992
27191
  var GURULU_PREFIXES = ["GURULU_", "NEXT_PUBLIC_GURULU", "VITE_GURULU"];
@@ -27013,23 +27212,23 @@ var uninstallCmd = defineCommand({
27013
27212
  const config = readProjectConfig(cwd);
27014
27213
  const detected = detectProject(cwd);
27015
27214
  const pkg = config?.sdk_preference === "node" ? "@gurulu/node" : "@gurulu/web";
27016
- p4.intro("Gurulu uninstall");
27017
- p4.note([
27215
+ p5.intro("Gurulu uninstall");
27216
+ p5.note([
27018
27217
  `• SDK kaldır: ${pkg} (${detected.packageManager})`,
27019
27218
  `• env temizle: ${ENV_FILES.join(", ")} (GURULU_* anahtarları)`,
27020
27219
  "• .gurulu/ dizinini sil"
27021
27220
  ].join(`
27022
27221
  `), "Şunlar yapılacak");
27023
27222
  if (!args.yes) {
27024
- const ok = await p4.confirm({ message: "Devam edilsin mi?" });
27025
- if (p4.isCancel(ok) || !ok) {
27026
- p4.cancel("İptal.");
27223
+ const ok = await p5.confirm({ message: "Devam edilsin mi?" });
27224
+ if (p5.isCancel(ok) || !ok) {
27225
+ p5.cancel("İptal.");
27027
27226
  process.exit(0);
27028
27227
  }
27029
27228
  }
27030
27229
  if (!args["no-deps"] && detected.hasPackageJson) {
27031
27230
  const { bin, args: a2 } = removeCmd(detected.packageManager, pkg);
27032
- const s2 = p4.spinner();
27231
+ const s2 = p5.spinner();
27033
27232
  s2.start(`${pkg} kaldırılıyor…`);
27034
27233
  try {
27035
27234
  await pexec2(bin, a2, { cwd, timeout: 120000 });
@@ -27040,7 +27239,7 @@ var uninstallCmd = defineCommand({
27040
27239
  }
27041
27240
  const cleaned = [];
27042
27241
  for (const f3 of ENV_FILES) {
27043
- const abs = join12(cwd, f3);
27242
+ const abs = join10(cwd, f3);
27044
27243
  if (!existsSync11(abs))
27045
27244
  continue;
27046
27245
  const { content, removed } = removeEnvKeys(readFileSync11(abs, "utf-8"), GURULU_PREFIXES);
@@ -27049,15 +27248,15 @@ var uninstallCmd = defineCommand({
27049
27248
  cleaned.push(`${f3} (-${removed.length})`);
27050
27249
  }
27051
27250
  }
27052
- const guruluDir = join12(cwd, ".gurulu");
27251
+ const guruluDir = join10(cwd, ".gurulu");
27053
27252
  if (existsSync11(guruluDir))
27054
27253
  rmSync(guruluDir, { recursive: true, force: true });
27055
- p4.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
27254
+ p5.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
27056
27255
  }
27057
27256
  });
27058
27257
 
27059
27258
  // src/index.ts
27060
- var VERSION = "1.2.1";
27259
+ var VERSION = "1.2.2";
27061
27260
  var mainCmd = defineCommand({
27062
27261
  meta: {
27063
27262
  name: "gurulu",