@gurulu/cli 1.2.2 → 1.4.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
@@ -24695,7 +24695,7 @@ function sleep(ms) {
24695
24695
  // src/wizard/run.ts
24696
24696
  import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync8 } from "node:fs";
24697
24697
  import { dirname as dirname3 } from "node:path";
24698
- import * as p3 from "@clack/prompts";
24698
+ import * as p4 from "@clack/prompts";
24699
24699
 
24700
24700
  // src/commands/pull.ts
24701
24701
  import { writeFileSync as writeFileSync3 } from "node:fs";
@@ -24839,6 +24839,17 @@ var pullCmd = defineCommand({
24839
24839
  // src/lib/detect.ts
24840
24840
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
24841
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
+ };
24842
24853
  function readPackageJson(dir) {
24843
24854
  const p = join5(dir, "package.json");
24844
24855
  if (!existsSync4(p))
@@ -24924,6 +24935,14 @@ function frameworkRuntime(fw) {
24924
24935
  return "unknown";
24925
24936
  }
24926
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
+ }
24927
24946
  function detectProject(dir) {
24928
24947
  const pkg = readPackageJson(dir);
24929
24948
  const framework = detectFramework(pkg, dir);
@@ -24933,7 +24952,8 @@ function detectProject(dir) {
24933
24952
  framework,
24934
24953
  runtime: frameworkRuntime(framework),
24935
24954
  packageManager: detectPackageManager(dir),
24936
- packageJson: pkg
24955
+ packageJson: pkg,
24956
+ llmFrameworks: detectLlmFrameworks(pkg)
24937
24957
  };
24938
24958
  }
24939
24959
 
@@ -25267,15 +25287,19 @@ function sdkFor(framework) {
25267
25287
  return "@gurulu/web";
25268
25288
  }
25269
25289
  }
25270
- function snippetWeb(workspaceKey) {
25290
+ function autocaptureLine(jsError, indent) {
25291
+ return jsError ? `
25292
+ ${indent}autocapture: { js_error: true }, // hata takibi açık` : "";
25293
+ }
25294
+ function snippetWeb(workspaceKey, jsError = false) {
25271
25295
  return `import gurulu from '@gurulu/web';
25272
25296
 
25273
25297
  gurulu.init({
25274
25298
  workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
25275
- endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
25299
+ endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io${autocaptureLine(jsError, " ")}
25276
25300
  });`;
25277
25301
  }
25278
- function snippetNext(workspaceKey) {
25302
+ function snippetNext(workspaceKey, jsError = false) {
25279
25303
  return `// src/app/gurulu-provider.tsx
25280
25304
  'use client';
25281
25305
  import { useEffect } from 'react';
@@ -25285,7 +25309,7 @@ export function GuruluProvider({ children }: { children: React.ReactNode }) {
25285
25309
  useEffect(() => {
25286
25310
  gurulu.init({
25287
25311
  workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
25288
- endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,
25312
+ endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,${autocaptureLine(jsError, " ")}
25289
25313
  });
25290
25314
  }, []);
25291
25315
  return <>{children}</>;
@@ -25364,7 +25388,7 @@ function placementHintFor(framework) {
25364
25388
  return "Initialize the SDK at your app entry point.";
25365
25389
  }
25366
25390
  }
25367
- function initSnippetFor(framework, sdk, workspaceKey) {
25391
+ function initSnippetFor(framework, sdk, workspaceKey, jsError = false) {
25368
25392
  if (sdk === "@gurulu/node") {
25369
25393
  if (framework === "express")
25370
25394
  return snippetExpress();
@@ -25373,8 +25397,8 @@ function initSnippetFor(framework, sdk, workspaceKey) {
25373
25397
  return snippetNode();
25374
25398
  }
25375
25399
  if (framework === "next")
25376
- return snippetNext(workspaceKey);
25377
- return snippetWeb(workspaceKey);
25400
+ return snippetNext(workspaceKey, jsError);
25401
+ return snippetWeb(workspaceKey, jsError);
25378
25402
  }
25379
25403
  function envKeysFor(sdk, framework) {
25380
25404
  if (sdk === "@gurulu/node") {
@@ -25400,7 +25424,7 @@ function buildInstallPlan(detected, ctx = {}) {
25400
25424
  sdk,
25401
25425
  packageManager: detected.packageManager,
25402
25426
  installCommand: installCmdFor(detected.packageManager, sdk),
25403
- initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey),
25427
+ initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey, ctx.autocaptureJsError),
25404
25428
  envKeys: envKeysFor(sdk, detected.framework),
25405
25429
  placementHint: placementHintFor(detected.framework),
25406
25430
  framework: detected.framework
@@ -25710,8 +25734,77 @@ function gatherContext(opts) {
25710
25734
  return { files, capped, totalBytes };
25711
25735
  }
25712
25736
 
25713
- // src/wizard/plan.ts
25737
+ // src/wizard/features.ts
25714
25738
  import * as p2 from "@clack/prompts";
25739
+ function availableFeatures(detected) {
25740
+ const isBrowser = frameworkRuntime(detected.framework) !== "node";
25741
+ const feats = [];
25742
+ feats.push({
25743
+ key: "error",
25744
+ label: "Hata takibi",
25745
+ detail: "Tarayıcı JS hatalarını otomatik yakala (js_error)",
25746
+ enableHint: isBrowser ? "gurulu.init({ ..., autocapture: { js_error: true } })" : "Sunucu hatalarında: gurulu.track('js_error', { message, error_type })",
25747
+ recommended: isBrowser
25748
+ });
25749
+ if (detected.llmFrameworks.length > 0) {
25750
+ feats.push({
25751
+ key: "llm",
25752
+ label: `LLM analizi (${detected.llmFrameworks.join(", ")} tespit edildi)`,
25753
+ detail: "AI çağrı maliyeti/latency/hata/model performansı",
25754
+ enableHint: "import { wrapOpenAI } from '@gurulu/node'; const client = wrapOpenAI(openai);",
25755
+ recommended: true
25756
+ });
25757
+ }
25758
+ if (isBrowser) {
25759
+ feats.push({
25760
+ key: "activation",
25761
+ label: "Activation (popup · tur · A/B test · kişiselleştirme)",
25762
+ detail: "Dashboard'dan yayınla, SDK render etsin (Action Layer)",
25763
+ enableHint: "import { runActivation } from '@gurulu/web/activate'; runActivation(gurulu);",
25764
+ recommended: false
25765
+ });
25766
+ }
25767
+ return feats;
25768
+ }
25769
+ async function runFeatures(client, detected, opts) {
25770
+ const avail = availableFeatures(detected);
25771
+ if (avail.length === 0 || !opts.authed)
25772
+ return { selected: [], registered: false };
25773
+ let selectedKeys;
25774
+ if (opts.yes) {
25775
+ selectedKeys = avail.filter((f3) => f3.recommended).map((f3) => f3.key);
25776
+ } else {
25777
+ const choice = await p2.multiselect({
25778
+ message: "Hangi özellikleri kuralım? (event'ler registry'ye otomatik eklenir)",
25779
+ options: avail.map((f3) => ({ value: f3.key, label: f3.label, hint: f3.detail })),
25780
+ initialValues: avail.filter((f3) => f3.recommended).map((f3) => f3.key),
25781
+ required: false
25782
+ });
25783
+ if (p2.isCancel(choice))
25784
+ return { selected: [], registered: false };
25785
+ selectedKeys = choice;
25786
+ }
25787
+ if (selectedKeys.length === 0)
25788
+ return { selected: [], registered: false };
25789
+ const selected = avail.filter((f3) => selectedKeys.includes(f3.key));
25790
+ const s2 = p2.spinner();
25791
+ s2.start("Özellikler registry'ye kuruluyor…");
25792
+ try {
25793
+ await client.post("/features/register", { features: selectedKeys });
25794
+ s2.stop(c3.neon(`✓ ${selected.length} özellik kuruldu`));
25795
+ } catch {
25796
+ s2.stop("Özellik kurulumu atlandı (gateway hatası) — sonra: gurulu doctor", 1);
25797
+ return { selected, registered: false };
25798
+ }
25799
+ p2.note(selected.map((f3) => `${c3.bold(f3.label)}
25800
+ ${c3.dim(f3.enableHint)}`).join(`
25801
+
25802
+ `), "Etkinleştirme");
25803
+ return { selected, registered: true };
25804
+ }
25805
+
25806
+ // src/wizard/plan.ts
25807
+ import * as p3 from "@clack/prompts";
25715
25808
  async function fetchPlan(client, context, input) {
25716
25809
  if (context.files.length === 0)
25717
25810
  return null;
@@ -25756,12 +25849,12 @@ function formatPlan(plan) {
25756
25849
  `).trimEnd();
25757
25850
  }
25758
25851
  async function renderPlan(plan) {
25759
- p2.note(formatPlan(plan), plan.summary);
25852
+ p3.note(formatPlan(plan), plan.summary);
25760
25853
  const newCount = plan.events.filter((e2) => !e2.existing).length;
25761
- const ok = await p2.confirm({
25854
+ const ok = await p3.confirm({
25762
25855
  message: `${plan.events.length} event (${newCount} yeni) — bu planı uygula?`
25763
25856
  });
25764
- if (p2.isCancel(ok) || !ok)
25857
+ if (p3.isCancel(ok) || !ok)
25765
25858
  return null;
25766
25859
  return plan.events;
25767
25860
  }
@@ -25776,8 +25869,8 @@ import { promisify } from "node:util";
25776
25869
 
25777
25870
  // src/wizard/guard.ts
25778
25871
  import { basename, isAbsolute, relative as relative2, resolve } from "node:path";
25779
- function resolveInCwd(p3, cwd) {
25780
- return isAbsolute(p3) ? p3 : resolve(cwd, p3);
25872
+ function resolveInCwd(p4, cwd) {
25873
+ return isAbsolute(p4) ? p4 : resolve(cwd, p4);
25781
25874
  }
25782
25875
  function isAdditiveEdit(find, replace) {
25783
25876
  if (find.length === 0)
@@ -25854,8 +25947,8 @@ function isAllowedBash(cmd) {
25854
25947
  }
25855
25948
  return { ok: false, reason: `allowlist dışı binary: ${bin}` };
25856
25949
  }
25857
- function isAllowedPath(p3, cwd) {
25858
- const abs = isAbsolute(p3) ? p3 : resolve(cwd, p3);
25950
+ function isAllowedPath(p4, cwd) {
25951
+ const abs = isAbsolute(p4) ? p4 : resolve(cwd, p4);
25859
25952
  const rel = relative2(cwd, abs);
25860
25953
  if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
25861
25954
  return { ok: false, reason: "cwd dışı yol" };
@@ -25966,11 +26059,14 @@ ${stderr}`.trim().slice(0, MAX_OBS);
25966
26059
  }
25967
26060
  function buildWireSystemPrompt() {
25968
26061
  return [
25969
- "You are Gurulu's capture wire agent. Add the approved analytics events to the user's code.",
26062
+ "You are Gurulu's capture wire agent. Add the approved analytics events to the user's code,",
26063
+ "plus any additional wiring tasks given (e.g. wrapping an LLM client, mounting a provider).",
25970
26064
  "Tools (one JSON action per turn): read{path} | edit{path,find,replace} | bash{cmd} | done{summary}.",
25971
26065
  "HARD RULES:",
25972
26066
  "- ADDITIVE-ONLY: every edit `replace` MUST contain `find` verbatim — you may only ADD code,",
25973
- " never delete or rewrite existing code. Edits violating this are rejected.",
26067
+ ' never delete or rewrite existing code. Edits violating this are rejected. To "wrap" an existing',
26068
+ " expression, keep the original verbatim inside the new wrapper (e.g. find `new OpenAI(cfg)` →",
26069
+ " replace `wrapOpenAI(new OpenAI(cfg))`), and add the import additively.",
25974
26070
  "- `find` must be an EXACT, UNIQUE snippet copied from a file you have read (read before edit).",
25975
26071
  "- Use the exact provided event_key (snake_case). Wire gurulu.track(...) at the right place and",
25976
26072
  " gurulu.identify(...) at the auth point if given.",
@@ -25978,21 +26074,23 @@ function buildWireSystemPrompt() {
25978
26074
  " `pnpm run lint`) or a checker binary (`tsc --noEmit`, `biome check`, `eslint .`). Installing or",
25979
26075
  " fetching packages (`npm install`, `npx`, `bunx`, `pnpm dlx`, `add`, `exec`) is REJECTED by the guard.",
25980
26076
  "- After wiring, run the project typecheck/build to verify; if it breaks, fix additively.",
25981
- "- When all events are wired and verify passes, emit done{summary}. Keep edits minimal."
26077
+ "- When all events AND additional tasks are done and verify passes, emit done{summary}. Keep edits minimal."
25982
26078
  ].join(`
25983
26079
  `);
25984
26080
  }
25985
- function buildWireUserPrompt(events, identifyHint, files) {
25986
- const evLines = events.map((e2) => `- ${e2.event_key} (${e2.event_type})${e2.capture_hint ? ` @ ${e2.capture_hint}` : ""}: ${e2.reason}`).join(`
26081
+ function buildWireUserPrompt(events, identifyHint, files, extraTasks = []) {
26082
+ const sections = [];
26083
+ if (events.length > 0) {
26084
+ const evLines = events.map((e2) => `- ${e2.event_key} (${e2.event_type})${e2.capture_hint ? ` @ ${e2.capture_hint}` : ""}: ${e2.reason}`).join(`
25987
26085
  `);
25988
- return [
25989
- "Wire these events into the codebase:",
25990
- evLines,
25991
- identifyHint ? `identify() at: ${identifyHint}` : "No identify hint.",
25992
- "",
25993
- `Project files (read the relevant ones): ${files.join(", ")}`,
25994
- "Start by reading the files you need, then edit additively."
25995
- ].join(`
26086
+ sections.push("Wire these events into the codebase:", evLines, identifyHint ? `identify() at: ${identifyHint}` : "No identify hint.");
26087
+ }
26088
+ if (extraTasks.length > 0) {
26089
+ sections.push("Additional wiring tasks:", extraTasks.map((t2, i2) => `${i2 + 1}. ${t2}`).join(`
26090
+ `));
26091
+ }
26092
+ sections.push("", `Project files (read the relevant ones): ${files.join(", ")}`, "Start by reading the files you need, then edit additively.");
26093
+ return sections.join(`
25996
26094
  `);
25997
26095
  }
25998
26096
 
@@ -26001,7 +26099,10 @@ var MAX_STEPS = 25;
26001
26099
  async function runWireAgent(client, input, snapshots) {
26002
26100
  const messages = [
26003
26101
  { role: "system", content: buildWireSystemPrompt() },
26004
- { role: "user", content: buildWireUserPrompt(input.events, input.identifyHint, input.files) }
26102
+ {
26103
+ role: "user",
26104
+ content: buildWireUserPrompt(input.events, input.identifyHint, input.files, input.extraTasks ?? [])
26105
+ }
26005
26106
  ];
26006
26107
  const edits = [];
26007
26108
  const changed = new Set;
@@ -26119,16 +26220,16 @@ var FRAMEWORKS = [
26119
26220
  "node-server"
26120
26221
  ];
26121
26222
  function bail() {
26122
- p3.cancel("İptal edildi.");
26223
+ p4.cancel("İptal edildi.");
26123
26224
  process.exit(0);
26124
26225
  }
26125
26226
  async function runWizard(opts) {
26126
26227
  if (process.stdout.isTTY)
26127
26228
  process.stdout.write(banner());
26128
- p3.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26229
+ p4.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26129
26230
  const TOTAL = 6;
26130
26231
  const phase = (n2, label) => {
26131
- p3.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26232
+ p4.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26132
26233
  };
26133
26234
  phase(1, "Kimlik doğrulama");
26134
26235
  const auth = await ensureAuth({
@@ -26145,25 +26246,23 @@ async function runWizard(opts) {
26145
26246
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
26146
26247
  framework = opts.framework;
26147
26248
  } else if (!opts.yes) {
26148
- const ok = await p3.confirm({
26249
+ const ok = await p4.confirm({
26149
26250
  message: `Saptanan: ${detected.framework} (${detected.packageManager}). Doğru mu?`
26150
26251
  });
26151
- if (p3.isCancel(ok))
26252
+ if (p4.isCancel(ok))
26152
26253
  bail();
26153
26254
  if (!ok) {
26154
- const choice = await p3.select({
26255
+ const choice = await p4.select({
26155
26256
  message: "Framework seç",
26156
26257
  options: FRAMEWORKS.map((f3) => ({ value: f3, label: f3 })),
26157
26258
  initialValue: detected.framework
26158
26259
  });
26159
- if (p3.isCancel(choice))
26260
+ if (p4.isCancel(choice))
26160
26261
  bail();
26161
26262
  framework = choice;
26162
26263
  }
26163
26264
  }
26164
26265
  const project = { ...detected, framework };
26165
- const plan = buildInstallPlan(project, { writeKey, workspaceId });
26166
- const isNode = plan.sdk === "@gurulu/node";
26167
26266
  const authed = Boolean(auth.apiKey);
26168
26267
  let approvedEvents = [];
26169
26268
  let identifyHint = null;
@@ -26176,17 +26275,28 @@ async function runWizard(opts) {
26176
26275
  if (approved)
26177
26276
  approvedEvents = approved;
26178
26277
  } else {
26179
- p3.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26278
+ p4.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26180
26279
  }
26181
26280
  }
26281
+ let featuresResult = { selected: [], registered: false };
26282
+ if (authed) {
26283
+ featuresResult = await runFeatures(client, project, { yes: opts.yes, authed });
26284
+ }
26285
+ const errorSelected = featuresResult.selected.some((f3) => f3.key === "error");
26286
+ const plan = buildInstallPlan(project, {
26287
+ writeKey,
26288
+ workspaceId,
26289
+ autocaptureJsError: errorSelected
26290
+ });
26291
+ const isNode = plan.sdk === "@gurulu/node";
26182
26292
  phase(4, "SDK kurulumu");
26183
26293
  let installed = false;
26184
26294
  if (opts.noInstall) {
26185
- p3.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26295
+ p4.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
26186
26296
  } else if (!detected.hasPackageJson) {
26187
- p3.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26297
+ p4.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
26188
26298
  } else {
26189
- const s2 = p3.spinner();
26299
+ const s2 = p4.spinner();
26190
26300
  s2.start(`${plan.sdk} kuruluyor (${plan.installCommand})…`);
26191
26301
  const res = await execInstall(plan, { cwd: opts.cwd });
26192
26302
  if (res.ok) {
@@ -26204,6 +26314,14 @@ async function runWizard(opts) {
26204
26314
  snippet: plan.initSnippet,
26205
26315
  placementHint: plan.placementHint
26206
26316
  });
26317
+ const extraTasks = [];
26318
+ const llmSelected = featuresResult.selected.some((f3) => f3.key === "llm");
26319
+ if (llmSelected && detected.llmFrameworks.length > 0) {
26320
+ extraTasks.push(`Wrap the project's LLM client(s) [${detected.llmFrameworks.join(", ")}] with @gurulu/node so ` + "every call is auto-captured. Find where the OpenAI/Anthropic client is instantiated and wrap " + "it in place additively: `import { wrapOpenAI } from '@gurulu/node';` then " + "`const client = wrapOpenAI(new OpenAI({...}))` (use wrapAnthropic for Anthropic SDK). " + "Keep the original instantiation verbatim inside the wrapper.");
26321
+ }
26322
+ if (inj.strategy === "create-module" && inj.file && (project.framework === "next" || project.framework === "nuxt")) {
26323
+ extraTasks.push(project.framework === "next" ? `Mount the Gurulu provider created at \`${inj.file}\` into the app root. Edit app/layout.tsx ` + "(App Router) or pages/_app.tsx (Pages Router) to import { GuruluProvider } from that module " + "and wrap the children additively: `<GuruluProvider>{children}</GuruluProvider>`." : `Register the Nuxt plugin created at \`${inj.file}\` so it loads on the client. If it is under ` + "plugins/ Nuxt auto-registers it; otherwise add it to the plugins array in nuxt.config.ts additively.");
26324
+ }
26207
26325
  const envFile = isNode ? ".env" : ".env.local";
26208
26326
  const vars = [];
26209
26327
  for (const k2 of plan.envKeys) {
@@ -26213,7 +26331,7 @@ async function runWizard(opts) {
26213
26331
  if (auth.apiKey.startsWith("sk_")) {
26214
26332
  vars.push({ key: k2.key, value: auth.apiKey });
26215
26333
  } else {
26216
- p3.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.");
26334
+ 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.");
26217
26335
  }
26218
26336
  }
26219
26337
  }
@@ -26233,38 +26351,45 @@ async function runWizard(opts) {
26233
26351
  }
26234
26352
  let registeredCount = 0;
26235
26353
  if (approvedEvents.length > 0) {
26236
- const s2 = p3.spinner();
26354
+ const s2 = p4.spinner();
26237
26355
  s2.start("Yeni eventler registry kuyruguna oneriliyor…");
26238
26356
  const res = await registerNewEvents(client, approvedEvents);
26239
26357
  registeredCount = res.registered.length;
26240
26358
  s2.stop(`${res.registered.length} yeni event verification queue'ya eklendi${res.failed.length > 0 ? ` (${res.failed.length} başarısız)` : ""}`);
26241
26359
  }
26242
26360
  let wiredCount = 0;
26243
- if (approvedEvents.length > 0) {
26361
+ const hasWireWork = approvedEvents.length > 0 || extraTasks.length > 0;
26362
+ if (hasWireWork) {
26244
26363
  if (opts.noAi) {
26245
- p3.log.message(`Capture şu çağrıları ekle:
26364
+ if (approvedEvents.length > 0) {
26365
+ p4.log.message(`Capture — şu çağrıları ekle:
26246
26366
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26367
+ }
26247
26368
  } else {
26248
26369
  const wireFiles = gatherContext({ cwd: opts.cwd }).files.map((f3) => f3.path);
26249
26370
  const snapshots = new Map;
26250
- const s2 = p3.spinner();
26371
+ const s2 = p4.spinner();
26251
26372
  s2.start("AI capture wiring (kod düzenleniyor)…");
26252
- const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles }, snapshots);
26373
+ const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles, extraTasks }, snapshots);
26253
26374
  s2.stop(`wire: ${outcome.changedFiles.length} dosya / ${outcome.steps} adım (${outcome.stoppedReason})`);
26254
26375
  if (outcome.changedFiles.length > 0) {
26255
- p3.log.message(formatWireDiff(opts.cwd, snapshots));
26256
- const keep = opts.yes ? true : await p3.confirm({
26376
+ p4.log.message(formatWireDiff(opts.cwd, snapshots));
26377
+ const keep = opts.yes ? true : await p4.confirm({
26257
26378
  message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?`
26258
26379
  });
26259
- if (p3.isCancel(keep) || !keep) {
26380
+ if (p4.isCancel(keep) || !keep) {
26260
26381
  restoreSnapshots(opts.cwd, snapshots);
26261
- p3.log.info("Wire geri alındı — capture snippet rehberi:");
26262
- p3.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26382
+ if (approvedEvents.length > 0) {
26383
+ p4.log.info("Wire geri alındı — capture snippet rehberi:");
26384
+ p4.log.message(captureGuide(approvedEvents, identifyHint, isNode));
26385
+ } else {
26386
+ p4.log.info("Wire geri alındı — etkinleştirme snippet'leri yukarıda.");
26387
+ }
26263
26388
  } else {
26264
26389
  wiredCount = outcome.changedFiles.length;
26265
26390
  }
26266
- } else {
26267
- p3.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26391
+ } else if (approvedEvents.length > 0) {
26392
+ p4.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
26268
26393
  ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26269
26394
  }
26270
26395
  }
@@ -26280,18 +26405,21 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26280
26405
  }
26281
26406
  if (approvedEvents.length > 0) {
26282
26407
  lines.push(`✓ ${approvedEvents.length} event planlandı, ${registeredCount} yeni registry kuyruğunda`);
26283
- if (wiredCount > 0)
26284
- lines.push(`✓ ${wiredCount} dosya AI ile capture wire edildi`);
26408
+ }
26409
+ if (wiredCount > 0)
26410
+ lines.push(`✓ ${wiredCount} dosya AI ile wire edildi (capture/LLM/provider)`);
26411
+ if (featuresResult.registered && featuresResult.selected.length > 0) {
26412
+ lines.push(`✓ özellikler: ${featuresResult.selected.map((f3) => f3.key).join(", ")} kuruldu`);
26285
26413
  }
26286
26414
  lines.push(`✓ .gurulu/config.json${pulled ? " + registry pull" : ""}`);
26287
- p3.note(lines.join(`
26415
+ p4.note(lines.join(`
26288
26416
  `), c3.neon("✦ Değişiklikler"));
26289
26417
  if (inj.strategy !== "prepend-entry" || inj.reason === "no-entry") {
26290
- p3.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26418
+ p4.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26291
26419
  if (inj.strategy === "manual")
26292
- p3.log.message(plan.initSnippet);
26420
+ p4.log.message(plan.initSnippet);
26293
26421
  }
26294
- 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")}`);
26422
+ 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")}`);
26295
26423
  }
26296
26424
  async function resolveWorkspace(client, authWorkspaceId, opts) {
26297
26425
  if (opts.writeKey) {
@@ -26314,7 +26442,7 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26314
26442
  selected = CREATE;
26315
26443
  } else {
26316
26444
  const initial = list.find((w2) => w2.workspace_id === authWorkspaceId)?.workspace_id ?? list[0]?.workspace_id ?? CREATE;
26317
- const choice = await p3.select({
26445
+ const choice = await p4.select({
26318
26446
  message: "Workspace seç",
26319
26447
  options: [
26320
26448
  ...list.map((w2) => ({ value: w2.workspace_id, label: w2.name, hint: w2.slug })),
@@ -26322,26 +26450,26 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
26322
26450
  ],
26323
26451
  initialValue: initial
26324
26452
  });
26325
- if (p3.isCancel(choice))
26453
+ if (p4.isCancel(choice))
26326
26454
  bail();
26327
26455
  selected = choice;
26328
26456
  }
26329
26457
  if (selected === CREATE) {
26330
- const name = await p3.text({
26458
+ const name = await p4.text({
26331
26459
  message: "Workspace adı",
26332
26460
  placeholder: "My App",
26333
26461
  validate: (v2) => v2.trim() ? undefined : "gerekli"
26334
26462
  });
26335
- if (p3.isCancel(name))
26463
+ if (p4.isCancel(name))
26336
26464
  bail();
26337
- const domain = await p3.text({
26465
+ const domain = await p4.text({
26338
26466
  message: "Domain",
26339
26467
  placeholder: "example.com",
26340
26468
  validate: (v2) => v2.trim().length >= 3 ? undefined : "geçerli domain gerekli"
26341
26469
  });
26342
- if (p3.isCancel(domain))
26470
+ if (p4.isCancel(domain))
26343
26471
  bail();
26344
- const s2 = p3.spinner();
26472
+ const s2 = p4.spinner();
26345
26473
  s2.start("Workspace oluşturuluyor…");
26346
26474
  const created = await client.createWorkspace({
26347
26475
  name: String(name).trim(),
@@ -26666,7 +26794,7 @@ import { execFile as execFile2 } from "node:child_process";
26666
26794
  import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
26667
26795
  import { join as join10 } from "node:path";
26668
26796
  import { promisify as promisify2 } from "node:util";
26669
- import * as p4 from "@clack/prompts";
26797
+ import * as p5 from "@clack/prompts";
26670
26798
  var pexec2 = promisify2(execFile2);
26671
26799
  var ENV_FILES = [".env.local", ".env"];
26672
26800
  var GURULU_PREFIXES = ["GURULU_", "NEXT_PUBLIC_GURULU", "VITE_GURULU"];
@@ -26693,23 +26821,23 @@ var uninstallCmd = defineCommand({
26693
26821
  const config = readProjectConfig(cwd);
26694
26822
  const detected = detectProject(cwd);
26695
26823
  const pkg = config?.sdk_preference === "node" ? "@gurulu/node" : "@gurulu/web";
26696
- p4.intro("Gurulu uninstall");
26697
- p4.note([
26824
+ p5.intro("Gurulu uninstall");
26825
+ p5.note([
26698
26826
  `• SDK kaldır: ${pkg} (${detected.packageManager})`,
26699
26827
  `• env temizle: ${ENV_FILES.join(", ")} (GURULU_* anahtarları)`,
26700
26828
  "• .gurulu/ dizinini sil"
26701
26829
  ].join(`
26702
26830
  `), "Şunlar yapılacak");
26703
26831
  if (!args.yes) {
26704
- const ok = await p4.confirm({ message: "Devam edilsin mi?" });
26705
- if (p4.isCancel(ok) || !ok) {
26706
- p4.cancel("İptal.");
26832
+ const ok = await p5.confirm({ message: "Devam edilsin mi?" });
26833
+ if (p5.isCancel(ok) || !ok) {
26834
+ p5.cancel("İptal.");
26707
26835
  process.exit(0);
26708
26836
  }
26709
26837
  }
26710
26838
  if (!args["no-deps"] && detected.hasPackageJson) {
26711
26839
  const { bin, args: a2 } = removeCmd(detected.packageManager, pkg);
26712
- const s2 = p4.spinner();
26840
+ const s2 = p5.spinner();
26713
26841
  s2.start(`${pkg} kaldırılıyor…`);
26714
26842
  try {
26715
26843
  await pexec2(bin, a2, { cwd, timeout: 120000 });
@@ -26732,7 +26860,7 @@ var uninstallCmd = defineCommand({
26732
26860
  const guruluDir = join10(cwd, ".gurulu");
26733
26861
  if (existsSync11(guruluDir))
26734
26862
  rmSync(guruluDir, { recursive: true, force: true });
26735
- p4.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
26863
+ p5.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
26736
26864
  }
26737
26865
  });
26738
26866
 
@@ -1,5 +1,6 @@
1
1
  export type Framework = 'next' | 'react' | 'vue' | 'nuxt' | 'svelte' | 'astro' | 'vite' | 'express' | 'fastify' | 'hono' | 'koa' | 'node-server' | 'unknown';
2
2
  export type Runtime = 'browser' | 'node' | 'unknown';
3
+ export type LlmFramework = 'openai' | 'anthropic' | 'langchain' | 'google-genai' | 'vercel-ai' | 'cohere' | 'mistral' | 'ollama';
3
4
  export type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';
4
5
  export interface DetectedProject {
5
6
  dir: string;
@@ -8,6 +9,8 @@ export interface DetectedProject {
8
9
  runtime: Runtime;
9
10
  packageManager: PackageManager;
10
11
  packageJson: PackageJsonShape | null;
12
+ /** M46 (K31) — tespit edilen LLM/AI bağımlılıkları (boş = yok). */
13
+ llmFrameworks: LlmFramework[];
11
14
  }
12
15
  interface PackageJsonShape {
13
16
  name?: string;
@@ -22,6 +25,8 @@ interface PackageJsonShape {
22
25
  export declare function detectPackageManager(dir: string): PackageManager;
23
26
  export declare function detectFramework(pkg: PackageJsonShape | null, dir: string): Framework;
24
27
  export declare function frameworkRuntime(fw: Framework): Runtime;
28
+ /** package.json dependencies içinde LLM/AI SDK'larını tespit eder (dedup). */
29
+ export declare function detectLlmFrameworks(pkg: PackageJsonShape | null): LlmFramework[];
25
30
  export declare function detectProject(dir: string): DetectedProject;
26
31
  export {};
27
32
  //# sourceMappingURL=detect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,KAAK,GACL,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACtC;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAchE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAkBpF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAmBvD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAW1D"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,KAAK,GACL,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAGrD,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,WAAW,GACX,WAAW,GACX,cAAc,GACd,WAAW,GACX,QAAQ,GACR,SAAS,GACT,QAAQ,CAAC;AAeb,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrC,mEAAmE;IACnE,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAchE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAmBpF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAmBvD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,GAAG,YAAY,EAAE,CAMhF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAY1D"}
@@ -1,6 +1,17 @@
1
1
  // src/lib/detect.ts
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { dirname, join, parse } from "node:path";
4
+ var LLM_DEP_MAP = {
5
+ openai: "openai",
6
+ "@anthropic-ai/sdk": "anthropic",
7
+ "@langchain/core": "langchain",
8
+ langchain: "langchain",
9
+ "@google/generative-ai": "google-genai",
10
+ ai: "vercel-ai",
11
+ "cohere-ai": "cohere",
12
+ "@mistralai/mistralai": "mistral",
13
+ ollama: "ollama"
14
+ };
4
15
  function readPackageJson(dir) {
5
16
  const p = join(dir, "package.json");
6
17
  if (!existsSync(p))
@@ -86,6 +97,14 @@ function frameworkRuntime(fw) {
86
97
  return "unknown";
87
98
  }
88
99
  }
100
+ function detectLlmFrameworks(pkg) {
101
+ const found = new Set;
102
+ for (const [dep, fw] of Object.entries(LLM_DEP_MAP)) {
103
+ if (hasDep(pkg, dep))
104
+ found.add(fw);
105
+ }
106
+ return [...found];
107
+ }
89
108
  function detectProject(dir) {
90
109
  const pkg = readPackageJson(dir);
91
110
  const framework = detectFramework(pkg, dir);
@@ -95,12 +114,14 @@ function detectProject(dir) {
95
114
  framework,
96
115
  runtime: frameworkRuntime(framework),
97
116
  packageManager: detectPackageManager(dir),
98
- packageJson: pkg
117
+ packageJson: pkg,
118
+ llmFrameworks: detectLlmFrameworks(pkg)
99
119
  };
100
120
  }
101
121
  export {
102
122
  frameworkRuntime,
103
123
  detectProject,
104
124
  detectPackageManager,
125
+ detectLlmFrameworks,
105
126
  detectFramework
106
127
  };
@@ -7,6 +7,12 @@ export interface InstallPlanContext {
7
7
  workspaceId?: string;
8
8
  /** Server-side projeler için API key (sk_xxx). */
9
9
  apiKey?: string;
10
+ /**
11
+ * "Hata takibi" özelliği seçildiyse browser init snippet'ine
12
+ * `autocapture: { js_error: true }` eklenir (default autocapture kapalı —
13
+ * registry'ye event kurmak yetmez, SDK'da da açılmalı). Sadece browser SDK.
14
+ */
15
+ autocaptureJsError?: boolean;
10
16
  }
11
17
  export interface InstallPlan {
12
18
  sdk: SdkPackage;