@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/index.js CHANGED
@@ -24106,7 +24106,7 @@ class ApiClient {
24106
24106
  }
24107
24107
 
24108
24108
  // src/lib/config.ts
24109
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24109
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24110
24110
  import { homedir } from "node:os";
24111
24111
  import { dirname, join } from "node:path";
24112
24112
  var DEFAULT_ENDPOINT = process.env.GURULU_ENDPOINT ?? "https://api.gurulu.io";
@@ -24163,10 +24163,9 @@ function writeGlobalCredentials(creds) {
24163
24163
  const path = globalCredentialsPath();
24164
24164
  ensureDir(path);
24165
24165
  writeFileSync(path, `${JSON.stringify(creds, null, 2)}
24166
- `, "utf-8");
24166
+ `, { encoding: "utf-8", mode: 384 });
24167
24167
  try {
24168
- const fs = __require("node:fs");
24169
- fs.chmodSync(path, 384);
24168
+ chmodSync(path, 384);
24170
24169
  } catch {}
24171
24170
  }
24172
24171
  function findCredentialForWorkspace(workspaceId) {
@@ -24696,7 +24695,7 @@ function sleep(ms) {
24696
24695
  // src/wizard/run.ts
24697
24696
  import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync8 } from "node:fs";
24698
24697
  import { dirname as dirname3 } from "node:path";
24699
- import * as p3 from "@clack/prompts";
24698
+ import * as p4 from "@clack/prompts";
24700
24699
 
24701
24700
  // src/commands/pull.ts
24702
24701
  import { writeFileSync as writeFileSync3 } from "node:fs";
@@ -24840,6 +24839,17 @@ var pullCmd = defineCommand({
24840
24839
  // src/lib/detect.ts
24841
24840
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
24842
24841
  import { dirname as dirname2, join as join5, parse } from "node:path";
24842
+ var LLM_DEP_MAP = {
24843
+ openai: "openai",
24844
+ "@anthropic-ai/sdk": "anthropic",
24845
+ "@langchain/core": "langchain",
24846
+ langchain: "langchain",
24847
+ "@google/generative-ai": "google-genai",
24848
+ ai: "vercel-ai",
24849
+ "cohere-ai": "cohere",
24850
+ "@mistralai/mistralai": "mistral",
24851
+ ollama: "ollama"
24852
+ };
24843
24853
  function readPackageJson(dir) {
24844
24854
  const p = join5(dir, "package.json");
24845
24855
  if (!existsSync4(p))
@@ -24925,6 +24935,14 @@ function frameworkRuntime(fw) {
24925
24935
  return "unknown";
24926
24936
  }
24927
24937
  }
24938
+ function detectLlmFrameworks(pkg) {
24939
+ const found = new Set;
24940
+ for (const [dep, fw] of Object.entries(LLM_DEP_MAP)) {
24941
+ if (hasDep(pkg, dep))
24942
+ found.add(fw);
24943
+ }
24944
+ return [...found];
24945
+ }
24928
24946
  function detectProject(dir) {
24929
24947
  const pkg = readPackageJson(dir);
24930
24948
  const framework = detectFramework(pkg, dir);
@@ -24934,7 +24952,8 @@ function detectProject(dir) {
24934
24952
  framework,
24935
24953
  runtime: frameworkRuntime(framework),
24936
24954
  packageManager: detectPackageManager(dir),
24937
- packageJson: pkg
24955
+ packageJson: pkg,
24956
+ llmFrameworks: detectLlmFrameworks(pkg)
24938
24957
  };
24939
24958
  }
24940
24959
 
@@ -25711,8 +25730,77 @@ function gatherContext(opts) {
25711
25730
  return { files, capped, totalBytes };
25712
25731
  }
25713
25732
 
25714
- // src/wizard/plan.ts
25733
+ // src/wizard/features.ts
25715
25734
  import * as p2 from "@clack/prompts";
25735
+ function availableFeatures(detected) {
25736
+ const isBrowser = frameworkRuntime(detected.framework) !== "node";
25737
+ const feats = [];
25738
+ feats.push({
25739
+ key: "error",
25740
+ label: "Hata takibi",
25741
+ detail: "Tarayıcı JS hatalarını otomatik yakala (js_error)",
25742
+ enableHint: isBrowser ? "gurulu.init({ ..., autocapture: { js_error: true } })" : "Sunucu hatalarında: gurulu.track('js_error', { message, error_type })",
25743
+ recommended: isBrowser
25744
+ });
25745
+ if (detected.llmFrameworks.length > 0) {
25746
+ feats.push({
25747
+ key: "llm",
25748
+ label: `LLM analizi (${detected.llmFrameworks.join(", ")} tespit edildi)`,
25749
+ detail: "AI çağrı maliyeti/latency/hata/model performansı",
25750
+ enableHint: "import { wrapOpenAI } from '@gurulu/node'; const client = wrapOpenAI(openai);",
25751
+ recommended: true
25752
+ });
25753
+ }
25754
+ if (isBrowser) {
25755
+ feats.push({
25756
+ key: "activation",
25757
+ label: "Activation (popup · tur · A/B test · kişiselleştirme)",
25758
+ detail: "Dashboard'dan yayınla, SDK render etsin (Action Layer)",
25759
+ enableHint: "import { runActivation } from '@gurulu/web/activate'; runActivation(gurulu);",
25760
+ recommended: false
25761
+ });
25762
+ }
25763
+ return feats;
25764
+ }
25765
+ async function runFeatures(client, detected, opts) {
25766
+ const avail = availableFeatures(detected);
25767
+ if (avail.length === 0 || !opts.authed)
25768
+ return { selected: [], registered: false };
25769
+ let selectedKeys;
25770
+ if (opts.yes) {
25771
+ selectedKeys = avail.filter((f3) => f3.recommended).map((f3) => f3.key);
25772
+ } else {
25773
+ const choice = await p2.multiselect({
25774
+ message: "Hangi özellikleri kuralım? (event'ler registry'ye otomatik eklenir)",
25775
+ options: avail.map((f3) => ({ value: f3.key, label: f3.label, hint: f3.detail })),
25776
+ initialValues: avail.filter((f3) => f3.recommended).map((f3) => f3.key),
25777
+ required: false
25778
+ });
25779
+ if (p2.isCancel(choice))
25780
+ return { selected: [], registered: false };
25781
+ selectedKeys = choice;
25782
+ }
25783
+ if (selectedKeys.length === 0)
25784
+ return { selected: [], registered: false };
25785
+ const selected = avail.filter((f3) => selectedKeys.includes(f3.key));
25786
+ const s2 = p2.spinner();
25787
+ s2.start("Özellikler registry'ye kuruluyor…");
25788
+ try {
25789
+ await client.post("/features/register", { features: selectedKeys });
25790
+ s2.stop(c3.neon(`✓ ${selected.length} özellik kuruldu`));
25791
+ } catch {
25792
+ s2.stop("Özellik kurulumu atlandı (gateway hatası) — sonra: gurulu doctor", 1);
25793
+ return { selected, registered: false };
25794
+ }
25795
+ p2.note(selected.map((f3) => `${c3.bold(f3.label)}
25796
+ ${c3.dim(f3.enableHint)}`).join(`
25797
+
25798
+ `), "Etkinleştirme");
25799
+ return { selected, registered: true };
25800
+ }
25801
+
25802
+ // src/wizard/plan.ts
25803
+ import * as p3 from "@clack/prompts";
25716
25804
  async function fetchPlan(client, context, input) {
25717
25805
  if (context.files.length === 0)
25718
25806
  return null;
@@ -25757,28 +25845,29 @@ function formatPlan(plan) {
25757
25845
  `).trimEnd();
25758
25846
  }
25759
25847
  async function renderPlan(plan) {
25760
- p2.note(formatPlan(plan), plan.summary);
25848
+ p3.note(formatPlan(plan), plan.summary);
25761
25849
  const newCount = plan.events.filter((e2) => !e2.existing).length;
25762
- const ok = await p2.confirm({
25850
+ const ok = await p3.confirm({
25763
25851
  message: `${plan.events.length} event (${newCount} yeni) — bu planı uygula?`
25764
25852
  });
25765
- if (p2.isCancel(ok) || !ok)
25853
+ if (p3.isCancel(ok) || !ok)
25766
25854
  return null;
25767
25855
  return plan.events;
25768
25856
  }
25769
25857
 
25770
25858
  // src/wizard/wire.ts
25771
25859
  import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
25772
- import { join as join10 } from "node:path";
25773
25860
 
25774
25861
  // src/wizard/agent.ts
25775
25862
  import { execFile } from "node:child_process";
25776
25863
  import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
25777
- import { join as join9 } from "node:path";
25778
25864
  import { promisify } from "node:util";
25779
25865
 
25780
25866
  // src/wizard/guard.ts
25781
25867
  import { basename, isAbsolute, relative as relative2, resolve } from "node:path";
25868
+ function resolveInCwd(p4, cwd) {
25869
+ return isAbsolute(p4) ? p4 : resolve(cwd, p4);
25870
+ }
25782
25871
  function isAdditiveEdit(find, replace) {
25783
25872
  if (find.length === 0)
25784
25873
  return { ok: false, reason: "empty find" };
@@ -25789,22 +25878,45 @@ function isAdditiveEdit(find, replace) {
25789
25878
  return { ok: false, reason: "no-op edit (replace === find)" };
25790
25879
  return { ok: true };
25791
25880
  }
25792
- var BASH_ALLOW_BINS = new Set([
25793
- "bun",
25794
- "bunx",
25795
- "npm",
25796
- "npx",
25797
- "pnpm",
25798
- "yarn",
25881
+ var CHECKER_BINS = new Set([
25799
25882
  "tsc",
25800
25883
  "tsgo",
25884
+ "vue-tsc",
25885
+ "svelte-check",
25801
25886
  "biome",
25802
25887
  "eslint",
25803
25888
  "prettier",
25804
- "vue-tsc",
25805
- "svelte-check",
25806
25889
  "astro"
25807
25890
  ]);
25891
+ var RUNNER_BINS = new Set(["bun", "npm", "pnpm", "yarn"]);
25892
+ var DENY_SUBCMDS = new Set([
25893
+ "install",
25894
+ "i",
25895
+ "add",
25896
+ "remove",
25897
+ "rm",
25898
+ "uninstall",
25899
+ "un",
25900
+ "ci",
25901
+ "dlx",
25902
+ "x",
25903
+ "exec",
25904
+ "create",
25905
+ "init",
25906
+ "up",
25907
+ "update",
25908
+ "upgrade",
25909
+ "link",
25910
+ "unlink",
25911
+ "global",
25912
+ "dedupe",
25913
+ "audit",
25914
+ "publish",
25915
+ "pack",
25916
+ "import",
25917
+ "config"
25918
+ ]);
25919
+ var SCRIPT_NAME = /^[a-z0-9][a-z0-9:._-]*$/i;
25808
25920
  var BASH_DENY = /[;&|`$<>]|\.\.\/|\b(rm|curl|wget|sudo|chmod|chown|mv|dd|kill|eval|sh|bash|node|python)\b/;
25809
25921
  function isAllowedBash(cmd) {
25810
25922
  const t2 = cmd.trim();
@@ -25812,13 +25924,27 @@ function isAllowedBash(cmd) {
25812
25924
  return { ok: false, reason: "empty cmd" };
25813
25925
  if (BASH_DENY.test(t2))
25814
25926
  return { ok: false, reason: "yasak operatör/binary" };
25815
- const bin = t2.split(/\s+/)[0] ?? "";
25816
- if (!BASH_ALLOW_BINS.has(bin))
25817
- return { ok: false, reason: `allowlist dışı binary: ${bin}` };
25818
- return { ok: true };
25927
+ const tokens = t2.split(/\s+/);
25928
+ const bin = tokens[0] ?? "";
25929
+ if (CHECKER_BINS.has(bin))
25930
+ return { ok: true };
25931
+ if (RUNNER_BINS.has(bin)) {
25932
+ const sub = tokens[1] ?? "";
25933
+ if (sub === "run") {
25934
+ const script = tokens[2] ?? "";
25935
+ if (!SCRIPT_NAME.test(script))
25936
+ return { ok: false, reason: `geçersiz script adı: ${script}` };
25937
+ return { ok: true };
25938
+ }
25939
+ if (bin === "yarn" && sub && !DENY_SUBCMDS.has(sub) && SCRIPT_NAME.test(sub)) {
25940
+ return { ok: true };
25941
+ }
25942
+ return { ok: false, reason: `yasak alt-komut (sadece 'run' izinli): ${bin} ${sub}` };
25943
+ }
25944
+ return { ok: false, reason: `allowlist dışı binary: ${bin}` };
25819
25945
  }
25820
- function isAllowedPath(p3, cwd) {
25821
- const abs = isAbsolute(p3) ? p3 : resolve(cwd, p3);
25946
+ function isAllowedPath(p4, cwd) {
25947
+ const abs = isAbsolute(p4) ? p4 : resolve(cwd, p4);
25822
25948
  const rel = relative2(cwd, abs);
25823
25949
  if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
25824
25950
  return { ok: false, reason: "cwd dışı yol" };
@@ -25835,6 +25961,31 @@ function hasPromptInjection(content) {
25835
25961
  // src/wizard/agent.ts
25836
25962
  var MAX_OBS = 4000;
25837
25963
  var pexec = promisify(execFile);
25964
+ function safeEnv() {
25965
+ const keep = [
25966
+ "PATH",
25967
+ "HOME",
25968
+ "USERPROFILE",
25969
+ "TMPDIR",
25970
+ "TEMP",
25971
+ "TMP",
25972
+ "LANG",
25973
+ "LC_ALL",
25974
+ "TERM",
25975
+ "SHELL",
25976
+ "NODE_ENV",
25977
+ "PATHEXT",
25978
+ "SystemRoot",
25979
+ "ComSpec"
25980
+ ];
25981
+ const env2 = {};
25982
+ for (const k2 of keep) {
25983
+ const v2 = process.env[k2];
25984
+ if (v2 !== undefined)
25985
+ env2[k2] = v2;
25986
+ }
25987
+ return env2;
25988
+ }
25838
25989
  async function defaultRunBash(cmd, cwd) {
25839
25990
  const parts = cmd.trim().split(/\s+/);
25840
25991
  const bin = parts[0] ?? "";
@@ -25842,7 +25993,8 @@ async function defaultRunBash(cmd, cwd) {
25842
25993
  const { stdout: stdout2, stderr } = await pexec(bin, parts.slice(1), {
25843
25994
  cwd,
25844
25995
  timeout: 120000,
25845
- maxBuffer: 4 * 1024 * 1024
25996
+ maxBuffer: 4 * 1024 * 1024,
25997
+ env: safeEnv()
25846
25998
  });
25847
25999
  return { stdout: stdout2, stderr };
25848
26000
  } catch (e2) {
@@ -25857,7 +26009,7 @@ async function executeTool(action, deps) {
25857
26009
  const g3 = isAllowedPath(action.path, cwd);
25858
26010
  if (!g3.ok)
25859
26011
  return { ok: false, observation: `read reddedildi: ${g3.reason}` };
25860
- const abs = join9(cwd, action.path);
26012
+ const abs = resolveInCwd(action.path, cwd);
25861
26013
  if (!existsSync7(abs))
25862
26014
  return { ok: false, observation: `dosya yok: ${action.path}` };
25863
26015
  let content = readFileSync8(abs, "utf-8");
@@ -25875,7 +26027,7 @@ async function executeTool(action, deps) {
25875
26027
  const ga = isAdditiveEdit(action.find, action.replace);
25876
26028
  if (!ga.ok)
25877
26029
  return { ok: false, observation: `edit reddedildi: ${ga.reason}` };
25878
- const abs = join9(cwd, action.path);
26030
+ const abs = resolveInCwd(action.path, cwd);
25879
26031
  if (!existsSync7(abs))
25880
26032
  return { ok: false, observation: `dosya yok: ${action.path}` };
25881
26033
  const src2 = readFileSync8(abs, "utf-8");
@@ -25911,7 +26063,9 @@ function buildWireSystemPrompt() {
25911
26063
  "- `find` must be an EXACT, UNIQUE snippet copied from a file you have read (read before edit).",
25912
26064
  "- Use the exact provided event_key (snake_case). Wire gurulu.track(...) at the right place and",
25913
26065
  " gurulu.identify(...) at the auth point if given.",
25914
- "- bash only for verification (typecheck/build/lint) no install, no other commands.",
26066
+ "- bash is ONLY for verification: the project's own scripts (`npm run typecheck`, `bun run build`,",
26067
+ " `pnpm run lint`) or a checker binary (`tsc --noEmit`, `biome check`, `eslint .`). Installing or",
26068
+ " fetching packages (`npm install`, `npx`, `bunx`, `pnpm dlx`, `add`, `exec`) is REJECTED by the guard.",
25915
26069
  "- After wiring, run the project typecheck/build to verify; if it breaks, fix additively.",
25916
26070
  "- When all events are wired and verify passes, emit done{summary}. Keep edits minimal."
25917
26071
  ].join(`
@@ -25945,17 +26099,35 @@ async function runWireAgent(client, input, snapshots) {
25945
26099
  try {
25946
26100
  res = await client.agentStep({ messages, first: i2 === 0 });
25947
26101
  } catch {
25948
- return { edits, changedFiles: [...changed], summary: "gateway hata", steps: i2, stoppedReason: "error" };
26102
+ return {
26103
+ edits,
26104
+ changedFiles: [...changed],
26105
+ summary: "gateway hata",
26106
+ steps: i2,
26107
+ stoppedReason: "error"
26108
+ };
25949
26109
  }
25950
26110
  if (res.status === "stub") {
25951
- return { edits, changedFiles: [...changed], summary: "AI kullanılamadı", steps: i2, stoppedReason: "stub" };
26111
+ return {
26112
+ edits,
26113
+ changedFiles: [...changed],
26114
+ summary: "AI kullanılamadı",
26115
+ steps: i2,
26116
+ stoppedReason: "stub"
26117
+ };
25952
26118
  }
25953
26119
  const { action, reasoning } = res.step;
25954
26120
  if (action.tool === "done") {
25955
- return { edits, changedFiles: [...changed], summary: action.summary, steps: i2, stoppedReason: "done" };
26121
+ return {
26122
+ edits,
26123
+ changedFiles: [...changed],
26124
+ summary: action.summary,
26125
+ steps: i2,
26126
+ stoppedReason: "done"
26127
+ };
25956
26128
  }
25957
26129
  if (action.tool === "edit") {
25958
- const abs = join10(input.cwd, action.path);
26130
+ const abs = resolveInCwd(action.path, input.cwd);
25959
26131
  if (!snapshots.has(action.path) && existsSync8(abs)) {
25960
26132
  snapshots.set(action.path, readFileSync9(abs, "utf-8"));
25961
26133
  }
@@ -25968,11 +26140,17 @@ async function runWireAgent(client, input, snapshots) {
25968
26140
  messages.push({ role: "assistant", content: JSON.stringify(res.step) });
25969
26141
  messages.push({ role: "user", content: `[${reasoning}] → ${out.observation}` });
25970
26142
  }
25971
- return { edits, changedFiles: [...changed], summary: "adım limiti", steps: MAX_STEPS, stoppedReason: "cap" };
26143
+ return {
26144
+ edits,
26145
+ changedFiles: [...changed],
26146
+ summary: "adım limiti",
26147
+ steps: MAX_STEPS,
26148
+ stoppedReason: "cap"
26149
+ };
25972
26150
  }
25973
26151
  function restoreSnapshots(cwd, snapshots) {
25974
26152
  for (const [rel, content] of snapshots) {
25975
- writeFileSync7(join10(cwd, rel), content, "utf-8");
26153
+ writeFileSync7(resolveInCwd(rel, cwd), content, "utf-8");
25976
26154
  }
25977
26155
  }
25978
26156
  function unifiedDiff(oldStr, newStr, file, context = 2) {
@@ -26004,7 +26182,7 @@ function unifiedDiff(oldStr, newStr, file, context = 2) {
26004
26182
  function formatWireDiff(cwd, snapshots) {
26005
26183
  const blocks = [];
26006
26184
  for (const [rel, oldContent] of snapshots) {
26007
- const abs = join10(cwd, rel);
26185
+ const abs = resolveInCwd(rel, cwd);
26008
26186
  const cur = existsSync8(abs) ? readFileSync9(abs, "utf-8") : "";
26009
26187
  if (cur !== oldContent)
26010
26188
  blocks.push(unifiedDiff(oldContent, cur, rel));
@@ -26030,16 +26208,16 @@ var FRAMEWORKS = [
26030
26208
  "node-server"
26031
26209
  ];
26032
26210
  function bail() {
26033
- p3.cancel("İptal edildi.");
26211
+ p4.cancel("İptal edildi.");
26034
26212
  process.exit(0);
26035
26213
  }
26036
26214
  async function runWizard(opts) {
26037
26215
  if (process.stdout.isTTY)
26038
26216
  process.stdout.write(banner());
26039
- p3.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26217
+ p4.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26040
26218
  const TOTAL = 6;
26041
26219
  const phase = (n2, label) => {
26042
- p3.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26220
+ p4.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26043
26221
  };
26044
26222
  phase(1, "Kimlik doğrulama");
26045
26223
  const auth = await ensureAuth({
@@ -26056,18 +26234,18 @@ async function runWizard(opts) {
26056
26234
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
26057
26235
  framework = opts.framework;
26058
26236
  } else if (!opts.yes) {
26059
- const ok = await p3.confirm({
26237
+ const ok = await p4.confirm({
26060
26238
  message: `Saptanan: ${detected.framework} (${detected.packageManager}). Doğru mu?`
26061
26239
  });
26062
- if (p3.isCancel(ok))
26240
+ if (p4.isCancel(ok))
26063
26241
  bail();
26064
26242
  if (!ok) {
26065
- const choice = await p3.select({
26243
+ const choice = await p4.select({
26066
26244
  message: "Framework seç",
26067
26245
  options: FRAMEWORKS.map((f3) => ({ value: f3, label: f3 })),
26068
26246
  initialValue: detected.framework
26069
26247
  });
26070
- if (p3.isCancel(choice))
26248
+ if (p4.isCancel(choice))
26071
26249
  bail();
26072
26250
  framework = choice;
26073
26251
  }
@@ -26075,9 +26253,10 @@ async function runWizard(opts) {
26075
26253
  const project = { ...detected, framework };
26076
26254
  const plan = buildInstallPlan(project, { writeKey, workspaceId });
26077
26255
  const isNode = plan.sdk === "@gurulu/node";
26256
+ const authed = Boolean(auth.apiKey);
26078
26257
  let approvedEvents = [];
26079
26258
  let identifyHint = null;
26080
- if (!opts.noAi && detected.hasPackageJson) {
26259
+ if (!opts.noAi && authed && detected.hasPackageJson) {
26081
26260
  const ctx = gatherContext({ cwd: opts.cwd });
26082
26261
  const aiPlan = await fetchPlan(client, ctx, { framework });
26083
26262
  if (aiPlan) {
@@ -26086,17 +26265,21 @@ async function runWizard(opts) {
26086
26265
  if (approved)
26087
26266
  approvedEvents = approved;
26088
26267
  } else {
26089
- p3.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26268
+ p4.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26090
26269
  }
26091
26270
  }
26271
+ let featuresResult = { selected: [], registered: false };
26272
+ if (authed) {
26273
+ featuresResult = await runFeatures(client, project, { yes: opts.yes, authed });
26274
+ }
26092
26275
  phase(4, "SDK kurulumu");
26093
26276
  let installed = false;
26094
26277
  if (opts.noInstall) {
26095
- p3.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26278
+ p4.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26096
26279
  } else if (!detected.hasPackageJson) {
26097
- p3.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26280
+ p4.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26098
26281
  } else {
26099
- const s2 = p3.spinner();
26282
+ const s2 = p4.spinner();
26100
26283
  s2.start(`${plan.sdk} kuruluyor (${plan.installCommand})…`);
26101
26284
  const res = await execInstall(plan, { cwd: opts.cwd });
26102
26285
  if (res.ok) {
@@ -26117,10 +26300,15 @@ async function runWizard(opts) {
26117
26300
  const envFile = isNode ? ".env" : ".env.local";
26118
26301
  const vars = [];
26119
26302
  for (const k2 of plan.envKeys) {
26120
- if (k2.key.endsWith("_WORKSPACE"))
26303
+ if (k2.key.endsWith("_WORKSPACE")) {
26121
26304
  vars.push({ key: k2.key, value: writeKey });
26122
- else if (k2.key === "GURULU_SECRET_KEY")
26123
- vars.push({ key: k2.key, value: auth.apiKey });
26305
+ } else if (k2.key === "GURULU_SECRET_KEY") {
26306
+ if (auth.apiKey.startsWith("sk_")) {
26307
+ vars.push({ key: k2.key, value: auth.apiKey });
26308
+ } else {
26309
+ 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.");
26310
+ }
26311
+ }
26124
26312
  }
26125
26313
  const envRes = vars.length > 0 ? writeEnvFile({ cwd: opts.cwd, file: envFile, vars }) : null;
26126
26314
  writeProjectScaffold(opts.cwd, {
@@ -26138,7 +26326,7 @@ async function runWizard(opts) {
26138
26326
  }
26139
26327
  let registeredCount = 0;
26140
26328
  if (approvedEvents.length > 0) {
26141
- const s2 = p3.spinner();
26329
+ const s2 = p4.spinner();
26142
26330
  s2.start("Yeni eventler registry kuyruguna oneriliyor…");
26143
26331
  const res = await registerNewEvents(client, approvedEvents);
26144
26332
  registeredCount = res.registered.length;
@@ -26147,29 +26335,29 @@ async function runWizard(opts) {
26147
26335
  let wiredCount = 0;
26148
26336
  if (approvedEvents.length > 0) {
26149
26337
  if (opts.noAi) {
26150
- p3.log.message(`Capture — şu çağrıları ekle:
26338
+ p4.log.message(`Capture — şu çağrıları ekle:
26151
26339
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26152
26340
  } else {
26153
26341
  const wireFiles = gatherContext({ cwd: opts.cwd }).files.map((f3) => f3.path);
26154
26342
  const snapshots = new Map;
26155
- const s2 = p3.spinner();
26343
+ const s2 = p4.spinner();
26156
26344
  s2.start("AI capture wiring (kod düzenleniyor)…");
26157
26345
  const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles }, snapshots);
26158
26346
  s2.stop(`wire: ${outcome.changedFiles.length} dosya / ${outcome.steps} adım (${outcome.stoppedReason})`);
26159
26347
  if (outcome.changedFiles.length > 0) {
26160
- p3.log.message(formatWireDiff(opts.cwd, snapshots));
26161
- const keep = opts.yes ? true : await p3.confirm({
26348
+ p4.log.message(formatWireDiff(opts.cwd, snapshots));
26349
+ const keep = opts.yes ? true : await p4.confirm({
26162
26350
  message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?`
26163
26351
  });
26164
- if (p3.isCancel(keep) || !keep) {
26352
+ if (p4.isCancel(keep) || !keep) {
26165
26353
  restoreSnapshots(opts.cwd, snapshots);
26166
- p3.log.info("Wire geri alındı — capture snippet rehberi:");
26167
- p3.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26354
+ p4.log.info("Wire geri alındı — capture snippet rehberi:");
26355
+ p4.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26168
26356
  } else {
26169
26357
  wiredCount = outcome.changedFiles.length;
26170
26358
  }
26171
26359
  } else {
26172
- p3.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26360
+ p4.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26173
26361
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26174
26362
  }
26175
26363
  }
@@ -26188,15 +26376,18 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26188
26376
  if (wiredCount > 0)
26189
26377
  lines.push(`✓ ${wiredCount} dosya AI ile capture wire edildi`);
26190
26378
  }
26379
+ if (featuresResult.registered && featuresResult.selected.length > 0) {
26380
+ lines.push(`✓ özellikler: ${featuresResult.selected.map((f3) => f3.key).join(", ")} kuruldu`);
26381
+ }
26191
26382
  lines.push(`✓ .gurulu/config.json${pulled ? " + registry pull" : ""}`);
26192
- p3.note(lines.join(`
26383
+ p4.note(lines.join(`
26193
26384
  `), c3.neon("✦ Değişiklikler"));
26194
26385
  if (inj.strategy !== "prepend-entry" || inj.reason === "no-entry") {
26195
- p3.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26386
+ p4.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26196
26387
  if (inj.strategy === "manual")
26197
- p3.log.message(plan.initSnippet);
26388
+ p4.log.message(plan.initSnippet);
26198
26389
  }
26199
- 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")}`);
26390
+ 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")}`);
26200
26391
  }
26201
26392
  async function resolveWorkspace(client, authWorkspaceId, opts) {
26202
26393
  if (opts.writeKey) {
@@ -26219,7 +26410,7 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26219
26410
  selected = CREATE;
26220
26411
  } else {
26221
26412
  const initial = list.find((w2) => w2.workspace_id === authWorkspaceId)?.workspace_id ?? list[0]?.workspace_id ?? CREATE;
26222
- const choice = await p3.select({
26413
+ const choice = await p4.select({
26223
26414
  message: "Workspace seç",
26224
26415
  options: [
26225
26416
  ...list.map((w2) => ({ value: w2.workspace_id, label: w2.name, hint: w2.slug })),
@@ -26227,26 +26418,26 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26227
26418
  ],
26228
26419
  initialValue: initial
26229
26420
  });
26230
- if (p3.isCancel(choice))
26421
+ if (p4.isCancel(choice))
26231
26422
  bail();
26232
26423
  selected = choice;
26233
26424
  }
26234
26425
  if (selected === CREATE) {
26235
- const name = await p3.text({
26426
+ const name = await p4.text({
26236
26427
  message: "Workspace adı",
26237
26428
  placeholder: "My App",
26238
26429
  validate: (v2) => v2.trim() ? undefined : "gerekli"
26239
26430
  });
26240
- if (p3.isCancel(name))
26431
+ if (p4.isCancel(name))
26241
26432
  bail();
26242
- const domain = await p3.text({
26433
+ const domain = await p4.text({
26243
26434
  message: "Domain",
26244
26435
  placeholder: "example.com",
26245
26436
  validate: (v2) => v2.trim().length >= 3 ? undefined : "geçerli domain gerekli"
26246
26437
  });
26247
- if (p3.isCancel(domain))
26438
+ if (p4.isCancel(domain))
26248
26439
  bail();
26249
- const s2 = p3.spinner();
26440
+ const s2 = p4.spinner();
26250
26441
  s2.start("Workspace oluşturuluyor…");
26251
26442
  const created = await client.createWorkspace({
26252
26443
  name: String(name).trim(),
@@ -26306,11 +26497,19 @@ var wizardArgs = {
26306
26497
  framework: { type: "string", description: "Framework override (auto-detect yerine)" },
26307
26498
  install: { type: "boolean", description: "SDK install (--no-install ile atla)", default: true },
26308
26499
  pull: { type: "boolean", description: "İlk registry pull (--no-pull ile atla)", default: true },
26309
- ai: { type: "boolean", description: "AI Plan/wire fazı (--no-ai ile atla → floor)", default: true },
26500
+ ai: {
26501
+ type: "boolean",
26502
+ description: "AI Plan/wire fazı (--no-ai ile atla → floor)",
26503
+ default: true
26504
+ },
26310
26505
  yes: { type: "boolean", description: "Onayları otomatik geç" },
26311
26506
  ci: { type: "boolean", description: "Non-interaktif (api-key + workspace zorunlu)" }
26312
26507
  };
26313
26508
  async function runWizardFromArgs(args) {
26509
+ if (args.ci && (!args["api-key"] || !args.workspace)) {
26510
+ console.error("[gurulu] --ci için --api-key <sk_...> ve --workspace <uuid> zorunlu (non-interaktif kurulum).");
26511
+ process.exit(1);
26512
+ }
26314
26513
  const opts = {
26315
26514
  cwd: process.cwd(),
26316
26515
  noInstall: args.install === false,
@@ -26344,7 +26543,7 @@ var initCmd = defineCommand({
26344
26543
  // src/lib/editor-mcp.ts
26345
26544
  import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
26346
26545
  import { homedir as homedir2 } from "node:os";
26347
- import { dirname as dirname4, join as join11 } from "node:path";
26546
+ import { dirname as dirname4, join as join9 } from "node:path";
26348
26547
  var SERVER_NAME = "gurulu";
26349
26548
  function buildMcpServerConfig(creds) {
26350
26549
  return {
@@ -26358,14 +26557,14 @@ function buildMcpServerConfig(creds) {
26358
26557
  };
26359
26558
  }
26360
26559
  var EDITORS = {
26361
- cursor: { path: () => join11(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26362
- claude: { path: () => join11(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26560
+ cursor: { path: () => join9(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26561
+ claude: { path: () => join9(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26363
26562
  windsurf: {
26364
- path: () => join11(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26563
+ path: () => join9(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26365
26564
  key: "mcpServers",
26366
26565
  label: "Windsurf"
26367
26566
  },
26368
- vscode: { path: (cwd) => join11(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26567
+ vscode: { path: (cwd) => join9(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26369
26568
  };
26370
26569
  function mergeMcpConfig(existing, serverConfig, key) {
26371
26570
  const servers = existing[key] ?? {};
@@ -26561,9 +26760,9 @@ var pushCmd = defineCommand({
26561
26760
  // src/commands/uninstall.ts
26562
26761
  import { execFile as execFile2 } from "node:child_process";
26563
26762
  import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
26564
- import { join as join12 } from "node:path";
26763
+ import { join as join10 } from "node:path";
26565
26764
  import { promisify as promisify2 } from "node:util";
26566
- import * as p4 from "@clack/prompts";
26765
+ import * as p5 from "@clack/prompts";
26567
26766
  var pexec2 = promisify2(execFile2);
26568
26767
  var ENV_FILES = [".env.local", ".env"];
26569
26768
  var GURULU_PREFIXES = ["GURULU_", "NEXT_PUBLIC_GURULU", "VITE_GURULU"];
@@ -26590,23 +26789,23 @@ var uninstallCmd = defineCommand({
26590
26789
  const config = readProjectConfig(cwd);
26591
26790
  const detected = detectProject(cwd);
26592
26791
  const pkg = config?.sdk_preference === "node" ? "@gurulu/node" : "@gurulu/web";
26593
- p4.intro("Gurulu uninstall");
26594
- p4.note([
26792
+ p5.intro("Gurulu uninstall");
26793
+ p5.note([
26595
26794
  `• SDK kaldır: ${pkg} (${detected.packageManager})`,
26596
26795
  `• env temizle: ${ENV_FILES.join(", ")} (GURULU_* anahtarları)`,
26597
26796
  "• .gurulu/ dizinini sil"
26598
26797
  ].join(`
26599
26798
  `), "Şunlar yapılacak");
26600
26799
  if (!args.yes) {
26601
- const ok = await p4.confirm({ message: "Devam edilsin mi?" });
26602
- if (p4.isCancel(ok) || !ok) {
26603
- p4.cancel("İptal.");
26800
+ const ok = await p5.confirm({ message: "Devam edilsin mi?" });
26801
+ if (p5.isCancel(ok) || !ok) {
26802
+ p5.cancel("İptal.");
26604
26803
  process.exit(0);
26605
26804
  }
26606
26805
  }
26607
26806
  if (!args["no-deps"] && detected.hasPackageJson) {
26608
26807
  const { bin, args: a2 } = removeCmd(detected.packageManager, pkg);
26609
- const s2 = p4.spinner();
26808
+ const s2 = p5.spinner();
26610
26809
  s2.start(`${pkg} kaldırılıyor…`);
26611
26810
  try {
26612
26811
  await pexec2(bin, a2, { cwd, timeout: 120000 });
@@ -26617,7 +26816,7 @@ var uninstallCmd = defineCommand({
26617
26816
  }
26618
26817
  const cleaned = [];
26619
26818
  for (const f3 of ENV_FILES) {
26620
- const abs = join12(cwd, f3);
26819
+ const abs = join10(cwd, f3);
26621
26820
  if (!existsSync11(abs))
26622
26821
  continue;
26623
26822
  const { content, removed } = removeEnvKeys(readFileSync11(abs, "utf-8"), GURULU_PREFIXES);
@@ -26626,15 +26825,15 @@ var uninstallCmd = defineCommand({
26626
26825
  cleaned.push(`${f3} (-${removed.length})`);
26627
26826
  }
26628
26827
  }
26629
- const guruluDir = join12(cwd, ".gurulu");
26828
+ const guruluDir = join10(cwd, ".gurulu");
26630
26829
  if (existsSync11(guruluDir))
26631
26830
  rmSync(guruluDir, { recursive: true, force: true });
26632
- p4.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
26831
+ p5.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
26633
26832
  }
26634
26833
  });
26635
26834
 
26636
26835
  // src/index.ts
26637
- var VERSION = "1.2.1";
26836
+ var VERSION = "1.2.2";
26638
26837
  var mainCmd = defineCommand({
26639
26838
  meta: {
26640
26839
  name: "gurulu",