lovecode-ai 0.1.2 → 0.1.4

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.
@@ -0,0 +1,86 @@
1
+ // src/config/env.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import chalk from "chalk";
5
+ var ENV_FILENAME = ".env";
6
+ var ENV_EXAMPLE = ".env.example";
7
+ var KNOWN_ENV_VARS = [
8
+ { key: "GROQ_API_KEY", value: "", description: "Groq API key for LLM access", required: false },
9
+ { key: "OPENROUTER_API_KEY", value: "", description: "OpenRouter API key for LLM access", required: false },
10
+ { key: "TOGETHER_API_KEY", value: "", description: "Together AI API key", required: false },
11
+ { key: "HUGGINGFACE_API_KEY", value: "", description: "HuggingFace API key", required: false },
12
+ { key: "OPENAI_API_KEY", value: "", description: "OpenAI API key (fallback)", required: false },
13
+ { key: "OLLAMA_URL", value: "http://localhost:11434", description: "Ollama server URL", required: false },
14
+ { key: "LOVECODE_LOG_LEVEL", value: "info", description: "Log level (debug|info|warn|error)", required: false },
15
+ { key: "LOVECODE_MODEL", value: "", description: "Override default model", required: false },
16
+ { key: "LOVECODE_THEME", value: "", description: "Override default theme", required: false },
17
+ { key: "LOVECODE_APPROVAL_MODE", value: "", description: "Override approval mode (smart|strict|permissive)", required: false },
18
+ { key: "LOVECODE_TELEMETRY", value: "", description: "Enable telemetry (true|false)", required: false }
19
+ ];
20
+ function loadEnv(rootDir) {
21
+ const envPath = path.resolve(rootDir || process.cwd(), ENV_FILENAME);
22
+ const vars = {};
23
+ if (fs.existsSync(envPath)) {
24
+ const content = fs.readFileSync(envPath, "utf-8");
25
+ for (const line of content.split("\n")) {
26
+ const trimmed = line.trim();
27
+ if (!trimmed || trimmed.startsWith("#")) continue;
28
+ const eqIdx = trimmed.indexOf("=");
29
+ if (eqIdx > 0) {
30
+ const key = trimmed.slice(0, eqIdx).trim();
31
+ let value = trimmed.slice(eqIdx + 1).trim();
32
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
33
+ value = value.slice(1, -1);
34
+ }
35
+ vars[key] = value;
36
+ }
37
+ }
38
+ }
39
+ for (const v of KNOWN_ENV_VARS) {
40
+ const envVal = process.env[v.key];
41
+ if (envVal) vars[v.key] = envVal;
42
+ }
43
+ return vars;
44
+ }
45
+ function saveEnv(vars, rootDir) {
46
+ const envPath = path.resolve(rootDir || process.cwd(), ENV_FILENAME);
47
+ const dir = path.dirname(envPath);
48
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
49
+ const lines = ["# LoveCode AI - Environment Configuration", `# Created: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, ""];
50
+ for (const v of KNOWN_ENV_VARS) {
51
+ const value = vars[v.key] !== void 0 ? vars[v.key] : v.value;
52
+ lines.push(`# ${v.description}`);
53
+ lines.push(`${v.key}=${value}`);
54
+ lines.push("");
55
+ }
56
+ fs.writeFileSync(envPath, lines.join("\n"), "utf-8");
57
+ }
58
+ function saveEnvExample(rootDir) {
59
+ const exPath = path.resolve(rootDir || process.cwd(), ENV_EXAMPLE);
60
+ const lines = ["# LoveCode AI - Environment Configuration Example", "# Copy this file to .env and fill in your values", ""];
61
+ for (const v of KNOWN_ENV_VARS) {
62
+ lines.push(`# ${v.description}`);
63
+ lines.push(v.required ? `${v.key}=` : `# ${v.key}=`);
64
+ lines.push("");
65
+ }
66
+ fs.writeFileSync(exPath, lines.join("\n"), "utf-8");
67
+ }
68
+ function formatEnvStatus(rootDir) {
69
+ const vars = loadEnv(rootDir);
70
+ const lines = [chalk.bold("\n Environment Variables")];
71
+ for (const v of KNOWN_ENV_VARS) {
72
+ const val = vars[v.key] || process.env[v.key] || "";
73
+ const set = val.length > 0;
74
+ const masked = set && v.key.includes("KEY") ? val.slice(0, 8) + "*".repeat(Math.min(val.length - 8, 20)) : val || "(not set)";
75
+ lines.push(` ${set ? chalk.green("\u2713") : chalk.dim("\u25CB")} ${chalk.dim(v.key.padEnd(25))} ${masked}`);
76
+ }
77
+ return lines.join("\n");
78
+ }
79
+
80
+ export {
81
+ KNOWN_ENV_VARS,
82
+ loadEnv,
83
+ saveEnv,
84
+ saveEnvExample,
85
+ formatEnvStatus
86
+ };
@@ -1,3 +1,8 @@
1
+ import {
2
+ getDefaultModel,
3
+ resolveModel
4
+ } from "./chunk-Y3HADLWO.js";
5
+
1
6
  // src/git/commands.ts
2
7
  import { execSync } from "child_process";
3
8
  import * as fs from "fs";
@@ -279,354 +284,6 @@ function abbreviateDiff(diff, maxLines = 100) {
279
284
  ... (${lines.length - maxLines} more lines)`;
280
285
  }
281
286
 
282
- // src/ai/registry.ts
283
- import chalk2 from "chalk";
284
-
285
- // src/utils/logger.ts
286
- import chalk from "chalk";
287
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
288
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
289
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
290
- LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
291
- LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
292
- return LogLevel2;
293
- })(LogLevel || {});
294
- var LOG_LEVEL = (process.env.LOVECODE_LOG_LEVEL || "info").toUpperCase();
295
- var currentLevel = LogLevel[LOG_LEVEL] ?? 1 /* INFO */;
296
- var Logger = class {
297
- static debug(...args) {
298
- if (currentLevel <= 0 /* DEBUG */) {
299
- console.error(chalk.dim("[debug]"), ...args);
300
- }
301
- }
302
- static info(...args) {
303
- if (currentLevel <= 1 /* INFO */) {
304
- console.error(chalk.blue("[info]"), ...args);
305
- }
306
- }
307
- static warn(...args) {
308
- if (currentLevel <= 2 /* WARN */) {
309
- console.error(chalk.yellow("[warn]"), ...args);
310
- }
311
- }
312
- static error(...args) {
313
- if (currentLevel <= 3 /* ERROR */) {
314
- console.error(chalk.red("[error]"), ...args);
315
- }
316
- }
317
- };
318
-
319
- // src/ai/ollama.ts
320
- var OllamaProvider = class {
321
- name = "ollama";
322
- async chat(messages, config) {
323
- const baseUrl = config.baseUrl || "http://localhost:11434";
324
- Logger.debug(`Ollama chat: ${config.model} (${messages.length} messages)`);
325
- const response = await fetch(`${baseUrl}/api/chat`, {
326
- method: "POST",
327
- headers: { "Content-Type": "application/json" },
328
- body: JSON.stringify({
329
- model: config.model,
330
- messages,
331
- stream: false,
332
- options: {
333
- temperature: config.temperature ?? 0.2,
334
- num_predict: config.maxTokens ?? 4096
335
- }
336
- })
337
- });
338
- if (!response.ok) {
339
- throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
340
- }
341
- const data = await response.json();
342
- return data.message?.content ?? "";
343
- }
344
- async *stream(messages, config) {
345
- const baseUrl = config.baseUrl || "http://localhost:11434";
346
- const response = await fetch(`${baseUrl}/api/chat`, {
347
- method: "POST",
348
- headers: { "Content-Type": "application/json" },
349
- body: JSON.stringify({
350
- model: config.model,
351
- messages,
352
- stream: true,
353
- options: {
354
- temperature: config.temperature ?? 0.2,
355
- num_predict: config.maxTokens ?? 4096
356
- }
357
- })
358
- });
359
- if (!response.ok) {
360
- throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
361
- }
362
- const reader = response.body?.getReader();
363
- if (!reader) return;
364
- const decoder = new TextDecoder();
365
- let buffer = "";
366
- while (true) {
367
- const { done, value } = await reader.read();
368
- if (done) break;
369
- buffer += decoder.decode(value, { stream: true });
370
- const lines = buffer.split("\n");
371
- buffer = lines.pop() ?? "";
372
- for (const line of lines) {
373
- if (!line.trim()) continue;
374
- try {
375
- const parsed = JSON.parse(line);
376
- if (parsed.done) return;
377
- if (parsed.message?.content) {
378
- yield parsed.message.content;
379
- }
380
- } catch {
381
- }
382
- }
383
- }
384
- }
385
- async isAvailable(baseUrl) {
386
- try {
387
- const res = await fetch(`${baseUrl || "http://localhost:11434"}/api/tags`);
388
- return res.ok;
389
- } catch {
390
- return false;
391
- }
392
- }
393
- };
394
-
395
- // src/ai/openai-like.ts
396
- var OpenAILikeProvider = class {
397
- name;
398
- baseUrl;
399
- constructor(name, baseUrl) {
400
- this.name = name;
401
- this.baseUrl = baseUrl.replace(/\/+$/, "");
402
- }
403
- async chat(messages, config) {
404
- const url = `${this.baseUrl}/chat/completions`;
405
- Logger.debug(`[${this.name}] chat: ${config.model} (${messages.length} messages)`);
406
- const apiKey = this.getApiKey();
407
- const response = await fetch(url, {
408
- method: "POST",
409
- headers: {
410
- "Content-Type": "application/json",
411
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
412
- },
413
- body: JSON.stringify({
414
- model: config.model,
415
- messages,
416
- temperature: config.temperature ?? 0.2,
417
- max_tokens: config.maxTokens ?? 4096,
418
- stream: false
419
- })
420
- });
421
- if (!response.ok) {
422
- const body = await response.text().catch(() => "");
423
- throw new Error(`[${this.name}] API error ${response.status}: ${body.slice(0, 200)}`);
424
- }
425
- const data = await response.json();
426
- return data.choices?.[0]?.message?.content ?? "";
427
- }
428
- async *stream(messages, config) {
429
- const url = `${this.baseUrl}/chat/completions`;
430
- const apiKey = this.getApiKey();
431
- const response = await fetch(url, {
432
- method: "POST",
433
- headers: {
434
- "Content-Type": "application/json",
435
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
436
- },
437
- body: JSON.stringify({
438
- model: config.model,
439
- messages,
440
- temperature: config.temperature ?? 0.2,
441
- max_tokens: config.maxTokens ?? 4096,
442
- stream: true
443
- })
444
- });
445
- if (!response.ok) {
446
- const body = await response.text().catch(() => "");
447
- throw new Error(`[${this.name}] API error ${response.status}: ${body.slice(0, 200)}`);
448
- }
449
- const reader = response.body?.getReader();
450
- if (!reader) return;
451
- const decoder = new TextDecoder();
452
- let buffer = "";
453
- while (true) {
454
- const { done, value } = await reader.read();
455
- if (done) break;
456
- buffer += decoder.decode(value, { stream: true });
457
- const lines = buffer.split("\n");
458
- buffer = lines.pop() ?? "";
459
- for (const line of lines) {
460
- const trimmed = line.trim();
461
- if (!trimmed || !trimmed.startsWith("data: ")) continue;
462
- const jsonStr = trimmed.slice(6).trim();
463
- if (jsonStr === "[DONE]") return;
464
- try {
465
- const parsed = JSON.parse(jsonStr);
466
- const content = parsed.choices?.[0]?.delta?.content;
467
- if (content) yield content;
468
- } catch {
469
- }
470
- }
471
- }
472
- }
473
- getApiKey() {
474
- const envVar = this.name.toUpperCase();
475
- return process.env[`${envVar}_API_KEY`] || process.env.OPENAI_API_KEY;
476
- }
477
- async isAvailable() {
478
- try {
479
- const res = await fetch(`${this.baseUrl}/models`, {
480
- headers: this.getApiKey() ? { Authorization: `Bearer ${this.getApiKey()}` } : {}
481
- });
482
- return res.ok;
483
- } catch {
484
- return false;
485
- }
486
- }
487
- };
488
-
489
- // src/ai/registry.ts
490
- var registry = [
491
- {
492
- name: "ollama",
493
- provider: new OllamaProvider(),
494
- models: ["codellama", "deepseek-coder", "llama3.2", "llama3.1", "mistral", "mixtral", "phi3", "qwen2.5-coder"],
495
- local: true,
496
- priority: 10,
497
- defaultModel: "codellama",
498
- getConfig: (model) => ({
499
- model,
500
- baseUrl: "http://localhost:11434",
501
- temperature: 0.2,
502
- maxTokens: 8192
503
- })
504
- },
505
- {
506
- name: "groq",
507
- provider: new OpenAILikeProvider("groq", "https://api.groq.com/openai/v1"),
508
- models: ["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma2-9b-it", "deepseek-r1-distill-llama-70b"],
509
- local: false,
510
- priority: 30,
511
- defaultModel: "llama3-70b-8192",
512
- getConfig: (model) => ({
513
- model,
514
- baseUrl: "https://api.groq.com/openai/v1",
515
- temperature: 0.2,
516
- maxTokens: 8192
517
- })
518
- },
519
- {
520
- name: "openrouter",
521
- provider: new OpenAILikeProvider("openrouter", "https://openrouter.ai/api/v1"),
522
- models: [
523
- "google/gemini-2.0-flash-001",
524
- "google/gemini-2.0-flash-lite-preview",
525
- "mistralai/mistral-7b-instruct",
526
- "meta-llama/llama-3.2-3b-instruct",
527
- "deepseek/deepseek-chat",
528
- "qwen/qwen-2.5-7b-instruct"
529
- ],
530
- local: false,
531
- priority: 40,
532
- defaultModel: "google/gemini-2.0-flash-001",
533
- getConfig: (model) => ({
534
- model,
535
- baseUrl: "https://openrouter.ai/api/v1",
536
- temperature: 0.2,
537
- maxTokens: 8192
538
- })
539
- },
540
- {
541
- name: "together",
542
- provider: new OpenAILikeProvider("together", "https://api.together.xyz/v1"),
543
- models: [
544
- "mistralai/Mixtral-8x22B-Instruct-v0.1",
545
- "mistralai/Mistral-7B-Instruct-v0.3",
546
- "meta-llama/Llama-3.2-3B-Instruct-Turbo",
547
- "meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo",
548
- "deepseek-ai/deepseek-coder-33b-instruct",
549
- "Qwen/Qwen2.5-7B-Instruct-Turbo"
550
- ],
551
- local: false,
552
- priority: 50,
553
- defaultModel: "mistralai/Mixtral-8x22B-Instruct-v0.1",
554
- getConfig: (model) => ({
555
- model,
556
- baseUrl: "https://api.together.xyz/v1",
557
- temperature: 0.2,
558
- maxTokens: 8192
559
- })
560
- },
561
- {
562
- name: "huggingface",
563
- provider: new OpenAILikeProvider("huggingface", "https://api-inference.huggingface.co/v1"),
564
- models: [
565
- "HuggingFaceH4/zephyr-7b-beta",
566
- "mistralai/Mistral-7B-Instruct-v0.3",
567
- "meta-llama/Meta-Llama-3-8B-Instruct",
568
- "google/gemma-2-9b-it"
569
- ],
570
- local: false,
571
- priority: 60,
572
- defaultModel: "HuggingFaceH4/zephyr-7b-beta",
573
- getConfig: (model) => ({
574
- model,
575
- baseUrl: "https://api-inference.huggingface.co/v1",
576
- temperature: 0.2,
577
- maxTokens: 4096
578
- })
579
- }
580
- ];
581
- function getProvider(name) {
582
- return registry.find((p) => p.name === name);
583
- }
584
- function getProviderForModel(model) {
585
- return registry.find((p) => p.models.includes(model) || p.defaultModel === model);
586
- }
587
- function getLocalProviders() {
588
- return registry.filter((p) => p.local);
589
- }
590
- function resolveModel(modelOrProvider) {
591
- const byName = getProvider(modelOrProvider);
592
- if (byName) {
593
- return { entry: byName, model: byName.defaultModel };
594
- }
595
- const byModel = getProviderForModel(modelOrProvider);
596
- if (byModel) {
597
- return { entry: byModel, model: modelOrProvider };
598
- }
599
- const local = getLocalProviders()[0];
600
- return { entry: local, model: modelOrProvider };
601
- }
602
- function printProviders() {
603
- const lines = [chalk2.bold("\n Available Providers")];
604
- for (const entry of registry) {
605
- const tag = entry.local ? chalk2.green(" LOCAL ") : chalk2.blue(" CLOUD ");
606
- const defaultModel = chalk2.dim(`(default: ${entry.defaultModel})`);
607
- const models = entry.models.slice(0, 4).join(", ");
608
- const more = entry.models.length > 4 ? chalk2.dim(` +${entry.models.length - 4} more`) : "";
609
- lines.push(`
610
- ${tag} ${chalk2.cyan(entry.name.padEnd(12))} ${defaultModel}`);
611
- lines.push(` ${chalk2.dim(models)}${more}`);
612
- }
613
- lines.push("");
614
- return lines.join("\n");
615
- }
616
- function setDefaultModel(model) {
617
- const resolved = resolveModel(model);
618
- resolved.entry.defaultModel = resolved.model;
619
- return { provider: resolved.entry.name, model: resolved.model };
620
- }
621
- function getDefaultModel() {
622
- const highest = [...registry].sort((a, b) => {
623
- if (a.local && !b.local) return -1;
624
- if (!a.local && b.local) return 1;
625
- return a.priority - b.priority;
626
- })[0];
627
- return { provider: highest.name, model: highest.defaultModel };
628
- }
629
-
630
287
  // src/git/message.ts
631
288
  function getDefaultProvider() {
632
289
  const defaultModel = getDefaultModel();
@@ -818,7 +475,6 @@ function formatResolutionSuggestions(suggestions) {
818
475
  }
819
476
 
820
477
  export {
821
- OllamaProvider,
822
478
  isGitAvailable,
823
479
  getGitRoot,
824
480
  isRepo,
@@ -846,9 +502,6 @@ export {
846
502
  formatBranches,
847
503
  formatLog,
848
504
  abbreviateDiff,
849
- getProvider,
850
- printProviders,
851
- setDefaultModel,
852
505
  generateCommitMessage,
853
506
  generatePRSummary,
854
507
  detectConflicts,
@@ -0,0 +1,154 @@
1
+ // src/config/config.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as YAML from "js-yaml";
5
+ import chalk from "chalk";
6
+ var DEFAULT_CONFIG = {
7
+ model: "deepseek",
8
+ theme: "neon",
9
+ approval_mode: "smart",
10
+ performance: {
11
+ lazy_load: true,
12
+ cache_ttl: 3e5,
13
+ max_memory_mb: 200
14
+ },
15
+ termux: {
16
+ low_ram: false,
17
+ touch_optimized: false
18
+ },
19
+ telemetry: {
20
+ enabled: false,
21
+ crash_reports: false
22
+ },
23
+ security: {
24
+ profile: "standard",
25
+ scan_secrets: true
26
+ }
27
+ };
28
+ var cachedConfig = null;
29
+ function configDir(rootDir) {
30
+ return path.resolve(rootDir || process.cwd(), ".lovecode");
31
+ }
32
+ function yamlPath(rootDir) {
33
+ return path.resolve(configDir(rootDir), "config.yaml");
34
+ }
35
+ function jsonPath(rootDir) {
36
+ return path.resolve(configDir(rootDir), "config.json");
37
+ }
38
+ function loadConfig(rootDir) {
39
+ if (cachedConfig) return cachedConfig;
40
+ const merged = { ...DEFAULT_CONFIG };
41
+ const yml = yamlPath(rootDir);
42
+ const json = jsonPath(rootDir);
43
+ let fileConfig = {};
44
+ if (fs.existsSync(yml)) {
45
+ try {
46
+ const raw = fs.readFileSync(yml, "utf-8");
47
+ const parsed = YAML.load(raw);
48
+ if (parsed) fileConfig = parsed;
49
+ } catch (err) {
50
+ console.error(chalk.yellow(`Warning: Failed to parse ${yml}: ${err}`));
51
+ }
52
+ } else if (fs.existsSync(json)) {
53
+ try {
54
+ fileConfig = JSON.parse(fs.readFileSync(json, "utf-8"));
55
+ } catch {
56
+ }
57
+ }
58
+ Object.assign(merged, fileConfig);
59
+ if (process.env.LOVECODE_MODEL) merged.model = process.env.LOVECODE_MODEL;
60
+ if (process.env.LOVECODE_THEME) merged.theme = process.env.LOVECODE_THEME;
61
+ if (process.env.LOVECODE_APPROVAL_MODE) merged.approval_mode = process.env.LOVECODE_APPROVAL_MODE;
62
+ cachedConfig = merged;
63
+ return merged;
64
+ }
65
+ function saveConfig(config, rootDir) {
66
+ const dir = configDir(rootDir);
67
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
68
+ const yml = {
69
+ model: config.model,
70
+ theme: config.theme,
71
+ approval_mode: config.approval_mode,
72
+ ...config.provider ? { provider: config.provider } : {},
73
+ ...config.model_params ? { model_params: config.model_params } : {},
74
+ ...config.api ? { api: config.api } : {},
75
+ ...config.performance ? { performance: config.performance } : {},
76
+ ...config.termux ? { termux: config.termux } : {},
77
+ ...config.telemetry ? { telemetry: config.telemetry } : {},
78
+ ...config.security ? { security: config.security } : {}
79
+ };
80
+ fs.writeFileSync(yamlPath(rootDir), YAML.dump(yml, { indent: 2, lineWidth: 120 }), "utf-8");
81
+ cachedConfig = config;
82
+ }
83
+ function getDefaults() {
84
+ return { ...DEFAULT_CONFIG };
85
+ }
86
+ function resetConfig(rootDir) {
87
+ const yml = yamlPath(rootDir);
88
+ if (fs.existsSync(yml)) fs.unlinkSync(yml);
89
+ const json = jsonPath(rootDir);
90
+ if (fs.existsSync(json)) fs.unlinkSync(json);
91
+ cachedConfig = null;
92
+ }
93
+ function formatConfig(config) {
94
+ const lines = [chalk.bold("\n LoveCode Configuration")];
95
+ lines.push(`
96
+ ${chalk.dim("Model:")} ${chalk.cyan(config.model)}`);
97
+ lines.push(` ${chalk.dim("Theme:")} ${chalk.magenta(config.theme)}`);
98
+ lines.push(` ${chalk.dim("Approval Mode:")} ${approvalColor(config.approval_mode)}`);
99
+ if (config.provider) lines.push(` ${chalk.dim("Provider:")} ${config.provider}`);
100
+ if (config.model_params) {
101
+ lines.push(`
102
+ ${chalk.bold("Model Params:")}`);
103
+ if (config.model_params.temperature !== void 0) lines.push(` temperature: ${config.model_params.temperature}`);
104
+ if (config.model_params.top_p !== void 0) lines.push(` top_p: ${config.model_params.top_p}`);
105
+ if (config.model_params.max_tokens !== void 0) lines.push(` max_tokens: ${config.model_params.max_tokens}`);
106
+ }
107
+ if (config.performance) {
108
+ lines.push(`
109
+ ${chalk.bold("Performance:")}`);
110
+ lines.push(` lazy_load: ${config.performance.lazy_load ? chalk.green("\u2713") : chalk.red("\u2717")}`);
111
+ lines.push(` cache_ttl: ${config.performance.cache_ttl}ms`);
112
+ lines.push(` max_memory: ${config.performance.max_memory_mb}MB`);
113
+ }
114
+ if (config.telemetry) {
115
+ lines.push(`
116
+ ${chalk.bold("Telemetry:")}`);
117
+ lines.push(` enabled: ${config.telemetry.enabled ? chalk.yellow("ON") : chalk.green("OFF")}`);
118
+ lines.push(` crash_reports:${config.telemetry.crash_reports ? chalk.yellow("ON") : chalk.green("OFF")}`);
119
+ }
120
+ if (config.security) {
121
+ lines.push(`
122
+ ${chalk.bold("Security:")}`);
123
+ lines.push(` profile: ${config.security.profile}`);
124
+ lines.push(` scan_secrets: ${config.security.scan_secrets ? chalk.green("\u2713") : chalk.red("\u2717")}`);
125
+ }
126
+ if (config.termux) {
127
+ lines.push(`
128
+ ${chalk.bold("Termux:")}`);
129
+ lines.push(` low_ram: ${config.termux.low_ram ? chalk.yellow("\u2713") : chalk.red("\u2717")}`);
130
+ lines.push(` touch_opt: ${config.termux.touch_optimized ? chalk.green("\u2713") : chalk.red("\u2717")}`);
131
+ }
132
+ return lines.join("\n");
133
+ }
134
+ function approvalColor(mode) {
135
+ switch (mode) {
136
+ case "strict":
137
+ return chalk.red(mode);
138
+ case "smart":
139
+ return chalk.yellow(mode);
140
+ case "permissive":
141
+ return chalk.green(mode);
142
+ default:
143
+ return mode;
144
+ }
145
+ }
146
+
147
+ export {
148
+ configDir,
149
+ loadConfig,
150
+ saveConfig,
151
+ getDefaults,
152
+ resetConfig,
153
+ formatConfig
154
+ };