@gurulu/cli 1.2.0 → 1.2.2

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) {
@@ -24698,6 +24697,145 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as wr
24698
24697
  import { dirname as dirname3 } from "node:path";
24699
24698
  import * as p3 from "@clack/prompts";
24700
24699
 
24700
+ // src/commands/pull.ts
24701
+ import { writeFileSync as writeFileSync3 } from "node:fs";
24702
+
24703
+ // src/lib/codegen.ts
24704
+ function enumKeyName(key) {
24705
+ return key.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
24706
+ }
24707
+ function escapePropName(name) {
24708
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
24709
+ return name;
24710
+ return `'${name.replace(/'/g, "\\'")}'`;
24711
+ }
24712
+ function tsType(t2) {
24713
+ switch (t2) {
24714
+ case "string":
24715
+ return "string";
24716
+ case "number":
24717
+ return "number";
24718
+ case "boolean":
24719
+ return "boolean";
24720
+ case "date":
24721
+ return "string | Date";
24722
+ case "array":
24723
+ return "unknown[]";
24724
+ case "json":
24725
+ return "Record<string, unknown>";
24726
+ default:
24727
+ return "unknown";
24728
+ }
24729
+ }
24730
+ function generateTypescript(manifest) {
24731
+ const banner = `// Generated by @gurulu/cli — DO NOT EDIT
24732
+ // Workspace: ${manifest.workspace_id} | Manifest version: ${manifest.manifest_version} | Schema: v${manifest.schema_version}
24733
+ // Generated: ${manifest.exported_at}
24734
+ // Events: ${manifest.events.length}
24735
+
24736
+ `;
24737
+ if (manifest.events.length === 0) {
24738
+ return `${banner}export const GuruluEvents = {} as const;
24739
+ export type GuruluEvents = (typeof GuruluEvents)[keyof typeof GuruluEvents];
24740
+
24741
+ export type EventProperties = Record<never, never>;
24742
+
24743
+ export declare function track<E extends GuruluEvents>(
24744
+ event: E,
24745
+ properties: EventProperties[E],
24746
+ ): Promise<void>;
24747
+ `;
24748
+ }
24749
+ const enumLines = manifest.events.map((e2) => ` ${enumKeyName(e2.key)}: '${e2.key}',`);
24750
+ const mapLines = manifest.events.map((e2) => {
24751
+ const props = (e2.properties ?? []).slice().sort((a2, b2) => (a2.position ?? 0) - (b2.position ?? 0)).map((p) => {
24752
+ const optional = p.required ? "" : "?";
24753
+ const comment = p.format ? ` // format: ${p.format}` : "";
24754
+ return ` ${escapePropName(p.name)}${optional}: ${tsType(p.type)};${comment}`;
24755
+ }).join(`
24756
+ `);
24757
+ return ` '${e2.key}': {
24758
+ ${props || " // no properties"}
24759
+ };`;
24760
+ });
24761
+ return `${banner}export const GuruluEvents = {
24762
+ ${enumLines.join(`
24763
+ `)}
24764
+ } as const;
24765
+ export type GuruluEvents = (typeof GuruluEvents)[keyof typeof GuruluEvents];
24766
+
24767
+ export interface EventProperties {
24768
+ ${mapLines.join(`
24769
+ `)}
24770
+ }
24771
+
24772
+ /**
24773
+ * Typed track — compile-time validation against pulled registry.
24774
+ * Runtime implementation: @gurulu/web or @gurulu/node.
24775
+ */
24776
+ export declare function track<E extends GuruluEvents>(
24777
+ event: E,
24778
+ properties: EventProperties[E],
24779
+ ): Promise<void>;
24780
+ `;
24781
+ }
24782
+ function generateManifestLock(manifest) {
24783
+ return `${manifest.manifest_version}
24784
+ `;
24785
+ }
24786
+
24787
+ // src/commands/pull.ts
24788
+ async function runPull(opts = {}) {
24789
+ const cwd = opts.cwd ?? process.cwd();
24790
+ const project = readProjectConfig(cwd);
24791
+ if (!project) {
24792
+ throw new Error("no .gurulu/config.json — run `gurulu init` first");
24793
+ }
24794
+ const cred = resolveActiveCredential({
24795
+ workspaceId: opts.workspaceId ?? project.workspace_id,
24796
+ cwd
24797
+ });
24798
+ if (!cred) {
24799
+ throw new Error("no credentials — run `gurulu login` or set GURULU_API_KEY env (workspace sk_xxx)");
24800
+ }
24801
+ const client = new ApiClient({
24802
+ endpoint: project.endpoint ?? cred.endpoint,
24803
+ apiKey: cred.apiKey
24804
+ });
24805
+ const manifest = await client.get("/v1/cli/registry/pull", {
24806
+ workspace_id: cred.workspaceId
24807
+ });
24808
+ writeFileSync3(projectRegistryPath(cwd), `${JSON.stringify(manifest, null, 2)}
24809
+ `, "utf-8");
24810
+ const ts = generateTypescript(manifest);
24811
+ writeFileSync3(projectGeneratedPath(cwd), ts, "utf-8");
24812
+ writeFileSync3(projectManifestLockPath(cwd), generateManifestLock(manifest), "utf-8");
24813
+ return manifest;
24814
+ }
24815
+ var pullCmd = defineCommand({
24816
+ meta: {
24817
+ name: "pull",
24818
+ description: "Pull registry snapshot + code-gen typed events"
24819
+ },
24820
+ args: {
24821
+ workspace: { type: "string", description: "Workspace ID (override project config)" }
24822
+ },
24823
+ async run({ args }) {
24824
+ try {
24825
+ const manifest = await runPull({
24826
+ ...args.workspace ? { workspaceId: String(args.workspace) } : {}
24827
+ });
24828
+ console.log(`[gurulu] pulled ${manifest.events.length} events (manifest ${manifest.manifest_version})`);
24829
+ console.log(` → ${projectRegistryPath()}`);
24830
+ console.log(` → ${projectGeneratedPath()}`);
24831
+ console.log(` → ${projectManifestLockPath()}`);
24832
+ } catch (err) {
24833
+ console.error(`[gurulu] pull failed: ${err instanceof Error ? err.message : String(err)}`);
24834
+ process.exit(1);
24835
+ }
24836
+ }
24837
+ });
24838
+
24701
24839
  // src/lib/detect.ts
24702
24840
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
24703
24841
  import { dirname as dirname2, join as join5, parse } from "node:path";
@@ -24800,7 +24938,7 @@ function detectProject(dir) {
24800
24938
  }
24801
24939
 
24802
24940
  // src/lib/env-file.ts
24803
- import { appendFileSync, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
24941
+ import { appendFileSync, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
24804
24942
  import { join as join6 } from "node:path";
24805
24943
  var ENV_KEY_LINE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
24806
24944
  function parseEnvKeys(content) {
@@ -24838,7 +24976,7 @@ function writeEnvFile(opts) {
24838
24976
  const block = opts.vars.filter((v2) => added.includes(v2.key)).map((v2) => `${v2.key}=${formatValue(v2.value)}`).join(`
24839
24977
  `);
24840
24978
  if (existing === "") {
24841
- writeFileSync3(envPath, `# Gurulu
24979
+ writeFileSync4(envPath, `# Gurulu
24842
24980
  ${block}
24843
24981
  `, "utf-8");
24844
24982
  } else {
@@ -24863,7 +25001,7 @@ function ensureGitignored(cwd, file) {
24863
25001
  if (covered)
24864
25002
  return false;
24865
25003
  if (content === "") {
24866
- writeFileSync3(giPath, `# Gurulu
25004
+ writeFileSync4(giPath, `# Gurulu
24867
25005
  ${file}
24868
25006
  `, "utf-8");
24869
25007
  } else {
@@ -24953,7 +25091,7 @@ async function execInstall(plan, opts) {
24953
25091
  }
24954
25092
 
24955
25093
  // src/lib/inject.ts
24956
- import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
25094
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
24957
25095
  import { join as join7 } from "node:path";
24958
25096
  var DIRECTIVE = /^\s*(['"])use [a-z ]+\1;?\s*$/;
24959
25097
  function lastImportEnd(lines, start) {
@@ -25075,7 +25213,7 @@ function applyInjection(opts) {
25075
25213
  const src2 = readFileSync6(abs, "utf-8");
25076
25214
  const res = injectInit(src2, webPieces(workspaceKey, detected.framework));
25077
25215
  if (res.changed)
25078
- writeFileSync4(abs, res.output, "utf-8");
25216
+ writeFileSync5(abs, res.output, "utf-8");
25079
25217
  return { strategy, changed: res.changed, file: entry, reason: res.reason };
25080
25218
  }
25081
25219
  if (strategy === "create-module") {
@@ -25084,7 +25222,7 @@ function applyInjection(opts) {
25084
25222
  if (existsSync6(abs)) {
25085
25223
  return { strategy, changed: false, file: rel, reason: "already-present", wireHint: opts.placementHint };
25086
25224
  }
25087
- writeFileSync4(abs, `${opts.snippet}
25225
+ writeFileSync5(abs, `${opts.snippet}
25088
25226
  `, "utf-8");
25089
25227
  return { strategy, changed: true, file: rel, reason: "created", wireHint: opts.placementHint };
25090
25228
  }
@@ -25269,145 +25407,57 @@ function buildInstallPlan(detected, ctx = {}) {
25269
25407
  };
25270
25408
  }
25271
25409
 
25272
- // src/commands/pull.ts
25273
- import { writeFileSync as writeFileSync5 } from "node:fs";
25274
-
25275
- // src/lib/codegen.ts
25276
- function enumKeyName(key) {
25277
- return key.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
25410
+ // src/lib/ui.ts
25411
+ var noColor = Boolean(process.env.NO_COLOR) || process.env.TERM === "dumb" || !process.stdout.isTTY;
25412
+ function wrap(open, close) {
25413
+ return (s2) => noColor ? s2 : `\x1B[${open}m${s2}\x1B[${close}m`;
25278
25414
  }
25279
- function escapePropName(name) {
25280
- if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
25281
- return name;
25282
- return `'${name.replace(/'/g, "\\'")}'`;
25415
+ function fg(code) {
25416
+ return (s2) => noColor ? s2 : `\x1B[38;5;${code}m${s2}\x1B[39m`;
25283
25417
  }
25284
- function tsType(t2) {
25285
- switch (t2) {
25286
- case "string":
25287
- return "string";
25288
- case "number":
25289
- return "number";
25290
- case "boolean":
25291
- return "boolean";
25292
- case "date":
25293
- return "string | Date";
25294
- case "array":
25295
- return "unknown[]";
25296
- case "json":
25297
- return "Record<string, unknown>";
25298
- default:
25299
- return "unknown";
25300
- }
25418
+ var c3 = {
25419
+ bold: wrap("1", "22"),
25420
+ dim: wrap("2", "22"),
25421
+ italic: wrap("3", "23"),
25422
+ underline: wrap("4", "24"),
25423
+ reset: (s2) => s2,
25424
+ neon: fg(120),
25425
+ green: fg(78),
25426
+ cyan: fg(45),
25427
+ mint: fg(85),
25428
+ violet: fg(141),
25429
+ pink: fg(212),
25430
+ gray: fg(245),
25431
+ slate: fg(240)
25432
+ };
25433
+ var GRAD = [120, 84, 85, 86, 80, 44, 45, 51];
25434
+ function gradient(text) {
25435
+ if (noColor)
25436
+ return text;
25437
+ const chars = [...text];
25438
+ return chars.map((ch, i2) => {
25439
+ if (ch === " ")
25440
+ return ch;
25441
+ const idx = Math.floor(i2 / Math.max(chars.length - 1, 1) * (GRAD.length - 1));
25442
+ const code = GRAD[idx] ?? GRAD[0];
25443
+ return `\x1B[38;5;${code}m${ch}`;
25444
+ }).join("") + "\x1B[39m";
25301
25445
  }
25302
- function generateTypescript(manifest) {
25303
- const banner = `// Generated by @gurulu/cli DO NOT EDIT
25304
- // Workspace: ${manifest.workspace_id} | Manifest version: ${manifest.manifest_version} | Schema: v${manifest.schema_version}
25305
- // Generated: ${manifest.exported_at}
25306
- // Events: ${manifest.events.length}
25307
-
25308
- `;
25309
- if (manifest.events.length === 0) {
25310
- return `${banner}export const GuruluEvents = {} as const;
25311
- export type GuruluEvents = (typeof GuruluEvents)[keyof typeof GuruluEvents];
25312
-
25313
- export type EventProperties = Record<never, never>;
25314
-
25315
- export declare function track<E extends GuruluEvents>(
25316
- event: E,
25317
- properties: EventProperties[E],
25318
- ): Promise<void>;
25319
- `;
25320
- }
25321
- const enumLines = manifest.events.map((e2) => ` ${enumKeyName(e2.key)}: '${e2.key}',`);
25322
- const mapLines = manifest.events.map((e2) => {
25323
- const props = (e2.properties ?? []).slice().sort((a2, b2) => (a2.position ?? 0) - (b2.position ?? 0)).map((p) => {
25324
- const optional = p.required ? "" : "?";
25325
- const comment = p.format ? ` // format: ${p.format}` : "";
25326
- return ` ${escapePropName(p.name)}${optional}: ${tsType(p.type)};${comment}`;
25327
- }).join(`
25328
- `);
25329
- return ` '${e2.key}': {
25330
- ${props || " // no properties"}
25331
- };`;
25332
- });
25333
- return `${banner}export const GuruluEvents = {
25334
- ${enumLines.join(`
25335
- `)}
25336
- } as const;
25337
- export type GuruluEvents = (typeof GuruluEvents)[keyof typeof GuruluEvents];
25338
-
25339
- export interface EventProperties {
25340
- ${mapLines.join(`
25446
+ var OWL = [" ╲▏▔▔▔▔▏╱ ", " ▕ ◉ ◉ ▏ ", " ▕ ╲╱ ▏ ", " ╲▁▁▁▁╱ "];
25447
+ var WORD = ["█▀▀ █▀▄ █ █ █", "█▄█ █▄█ █▀▄ █▄█ █▄▄ █▄█"];
25448
+ function banner(subtitle = "truth layer · otonom kurulum") {
25449
+ if (noColor)
25450
+ return `\uD83E\uDD89 Gurulu — ${subtitle}`;
25451
+ const owlC = OWL.map((l2) => c3.neon(l2));
25452
+ const wordC = WORD.map((l2) => gradient(l2));
25453
+ const right = ["", wordC[0] ?? "", wordC[1] ?? "", c3.gray(subtitle)];
25454
+ const lines = owlC.map((owl, i2) => right[i2] ? `${owl} ${right[i2]}` : owl);
25455
+ return `
25456
+ ${lines.join(`
25341
25457
  `)}
25342
- }
25343
-
25344
- /**
25345
- * Typed track — compile-time validation against pulled registry.
25346
- * Runtime implementation: @gurulu/web or @gurulu/node.
25347
- */
25348
- export declare function track<E extends GuruluEvents>(
25349
- event: E,
25350
- properties: EventProperties[E],
25351
- ): Promise<void>;
25352
- `;
25353
- }
25354
- function generateManifestLock(manifest) {
25355
- return `${manifest.manifest_version}
25356
25458
  `;
25357
25459
  }
25358
25460
 
25359
- // src/commands/pull.ts
25360
- async function runPull(opts = {}) {
25361
- const cwd = opts.cwd ?? process.cwd();
25362
- const project = readProjectConfig(cwd);
25363
- if (!project) {
25364
- throw new Error("no .gurulu/config.json — run `gurulu init` first");
25365
- }
25366
- const cred = resolveActiveCredential({
25367
- workspaceId: opts.workspaceId ?? project.workspace_id,
25368
- cwd
25369
- });
25370
- if (!cred) {
25371
- throw new Error("no credentials — run `gurulu login` or set GURULU_API_KEY env (workspace sk_xxx)");
25372
- }
25373
- const client = new ApiClient({
25374
- endpoint: project.endpoint ?? cred.endpoint,
25375
- apiKey: cred.apiKey
25376
- });
25377
- const manifest = await client.get("/v1/cli/registry/pull", {
25378
- workspace_id: cred.workspaceId
25379
- });
25380
- writeFileSync5(projectRegistryPath(cwd), `${JSON.stringify(manifest, null, 2)}
25381
- `, "utf-8");
25382
- const ts = generateTypescript(manifest);
25383
- writeFileSync5(projectGeneratedPath(cwd), ts, "utf-8");
25384
- writeFileSync5(projectManifestLockPath(cwd), generateManifestLock(manifest), "utf-8");
25385
- return manifest;
25386
- }
25387
- var pullCmd = defineCommand({
25388
- meta: {
25389
- name: "pull",
25390
- description: "Pull registry snapshot + code-gen typed events"
25391
- },
25392
- args: {
25393
- workspace: { type: "string", description: "Workspace ID (override project config)" }
25394
- },
25395
- async run({ args }) {
25396
- try {
25397
- const manifest = await runPull({
25398
- ...args.workspace ? { workspaceId: String(args.workspace) } : {}
25399
- });
25400
- console.log(`[gurulu] pulled ${manifest.events.length} events (manifest ${manifest.manifest_version})`);
25401
- console.log(` → ${projectRegistryPath()}`);
25402
- console.log(` → ${projectGeneratedPath()}`);
25403
- console.log(` → ${projectManifestLockPath()}`);
25404
- } catch (err) {
25405
- console.error(`[gurulu] pull failed: ${err instanceof Error ? err.message : String(err)}`);
25406
- process.exit(1);
25407
- }
25408
- }
25409
- });
25410
-
25411
25461
  // src/wizard/apply.ts
25412
25462
  async function registerNewEvents(client, events) {
25413
25463
  const registered = [];
@@ -25718,16 +25768,17 @@ async function renderPlan(plan) {
25718
25768
 
25719
25769
  // src/wizard/wire.ts
25720
25770
  import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
25721
- import { join as join10 } from "node:path";
25722
25771
 
25723
25772
  // src/wizard/agent.ts
25724
25773
  import { execFile } from "node:child_process";
25725
25774
  import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
25726
- import { join as join9 } from "node:path";
25727
25775
  import { promisify } from "node:util";
25728
25776
 
25729
25777
  // src/wizard/guard.ts
25730
25778
  import { basename, isAbsolute, relative as relative2, resolve } from "node:path";
25779
+ function resolveInCwd(p3, cwd) {
25780
+ return isAbsolute(p3) ? p3 : resolve(cwd, p3);
25781
+ }
25731
25782
  function isAdditiveEdit(find, replace) {
25732
25783
  if (find.length === 0)
25733
25784
  return { ok: false, reason: "empty find" };
@@ -25738,22 +25789,45 @@ function isAdditiveEdit(find, replace) {
25738
25789
  return { ok: false, reason: "no-op edit (replace === find)" };
25739
25790
  return { ok: true };
25740
25791
  }
25741
- var BASH_ALLOW_BINS = new Set([
25742
- "bun",
25743
- "bunx",
25744
- "npm",
25745
- "npx",
25746
- "pnpm",
25747
- "yarn",
25792
+ var CHECKER_BINS = new Set([
25748
25793
  "tsc",
25749
25794
  "tsgo",
25795
+ "vue-tsc",
25796
+ "svelte-check",
25750
25797
  "biome",
25751
25798
  "eslint",
25752
25799
  "prettier",
25753
- "vue-tsc",
25754
- "svelte-check",
25755
25800
  "astro"
25756
25801
  ]);
25802
+ var RUNNER_BINS = new Set(["bun", "npm", "pnpm", "yarn"]);
25803
+ var DENY_SUBCMDS = new Set([
25804
+ "install",
25805
+ "i",
25806
+ "add",
25807
+ "remove",
25808
+ "rm",
25809
+ "uninstall",
25810
+ "un",
25811
+ "ci",
25812
+ "dlx",
25813
+ "x",
25814
+ "exec",
25815
+ "create",
25816
+ "init",
25817
+ "up",
25818
+ "update",
25819
+ "upgrade",
25820
+ "link",
25821
+ "unlink",
25822
+ "global",
25823
+ "dedupe",
25824
+ "audit",
25825
+ "publish",
25826
+ "pack",
25827
+ "import",
25828
+ "config"
25829
+ ]);
25830
+ var SCRIPT_NAME = /^[a-z0-9][a-z0-9:._-]*$/i;
25757
25831
  var BASH_DENY = /[;&|`$<>]|\.\.\/|\b(rm|curl|wget|sudo|chmod|chown|mv|dd|kill|eval|sh|bash|node|python)\b/;
25758
25832
  function isAllowedBash(cmd) {
25759
25833
  const t2 = cmd.trim();
@@ -25761,10 +25835,24 @@ function isAllowedBash(cmd) {
25761
25835
  return { ok: false, reason: "empty cmd" };
25762
25836
  if (BASH_DENY.test(t2))
25763
25837
  return { ok: false, reason: "yasak operatör/binary" };
25764
- const bin = t2.split(/\s+/)[0] ?? "";
25765
- if (!BASH_ALLOW_BINS.has(bin))
25766
- return { ok: false, reason: `allowlist dışı binary: ${bin}` };
25767
- return { ok: true };
25838
+ const tokens = t2.split(/\s+/);
25839
+ const bin = tokens[0] ?? "";
25840
+ if (CHECKER_BINS.has(bin))
25841
+ return { ok: true };
25842
+ if (RUNNER_BINS.has(bin)) {
25843
+ const sub = tokens[1] ?? "";
25844
+ if (sub === "run") {
25845
+ const script = tokens[2] ?? "";
25846
+ if (!SCRIPT_NAME.test(script))
25847
+ return { ok: false, reason: `geçersiz script adı: ${script}` };
25848
+ return { ok: true };
25849
+ }
25850
+ if (bin === "yarn" && sub && !DENY_SUBCMDS.has(sub) && SCRIPT_NAME.test(sub)) {
25851
+ return { ok: true };
25852
+ }
25853
+ return { ok: false, reason: `yasak alt-komut (sadece 'run' izinli): ${bin} ${sub}` };
25854
+ }
25855
+ return { ok: false, reason: `allowlist dışı binary: ${bin}` };
25768
25856
  }
25769
25857
  function isAllowedPath(p3, cwd) {
25770
25858
  const abs = isAbsolute(p3) ? p3 : resolve(cwd, p3);
@@ -25784,6 +25872,31 @@ function hasPromptInjection(content) {
25784
25872
  // src/wizard/agent.ts
25785
25873
  var MAX_OBS = 4000;
25786
25874
  var pexec = promisify(execFile);
25875
+ function safeEnv() {
25876
+ const keep = [
25877
+ "PATH",
25878
+ "HOME",
25879
+ "USERPROFILE",
25880
+ "TMPDIR",
25881
+ "TEMP",
25882
+ "TMP",
25883
+ "LANG",
25884
+ "LC_ALL",
25885
+ "TERM",
25886
+ "SHELL",
25887
+ "NODE_ENV",
25888
+ "PATHEXT",
25889
+ "SystemRoot",
25890
+ "ComSpec"
25891
+ ];
25892
+ const env2 = {};
25893
+ for (const k2 of keep) {
25894
+ const v2 = process.env[k2];
25895
+ if (v2 !== undefined)
25896
+ env2[k2] = v2;
25897
+ }
25898
+ return env2;
25899
+ }
25787
25900
  async function defaultRunBash(cmd, cwd) {
25788
25901
  const parts = cmd.trim().split(/\s+/);
25789
25902
  const bin = parts[0] ?? "";
@@ -25791,7 +25904,8 @@ async function defaultRunBash(cmd, cwd) {
25791
25904
  const { stdout: stdout2, stderr } = await pexec(bin, parts.slice(1), {
25792
25905
  cwd,
25793
25906
  timeout: 120000,
25794
- maxBuffer: 4 * 1024 * 1024
25907
+ maxBuffer: 4 * 1024 * 1024,
25908
+ env: safeEnv()
25795
25909
  });
25796
25910
  return { stdout: stdout2, stderr };
25797
25911
  } catch (e2) {
@@ -25806,7 +25920,7 @@ async function executeTool(action, deps) {
25806
25920
  const g3 = isAllowedPath(action.path, cwd);
25807
25921
  if (!g3.ok)
25808
25922
  return { ok: false, observation: `read reddedildi: ${g3.reason}` };
25809
- const abs = join9(cwd, action.path);
25923
+ const abs = resolveInCwd(action.path, cwd);
25810
25924
  if (!existsSync7(abs))
25811
25925
  return { ok: false, observation: `dosya yok: ${action.path}` };
25812
25926
  let content = readFileSync8(abs, "utf-8");
@@ -25824,7 +25938,7 @@ async function executeTool(action, deps) {
25824
25938
  const ga = isAdditiveEdit(action.find, action.replace);
25825
25939
  if (!ga.ok)
25826
25940
  return { ok: false, observation: `edit reddedildi: ${ga.reason}` };
25827
- const abs = join9(cwd, action.path);
25941
+ const abs = resolveInCwd(action.path, cwd);
25828
25942
  if (!existsSync7(abs))
25829
25943
  return { ok: false, observation: `dosya yok: ${action.path}` };
25830
25944
  const src2 = readFileSync8(abs, "utf-8");
@@ -25860,7 +25974,9 @@ function buildWireSystemPrompt() {
25860
25974
  "- `find` must be an EXACT, UNIQUE snippet copied from a file you have read (read before edit).",
25861
25975
  "- Use the exact provided event_key (snake_case). Wire gurulu.track(...) at the right place and",
25862
25976
  " gurulu.identify(...) at the auth point if given.",
25863
- "- bash only for verification (typecheck/build/lint) no install, no other commands.",
25977
+ "- bash is ONLY for verification: the project's own scripts (`npm run typecheck`, `bun run build`,",
25978
+ " `pnpm run lint`) or a checker binary (`tsc --noEmit`, `biome check`, `eslint .`). Installing or",
25979
+ " fetching packages (`npm install`, `npx`, `bunx`, `pnpm dlx`, `add`, `exec`) is REJECTED by the guard.",
25864
25980
  "- After wiring, run the project typecheck/build to verify; if it breaks, fix additively.",
25865
25981
  "- When all events are wired and verify passes, emit done{summary}. Keep edits minimal."
25866
25982
  ].join(`
@@ -25894,17 +26010,35 @@ async function runWireAgent(client, input, snapshots) {
25894
26010
  try {
25895
26011
  res = await client.agentStep({ messages, first: i2 === 0 });
25896
26012
  } catch {
25897
- return { edits, changedFiles: [...changed], summary: "gateway hata", steps: i2, stoppedReason: "error" };
26013
+ return {
26014
+ edits,
26015
+ changedFiles: [...changed],
26016
+ summary: "gateway hata",
26017
+ steps: i2,
26018
+ stoppedReason: "error"
26019
+ };
25898
26020
  }
25899
26021
  if (res.status === "stub") {
25900
- return { edits, changedFiles: [...changed], summary: "AI kullanılamadı", steps: i2, stoppedReason: "stub" };
26022
+ return {
26023
+ edits,
26024
+ changedFiles: [...changed],
26025
+ summary: "AI kullanılamadı",
26026
+ steps: i2,
26027
+ stoppedReason: "stub"
26028
+ };
25901
26029
  }
25902
26030
  const { action, reasoning } = res.step;
25903
26031
  if (action.tool === "done") {
25904
- return { edits, changedFiles: [...changed], summary: action.summary, steps: i2, stoppedReason: "done" };
26032
+ return {
26033
+ edits,
26034
+ changedFiles: [...changed],
26035
+ summary: action.summary,
26036
+ steps: i2,
26037
+ stoppedReason: "done"
26038
+ };
25905
26039
  }
25906
26040
  if (action.tool === "edit") {
25907
- const abs = join10(input.cwd, action.path);
26041
+ const abs = resolveInCwd(action.path, input.cwd);
25908
26042
  if (!snapshots.has(action.path) && existsSync8(abs)) {
25909
26043
  snapshots.set(action.path, readFileSync9(abs, "utf-8"));
25910
26044
  }
@@ -25917,11 +26051,17 @@ async function runWireAgent(client, input, snapshots) {
25917
26051
  messages.push({ role: "assistant", content: JSON.stringify(res.step) });
25918
26052
  messages.push({ role: "user", content: `[${reasoning}] → ${out.observation}` });
25919
26053
  }
25920
- return { edits, changedFiles: [...changed], summary: "adım limiti", steps: MAX_STEPS, stoppedReason: "cap" };
26054
+ return {
26055
+ edits,
26056
+ changedFiles: [...changed],
26057
+ summary: "adım limiti",
26058
+ steps: MAX_STEPS,
26059
+ stoppedReason: "cap"
26060
+ };
25921
26061
  }
25922
26062
  function restoreSnapshots(cwd, snapshots) {
25923
26063
  for (const [rel, content] of snapshots) {
25924
- writeFileSync7(join10(cwd, rel), content, "utf-8");
26064
+ writeFileSync7(resolveInCwd(rel, cwd), content, "utf-8");
25925
26065
  }
25926
26066
  }
25927
26067
  function unifiedDiff(oldStr, newStr, file, context = 2) {
@@ -25953,7 +26093,7 @@ function unifiedDiff(oldStr, newStr, file, context = 2) {
25953
26093
  function formatWireDiff(cwd, snapshots) {
25954
26094
  const blocks = [];
25955
26095
  for (const [rel, oldContent] of snapshots) {
25956
- const abs = join10(cwd, rel);
26096
+ const abs = resolveInCwd(rel, cwd);
25957
26097
  const cur = existsSync8(abs) ? readFileSync9(abs, "utf-8") : "";
25958
26098
  if (cur !== oldContent)
25959
26099
  blocks.push(unifiedDiff(oldContent, cur, rel));
@@ -25983,14 +26123,23 @@ function bail() {
25983
26123
  process.exit(0);
25984
26124
  }
25985
26125
  async function runWizard(opts) {
25986
- p3.intro("\uD83E\uDD89 Gurulu kurulum sihirbazı");
26126
+ if (process.stdout.isTTY)
26127
+ process.stdout.write(banner());
26128
+ p3.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
26129
+ const TOTAL = 6;
26130
+ const phase = (n2, label) => {
26131
+ p3.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
26132
+ };
26133
+ phase(1, "Kimlik doğrulama");
25987
26134
  const auth = await ensureAuth({
25988
26135
  ...opts.endpoint ? { endpoint: opts.endpoint } : {},
25989
26136
  ...opts.apiKey ? { apiKey: opts.apiKey } : {},
25990
26137
  ...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
25991
26138
  });
25992
26139
  const client = new ApiClient({ endpoint: auth.endpoint, apiKey: auth.apiKey });
26140
+ phase(2, "Workspace");
25993
26141
  const { workspaceId, writeKey } = await resolveWorkspace(client, auth.workspaceId, opts);
26142
+ phase(3, "Proje tespiti & plan");
25994
26143
  const detected = detectProject(opts.cwd);
25995
26144
  let framework = detected.framework;
25996
26145
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
@@ -26015,9 +26164,10 @@ async function runWizard(opts) {
26015
26164
  const project = { ...detected, framework };
26016
26165
  const plan = buildInstallPlan(project, { writeKey, workspaceId });
26017
26166
  const isNode = plan.sdk === "@gurulu/node";
26167
+ const authed = Boolean(auth.apiKey);
26018
26168
  let approvedEvents = [];
26019
26169
  let identifyHint = null;
26020
- if (!opts.noAi && detected.hasPackageJson) {
26170
+ if (!opts.noAi && authed && detected.hasPackageJson) {
26021
26171
  const ctx = gatherContext({ cwd: opts.cwd });
26022
26172
  const aiPlan = await fetchPlan(client, ctx, { framework });
26023
26173
  if (aiPlan) {
@@ -26029,6 +26179,7 @@ async function runWizard(opts) {
26029
26179
  p3.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
26030
26180
  }
26031
26181
  }
26182
+ phase(4, "SDK kurulumu");
26032
26183
  let installed = false;
26033
26184
  if (opts.noInstall) {
26034
26185
  p3.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
@@ -26045,6 +26196,7 @@ async function runWizard(opts) {
26045
26196
  s2.stop(`Kurulum başarısız — elle çalıştır: ${plan.installCommand}`, 1);
26046
26197
  }
26047
26198
  }
26199
+ phase(5, "Kod & env wiring");
26048
26200
  const inj = applyInjection({
26049
26201
  cwd: opts.cwd,
26050
26202
  detected: project,
@@ -26055,10 +26207,15 @@ async function runWizard(opts) {
26055
26207
  const envFile = isNode ? ".env" : ".env.local";
26056
26208
  const vars = [];
26057
26209
  for (const k2 of plan.envKeys) {
26058
- if (k2.key.endsWith("_WORKSPACE"))
26210
+ if (k2.key.endsWith("_WORKSPACE")) {
26059
26211
  vars.push({ key: k2.key, value: writeKey });
26060
- else if (k2.key === "GURULU_SECRET_KEY")
26061
- vars.push({ key: k2.key, value: auth.apiKey });
26212
+ } else if (k2.key === "GURULU_SECRET_KEY") {
26213
+ if (auth.apiKey.startsWith("sk_")) {
26214
+ vars.push({ key: k2.key, value: auth.apiKey });
26215
+ } 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.");
26217
+ }
26218
+ }
26062
26219
  }
26063
26220
  const envRes = vars.length > 0 ? writeEnvFile({ cwd: opts.cwd, file: envFile, vars }) : null;
26064
26221
  writeProjectScaffold(opts.cwd, {
@@ -26066,6 +26223,7 @@ async function runWizard(opts) {
26066
26223
  endpoint: auth.endpoint,
26067
26224
  sdkPref: isNode ? "node" : "web"
26068
26225
  });
26226
+ phase(6, "Registry & rapor");
26069
26227
  let pulled = false;
26070
26228
  if (!opts.noPull) {
26071
26229
  try {
@@ -26095,7 +26253,9 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26095
26253
  s2.stop(`wire: ${outcome.changedFiles.length} dosya / ${outcome.steps} adım (${outcome.stoppedReason})`);
26096
26254
  if (outcome.changedFiles.length > 0) {
26097
26255
  p3.log.message(formatWireDiff(opts.cwd, snapshots));
26098
- const keep = opts.yes ? true : await p3.confirm({ message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?` });
26256
+ const keep = opts.yes ? true : await p3.confirm({
26257
+ message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?`
26258
+ });
26099
26259
  if (p3.isCancel(keep) || !keep) {
26100
26260
  restoreSnapshots(opts.cwd, snapshots);
26101
26261
  p3.log.info("Wire geri alındı — capture snippet rehberi:");
@@ -26125,13 +26285,13 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
26125
26285
  }
26126
26286
  lines.push(`✓ .gurulu/config.json${pulled ? " + registry pull" : ""}`);
26127
26287
  p3.note(lines.join(`
26128
- `), "Değişiklikler");
26288
+ `), c3.neon("Değişiklikler"));
26129
26289
  if (inj.strategy !== "prepend-entry" || inj.reason === "no-entry") {
26130
26290
  p3.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
26131
26291
  if (inj.strategy === "manual")
26132
26292
  p3.log.message(plan.initSnippet);
26133
26293
  }
26134
- p3.outro(`Hazır \uD83C\uDF89 Doğrula: gurulu doctor · Dashboard: https://dashboard.gurulu.io/app?onboard=done`);
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")}`);
26135
26295
  }
26136
26296
  async function resolveWorkspace(client, authWorkspaceId, opts) {
26137
26297
  if (opts.writeKey) {
@@ -26241,11 +26401,19 @@ var wizardArgs = {
26241
26401
  framework: { type: "string", description: "Framework override (auto-detect yerine)" },
26242
26402
  install: { type: "boolean", description: "SDK install (--no-install ile atla)", default: true },
26243
26403
  pull: { type: "boolean", description: "İlk registry pull (--no-pull ile atla)", default: true },
26244
- ai: { type: "boolean", description: "AI Plan/wire fazı (--no-ai ile atla → floor)", default: true },
26404
+ ai: {
26405
+ type: "boolean",
26406
+ description: "AI Plan/wire fazı (--no-ai ile atla → floor)",
26407
+ default: true
26408
+ },
26245
26409
  yes: { type: "boolean", description: "Onayları otomatik geç" },
26246
26410
  ci: { type: "boolean", description: "Non-interaktif (api-key + workspace zorunlu)" }
26247
26411
  };
26248
26412
  async function runWizardFromArgs(args) {
26413
+ if (args.ci && (!args["api-key"] || !args.workspace)) {
26414
+ console.error("[gurulu] --ci için --api-key <sk_...> ve --workspace <uuid> zorunlu (non-interaktif kurulum).");
26415
+ process.exit(1);
26416
+ }
26249
26417
  const opts = {
26250
26418
  cwd: process.cwd(),
26251
26419
  noInstall: args.install === false,
@@ -26279,7 +26447,7 @@ var initCmd = defineCommand({
26279
26447
  // src/lib/editor-mcp.ts
26280
26448
  import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
26281
26449
  import { homedir as homedir2 } from "node:os";
26282
- import { dirname as dirname4, join as join11 } from "node:path";
26450
+ import { dirname as dirname4, join as join9 } from "node:path";
26283
26451
  var SERVER_NAME = "gurulu";
26284
26452
  function buildMcpServerConfig(creds) {
26285
26453
  return {
@@ -26293,14 +26461,14 @@ function buildMcpServerConfig(creds) {
26293
26461
  };
26294
26462
  }
26295
26463
  var EDITORS = {
26296
- cursor: { path: () => join11(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26297
- claude: { path: () => join11(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26464
+ cursor: { path: () => join9(homedir2(), ".cursor", "mcp.json"), key: "mcpServers", label: "Cursor" },
26465
+ claude: { path: () => join9(homedir2(), ".claude.json"), key: "mcpServers", label: "Claude Code" },
26298
26466
  windsurf: {
26299
- path: () => join11(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26467
+ path: () => join9(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
26300
26468
  key: "mcpServers",
26301
26469
  label: "Windsurf"
26302
26470
  },
26303
- vscode: { path: (cwd) => join11(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26471
+ vscode: { path: (cwd) => join9(cwd, ".vscode", "mcp.json"), key: "servers", label: "VS Code" }
26304
26472
  };
26305
26473
  function mergeMcpConfig(existing, serverConfig, key) {
26306
26474
  const servers = existing[key] ?? {};
@@ -26496,7 +26664,7 @@ var pushCmd = defineCommand({
26496
26664
  // src/commands/uninstall.ts
26497
26665
  import { execFile as execFile2 } from "node:child_process";
26498
26666
  import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
26499
- import { join as join12 } from "node:path";
26667
+ import { join as join10 } from "node:path";
26500
26668
  import { promisify as promisify2 } from "node:util";
26501
26669
  import * as p4 from "@clack/prompts";
26502
26670
  var pexec2 = promisify2(execFile2);
@@ -26552,7 +26720,7 @@ var uninstallCmd = defineCommand({
26552
26720
  }
26553
26721
  const cleaned = [];
26554
26722
  for (const f3 of ENV_FILES) {
26555
- const abs = join12(cwd, f3);
26723
+ const abs = join10(cwd, f3);
26556
26724
  if (!existsSync11(abs))
26557
26725
  continue;
26558
26726
  const { content, removed } = removeEnvKeys(readFileSync11(abs, "utf-8"), GURULU_PREFIXES);
@@ -26561,7 +26729,7 @@ var uninstallCmd = defineCommand({
26561
26729
  cleaned.push(`${f3} (-${removed.length})`);
26562
26730
  }
26563
26731
  }
26564
- const guruluDir = join12(cwd, ".gurulu");
26732
+ const guruluDir = join10(cwd, ".gurulu");
26565
26733
  if (existsSync11(guruluDir))
26566
26734
  rmSync(guruluDir, { recursive: true, force: true });
26567
26735
  p4.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
@@ -26569,7 +26737,7 @@ var uninstallCmd = defineCommand({
26569
26737
  });
26570
26738
 
26571
26739
  // src/index.ts
26572
- var VERSION = "1.2.0";
26740
+ var VERSION = "1.2.2";
26573
26741
  var mainCmd = defineCommand({
26574
26742
  meta: {
26575
26743
  name: "gurulu",