geeto 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -7
- package/lib/api/codex-sdk.d.ts +15 -0
- package/lib/api/codex-sdk.d.ts.map +1 -0
- package/lib/api/codex-sdk.js +180 -0
- package/lib/api/codex-sdk.js.map +1 -0
- package/lib/api/codex.d.ts +9 -0
- package/lib/api/codex.d.ts.map +1 -0
- package/lib/api/codex.js +33 -0
- package/lib/api/codex.js.map +1 -0
- package/lib/api/copilot-sdk.d.ts.map +1 -1
- package/lib/api/copilot-sdk.js +2 -2
- package/lib/api/copilot-sdk.js.map +1 -1
- package/lib/api/gemini-sdk.d.ts.map +1 -1
- package/lib/api/gemini-sdk.js +2 -2
- package/lib/api/gemini-sdk.js.map +1 -1
- package/lib/api/groq-sdk.d.ts.map +1 -1
- package/lib/api/groq-sdk.js +2 -2
- package/lib/api/groq-sdk.js.map +1 -1
- package/lib/api/openrouter-sdk.d.ts.map +1 -1
- package/lib/api/openrouter-sdk.js +2 -2
- package/lib/api/openrouter-sdk.js.map +1 -1
- package/lib/cli/input.d.ts.map +1 -1
- package/lib/cli/input.js +47 -7
- package/lib/cli/input.js.map +1 -1
- package/lib/core/codex-sdk-setup.d.ts +10 -0
- package/lib/core/codex-sdk-setup.d.ts.map +1 -0
- package/lib/core/codex-sdk-setup.js +210 -0
- package/lib/core/codex-sdk-setup.js.map +1 -0
- package/lib/core/setup.d.ts +2 -1
- package/lib/core/setup.d.ts.map +1 -1
- package/lib/core/setup.js +19 -0
- package/lib/core/setup.js.map +1 -1
- package/lib/index.js +37 -4
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +11 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/utils/ai-text.d.ts +4 -0
- package/lib/utils/ai-text.d.ts.map +1 -1
- package/lib/utils/ai-text.js +27 -0
- package/lib/utils/ai-text.js.map +1 -1
- package/lib/utils/ai-workflow.d.ts +1 -1
- package/lib/utils/ai-workflow.d.ts.map +1 -1
- package/lib/utils/ai-workflow.js +7 -1
- package/lib/utils/ai-workflow.js.map +1 -1
- package/lib/utils/branch-naming.d.ts +1 -1
- package/lib/utils/branch-naming.d.ts.map +1 -1
- package/lib/utils/branch-naming.js +5 -0
- package/lib/utils/branch-naming.js.map +1 -1
- package/lib/utils/config.d.ts +10 -1
- package/lib/utils/config.d.ts.map +1 -1
- package/lib/utils/config.js +56 -1
- package/lib/utils/config.js.map +1 -1
- package/lib/utils/git-ai.d.ts +7 -7
- package/lib/utils/git-ai.d.ts.map +1 -1
- package/lib/utils/git-ai.js +126 -7
- package/lib/utils/git-ai.js.map +1 -1
- package/lib/utils/git.js +2 -2
- package/lib/utils/git.js.map +1 -1
- package/lib/utils/prompts-embedded.d.ts.map +1 -1
- package/lib/utils/prompts-embedded.js +9 -2
- package/lib/utils/prompts-embedded.js.map +1 -1
- package/lib/utils/state.d.ts.map +1 -1
- package/lib/utils/state.js +2 -1
- package/lib/utils/state.js.map +1 -1
- package/lib/utils/update-checker.d.ts +16 -0
- package/lib/utils/update-checker.d.ts.map +1 -0
- package/lib/utils/update-checker.js +290 -0
- package/lib/utils/update-checker.js.map +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/workflows/ai-provider.d.ts +2 -1
- package/lib/workflows/ai-provider.d.ts.map +1 -1
- package/lib/workflows/ai-provider.js +7 -1
- package/lib/workflows/ai-provider.js.map +1 -1
- package/lib/workflows/alias.d.ts.map +1 -1
- package/lib/workflows/alias.js +40 -6
- package/lib/workflows/alias.js.map +1 -1
- package/lib/workflows/branch-helpers.d.ts.map +1 -1
- package/lib/workflows/branch-helpers.js +10 -1
- package/lib/workflows/branch-helpers.js.map +1 -1
- package/lib/workflows/branch.d.ts.map +1 -1
- package/lib/workflows/branch.js +58 -13
- package/lib/workflows/branch.js.map +1 -1
- package/lib/workflows/commit.d.ts +1 -1
- package/lib/workflows/commit.d.ts.map +1 -1
- package/lib/workflows/commit.js +109 -13
- package/lib/workflows/commit.js.map +1 -1
- package/lib/workflows/doctor.d.ts.map +1 -1
- package/lib/workflows/doctor.js +41 -4
- package/lib/workflows/doctor.js.map +1 -1
- package/lib/workflows/issue.d.ts.map +1 -1
- package/lib/workflows/issue.js +1 -1
- package/lib/workflows/issue.js.map +1 -1
- package/lib/workflows/main-helpers.d.ts +2 -1
- package/lib/workflows/main-helpers.d.ts.map +1 -1
- package/lib/workflows/main-helpers.js +11 -0
- package/lib/workflows/main-helpers.js.map +1 -1
- package/lib/workflows/pr.d.ts.map +1 -1
- package/lib/workflows/pr.js +52 -6
- package/lib/workflows/pr.js.map +1 -1
- package/lib/workflows/release-merge.d.ts.map +1 -1
- package/lib/workflows/release-merge.js +23 -3
- package/lib/workflows/release-merge.js.map +1 -1
- package/lib/workflows/release-sync.d.ts.map +1 -1
- package/lib/workflows/release-sync.js +13 -3
- package/lib/workflows/release-sync.js.map +1 -1
- package/lib/workflows/release.d.ts.map +1 -1
- package/lib/workflows/release.js +37 -3
- package/lib/workflows/release.js.map +1 -1
- package/lib/workflows/repo-settings.d.ts.map +1 -1
- package/lib/workflows/repo-settings.js +23 -3
- package/lib/workflows/repo-settings.js.map +1 -1
- package/lib/workflows/reword.d.ts.map +1 -1
- package/lib/workflows/reword.js +79 -13
- package/lib/workflows/reword.js.map +1 -1
- package/lib/workflows/settings.d.ts.map +1 -1
- package/lib/workflows/settings.js +386 -39
- package/lib/workflows/settings.js.map +1 -1
- package/lib/workflows/update.d.ts +2 -0
- package/lib/workflows/update.d.ts.map +1 -0
- package/lib/workflows/update.js +29 -0
- package/lib/workflows/update.js.map +1 -0
- package/package.json +2 -1
- package/prompts/commit-message-prompt.md +9 -2
|
@@ -6,7 +6,7 @@ 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, GLOBAL_GEETO_DIR, hasGeminiConfig, hasTrelloConfig, saveBranchStrategyConfig, } from '../utils/config.js';
|
|
9
|
+
import { getBranchStrategyConfig, getCommitConfig, getProtectedBranches, GLOBAL_GEETO_DIR, hasGeminiConfig, hasTrelloConfig, saveBranchStrategyConfig, saveCommitConfig, } 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');
|
|
@@ -35,7 +35,7 @@ const moveConfigToGlobal = (name) => {
|
|
|
35
35
|
return false;
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
const AI_PROVIDERS = ['gemini', 'openrouter', 'groq', 'github', 'gitlab'];
|
|
38
|
+
const AI_PROVIDERS = ['gemini', 'openrouter', 'groq', 'codex', 'github', 'gitlab'];
|
|
39
39
|
const globalConfigPath = (name) => path.join(GLOBAL_GEETO_DIR, `${name}.toml`);
|
|
40
40
|
const isConfigGlobal = (name) => existsSync(globalConfigPath(name));
|
|
41
41
|
const globalProviders = () => AI_PROVIDERS.filter((p) => isConfigGlobal(p));
|
|
@@ -78,6 +78,7 @@ const handleGlobalConfigSetting = async () => {
|
|
|
78
78
|
{ label: 'Gemini', value: 'gemini' },
|
|
79
79
|
{ label: 'OpenRouter', value: 'openrouter' },
|
|
80
80
|
{ label: 'Groq', value: 'groq' },
|
|
81
|
+
{ label: 'Codex', value: 'codex' },
|
|
81
82
|
{ label: 'GitHub Copilot', value: 'github' },
|
|
82
83
|
{ label: 'GitLab', value: 'gitlab' },
|
|
83
84
|
{ label: 'Back', value: 'back' },
|
|
@@ -103,6 +104,11 @@ const handleGlobalConfigSetting = async () => {
|
|
|
103
104
|
setupGroqConfigInteractive();
|
|
104
105
|
break;
|
|
105
106
|
}
|
|
107
|
+
case 'codex': {
|
|
108
|
+
const { setupCodexConfigInteractive } = await import('../core/codex-sdk-setup.js');
|
|
109
|
+
await setupCodexConfigInteractive();
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
106
112
|
case 'github': {
|
|
107
113
|
const { setupGithubConfigInteractive } = await import('../core/github-setup.js');
|
|
108
114
|
setupGithubConfigInteractive();
|
|
@@ -168,6 +174,17 @@ const runInteractiveSetup = async (name) => {
|
|
|
168
174
|
}
|
|
169
175
|
return;
|
|
170
176
|
}
|
|
177
|
+
if (name === 'codex') {
|
|
178
|
+
const { setupCodexConfigInteractive } = await import('../core/codex-sdk-setup.js');
|
|
179
|
+
const codexSetupSuccess = await setupCodexConfigInteractive();
|
|
180
|
+
if (codexSetupSuccess) {
|
|
181
|
+
log.success('Codex integration configured!');
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
log.warn('Codex setup failed or cancelled.');
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
171
188
|
const { setupOpenRouterConfigInteractive } = await import('../core/openrouter-setup.js');
|
|
172
189
|
const openRouterSetupSuccess = setupOpenRouterConfigInteractive();
|
|
173
190
|
if (openRouterSetupSuccess) {
|
|
@@ -562,6 +579,58 @@ const syncGroqModels = async () => {
|
|
|
562
579
|
log.warn(`Groq model sync failed: ${msg}`);
|
|
563
580
|
}
|
|
564
581
|
};
|
|
582
|
+
// Sync Codex models (fetch from SDK & persist user favorites)
|
|
583
|
+
const syncCodexModels = async () => {
|
|
584
|
+
try {
|
|
585
|
+
let sdkModule = null;
|
|
586
|
+
try {
|
|
587
|
+
sdkModule = await import('../api/codex-sdk.js');
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
log.warn('Codex unavailable. Configure Codex first.');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const sdk = sdkModule;
|
|
594
|
+
if (!sdk || typeof sdk.getCodexModels !== 'function') {
|
|
595
|
+
log.warn('Codex unavailable. Configure Codex first.');
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (typeof sdk.isAvailable === 'function' && !sdk.isAvailable()) {
|
|
599
|
+
const { setupCodexConfigInteractive } = await import('../core/codex-sdk-setup.js');
|
|
600
|
+
const setupOk = await setupCodexConfigInteractive();
|
|
601
|
+
if (!setupOk)
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const spinner = new ScrambleProgress();
|
|
605
|
+
spinner.start(['Fetching Codex models...']);
|
|
606
|
+
const models = (await sdk.getCodexModels());
|
|
607
|
+
spinner.stop();
|
|
608
|
+
if (!Array.isArray(models) || models.length === 0) {
|
|
609
|
+
log.warn('No Codex models found.');
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const defaults = models.map((m) => m.value);
|
|
613
|
+
const selected = await multiSelect('Pick your favorite Codex models:', models, defaults);
|
|
614
|
+
if (!selected || selected.length === 0) {
|
|
615
|
+
log.info('No models selected. Sync cancelled.');
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const simple = selected.map((val, idx) => {
|
|
619
|
+
const detail = models.find((m) => m.value === val);
|
|
620
|
+
return { label: `${idx + 1}. ${detail?.label ?? val}`, value: val };
|
|
621
|
+
});
|
|
622
|
+
const fsModule = await import('node:fs');
|
|
623
|
+
const outDir = path.join(process.cwd(), '.geeto');
|
|
624
|
+
await fsModule.promises.mkdir(outDir, { recursive: true });
|
|
625
|
+
const outFile = path.join(outDir, 'codex-model.json');
|
|
626
|
+
await fsModule.promises.writeFile(outFile, JSON.stringify(simple, null, 2));
|
|
627
|
+
log.success(`Saved ${simple.length} Codex model(s) to .geeto/codex-model.json`);
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
631
|
+
log.warn(`Codex model sync failed: ${msg}`);
|
|
632
|
+
}
|
|
633
|
+
};
|
|
565
634
|
// Sync Copilot models (fetch from SDK & persist user favorites)
|
|
566
635
|
const syncCopilotModels = async () => {
|
|
567
636
|
try {
|
|
@@ -645,6 +714,7 @@ const handleModelResetSetting = async () => {
|
|
|
645
714
|
{ label: 'Gemini', value: 'gemini' },
|
|
646
715
|
{ label: 'OpenRouter', value: 'openrouter' },
|
|
647
716
|
{ label: 'Groq', value: 'groq' },
|
|
717
|
+
{ label: 'Codex', value: 'codex' },
|
|
648
718
|
{ label: 'Back to settings menu', value: 'back' },
|
|
649
719
|
]);
|
|
650
720
|
if (resetChoice === 'back') {
|
|
@@ -663,6 +733,9 @@ const handleModelResetSetting = async () => {
|
|
|
663
733
|
if (resetChoice === 'groq') {
|
|
664
734
|
await syncGroqModels();
|
|
665
735
|
}
|
|
736
|
+
if (resetChoice === 'codex') {
|
|
737
|
+
await syncCodexModels();
|
|
738
|
+
}
|
|
666
739
|
log.success('Model sync completed!');
|
|
667
740
|
}
|
|
668
741
|
catch (error) {
|
|
@@ -681,6 +754,7 @@ const handleChangeModelSetting = async () => {
|
|
|
681
754
|
{ label: 'GitHub Copilot', value: 'copilot' },
|
|
682
755
|
{ label: 'OpenRouter', value: 'openrouter' },
|
|
683
756
|
{ label: 'Groq', value: 'groq' },
|
|
757
|
+
{ label: 'Codex', value: 'codex' },
|
|
684
758
|
{ label: 'Back to settings menu', value: 'back' },
|
|
685
759
|
];
|
|
686
760
|
const chosenProv = await select('Choose provider to change model for:', provOptions);
|
|
@@ -712,6 +786,7 @@ const handleChangeModelSetting = async () => {
|
|
|
712
786
|
copilotModel: undefined,
|
|
713
787
|
openrouterModel: undefined,
|
|
714
788
|
geminiModel: undefined,
|
|
789
|
+
codexModel: undefined,
|
|
715
790
|
};
|
|
716
791
|
base.aiProvider = chosenProv;
|
|
717
792
|
switch (chosenProv) {
|
|
@@ -738,6 +813,15 @@ const handleChangeModelSetting = async () => {
|
|
|
738
813
|
base.copilotModel = undefined;
|
|
739
814
|
base.openrouterModel = undefined;
|
|
740
815
|
base.geminiModel = undefined;
|
|
816
|
+
base.codexModel = undefined;
|
|
817
|
+
break;
|
|
818
|
+
}
|
|
819
|
+
case 'codex': {
|
|
820
|
+
base.codexModel = picked;
|
|
821
|
+
base.copilotModel = undefined;
|
|
822
|
+
base.openrouterModel = undefined;
|
|
823
|
+
base.geminiModel = undefined;
|
|
824
|
+
base.groqModel = undefined;
|
|
741
825
|
break;
|
|
742
826
|
}
|
|
743
827
|
default: {
|
|
@@ -746,8 +830,13 @@ const handleChangeModelSetting = async () => {
|
|
|
746
830
|
}
|
|
747
831
|
base.timestamp = now;
|
|
748
832
|
saveState(base);
|
|
749
|
-
const providerLabel = {
|
|
750
|
-
|
|
833
|
+
const providerLabel = {
|
|
834
|
+
gemini: 'Gemini',
|
|
835
|
+
copilot: 'Copilot',
|
|
836
|
+
openrouter: 'OpenRouter',
|
|
837
|
+
groq: 'Groq',
|
|
838
|
+
codex: 'Codex',
|
|
839
|
+
}[chosenProv] ?? chosenProv;
|
|
751
840
|
log.success(`Set ${providerLabel} model to: ${picked}`);
|
|
752
841
|
// Done; do not go back to settings menu
|
|
753
842
|
return false;
|
|
@@ -945,8 +1034,47 @@ const handleGroqSetting = async () => {
|
|
|
945
1034
|
}
|
|
946
1035
|
return false;
|
|
947
1036
|
};
|
|
1037
|
+
const handleCodexSetting = async () => {
|
|
1038
|
+
const { hasCodexConfig } = await import('../utils/config.js');
|
|
1039
|
+
const hasConfig = hasCodexConfig();
|
|
1040
|
+
if (!hasConfig) {
|
|
1041
|
+
await runInteractiveSetup('codex');
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
const action = await select('Codex integration is already configured. What would you like to do?', [
|
|
1045
|
+
{ label: 'Reconfigure (check local installation)', value: 'reconfigure' },
|
|
1046
|
+
{ label: 'Remove configuration', value: 'remove' },
|
|
1047
|
+
{ label: 'Back to settings menu', value: 'back' },
|
|
1048
|
+
]);
|
|
1049
|
+
switch (action) {
|
|
1050
|
+
case 'reconfigure': {
|
|
1051
|
+
log.info('Reconfiguring Codex integration...');
|
|
1052
|
+
if (removeConfigFile('codex'))
|
|
1053
|
+
log.info('Cleared existing Codex configuration');
|
|
1054
|
+
await runInteractiveSetup('codex');
|
|
1055
|
+
log.success('Codex integration reconfigured!');
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
case 'remove': {
|
|
1059
|
+
const confirmRemove = confirm('Are you sure you want to remove Codex configuration?');
|
|
1060
|
+
if (!confirmRemove)
|
|
1061
|
+
return false;
|
|
1062
|
+
if (removeConfigFile('codex')) {
|
|
1063
|
+
log.success('Codex configuration removed!');
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
log.info('No Codex configuration found to remove');
|
|
1067
|
+
}
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
case 'back': {
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return false;
|
|
1075
|
+
};
|
|
948
1076
|
const handleSaveGlobalAiConfig = () => {
|
|
949
|
-
const providers = ['gemini', 'openrouter', 'groq'];
|
|
1077
|
+
const providers = ['gemini', 'openrouter', 'groq', 'codex'];
|
|
950
1078
|
const local = providers.filter((p) => isConfigLocal(p));
|
|
951
1079
|
if (local.length === 0) {
|
|
952
1080
|
log.info('No local AI config found — already global or not configured.');
|
|
@@ -970,73 +1098,286 @@ const handleSaveGlobalAiConfig = () => {
|
|
|
970
1098
|
log.success(`Done — ${saved} config(s) saved to ${GLOBAL_GEETO_DIR}`);
|
|
971
1099
|
return false;
|
|
972
1100
|
};
|
|
1101
|
+
const handleCommitStyleSetting = async () => {
|
|
1102
|
+
const current = getCommitConfig();
|
|
1103
|
+
while (true) {
|
|
1104
|
+
const commitChoice = await select('Commit settings:', [
|
|
1105
|
+
{
|
|
1106
|
+
label: `Style (${current?.tone ?? 'technical'})`,
|
|
1107
|
+
value: 'tone',
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
label: `Subject length (${current?.subjectLength ?? 72} chars)`,
|
|
1111
|
+
value: 'subject-length',
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
label: `Body style (${current?.style ?? 'multiline'})`,
|
|
1115
|
+
value: 'body-style',
|
|
1116
|
+
},
|
|
1117
|
+
{ label: 'Back to settings menu', value: 'back' },
|
|
1118
|
+
]);
|
|
1119
|
+
if (commitChoice === 'back')
|
|
1120
|
+
return true;
|
|
1121
|
+
if (commitChoice === 'tone') {
|
|
1122
|
+
const toneChoice = await select('Commit tone:', [
|
|
1123
|
+
{ label: 'Technical (Precise and specific)', value: 'technical' },
|
|
1124
|
+
{ label: 'Concise (Short and compact)', value: 'concise' },
|
|
1125
|
+
{ label: 'Descriptive (Natural and explanatory)', value: 'descriptive' },
|
|
1126
|
+
{ label: 'Back', value: 'back' },
|
|
1127
|
+
]);
|
|
1128
|
+
if (toneChoice === 'back')
|
|
1129
|
+
continue;
|
|
1130
|
+
saveCommitConfig({
|
|
1131
|
+
style: current?.style ?? 'multiline',
|
|
1132
|
+
subjectLength: current?.subjectLength ?? 72,
|
|
1133
|
+
tone: toneChoice,
|
|
1134
|
+
});
|
|
1135
|
+
log.success(`Commit tone set to ${toneChoice}`);
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
if (commitChoice === 'subject-length') {
|
|
1139
|
+
const lengthChoice = await select(`Subject length (current: ${current?.subjectLength ?? 72}):`, [
|
|
1140
|
+
{ label: '50 chars (conventional commits standard)', value: '50' },
|
|
1141
|
+
{ label: '72 chars (git standard)', value: '72' },
|
|
1142
|
+
{ label: '100 chars (modern projects)', value: '100' },
|
|
1143
|
+
{ label: 'Back', value: 'back' },
|
|
1144
|
+
]);
|
|
1145
|
+
if (lengthChoice === 'back')
|
|
1146
|
+
continue;
|
|
1147
|
+
saveCommitConfig({
|
|
1148
|
+
style: current?.style ?? 'multiline',
|
|
1149
|
+
subjectLength: Number.parseInt(lengthChoice, 10),
|
|
1150
|
+
tone: current?.tone,
|
|
1151
|
+
});
|
|
1152
|
+
log.success(`Subject length set to ${lengthChoice} chars`);
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
if (commitChoice === 'body-style') {
|
|
1156
|
+
const bodyChoice = await select('Body style:', [
|
|
1157
|
+
{ label: 'Multiline (subject + body with blank line)', value: 'multiline' },
|
|
1158
|
+
{ label: 'Singleline (subject only, no body)', value: 'singleline' },
|
|
1159
|
+
{ label: 'Back', value: 'back' },
|
|
1160
|
+
]);
|
|
1161
|
+
if (bodyChoice === 'back')
|
|
1162
|
+
continue;
|
|
1163
|
+
saveCommitConfig({
|
|
1164
|
+
style: bodyChoice,
|
|
1165
|
+
subjectLength: current?.subjectLength ?? 72,
|
|
1166
|
+
tone: current?.tone,
|
|
1167
|
+
});
|
|
1168
|
+
log.success(`Body style set to ${bodyChoice}`);
|
|
1169
|
+
return false;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
973
1173
|
export const showSettingsMenu = async () => {
|
|
974
1174
|
while (true) {
|
|
975
1175
|
log.info('Settings Menu');
|
|
976
1176
|
const hasGlobalConfig = globalProviders().length > 0;
|
|
1177
|
+
const hasLocalGeetoFolder = existsSync(configDirPath());
|
|
1178
|
+
const hasLocalAiConfig = ['gemini', 'openrouter', 'groq', 'codex'].some((p) => isConfigLocal(p));
|
|
977
1179
|
const menuOptions = [
|
|
978
|
-
{ label: '
|
|
979
|
-
{ label: '
|
|
980
|
-
{ label: '
|
|
981
|
-
{ label: '
|
|
982
|
-
{ label: '
|
|
983
|
-
{ label: '
|
|
984
|
-
{ label: ' Saved models (manage favorites per provider)', value: 'models' },
|
|
985
|
-
{ label: ' Move local AI config to global (~/.geeto/)', value: 'save-global' },
|
|
1180
|
+
{ label: 'AI', value: '_ai' },
|
|
1181
|
+
{ label: 'Branch', value: '_branch' },
|
|
1182
|
+
{ label: 'Commit', value: '_commit' },
|
|
1183
|
+
{ label: 'Setup', value: '_setup' },
|
|
1184
|
+
{ label: 'System', value: '_system' },
|
|
1185
|
+
{ label: 'Back', value: 'back' },
|
|
986
1186
|
];
|
|
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
1187
|
const settingChoice = await select('Settings:', menuOptions);
|
|
992
1188
|
if (settingChoice === 'back') {
|
|
993
1189
|
break;
|
|
994
1190
|
}
|
|
1191
|
+
if (settingChoice === '_branch') {
|
|
1192
|
+
const branchConfig = getBranchStrategyConfig();
|
|
1193
|
+
const branchChoice = await select('Branch settings:', [
|
|
1194
|
+
{ label: 'Branch prefix (dev#name / dev/name)', value: 'prefix' },
|
|
1195
|
+
{ label: 'Branch separator (hyphen / underscore)', value: 'separator' },
|
|
1196
|
+
{ label: `Max words (${branchConfig?.maxWords ?? 3})`, value: 'max-words' },
|
|
1197
|
+
{ label: 'Protected branches', value: 'protected' },
|
|
1198
|
+
{ label: 'Back', value: 'back' },
|
|
1199
|
+
]);
|
|
1200
|
+
if (branchChoice === 'back')
|
|
1201
|
+
continue;
|
|
1202
|
+
if (branchChoice === 'prefix') {
|
|
1203
|
+
const back = await handlePrefixFormatSetting();
|
|
1204
|
+
if (back)
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (branchChoice === 'separator') {
|
|
1208
|
+
const back = await handleSeparatorSetting();
|
|
1209
|
+
if (back)
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (branchChoice === 'max-words') {
|
|
1213
|
+
const wordChoice = await select(`Max words in branch name (current: ${branchConfig?.maxWords ?? 3}):`, [
|
|
1214
|
+
{ label: '1 word', value: '1' },
|
|
1215
|
+
{ label: '2 words', value: '2' },
|
|
1216
|
+
{ label: '3 words', value: '3' },
|
|
1217
|
+
{ label: 'Back', value: 'back' },
|
|
1218
|
+
]);
|
|
1219
|
+
if (wordChoice === 'back')
|
|
1220
|
+
continue;
|
|
1221
|
+
const updated = branchConfig ?? { separator: '-' };
|
|
1222
|
+
updated.maxWords = Number.parseInt(wordChoice, 10);
|
|
1223
|
+
saveBranchStrategyConfig(updated);
|
|
1224
|
+
log.success(`Branch max words set to ${wordChoice}`);
|
|
1225
|
+
// fall through to "Configure another setting?"
|
|
1226
|
+
}
|
|
1227
|
+
if (branchChoice === 'protected') {
|
|
1228
|
+
const back = await handleProtectedBranchesSetting();
|
|
1229
|
+
if (back)
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
// fall through to "Configure another setting?"
|
|
1233
|
+
}
|
|
1234
|
+
if (settingChoice === '_ai') {
|
|
1235
|
+
const aiChoice = await select('AI settings:', [
|
|
1236
|
+
{ label: 'Active model (switch provider & model)', value: 'change-model' },
|
|
1237
|
+
{ label: 'Saved models (manage favorites per provider)', value: 'models' },
|
|
1238
|
+
{ label: 'Back', value: 'back' },
|
|
1239
|
+
]);
|
|
1240
|
+
if (aiChoice === 'back')
|
|
1241
|
+
continue;
|
|
1242
|
+
if (aiChoice === 'change-model') {
|
|
1243
|
+
const back = await handleChangeModelSetting();
|
|
1244
|
+
if (back)
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
if (aiChoice === 'models') {
|
|
1248
|
+
const back = await handleModelResetSetting();
|
|
1249
|
+
if (back)
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
// fall through to "Configure another setting?"
|
|
1253
|
+
}
|
|
1254
|
+
if (settingChoice === '_commit') {
|
|
1255
|
+
const back = await handleCommitStyleSetting();
|
|
1256
|
+
if (back)
|
|
1257
|
+
continue;
|
|
1258
|
+
// fall through to "Configure another setting?"
|
|
1259
|
+
}
|
|
1260
|
+
if (settingChoice === '_setup') {
|
|
1261
|
+
const setupChoice = await select('Setup:', [
|
|
1262
|
+
{ label: 'GitHub Copilot', value: 'copilot' },
|
|
1263
|
+
{ label: 'Gemini', value: 'gemini' },
|
|
1264
|
+
{ label: 'OpenRouter', value: 'openrouter' },
|
|
1265
|
+
{ label: 'Groq', value: 'groq' },
|
|
1266
|
+
{ label: 'Codex', value: 'codex' },
|
|
1267
|
+
{ label: 'Trello', value: 'trello' },
|
|
1268
|
+
{ label: 'Back', value: 'back' },
|
|
1269
|
+
]);
|
|
1270
|
+
if (setupChoice === 'back')
|
|
1271
|
+
continue;
|
|
1272
|
+
if (setupChoice === 'copilot') {
|
|
1273
|
+
const back = await handleCopilotSetting();
|
|
1274
|
+
if (back)
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
if (setupChoice === 'gemini') {
|
|
1278
|
+
const back = await handleGeminiSetting();
|
|
1279
|
+
if (back)
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
if (setupChoice === 'openrouter') {
|
|
1283
|
+
const back = await handleOpenRouterSetting();
|
|
1284
|
+
if (back)
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
if (setupChoice === 'groq') {
|
|
1288
|
+
const back = await handleGroqSetting();
|
|
1289
|
+
if (back)
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
if (setupChoice === 'codex') {
|
|
1293
|
+
const back = await handleCodexSetting();
|
|
1294
|
+
if (back)
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (setupChoice === 'trello') {
|
|
1298
|
+
const back = await handleTrelloSetting();
|
|
1299
|
+
if (back)
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
// fall through to "Configure another setting?"
|
|
1303
|
+
}
|
|
1304
|
+
if (settingChoice === '_system') {
|
|
1305
|
+
const systemOptions = [
|
|
1306
|
+
{ label: 'Installation info', value: 'where' },
|
|
1307
|
+
{ label: 'Uninstall geeto', value: 'uninstall' },
|
|
1308
|
+
];
|
|
1309
|
+
if (hasLocalGeetoFolder && hasLocalAiConfig) {
|
|
1310
|
+
systemOptions.push({
|
|
1311
|
+
label: 'Move local AI config to global (~/.geeto/)',
|
|
1312
|
+
value: 'save-global',
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
if (hasLocalGeetoFolder && hasGlobalConfig) {
|
|
1316
|
+
systemOptions.push({ label: 'Manage global config (~/.geeto/)', value: 'global-config' });
|
|
1317
|
+
}
|
|
1318
|
+
systemOptions.push({ label: 'Back', value: 'back' });
|
|
1319
|
+
const systemChoice = await select('System:', systemOptions);
|
|
1320
|
+
if (systemChoice === 'back')
|
|
1321
|
+
continue;
|
|
1322
|
+
switch (systemChoice) {
|
|
1323
|
+
case 'where': {
|
|
1324
|
+
const { handleWhereInstalled } = await import('./doctor.js');
|
|
1325
|
+
await handleWhereInstalled();
|
|
1326
|
+
// fall through to "Configure another setting?"
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
case 'uninstall': {
|
|
1330
|
+
const { handleUninstall } = await import('./doctor.js');
|
|
1331
|
+
await handleUninstall();
|
|
1332
|
+
process.exit(0);
|
|
1333
|
+
break;
|
|
1334
|
+
}
|
|
1335
|
+
case 'save-global': {
|
|
1336
|
+
handleSaveGlobalAiConfig();
|
|
1337
|
+
// fall through to "Configure another setting?"
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
case 'global-config': {
|
|
1341
|
+
const back = await handleGlobalConfigSetting();
|
|
1342
|
+
if (back)
|
|
1343
|
+
continue;
|
|
1344
|
+
// fall through to "Configure another setting?"
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
// No default
|
|
1348
|
+
}
|
|
1349
|
+
// fall through to "Configure another setting?"
|
|
1350
|
+
}
|
|
1351
|
+
// Legacy flat handlers — kept for safety
|
|
995
1352
|
if (settingChoice === 'prefix') {
|
|
996
1353
|
const back = await handlePrefixFormatSetting();
|
|
997
|
-
if (back)
|
|
1354
|
+
if (back)
|
|
998
1355
|
continue;
|
|
999
|
-
}
|
|
1000
1356
|
}
|
|
1001
1357
|
if (settingChoice === 'separator') {
|
|
1002
1358
|
const back = await handleSeparatorSetting();
|
|
1003
|
-
if (back)
|
|
1359
|
+
if (back)
|
|
1004
1360
|
continue;
|
|
1005
|
-
}
|
|
1006
1361
|
}
|
|
1007
1362
|
if (settingChoice === 'protected') {
|
|
1008
1363
|
const back = await handleProtectedBranchesSetting();
|
|
1009
|
-
if (back)
|
|
1364
|
+
if (back)
|
|
1010
1365
|
continue;
|
|
1011
|
-
}
|
|
1012
1366
|
}
|
|
1013
1367
|
if (settingChoice === 'models') {
|
|
1014
1368
|
const back = await handleModelResetSetting();
|
|
1015
|
-
if (back)
|
|
1369
|
+
if (back)
|
|
1016
1370
|
continue;
|
|
1017
|
-
}
|
|
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
1371
|
}
|
|
1028
1372
|
if (settingChoice === 'change-model') {
|
|
1029
1373
|
const back = await handleChangeModelSetting();
|
|
1030
|
-
if (back)
|
|
1031
|
-
// user chose to go back from within handler — return to settings menu
|
|
1374
|
+
if (back)
|
|
1032
1375
|
continue;
|
|
1033
|
-
}
|
|
1034
1376
|
}
|
|
1035
1377
|
if (settingChoice === 'copilot') {
|
|
1036
1378
|
const back = await handleCopilotSetting();
|
|
1037
|
-
if (back)
|
|
1379
|
+
if (back)
|
|
1038
1380
|
continue;
|
|
1039
|
-
}
|
|
1040
1381
|
}
|
|
1041
1382
|
if (settingChoice === 'gemini') {
|
|
1042
1383
|
const back = await handleGeminiSetting();
|
|
@@ -1062,6 +1403,12 @@ export const showSettingsMenu = async () => {
|
|
|
1062
1403
|
continue;
|
|
1063
1404
|
}
|
|
1064
1405
|
}
|
|
1406
|
+
if (settingChoice === 'codex') {
|
|
1407
|
+
const back = await handleCodexSetting();
|
|
1408
|
+
if (back) {
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1065
1412
|
if (settingChoice === 'where') {
|
|
1066
1413
|
const { handleWhereInstalled } = await import('./doctor.js');
|
|
1067
1414
|
await handleWhereInstalled();
|