geeto 0.9.1 → 0.10.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.
Files changed (159) hide show
  1. package/README.md +77 -18
  2. package/lib/api/copilot-sdk.d.ts.map +1 -1
  3. package/lib/api/copilot-sdk.js +29 -24
  4. package/lib/api/copilot-sdk.js.map +1 -1
  5. package/lib/api/copilot.d.ts.map +1 -1
  6. package/lib/api/copilot.js +6 -6
  7. package/lib/api/copilot.js.map +1 -1
  8. package/lib/api/gemini.d.ts.map +1 -1
  9. package/lib/api/gemini.js +2 -2
  10. package/lib/api/gemini.js.map +1 -1
  11. package/lib/api/groq-sdk.d.ts +11 -0
  12. package/lib/api/groq-sdk.d.ts.map +1 -0
  13. package/lib/api/groq-sdk.js +131 -0
  14. package/lib/api/groq-sdk.js.map +1 -0
  15. package/lib/api/groq.d.ts +10 -0
  16. package/lib/api/groq.d.ts.map +1 -0
  17. package/lib/api/groq.js +36 -0
  18. package/lib/api/groq.js.map +1 -0
  19. package/lib/api/openrouter-sdk.d.ts.map +1 -1
  20. package/lib/api/openrouter-sdk.js +58 -73
  21. package/lib/api/openrouter-sdk.js.map +1 -1
  22. package/lib/api/openrouter.d.ts.map +1 -1
  23. package/lib/api/openrouter.js +2 -2
  24. package/lib/api/openrouter.js.map +1 -1
  25. package/lib/api/trello.d.ts +16 -1
  26. package/lib/api/trello.d.ts.map +1 -1
  27. package/lib/api/trello.js +90 -2
  28. package/lib/api/trello.js.map +1 -1
  29. package/lib/cli/input.d.ts +1 -9
  30. package/lib/cli/input.d.ts.map +1 -1
  31. package/lib/cli/input.js +428 -169
  32. package/lib/cli/input.js.map +1 -1
  33. package/lib/cli/menu.d.ts.map +1 -1
  34. package/lib/cli/menu.js +33 -20
  35. package/lib/cli/menu.js.map +1 -1
  36. package/lib/core/copilot-setup.d.ts +5 -0
  37. package/lib/core/copilot-setup.d.ts.map +1 -1
  38. package/lib/core/copilot-setup.js +18 -0
  39. package/lib/core/copilot-setup.js.map +1 -1
  40. package/lib/core/gemini-setup.d.ts.map +1 -1
  41. package/lib/core/gemini-setup.js +7 -13
  42. package/lib/core/gemini-setup.js.map +1 -1
  43. package/lib/core/github-setup.d.ts.map +1 -1
  44. package/lib/core/github-setup.js +13 -7
  45. package/lib/core/github-setup.js.map +1 -1
  46. package/lib/core/gitlab-setup.d.ts.map +1 -1
  47. package/lib/core/gitlab-setup.js +13 -6
  48. package/lib/core/gitlab-setup.js.map +1 -1
  49. package/lib/core/groq-setup.d.ts +5 -0
  50. package/lib/core/groq-setup.d.ts.map +1 -0
  51. package/lib/core/groq-setup.js +67 -0
  52. package/lib/core/groq-setup.js.map +1 -0
  53. package/lib/core/openrouter-setup.d.ts.map +1 -1
  54. package/lib/core/openrouter-setup.js +11 -21
  55. package/lib/core/openrouter-setup.js.map +1 -1
  56. package/lib/core/setup.d.ts +2 -1
  57. package/lib/core/setup.d.ts.map +1 -1
  58. package/lib/core/setup.js +44 -7
  59. package/lib/core/setup.js.map +1 -1
  60. package/lib/index.js +8 -0
  61. package/lib/index.js.map +1 -1
  62. package/lib/types/index.d.ts +9 -1
  63. package/lib/types/index.d.ts.map +1 -1
  64. package/lib/utils/ai-workflow.d.ts +10 -2
  65. package/lib/utils/ai-workflow.d.ts.map +1 -1
  66. package/lib/utils/ai-workflow.js +15 -0
  67. package/lib/utils/ai-workflow.js.map +1 -1
  68. package/lib/utils/branch-naming.d.ts +2 -1
  69. package/lib/utils/branch-naming.d.ts.map +1 -1
  70. package/lib/utils/branch-naming.js +114 -68
  71. package/lib/utils/branch-naming.js.map +1 -1
  72. package/lib/utils/config.d.ts +11 -3
  73. package/lib/utils/config.d.ts.map +1 -1
  74. package/lib/utils/config.js +46 -18
  75. package/lib/utils/config.js.map +1 -1
  76. package/lib/utils/git-ai-errors.d.ts.map +1 -1
  77. package/lib/utils/git-ai-errors.js +5 -1
  78. package/lib/utils/git-ai-errors.js.map +1 -1
  79. package/lib/utils/git-ai.d.ts +8 -7
  80. package/lib/utils/git-ai.d.ts.map +1 -1
  81. package/lib/utils/git-ai.js +239 -117
  82. package/lib/utils/git-ai.js.map +1 -1
  83. package/lib/utils/logging.d.ts.map +1 -1
  84. package/lib/utils/logging.js +14 -3
  85. package/lib/utils/logging.js.map +1 -1
  86. package/lib/utils/menu-builders.js +1 -1
  87. package/lib/utils/menu-builders.js.map +1 -1
  88. package/lib/utils/prompts-embedded.d.ts.map +1 -1
  89. package/lib/utils/prompts-embedded.js +0 -81
  90. package/lib/utils/prompts-embedded.js.map +1 -1
  91. package/lib/utils/scramble.d.ts.map +1 -1
  92. package/lib/utils/scramble.js +14 -3
  93. package/lib/utils/scramble.js.map +1 -1
  94. package/lib/utils/state.d.ts.map +1 -1
  95. package/lib/utils/state.js +12 -1
  96. package/lib/utils/state.js.map +1 -1
  97. package/lib/version.d.ts +1 -1
  98. package/lib/version.d.ts.map +1 -1
  99. package/lib/version.js +1 -1
  100. package/lib/version.js.map +1 -1
  101. package/lib/workflows/ai-provider.d.ts +3 -1
  102. package/lib/workflows/ai-provider.d.ts.map +1 -1
  103. package/lib/workflows/ai-provider.js +8 -2
  104. package/lib/workflows/ai-provider.js.map +1 -1
  105. package/lib/workflows/branch-helpers.d.ts.map +1 -1
  106. package/lib/workflows/branch-helpers.js +142 -71
  107. package/lib/workflows/branch-helpers.js.map +1 -1
  108. package/lib/workflows/branch.d.ts.map +1 -1
  109. package/lib/workflows/branch.js +97 -58
  110. package/lib/workflows/branch.js.map +1 -1
  111. package/lib/workflows/commit.d.ts +1 -1
  112. package/lib/workflows/commit.d.ts.map +1 -1
  113. package/lib/workflows/commit.js +97 -35
  114. package/lib/workflows/commit.js.map +1 -1
  115. package/lib/workflows/dry-run.d.ts.map +1 -1
  116. package/lib/workflows/dry-run.js +6 -0
  117. package/lib/workflows/dry-run.js.map +1 -1
  118. package/lib/workflows/issue.d.ts.map +1 -1
  119. package/lib/workflows/issue.js +12 -12
  120. package/lib/workflows/issue.js.map +1 -1
  121. package/lib/workflows/main-helpers.d.ts +3 -1
  122. package/lib/workflows/main-helpers.d.ts.map +1 -1
  123. package/lib/workflows/main-helpers.js +30 -26
  124. package/lib/workflows/main-helpers.js.map +1 -1
  125. package/lib/workflows/main.d.ts.map +1 -1
  126. package/lib/workflows/main.js +164 -13
  127. package/lib/workflows/main.js.map +1 -1
  128. package/lib/workflows/pr.d.ts.map +1 -1
  129. package/lib/workflows/pr.js +12 -12
  130. package/lib/workflows/pr.js.map +1 -1
  131. package/lib/workflows/release-merge.d.ts.map +1 -1
  132. package/lib/workflows/release-merge.js +40 -10
  133. package/lib/workflows/release-merge.js.map +1 -1
  134. package/lib/workflows/release-sync.d.ts.map +1 -1
  135. package/lib/workflows/release-sync.js +25 -7
  136. package/lib/workflows/release-sync.js.map +1 -1
  137. package/lib/workflows/release.d.ts.map +1 -1
  138. package/lib/workflows/release.js +130 -15
  139. package/lib/workflows/release.js.map +1 -1
  140. package/lib/workflows/repo-settings.d.ts.map +1 -1
  141. package/lib/workflows/repo-settings.js +35 -8
  142. package/lib/workflows/repo-settings.js.map +1 -1
  143. package/lib/workflows/reword.d.ts.map +1 -1
  144. package/lib/workflows/reword.js +60 -17
  145. package/lib/workflows/reword.js.map +1 -1
  146. package/lib/workflows/settings.d.ts +2 -1
  147. package/lib/workflows/settings.d.ts.map +1 -1
  148. package/lib/workflows/settings.js +364 -76
  149. package/lib/workflows/settings.js.map +1 -1
  150. package/lib/workflows/trello-menu.d.ts +0 -3
  151. package/lib/workflows/trello-menu.d.ts.map +1 -1
  152. package/lib/workflows/trello-menu.js +349 -19
  153. package/lib/workflows/trello-menu.js.map +1 -1
  154. package/package.json +12 -2
  155. package/lib/workflows/security-gate.d.ts +0 -8
  156. package/lib/workflows/security-gate.d.ts.map +0 -1
  157. package/lib/workflows/security-gate.js +0 -455
  158. package/lib/workflows/security-gate.js.map +0 -1
  159. package/prompts/security-gate-prompt.md +0 -80
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Settings workflow - handles all settings menu interactions
3
3
  */
4
- import { existsSync, unlinkSync } from 'node:fs';
4
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { askQuestion, confirm } from '../cli/input.js';
7
7
  import { multiSelect, select } from '../cli/menu.js';
8
8
  import { colors } from '../utils/colors.js';
9
- import { getBranchStrategyConfig, getProtectedBranches, hasGeminiConfig, hasTrelloConfig, saveBranchStrategyConfig, } from '../utils/config.js';
9
+ import { getBranchStrategyConfig, getProtectedBranches, GLOBAL_GEETO_DIR, hasGeminiConfig, hasTrelloConfig, saveBranchStrategyConfig, } from '../utils/config.js';
10
10
  import { log } from '../utils/logging.js';
11
11
  import { ScrambleProgress } from '../utils/scramble.js';
12
12
  const configDirPath = () => path.join(process.cwd(), '.geeto');
@@ -19,6 +19,121 @@ const removeConfigFile = (name) => {
19
19
  }
20
20
  return false;
21
21
  };
22
+ const isConfigLocal = (name) => existsSync(configFilePath(name));
23
+ const moveConfigToGlobal = (name) => {
24
+ const localPath = configFilePath(name);
25
+ if (!existsSync(localPath))
26
+ return false;
27
+ try {
28
+ if (!existsSync(GLOBAL_GEETO_DIR))
29
+ mkdirSync(GLOBAL_GEETO_DIR, { recursive: true });
30
+ writeFileSync(path.join(GLOBAL_GEETO_DIR, `${name}.toml`), readFileSync(localPath, 'utf8'), 'utf8');
31
+ unlinkSync(localPath);
32
+ return true;
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ };
38
+ const AI_PROVIDERS = ['gemini', 'openrouter', 'groq', 'github', 'gitlab'];
39
+ const globalConfigPath = (name) => path.join(GLOBAL_GEETO_DIR, `${name}.toml`);
40
+ const isConfigGlobal = (name) => existsSync(globalConfigPath(name));
41
+ const globalProviders = () => AI_PROVIDERS.filter((p) => isConfigGlobal(p));
42
+ const maskValue = (val) => val.length <= 8 ? '***' : `${val.slice(0, 4)}...${val.slice(-4)}`;
43
+ const readGlobalConfigInfo = (name) => {
44
+ try {
45
+ const content = readFileSync(globalConfigPath(name), 'utf8');
46
+ const match = content.match(/(?:gemini_api_key|openrouter_api_key|api_key|token)\s*=\s*["']([^"']+)["']/);
47
+ return match?.[1] ? maskValue(match[1]) : '(configured)';
48
+ }
49
+ catch {
50
+ return '(configured)';
51
+ }
52
+ };
53
+ const handleGlobalConfigSetting = async () => {
54
+ while (true) {
55
+ const configured = globalProviders();
56
+ if (configured.length === 0) {
57
+ log.info('No global AI config found in ~/.geeto/');
58
+ return false;
59
+ }
60
+ const action = await select('Global config (~/.geeto/):', [
61
+ { label: 'View info', value: 'view' },
62
+ { label: 'Configure a provider globally', value: 'configure' },
63
+ { label: 'Remove a provider from global', value: 'remove' },
64
+ { label: 'Back to settings menu', value: 'back' },
65
+ ]);
66
+ if (action === 'back')
67
+ return true;
68
+ if (action === 'view') {
69
+ log.info(`Global config directory: ${GLOBAL_GEETO_DIR}\n`);
70
+ for (const p of configured) {
71
+ log.info(` ${p.padEnd(12)} ${readGlobalConfigInfo(p)}`);
72
+ }
73
+ askQuestion(`\n ${colors.gray}Press Enter to go back${colors.reset}`);
74
+ continue;
75
+ }
76
+ if (action === 'configure') {
77
+ const provider = await select('Which provider to configure globally?', [
78
+ { label: 'Gemini', value: 'gemini' },
79
+ { label: 'OpenRouter', value: 'openrouter' },
80
+ { label: 'Groq', value: 'groq' },
81
+ { label: 'GitHub Copilot', value: 'github' },
82
+ { label: 'GitLab', value: 'gitlab' },
83
+ { label: 'Back', value: 'back' },
84
+ ]);
85
+ if (provider === 'back')
86
+ continue;
87
+ const gp = globalConfigPath(provider);
88
+ if (existsSync(gp))
89
+ unlinkSync(gp);
90
+ switch (provider) {
91
+ case 'gemini': {
92
+ const { setupGeminiConfigInteractive } = await import('../core/gemini-setup.js');
93
+ setupGeminiConfigInteractive();
94
+ break;
95
+ }
96
+ case 'openrouter': {
97
+ const { setupOpenRouterConfigInteractive } = await import('../core/openrouter-setup.js');
98
+ setupOpenRouterConfigInteractive();
99
+ break;
100
+ }
101
+ case 'groq': {
102
+ const { setupGroqConfigInteractive } = await import('../core/groq-setup.js');
103
+ setupGroqConfigInteractive();
104
+ break;
105
+ }
106
+ case 'github': {
107
+ const { setupGithubConfigInteractive } = await import('../core/github-setup.js');
108
+ setupGithubConfigInteractive();
109
+ break;
110
+ }
111
+ case 'gitlab': {
112
+ const { setupGitlabConfigInteractive } = await import('../core/gitlab-setup.js');
113
+ setupGitlabConfigInteractive();
114
+ break;
115
+ }
116
+ }
117
+ continue;
118
+ }
119
+ if (action === 'remove') {
120
+ const choices = configured.map((p) => ({
121
+ label: p,
122
+ value: p,
123
+ }));
124
+ choices.push({ label: 'Back', value: 'back' });
125
+ const provider = await select('Which provider to remove from global?', choices);
126
+ if (provider === 'back')
127
+ continue;
128
+ const gp = globalConfigPath(provider);
129
+ if (existsSync(gp)) {
130
+ unlinkSync(gp);
131
+ log.success(`Removed ${provider} from ~/.geeto/`);
132
+ }
133
+ continue;
134
+ }
135
+ }
136
+ };
22
137
  const runInteractiveSetup = async (name) => {
23
138
  if (name === 'trello') {
24
139
  const { setupTrelloConfigInteractive } = await import('../core/trello-setup.js');
@@ -42,6 +157,17 @@ const runInteractiveSetup = async (name) => {
42
157
  }
43
158
  return;
44
159
  }
160
+ if (name === 'groq') {
161
+ const { setupGroqConfigInteractive } = await import('../core/groq-setup.js');
162
+ const groqSetupSuccess = setupGroqConfigInteractive();
163
+ if (groqSetupSuccess) {
164
+ log.success('Groq integration configured!');
165
+ }
166
+ else {
167
+ log.warn('Groq setup failed or cancelled.');
168
+ }
169
+ return;
170
+ }
45
171
  const { setupOpenRouterConfigInteractive } = await import('../core/openrouter-setup.js');
46
172
  const openRouterSetupSuccess = setupOpenRouterConfigInteractive();
47
173
  if (openRouterSetupSuccess) {
@@ -166,9 +292,6 @@ const syncOpenRouterModels = async () => {
166
292
  const detailed = (await sdk.getAvailableModelChoices());
167
293
  spinner.stop();
168
294
  if (Array.isArray(detailed) && detailed.length > 0) {
169
- // Persist detailed sync file
170
- const syncFile = path.join(outDir, 'openrouter-model-live-sample.json');
171
- await fs.promises.writeFile(syncFile, JSON.stringify(detailed, null, 2));
172
295
  // Filter out image-generation-only models (geeto is for text/code tasks)
173
296
  const imageOnlyPrefixes = [
174
297
  'stabilityai/',
@@ -241,8 +364,7 @@ const syncOpenRouterModels = async () => {
241
364
  });
242
365
  const outModelFile = path.join(outDir, 'openrouter-model.json');
243
366
  await fs.promises.writeFile(outModelFile, JSON.stringify(simple, null, 2));
244
- log.info(`Saved ${simple.length} OpenRouter model(s) to .geeto/openrouter-model.json ` +
245
- '(and _live-sample.json).');
367
+ log.info(`Saved ${simple.length} OpenRouter model(s) to .geeto/openrouter-model.json`);
246
368
  return;
247
369
  }
248
370
  }
@@ -251,18 +373,31 @@ const syncOpenRouterModels = async () => {
251
373
  log.warn(`Failed to fetch OpenRouter models from SDK: ${msg}`);
252
374
  }
253
375
  }
254
- // SDK unavailable or no models; fall back to persisted file/guidance
376
+ // SDK unavailable or returned no models; use saved models with multiSelect
255
377
  const modelFilePath = path.join(outDir, 'openrouter-model.json');
256
378
  if (fs.existsSync(modelFilePath)) {
257
379
  try {
258
380
  const raw = fs.readFileSync(modelFilePath, 'utf8');
259
381
  const parsed = JSON.parse(raw);
260
382
  if (Array.isArray(parsed) && parsed.length > 0) {
261
- log.info(`Found ${parsed.length} OpenRouter model(s) in .geeto/openrouter-model.json:`);
262
- for (const m of parsed) {
263
- log.info(` - ${m.label ?? m.value ?? JSON.stringify(m)}`);
383
+ const choices = parsed.map((m) => ({
384
+ label: String(m.label ?? m.value ?? ''),
385
+ value: String(m.value ?? ''),
386
+ }));
387
+ const defaults = choices.map((c) => c.value);
388
+ const selected = await multiSelect('Pick your favorite OpenRouter models (from saved list):', choices, defaults);
389
+ if (!selected || selected.length === 0) {
390
+ log.info('No models selected. Sync cancelled.');
391
+ return;
264
392
  }
265
- log.info('No new sync performed; using existing persisted OpenRouter model configuration.');
393
+ const simple = selected.map((val, idx) => {
394
+ const detail = parsed.find((m) => m.value === val);
395
+ const rawLabel = String(detail?.label ?? val);
396
+ const label = rawLabel.replace(/^\s*\d+\.\s*/, `${idx + 1}. `);
397
+ return { label, value: val };
398
+ });
399
+ await fs.promises.writeFile(modelFilePath, JSON.stringify(simple, null, 2));
400
+ log.success(`Saved ${simple.length} OpenRouter model(s) to .geeto/openrouter-model.json`);
266
401
  return;
267
402
  }
268
403
  }
@@ -271,8 +406,7 @@ const syncOpenRouterModels = async () => {
271
406
  log.warn(`Could not read OpenRouter model config: ${msg}`);
272
407
  }
273
408
  }
274
- log.info('No OpenRouter model configuration found and no SDK sync possible.');
275
- log.info('Run the sync again after installing/configuring the OpenRouter SDK to fetch models.');
409
+ log.warn('No OpenRouter models available. Run --sync-models after configuring OpenRouter.');
276
410
  }
277
411
  catch (error) {
278
412
  const msg = error instanceof Error ? error.message : String(error);
@@ -369,6 +503,65 @@ const syncGeminiModels = async () => {
369
503
  log.warn(`Gemini model sync failed: ${msg}`);
370
504
  }
371
505
  };
506
+ // Sync Groq models (fetch live, pre-select free ones as defaults)
507
+ const syncGroqModels = async () => {
508
+ try {
509
+ let sdkModule = null;
510
+ try {
511
+ sdkModule = await import('../api/groq-sdk.js');
512
+ }
513
+ catch {
514
+ log.warn('Groq SDK unavailable. Configure Groq first with --setup-groq.');
515
+ return;
516
+ }
517
+ const sdk = sdkModule;
518
+ if (!sdk || typeof sdk.getGroqModels !== 'function') {
519
+ log.warn('Groq SDK unavailable. Configure Groq first with --setup-groq.');
520
+ return;
521
+ }
522
+ if (typeof sdk.isAvailable === 'function' && !sdk.isAvailable()) {
523
+ const { setupGroqConfigInteractive } = await import('../core/groq-setup.js');
524
+ const setupOk = setupGroqConfigInteractive();
525
+ if (!setupOk)
526
+ return;
527
+ }
528
+ const spinner = new ScrambleProgress();
529
+ spinner.start(['Fetching Groq models...']);
530
+ const models = (await sdk.getGroqModels());
531
+ spinner.stop();
532
+ if (!Array.isArray(models) || models.length === 0) {
533
+ log.warn('No Groq models found.');
534
+ return;
535
+ }
536
+ // Pre-select all free models as defaults
537
+ const freeModels = new Set([
538
+ 'llama-3.3-70b-versatile',
539
+ 'llama-3.1-8b-instant',
540
+ 'gemma2-9b-it',
541
+ 'mixtral-8x7b-32768',
542
+ ]);
543
+ const defaults = models.filter((m) => freeModels.has(m.value)).map((m) => m.value);
544
+ const selected = await multiSelect('Pick your favorite Groq models:', models, defaults);
545
+ if (!selected || selected.length === 0) {
546
+ log.info('No models selected. Sync cancelled.');
547
+ return;
548
+ }
549
+ const simple = selected.map((val, idx) => {
550
+ const detail = models.find((m) => m.value === val);
551
+ return { label: `${idx + 1}. ${detail?.label ?? val}`, value: val };
552
+ });
553
+ const fsModule = await import('node:fs');
554
+ const outDir = path.join(process.cwd(), '.geeto');
555
+ await fsModule.promises.mkdir(outDir, { recursive: true });
556
+ const outFile = path.join(outDir, 'groq-model.json');
557
+ await fsModule.promises.writeFile(outFile, JSON.stringify(simple, null, 2));
558
+ log.success(`Saved ${simple.length} Groq model(s) to .geeto/groq-model.json`);
559
+ }
560
+ catch (error) {
561
+ const msg = error instanceof Error ? error.message : String(error);
562
+ log.warn(`Groq model sync failed: ${msg}`);
563
+ }
564
+ };
372
565
  // Sync Copilot models (fetch from SDK & persist user favorites)
373
566
  const syncCopilotModels = async () => {
374
567
  try {
@@ -448,9 +641,10 @@ const syncCopilotModels = async () => {
448
641
  };
449
642
  const handleModelResetSetting = async () => {
450
643
  const resetChoice = await select('Saved AI models — choose provider:', [
451
- { label: 'Copilot', value: 'copilot' },
644
+ { label: 'GitHub Copilot', value: 'copilot' },
452
645
  { label: 'Gemini', value: 'gemini' },
453
646
  { label: 'OpenRouter', value: 'openrouter' },
647
+ { label: 'Groq', value: 'groq' },
454
648
  { label: 'Back to settings menu', value: 'back' },
455
649
  ]);
456
650
  if (resetChoice === 'back') {
@@ -466,6 +660,9 @@ const handleModelResetSetting = async () => {
466
660
  if (resetChoice === 'copilot') {
467
661
  await syncCopilotModels();
468
662
  }
663
+ if (resetChoice === 'groq') {
664
+ await syncGroqModels();
665
+ }
469
666
  log.success('Model sync completed!');
470
667
  }
471
668
  catch (error) {
@@ -481,8 +678,9 @@ const handleChangeModelSetting = async () => {
481
678
  const { chooseModelForProvider } = await import('../utils/git-ai.js');
482
679
  const provOptions = [
483
680
  { label: 'Gemini', value: 'gemini' },
484
- { label: 'Copilot', value: 'copilot' },
681
+ { label: 'GitHub Copilot', value: 'copilot' },
485
682
  { label: 'OpenRouter', value: 'openrouter' },
683
+ { label: 'Groq', value: 'groq' },
486
684
  { label: 'Back to settings menu', value: 'back' },
487
685
  ];
488
686
  const chosenProv = await select('Choose provider to change model for:', provOptions);
@@ -535,13 +733,21 @@ const handleChangeModelSetting = async () => {
535
733
  base.openrouterModel = undefined;
536
734
  break;
537
735
  }
736
+ case 'groq': {
737
+ base.groqModel = picked;
738
+ base.copilotModel = undefined;
739
+ base.openrouterModel = undefined;
740
+ base.geminiModel = undefined;
741
+ break;
742
+ }
538
743
  default: {
539
744
  break;
540
745
  }
541
746
  }
542
747
  base.timestamp = now;
543
748
  saveState(base);
544
- const providerLabel = chosenProv === 'copilot' ? 'Copilot' : chosenProv === 'gemini' ? 'Gemini' : 'OpenRouter';
749
+ const providerLabel = { gemini: 'Gemini', copilot: 'Copilot', openrouter: 'OpenRouter', groq: 'Groq' }[chosenProv] ??
750
+ chosenProv;
545
751
  log.success(`Set ${providerLabel} model to: ${picked}`);
546
752
  // Done; do not go back to settings menu
547
753
  return false;
@@ -583,33 +789,36 @@ const handleGeminiSetting = async () => {
583
789
  { label: 'Remove configuration', value: 'remove' },
584
790
  { label: 'Back to settings menu', value: 'back' },
585
791
  ]);
586
- if (action === 'reconfigure') {
587
- log.info('Reconfiguring Gemini AI integration...');
588
- if (removeConfigFile('gemini')) {
589
- log.info('Cleared existing Gemini configuration');
590
- }
591
- const { setupGeminiConfigInteractive } = await import('../core/gemini-setup.js');
592
- const setupSuccess = setupGeminiConfigInteractive();
593
- if (setupSuccess) {
594
- log.success('Gemini AI integration reconfigured!');
595
- }
596
- else {
597
- log.warn('Gemini setup failed or cancelled.');
598
- }
599
- }
600
- else if (action === 'remove') {
601
- const confirmRemove = confirm('Are you sure you want to remove Gemini configuration?');
602
- if (confirmRemove) {
603
- if (removeConfigFile('gemini')) {
604
- log.success('Gemini configuration removed!');
792
+ switch (action) {
793
+ case 'reconfigure': {
794
+ log.info('Reconfiguring Gemini AI integration...');
795
+ if (removeConfigFile('gemini'))
796
+ log.info('Cleared existing Gemini configuration');
797
+ const { setupGeminiConfigInteractive } = await import('../core/gemini-setup.js');
798
+ const setupSuccess = setupGeminiConfigInteractive();
799
+ if (setupSuccess) {
800
+ log.success('Gemini AI integration reconfigured!');
605
801
  }
606
802
  else {
607
- log.warn('Failed to remove Gemini configuration.');
803
+ log.warn('Gemini setup failed or cancelled.');
608
804
  }
805
+ break;
806
+ }
807
+ case 'remove': {
808
+ const confirmRemove = confirm('Are you sure you want to remove Gemini configuration?');
809
+ if (confirmRemove) {
810
+ if (removeConfigFile('gemini')) {
811
+ log.success('Gemini configuration removed!');
812
+ }
813
+ else {
814
+ log.warn('Failed to remove Gemini configuration.');
815
+ }
816
+ }
817
+ break;
818
+ }
819
+ case 'back': {
820
+ return true;
609
821
  }
610
- }
611
- if (action === 'back') {
612
- return true;
613
822
  }
614
823
  // Completed without returning to settings menu
615
824
  return false;
@@ -669,53 +878,117 @@ const handleOpenRouterSetting = async () => {
669
878
  { label: 'Remove configuration', value: 'remove' },
670
879
  { label: 'Back to settings menu', value: 'back' },
671
880
  ]);
672
- if (action === 'reconfigure') {
673
- log.info('Reconfiguring OpenRouter integration...');
674
- if (removeConfigFile('openrouter')) {
675
- log.info('Cleared existing OpenRouter configuration');
881
+ switch (action) {
882
+ case 'reconfigure': {
883
+ log.info('Reconfiguring OpenRouter integration...');
884
+ if (removeConfigFile('openrouter'))
885
+ log.info('Cleared existing OpenRouter configuration');
886
+ await runInteractiveSetup('openrouter');
887
+ log.success('OpenRouter integration reconfigured!');
888
+ break;
889
+ }
890
+ case 'remove': {
891
+ const confirmRemove = confirm('Are you sure you want to remove OpenRouter configuration?');
892
+ if (!confirmRemove)
893
+ return false;
894
+ if (removeConfigFile('openrouter')) {
895
+ log.success('OpenRouter configuration removed!');
896
+ }
897
+ else {
898
+ log.info('No OpenRouter configuration found to remove');
899
+ }
900
+ break;
901
+ }
902
+ case 'back': {
903
+ return true;
676
904
  }
677
- await runInteractiveSetup('openrouter');
678
- log.success('OpenRouter integration reconfigured!');
679
905
  }
680
- else if (action === 'remove') {
681
- const confirmRemove = confirm('Are you sure you want to remove OpenRouter configuration?');
682
- if (!confirmRemove) {
683
- return false;
906
+ // Completed without returning to settings menu
907
+ return false;
908
+ };
909
+ const handleGroqSetting = async () => {
910
+ const { hasGroqConfig } = await import('../utils/config.js');
911
+ const hasConfig = hasGroqConfig();
912
+ if (!hasConfig) {
913
+ await runInteractiveSetup('groq');
914
+ return false;
915
+ }
916
+ const action = await select('Groq integration is already configured. What would you like to do?', [
917
+ { label: 'Reconfigure (replace existing config)', value: 'reconfigure' },
918
+ { label: 'Remove configuration', value: 'remove' },
919
+ { label: 'Back to settings menu', value: 'back' },
920
+ ]);
921
+ switch (action) {
922
+ case 'reconfigure': {
923
+ log.info('Reconfiguring Groq integration...');
924
+ if (removeConfigFile('groq'))
925
+ log.info('Cleared existing Groq configuration');
926
+ await runInteractiveSetup('groq');
927
+ log.success('Groq integration reconfigured!');
928
+ break;
684
929
  }
685
- if (removeConfigFile('openrouter')) {
686
- log.success('OpenRouter configuration removed!');
930
+ case 'remove': {
931
+ const confirmRemove = confirm('Are you sure you want to remove Groq configuration?');
932
+ if (!confirmRemove)
933
+ return false;
934
+ if (removeConfigFile('groq')) {
935
+ log.success('Groq configuration removed!');
936
+ }
937
+ else {
938
+ log.info('No Groq configuration found to remove');
939
+ }
940
+ break;
687
941
  }
688
- else {
689
- log.info('No OpenRouter configuration found to remove');
942
+ case 'back': {
943
+ return true;
690
944
  }
691
945
  }
692
- if (action === 'back') {
693
- return true;
946
+ return false;
947
+ };
948
+ const handleSaveGlobalAiConfig = () => {
949
+ const providers = ['gemini', 'openrouter', 'groq'];
950
+ const local = providers.filter((p) => isConfigLocal(p));
951
+ if (local.length === 0) {
952
+ log.info('No local AI config found — already global or not configured.');
953
+ return false;
694
954
  }
695
- // Completed without returning to settings menu
955
+ log.info(`Local AI config found: ${local.join(', ')}`);
956
+ const ok = confirm(`Save all to ~/.geeto/ and use across all projects?`);
957
+ if (!ok)
958
+ return false;
959
+ let saved = 0;
960
+ for (const p of local) {
961
+ if (moveConfigToGlobal(p)) {
962
+ log.success(`${p}: saved to ${GLOBAL_GEETO_DIR}/${p}.toml`);
963
+ saved++;
964
+ }
965
+ else {
966
+ log.error(`${p}: failed`);
967
+ }
968
+ }
969
+ if (saved > 0)
970
+ log.success(`Done — ${saved} config(s) saved to ${GLOBAL_GEETO_DIR}`);
696
971
  return false;
697
972
  };
698
973
  export const showSettingsMenu = async () => {
699
974
  while (true) {
700
975
  log.info('Settings Menu');
701
- const settingChoice = await select('Choose a setting to configure:', [
702
- { label: '── Branch ──', value: '_branch', disabled: true },
703
- { label: 'Branch prefix format (dev#name / dev/name)', value: 'prefix' },
704
- { label: 'Branch separator (hyphen/underscore)', value: 'separator' },
705
- { label: 'Protected branches', value: 'protected' },
706
- { label: '── AI Configuration ──', value: '_ai', disabled: true },
707
- { label: 'Saved AI models (manage favorite models per provider)', value: 'models' },
708
- { label: 'Active AI model (switch provider & model for generation)', value: 'change-model' },
709
- { label: '── Integration Setup ──', value: '_setup', disabled: true },
710
- { label: 'GitHub Copilot setup', value: 'copilot' },
711
- { label: 'Gemini AI setup', value: 'gemini' },
712
- { label: 'OpenRouter AI setup', value: 'openrouter' },
713
- { label: 'Trello integration setup', value: 'trello' },
714
- { label: '── System ──', value: '_system', disabled: true },
715
- { label: 'Installation info', value: 'where' },
716
- { label: 'Uninstall geeto', value: 'uninstall' },
717
- { label: 'Back to main menu', value: 'back' },
718
- ]);
976
+ const hasGlobalConfig = globalProviders().length > 0;
977
+ const menuOptions = [
978
+ { label: 'Branch', value: '_branch', disabled: true },
979
+ { label: ' Branch prefix (dev#name / dev/name)', value: 'prefix' },
980
+ { label: ' Branch separator (hyphen / underscore)', value: 'separator' },
981
+ { label: ' Protected branches', value: 'protected' },
982
+ { label: 'AI', value: '_ai', disabled: true },
983
+ { label: ' Active model (switch provider & model)', value: 'change-model' },
984
+ { label: ' Saved models (manage favorites per provider)', value: 'models' },
985
+ { label: ' Move local AI config to global (~/.geeto/)', value: 'save-global' },
986
+ ];
987
+ if (hasGlobalConfig) {
988
+ menuOptions.push({ label: ' Manage global config (~/.geeto/)', value: 'global-config' });
989
+ }
990
+ menuOptions.push({ label: 'Setup', value: '_setup', disabled: true }, { label: ' GitHub Copilot', value: 'copilot' }, { label: ' Gemini', value: 'gemini' }, { label: ' OpenRouter', value: 'openrouter' }, { label: ' Groq', value: 'groq' }, { label: ' Trello', value: 'trello' }, { label: 'System', value: '_system', disabled: true }, { label: ' Installation info', value: 'where' }, { label: ' Uninstall geeto', value: 'uninstall' }, { label: 'Back', value: 'back' });
991
+ const settingChoice = await select('Settings:', menuOptions);
719
992
  if (settingChoice === 'back') {
720
993
  break;
721
994
  }
@@ -743,6 +1016,15 @@ export const showSettingsMenu = async () => {
743
1016
  continue;
744
1017
  }
745
1018
  }
1019
+ if (settingChoice === 'save-global') {
1020
+ handleSaveGlobalAiConfig();
1021
+ }
1022
+ if (settingChoice === 'global-config') {
1023
+ const back = await handleGlobalConfigSetting();
1024
+ if (back) {
1025
+ continue;
1026
+ }
1027
+ }
746
1028
  if (settingChoice === 'change-model') {
747
1029
  const back = await handleChangeModelSetting();
748
1030
  if (back) {
@@ -774,6 +1056,12 @@ export const showSettingsMenu = async () => {
774
1056
  continue;
775
1057
  }
776
1058
  }
1059
+ if (settingChoice === 'groq') {
1060
+ const back = await handleGroqSetting();
1061
+ if (back) {
1062
+ continue;
1063
+ }
1064
+ }
777
1065
  if (settingChoice === 'where') {
778
1066
  const { handleWhereInstalled } = await import('./doctor.js');
779
1067
  await handleWhereInstalled();
@@ -793,5 +1081,5 @@ export const showSettingsMenu = async () => {
793
1081
  }
794
1082
  }
795
1083
  };
796
- export { handlePrefixFormatSetting, handleSeparatorSetting, handleProtectedBranchesSetting, handleModelResetSetting, handleChangeModelSetting, handleCopilotSetting, handleGeminiSetting, handleOpenRouterSetting, handleTrelloSetting, };
1084
+ export { handlePrefixFormatSetting, handleSeparatorSetting, handleProtectedBranchesSetting, handleModelResetSetting, handleChangeModelSetting, handleCopilotSetting, handleGeminiSetting, handleOpenRouterSetting, handleGroqSetting, handleTrelloSetting, };
797
1085
  //# sourceMappingURL=settings.js.map