@rlabs-inc/gemini-mcp 0.6.3 → 0.7.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 +46 -43
- package/dist/cli/commands/config.d.ts +8 -0
- package/dist/cli/commands/config.js +147 -0
- package/dist/cli/commands/image.d.ts +7 -0
- package/dist/cli/commands/image.js +133 -0
- package/dist/cli/commands/query.d.ts +7 -0
- package/dist/cli/commands/query.js +94 -0
- package/dist/cli/commands/research.d.ts +7 -0
- package/dist/cli/commands/research.js +147 -0
- package/dist/cli/commands/search.d.ts +7 -0
- package/dist/cli/commands/search.js +152 -0
- package/dist/cli/commands/speak.d.ts +7 -0
- package/dist/cli/commands/speak.js +168 -0
- package/dist/cli/commands/tokens.d.ts +8 -0
- package/dist/cli/commands/tokens.js +105 -0
- package/dist/cli/commands/video.d.ts +7 -0
- package/dist/cli/commands/video.js +154 -0
- package/dist/cli/config.d.ts +23 -0
- package/dist/cli/config.js +89 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +180 -0
- package/dist/cli/ui/box.d.ts +20 -0
- package/dist/cli/ui/box.js +112 -0
- package/dist/cli/ui/colors.d.ts +46 -0
- package/dist/cli/ui/colors.js +106 -0
- package/dist/cli/ui/index.d.ts +21 -0
- package/dist/cli/ui/index.js +42 -0
- package/dist/cli/ui/progress.d.ts +37 -0
- package/dist/cli/ui/progress.js +125 -0
- package/dist/cli/ui/spinner.d.ts +42 -0
- package/dist/cli/ui/spinner.js +96 -0
- package/dist/cli/ui/theme.d.ts +48 -0
- package/dist/cli/ui/theme.js +200 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +26 -218
- package/dist/server.d.ts +7 -0
- package/dist/server.js +221 -0
- package/dist/tools/deep-research.js +2 -2
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,58 +1,49 @@
|
|
|
1
1
|
# MCP Server Gemini
|
|
2
2
|
|
|
3
|
-
A Model Context Protocol (MCP) server for integrating Google's Gemini 3 models with Claude Code, enabling powerful collaboration between both AI systems.
|
|
3
|
+
A Model Context Protocol (MCP) server for integrating Google's Gemini 3 models with Claude Code, enabling powerful collaboration between both AI systems. Now with a beautiful CLI!
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@rlabs-inc/gemini-mcp)
|
|
6
|
+
[](https://registry.modelcontextprotocol.io)
|
|
6
7
|
|
|
7
|
-
## What's New in v0.
|
|
8
|
+
## What's New in v0.7.0
|
|
8
9
|
|
|
9
|
-
**
|
|
10
|
+
**Beautiful CLI with Themes!** Use Gemini directly from your terminal:
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- **gemini-research-followup** - Ask follow-up questions on completed research
|
|
15
|
-
|
|
16
|
-
**Token Management (NEW!):**
|
|
17
|
-
- **gemini-count-tokens** - Count tokens before API calls with cost estimates
|
|
18
|
-
|
|
19
|
-
**Text-to-Speech:**
|
|
20
|
-
- **gemini-speak** - Convert text to speech with 30 unique voices
|
|
21
|
-
- **gemini-dialogue** - Generate two-speaker conversations
|
|
22
|
-
- **gemini-list-voices** - Browse all available voices
|
|
12
|
+
```bash
|
|
13
|
+
# Install globally
|
|
14
|
+
npm install -g @rlabs-inc/gemini-mcp
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- **gemini-compare-urls** - Compare two URLs side by side
|
|
27
|
-
- **gemini-extract-from-url** - Extract structured data from pages
|
|
16
|
+
# Set your API key once
|
|
17
|
+
gemini config set api-key YOUR_KEY
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
# Generate images, videos, search, research, and more!
|
|
20
|
+
gemini image "a cat astronaut" --size 4K
|
|
21
|
+
gemini search "latest AI news"
|
|
22
|
+
gemini research "quantum computing applications" --wait
|
|
23
|
+
gemini speak "Hello world" --voice Puck
|
|
24
|
+
```
|
|
33
25
|
|
|
34
|
-
**
|
|
35
|
-
- **YouTube Analysis** - Analyze videos by URL with timestamps and clipping
|
|
36
|
-
- **Document Analysis** - PDFs, DOCX, spreadsheets with table extraction
|
|
26
|
+
**5 Beautiful Themes:** terminal, neon, ocean, forest, minimal
|
|
37
27
|
|
|
38
|
-
**
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
28
|
+
**CLI Commands:**
|
|
29
|
+
- `gemini query` - Direct Gemini queries with thinking levels
|
|
30
|
+
- `gemini search` - Real-time web search with citations
|
|
31
|
+
- `gemini research` - Deep research agent
|
|
32
|
+
- `gemini image` - Generate images (up to 4K)
|
|
33
|
+
- `gemini video` - Generate videos with Veo
|
|
34
|
+
- `gemini speak` - Text-to-speech with 30 voices
|
|
35
|
+
- `gemini tokens` - Count tokens and estimate costs
|
|
36
|
+
- `gemini config` - Manage settings
|
|
42
37
|
|
|
43
|
-
**
|
|
44
|
-
- **Code Execution** - Gemini writes AND runs Python code
|
|
45
|
-
- **Google Search** - Real-time web information with citations
|
|
46
|
-
- **Structured Output** - JSON schema responses with validation
|
|
47
|
-
- **Brainstorming** - Claude + Gemini collaborative problem-solving
|
|
38
|
+
**MCP Registry Support:** Now discoverable in the official MCP ecosystem!
|
|
48
39
|
|
|
49
40
|
### Previous Versions
|
|
50
41
|
|
|
51
|
-
**v0.
|
|
52
|
-
**v0.5.
|
|
53
|
-
**v0.4.
|
|
54
|
-
**v0.3.
|
|
55
|
-
**v0.2.
|
|
42
|
+
**v0.6.x:** Deep Research, Token Counting, TTS, URL analysis, Context Caching
|
|
43
|
+
**v0.5.x:** 30+ tools, YouTube analysis, Document analysis
|
|
44
|
+
**v0.4.x:** Code execution, Google Search
|
|
45
|
+
**v0.3.x:** Thinking levels, Structured output, 4K images
|
|
46
|
+
**v0.2.x:** Image/Video generation with Veo
|
|
56
47
|
|
|
57
48
|
---
|
|
58
49
|
|
|
@@ -84,16 +75,28 @@ A Model Context Protocol (MCP) server for integrating Google's Gemini 3 models w
|
|
|
84
75
|
|
|
85
76
|
## Quick Installation
|
|
86
77
|
|
|
87
|
-
###
|
|
78
|
+
### MCP Server for Claude Code
|
|
88
79
|
|
|
89
80
|
```bash
|
|
81
|
+
# Using npm (Recommended)
|
|
90
82
|
claude mcp add gemini -s user -- env GEMINI_API_KEY=YOUR_KEY npx -y @rlabs-inc/gemini-mcp
|
|
83
|
+
|
|
84
|
+
# Using bun
|
|
85
|
+
claude mcp add gemini -s user -- env GEMINI_API_KEY=YOUR_KEY bunx @rlabs-inc/gemini-mcp
|
|
91
86
|
```
|
|
92
87
|
|
|
93
|
-
###
|
|
88
|
+
### CLI (Global Install)
|
|
94
89
|
|
|
95
90
|
```bash
|
|
96
|
-
|
|
91
|
+
# Install globally
|
|
92
|
+
npm install -g @rlabs-inc/gemini-mcp
|
|
93
|
+
|
|
94
|
+
# Set your API key once (stored securely)
|
|
95
|
+
gemini config set api-key YOUR_KEY
|
|
96
|
+
|
|
97
|
+
# Now use any command!
|
|
98
|
+
gemini search "latest news"
|
|
99
|
+
gemini image "sunset over mountains" --ratio 16:9
|
|
97
100
|
```
|
|
98
101
|
|
|
99
102
|
**Get your API key:** Visit [Google AI Studio](https://aistudio.google.com/apikey) - it's free and takes seconds!
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Command
|
|
3
|
+
*
|
|
4
|
+
* Set and view CLI configuration.
|
|
5
|
+
* gemini config set api-key YOUR_KEY
|
|
6
|
+
* gemini config show
|
|
7
|
+
*/
|
|
8
|
+
import { parseArgs } from 'node:util';
|
|
9
|
+
import { loadConfig, saveConfig, getConfigPath } from '../config.js';
|
|
10
|
+
import { print, printError, printSuccess, printMuted, printWarning, t, header, box } from '../ui/index.js';
|
|
11
|
+
function showHelp() {
|
|
12
|
+
const theme = t();
|
|
13
|
+
print(header('gemini config', 'CLI configuration'));
|
|
14
|
+
print('');
|
|
15
|
+
print(theme.colors.primary('Usage:'));
|
|
16
|
+
print(` gemini config ${theme.colors.muted('<command>')} [options]`);
|
|
17
|
+
print('');
|
|
18
|
+
print(theme.colors.primary('Commands:'));
|
|
19
|
+
print(` ${theme.colors.highlight('set')} ${theme.colors.muted('Set a configuration value')}`);
|
|
20
|
+
print(` ${theme.colors.highlight('show')} ${theme.colors.muted('Show current configuration')}`);
|
|
21
|
+
print(` ${theme.colors.highlight('path')} ${theme.colors.muted('Show config file path')}`);
|
|
22
|
+
print('');
|
|
23
|
+
print(theme.colors.primary('Settings:'));
|
|
24
|
+
print(` ${theme.colors.highlight('api-key')} ${theme.colors.muted('Your Gemini API key')}`);
|
|
25
|
+
print(` ${theme.colors.highlight('theme')} ${theme.colors.muted('Default theme (terminal, neon, minimal, ocean, forest)')}`);
|
|
26
|
+
print(` ${theme.colors.highlight('output-dir')} ${theme.colors.muted('Directory for generated files')}`);
|
|
27
|
+
print(` ${theme.colors.highlight('default-voice')} ${theme.colors.muted('Default TTS voice')}`);
|
|
28
|
+
print(` ${theme.colors.highlight('default-model')} ${theme.colors.muted('Default model (pro or flash)')}`);
|
|
29
|
+
print('');
|
|
30
|
+
print(theme.colors.primary('Examples:'));
|
|
31
|
+
print(theme.colors.muted(' gemini config set api-key AIzaSy...'));
|
|
32
|
+
print(theme.colors.muted(' gemini config set theme neon'));
|
|
33
|
+
print(theme.colors.muted(' gemini config set output-dir ~/Documents/Gemini'));
|
|
34
|
+
print(theme.colors.muted(' gemini config show'));
|
|
35
|
+
}
|
|
36
|
+
async function showConfig() {
|
|
37
|
+
const theme = t();
|
|
38
|
+
const config = await loadConfig();
|
|
39
|
+
print(header('Current Configuration', getConfigPath()));
|
|
40
|
+
print('');
|
|
41
|
+
const lines = [
|
|
42
|
+
`${theme.colors.primary('API Key:')} ${config.apiKey ? theme.colors.success('Set (hidden)') : theme.colors.error('Not set')}`,
|
|
43
|
+
`${theme.colors.primary('Theme:')} ${config.theme}`,
|
|
44
|
+
`${theme.colors.primary('Output Dir:')} ${config.outputDir}`,
|
|
45
|
+
`${theme.colors.primary('Default Voice:')} ${config.defaultVoice}`,
|
|
46
|
+
`${theme.colors.primary('Image Size:')} ${config.defaultImageSize}`,
|
|
47
|
+
`${theme.colors.primary('Image Ratio:')} ${config.defaultImageRatio}`,
|
|
48
|
+
`${theme.colors.primary('Video Ratio:')} ${config.defaultVideoRatio}`,
|
|
49
|
+
];
|
|
50
|
+
print(box(lines, { title: 'Settings' }));
|
|
51
|
+
print('');
|
|
52
|
+
if (!config.apiKey) {
|
|
53
|
+
printWarning('API key not set. Set it with:');
|
|
54
|
+
print(theme.colors.muted(' gemini config set api-key YOUR_API_KEY'));
|
|
55
|
+
print('');
|
|
56
|
+
print(theme.colors.muted('Or set the GEMINI_API_KEY environment variable.'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function setConfig(key, value) {
|
|
60
|
+
const theme = t();
|
|
61
|
+
// Map user-friendly keys to config keys
|
|
62
|
+
const keyMap = {
|
|
63
|
+
'api-key': 'apiKey',
|
|
64
|
+
'apikey': 'apiKey',
|
|
65
|
+
'theme': 'theme',
|
|
66
|
+
'output-dir': 'outputDir',
|
|
67
|
+
'outputdir': 'outputDir',
|
|
68
|
+
'default-voice': 'defaultVoice',
|
|
69
|
+
'voice': 'defaultVoice',
|
|
70
|
+
'default-model': 'defaultModel',
|
|
71
|
+
'model': 'defaultModel',
|
|
72
|
+
'image-size': 'defaultImageSize',
|
|
73
|
+
'image-ratio': 'defaultImageRatio',
|
|
74
|
+
'video-ratio': 'defaultVideoRatio',
|
|
75
|
+
};
|
|
76
|
+
const configKey = keyMap[key.toLowerCase()];
|
|
77
|
+
if (!configKey) {
|
|
78
|
+
printError(`Unknown setting: ${key}`);
|
|
79
|
+
printMuted('Valid settings: api-key, theme, output-dir, default-voice');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
// Validate values
|
|
83
|
+
if (configKey === 'theme') {
|
|
84
|
+
const validThemes = ['terminal', 'neon', 'minimal', 'ocean', 'forest'];
|
|
85
|
+
if (!validThemes.includes(value)) {
|
|
86
|
+
printError(`Invalid theme: ${value}`);
|
|
87
|
+
printMuted(`Valid themes: ${validThemes.join(', ')}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (configKey === 'defaultImageSize') {
|
|
92
|
+
const validSizes = ['1K', '2K', '4K'];
|
|
93
|
+
if (!validSizes.includes(value)) {
|
|
94
|
+
printError(`Invalid image size: ${value}`);
|
|
95
|
+
printMuted(`Valid sizes: ${validSizes.join(', ')}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Save the config
|
|
100
|
+
await saveConfig({ [configKey]: value });
|
|
101
|
+
// Show confirmation
|
|
102
|
+
const displayValue = configKey === 'apiKey' ? '***' + value.slice(-4) : value;
|
|
103
|
+
printSuccess(`Set ${key} = ${displayValue}`);
|
|
104
|
+
if (configKey === 'apiKey') {
|
|
105
|
+
print('');
|
|
106
|
+
print(theme.colors.muted('Your API key is now stored in:'));
|
|
107
|
+
print(theme.colors.muted(` ${getConfigPath()}`));
|
|
108
|
+
print('');
|
|
109
|
+
printWarning('Keep this file secure. Do not share it.');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export async function configCommand(argv) {
|
|
113
|
+
const { positionals } = parseArgs({
|
|
114
|
+
args: argv,
|
|
115
|
+
options: {
|
|
116
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
117
|
+
},
|
|
118
|
+
allowPositionals: true,
|
|
119
|
+
});
|
|
120
|
+
const subCommand = positionals[0];
|
|
121
|
+
if (!subCommand || subCommand === 'help') {
|
|
122
|
+
showHelp();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
switch (subCommand) {
|
|
126
|
+
case 'show':
|
|
127
|
+
await showConfig();
|
|
128
|
+
break;
|
|
129
|
+
case 'path':
|
|
130
|
+
print(getConfigPath());
|
|
131
|
+
break;
|
|
132
|
+
case 'set':
|
|
133
|
+
const key = positionals[1];
|
|
134
|
+
const value = positionals.slice(2).join(' ');
|
|
135
|
+
if (!key || !value) {
|
|
136
|
+
printError('Usage: gemini config set <key> <value>');
|
|
137
|
+
printMuted('Example: gemini config set api-key YOUR_KEY');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
await setConfig(key, value);
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
printError(`Unknown config command: ${subCommand}`);
|
|
144
|
+
printMuted('Valid commands: set, show, path');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Command
|
|
3
|
+
*
|
|
4
|
+
* Generate images with Gemini's Imagen model.
|
|
5
|
+
* gemini image "a cat in space"
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'node:util';
|
|
8
|
+
import { initGeminiClient, generateImage } from '../../gemini-client.js';
|
|
9
|
+
import { setupLogger } from '../../utils/logger.js';
|
|
10
|
+
import { spinner, print, printError, printSuccess, printMuted, t, header, box } from '../ui/index.js';
|
|
11
|
+
// Valid options
|
|
12
|
+
const VALID_SIZES = ['1K', '2K', '4K'];
|
|
13
|
+
const VALID_RATIOS = ['1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9'];
|
|
14
|
+
function showHelp() {
|
|
15
|
+
const theme = t();
|
|
16
|
+
print(header('gemini image', 'Generate images with AI'));
|
|
17
|
+
print('');
|
|
18
|
+
print(theme.colors.primary('Usage:'));
|
|
19
|
+
print(` gemini image ${theme.colors.muted('"your prompt"')} [options]`);
|
|
20
|
+
print('');
|
|
21
|
+
print(theme.colors.primary('Options:'));
|
|
22
|
+
print(` ${theme.colors.highlight('--size, -s')} ${theme.colors.muted('Resolution: 1K, 2K, 4K (default: 2K)')}`);
|
|
23
|
+
print(` ${theme.colors.highlight('--ratio, -r')} ${theme.colors.muted('Aspect ratio (default: 1:1)')}`);
|
|
24
|
+
print(` ${theme.colors.highlight('--output, -o')} ${theme.colors.muted('Output file path')}`);
|
|
25
|
+
print(` ${theme.colors.highlight('--style')} ${theme.colors.muted('Art style (e.g., "watercolor", "cyberpunk")')}`);
|
|
26
|
+
print(` ${theme.colors.highlight('--search')} ${theme.colors.muted('Use Google Search for real-world accuracy')}`);
|
|
27
|
+
print(` ${theme.colors.highlight('--help, -h')} ${theme.colors.muted('Show this help')}`);
|
|
28
|
+
print('');
|
|
29
|
+
print(theme.colors.primary('Aspect Ratios:'));
|
|
30
|
+
print(theme.colors.muted(' 1:1 - Square (default)'));
|
|
31
|
+
print(theme.colors.muted(' 16:9 - Widescreen landscape'));
|
|
32
|
+
print(theme.colors.muted(' 9:16 - Portrait/mobile'));
|
|
33
|
+
print(theme.colors.muted(' 4:3 - Classic landscape'));
|
|
34
|
+
print(theme.colors.muted(' 3:4 - Classic portrait'));
|
|
35
|
+
print(theme.colors.muted(' 21:9 - Ultrawide cinematic'));
|
|
36
|
+
print('');
|
|
37
|
+
print(theme.colors.primary('Examples:'));
|
|
38
|
+
print(theme.colors.muted(' gemini image "a cat astronaut floating in space"'));
|
|
39
|
+
print(theme.colors.muted(' gemini image "sunset over mountains" --ratio 16:9 --size 4K'));
|
|
40
|
+
print(theme.colors.muted(' gemini image "portrait of a robot" -r 3:4 --style "oil painting"'));
|
|
41
|
+
print(theme.colors.muted(' gemini image "Eiffel Tower at night" --search'));
|
|
42
|
+
}
|
|
43
|
+
export async function imageCommand(argv) {
|
|
44
|
+
const { values, positionals } = parseArgs({
|
|
45
|
+
args: argv,
|
|
46
|
+
options: {
|
|
47
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
48
|
+
size: { type: 'string', short: 's', default: '2K' },
|
|
49
|
+
ratio: { type: 'string', short: 'r', default: '1:1' },
|
|
50
|
+
output: { type: 'string', short: 'o' },
|
|
51
|
+
style: { type: 'string' },
|
|
52
|
+
search: { type: 'boolean', default: false },
|
|
53
|
+
},
|
|
54
|
+
allowPositionals: true,
|
|
55
|
+
});
|
|
56
|
+
if (values.help) {
|
|
57
|
+
showHelp();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Get prompt from positional args
|
|
61
|
+
const prompt = positionals.join(' ');
|
|
62
|
+
if (!prompt) {
|
|
63
|
+
printError('No image prompt provided');
|
|
64
|
+
printMuted('Usage: gemini image "your prompt"');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const theme = t();
|
|
68
|
+
const s = spinner();
|
|
69
|
+
const size = values.size;
|
|
70
|
+
const ratio = values.ratio;
|
|
71
|
+
const style = values.style;
|
|
72
|
+
const useSearch = values.search;
|
|
73
|
+
// Validate size
|
|
74
|
+
if (!VALID_SIZES.includes(size)) {
|
|
75
|
+
printError(`Invalid size: ${size}`);
|
|
76
|
+
printMuted(`Valid sizes: ${VALID_SIZES.join(', ')}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
// Validate ratio
|
|
80
|
+
if (!VALID_RATIOS.includes(ratio)) {
|
|
81
|
+
printError(`Invalid ratio: ${ratio}`);
|
|
82
|
+
printMuted(`Valid ratios: ${VALID_RATIOS.join(', ')}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
// Suppress logger output for CLI
|
|
87
|
+
setupLogger('quiet');
|
|
88
|
+
// Initialize Gemini client
|
|
89
|
+
s.start('Connecting to Gemini...');
|
|
90
|
+
await initGeminiClient();
|
|
91
|
+
// Build full prompt with style if provided
|
|
92
|
+
let fullPrompt = prompt;
|
|
93
|
+
if (style) {
|
|
94
|
+
fullPrompt = `${prompt}, in ${style} style`;
|
|
95
|
+
}
|
|
96
|
+
// Generate image
|
|
97
|
+
s.update('Generating image...');
|
|
98
|
+
const result = await generateImage(fullPrompt, {
|
|
99
|
+
aspectRatio: ratio, // Type assertion for ratio string
|
|
100
|
+
imageSize: size,
|
|
101
|
+
useGoogleSearch: useSearch,
|
|
102
|
+
});
|
|
103
|
+
if (!result.filePath) {
|
|
104
|
+
throw new Error('Image generation failed - no file created');
|
|
105
|
+
}
|
|
106
|
+
s.success('Image generated!');
|
|
107
|
+
print('');
|
|
108
|
+
// Get file stats
|
|
109
|
+
const file = Bun.file(result.filePath);
|
|
110
|
+
const fileSize = file.size;
|
|
111
|
+
// Show info
|
|
112
|
+
const infoLines = [
|
|
113
|
+
`${theme.colors.primary('Prompt:')} ${prompt.substring(0, 50)}${prompt.length > 50 ? '...' : ''}`,
|
|
114
|
+
style ? `${theme.colors.primary('Style:')} ${style}` : null,
|
|
115
|
+
`${theme.colors.primary('Size:')} ${size}`,
|
|
116
|
+
`${theme.colors.primary('Ratio:')} ${ratio}`,
|
|
117
|
+
useSearch ? `${theme.colors.primary('Search:')} Enabled` : null,
|
|
118
|
+
`${theme.colors.primary('File:')} ${result.filePath}`,
|
|
119
|
+
`${theme.colors.primary('File Size:')} ${(fileSize / 1024).toFixed(1)} KB`,
|
|
120
|
+
].filter(Boolean);
|
|
121
|
+
print(box(infoLines, { title: 'Image Generated' }));
|
|
122
|
+
print('');
|
|
123
|
+
printSuccess(`Image saved to: ${result.filePath}`);
|
|
124
|
+
// Hint about opening
|
|
125
|
+
print('');
|
|
126
|
+
print(theme.colors.muted(`Open with: open "${result.filePath}"`));
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
s.error('Image generation failed');
|
|
130
|
+
printError(error instanceof Error ? error.message : String(error));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Command
|
|
3
|
+
*
|
|
4
|
+
* Direct queries to Gemini with thinking level control.
|
|
5
|
+
* gemini query "your prompt" [--thinking high]
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'node:util';
|
|
8
|
+
import { initGeminiClient, generateWithGeminiPro, generateWithGeminiFlash } from '../../gemini-client.js';
|
|
9
|
+
import { setupLogger } from '../../utils/logger.js';
|
|
10
|
+
import { spinner, print, printError, printMuted, t, header } from '../ui/index.js';
|
|
11
|
+
function showHelp() {
|
|
12
|
+
const theme = t();
|
|
13
|
+
print(header('gemini query', 'Query Gemini directly'));
|
|
14
|
+
print('');
|
|
15
|
+
print(theme.colors.primary('Usage:'));
|
|
16
|
+
print(` gemini query ${theme.colors.muted('"your prompt"')} [options]`);
|
|
17
|
+
print('');
|
|
18
|
+
print(theme.colors.primary('Options:'));
|
|
19
|
+
print(` ${theme.colors.highlight('--thinking, -t')} ${theme.colors.muted('Thinking level: minimal, low, medium, high (default: high)')}`);
|
|
20
|
+
print(` ${theme.colors.highlight('--model, -m')} ${theme.colors.muted('Model to use: pro, flash (default: pro)')}`);
|
|
21
|
+
print(` ${theme.colors.highlight('--help, -h')} ${theme.colors.muted('Show this help')}`);
|
|
22
|
+
print('');
|
|
23
|
+
print(theme.colors.primary('Examples:'));
|
|
24
|
+
print(theme.colors.muted(' gemini query "What is the meaning of life?"'));
|
|
25
|
+
print(theme.colors.muted(' gemini query "Explain quantum computing" --thinking high'));
|
|
26
|
+
print(theme.colors.muted(' gemini query "Quick fact check" -t minimal -m flash'));
|
|
27
|
+
}
|
|
28
|
+
export async function queryCommand(argv) {
|
|
29
|
+
const { values, positionals } = parseArgs({
|
|
30
|
+
args: argv,
|
|
31
|
+
options: {
|
|
32
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
33
|
+
thinking: { type: 'string', short: 't', default: 'high' },
|
|
34
|
+
model: { type: 'string', short: 'm', default: 'pro' },
|
|
35
|
+
},
|
|
36
|
+
allowPositionals: true,
|
|
37
|
+
});
|
|
38
|
+
if (values.help) {
|
|
39
|
+
showHelp();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Get prompt from positional args
|
|
43
|
+
const prompt = positionals.join(' ');
|
|
44
|
+
if (!prompt) {
|
|
45
|
+
printError('No prompt provided');
|
|
46
|
+
printMuted('Usage: gemini query "your prompt"');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
// Validate thinking level
|
|
50
|
+
const validThinking = ['minimal', 'low', 'medium', 'high'];
|
|
51
|
+
const thinking = values.thinking;
|
|
52
|
+
if (!validThinking.includes(thinking)) {
|
|
53
|
+
printError(`Invalid thinking level: ${thinking}`);
|
|
54
|
+
printMuted(`Valid options: ${validThinking.join(', ')}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Validate model
|
|
58
|
+
const validModels = ['pro', 'flash'];
|
|
59
|
+
const model = values.model;
|
|
60
|
+
if (!validModels.includes(model)) {
|
|
61
|
+
printError(`Invalid model: ${model}`);
|
|
62
|
+
printMuted(`Valid options: ${validModels.join(', ')}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const theme = t();
|
|
66
|
+
const s = spinner();
|
|
67
|
+
try {
|
|
68
|
+
// Suppress logger output for CLI (we use our own spinner/output)
|
|
69
|
+
setupLogger('quiet');
|
|
70
|
+
// Initialize Gemini client
|
|
71
|
+
s.start('Connecting to Gemini...');
|
|
72
|
+
await initGeminiClient();
|
|
73
|
+
// Query Gemini based on model
|
|
74
|
+
s.update(`Thinking (${thinking})...`);
|
|
75
|
+
const options = {
|
|
76
|
+
thinkingLevel: thinking
|
|
77
|
+
};
|
|
78
|
+
const result = model === 'flash'
|
|
79
|
+
? await generateWithGeminiFlash(prompt, options)
|
|
80
|
+
: await generateWithGeminiPro(prompt, options);
|
|
81
|
+
s.success('Response received');
|
|
82
|
+
print('');
|
|
83
|
+
// Show the response
|
|
84
|
+
print(theme.colors.primary('Response:'));
|
|
85
|
+
print('');
|
|
86
|
+
print(result);
|
|
87
|
+
print('');
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
s.error('Query failed');
|
|
91
|
+
printError(error instanceof Error ? error.message : String(error));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|