askimo 1.3.1 → 1.5.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 CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  A CLI tool for communicating with AI providers.
9
9
 
10
- **Supported providers:** Perplexity · OpenAI · Anthropic · xAI (Grok)
10
+ **Supported providers:** Perplexity · OpenAI · Anthropic · xAI (Grok) · Google Gemini
11
11
 
12
12
  ---
13
13
 
@@ -27,6 +27,7 @@ PERPLEXITY_API_KEY=your-perplexity-key
27
27
  OPENAI_API_KEY=your-openai-key
28
28
  ANTHROPIC_API_KEY=your-anthropic-key
29
29
  XAI_API_KEY=your-xai-key
30
+ GOOGLE_GENERATIVE_AI_API_KEY=your-google-key
30
31
 
31
32
  # Optional settings
32
33
  DEFAULT_PROVIDER=perplexity
@@ -34,6 +35,7 @@ PERPLEXITY_MODEL=sonar
34
35
  OPENAI_MODEL=gpt-4o
35
36
  ANTHROPIC_MODEL=claude-sonnet-4-20250514
36
37
  XAI_MODEL=grok-4
38
+ GEMINI_MODEL=gemini-3-pro-preview
37
39
  ```
38
40
 
39
41
  ---
@@ -54,11 +56,13 @@ askimo "What is the capital of France?"
54
56
  | `-o` | OpenAI |
55
57
  | `-a` | Anthropic |
56
58
  | `-x` | xAI (Grok) |
59
+ | `-g` | Google Gemini |
57
60
 
58
61
  ```bash
59
62
  askimo "explain quantum computing" -o # Use OpenAI
60
63
  askimo "write a haiku" -a # Use Anthropic
61
64
  askimo "what's happening today?" -x # Use xAI Grok
65
+ askimo "summarize this topic" -g # Use Google Gemini
62
66
  ```
63
67
 
64
68
  ### Continue a conversation
@@ -94,9 +98,9 @@ askimo -f error.log "find the bug"
94
98
  ```bash
95
99
  askimo chat # Start new chat
96
100
  askimo chat -o # Chat with OpenAI
97
- askimo chat -x # Chat with xAI Grok
101
+ askimo chat -g # Chat with Gemini
98
102
  askimo chat -c 1 # Continue last conversation
99
- askimo chat --cid abc123 # Continue by conversation ID
103
+ askimo chat --cid abc123 # Continue by conversation ID
100
104
  ```
101
105
 
102
106
  Type `exit` or `Ctrl+C` to quit.
@@ -106,7 +110,7 @@ Type `exit` or `Ctrl+C` to quit.
106
110
  ```bash
107
111
  askimo models # All providers
108
112
  askimo models -p # Perplexity only
109
- askimo models -x # xAI only
113
+ askimo models -g # Gemini only
110
114
  ```
111
115
 
112
116
  ### View conversation history
package/index.mjs CHANGED
@@ -22,6 +22,7 @@ program
22
22
  .option('-o, --openai', 'Use OpenAI')
23
23
  .option('-a, --anthropic', 'Use Anthropic Claude')
24
24
  .option('-x, --xai', 'Use xAI Grok')
25
+ .option('-g, --gemini', 'Use Google Gemini')
25
26
  .option('-j, --json', 'Output as JSON instead of streaming')
26
27
  .option('-c, --continue <n>', 'Continue conversation N (1=last, 2=second-to-last)', Number.parseInt)
27
28
  .option('--cid <id>', 'Continue conversation by ID')
@@ -115,6 +116,7 @@ program
115
116
  .option('-o, --openai', 'Use OpenAI')
116
117
  .option('-a, --anthropic', 'Use Anthropic Claude')
117
118
  .option('-x, --xai', 'Use xAI Grok')
119
+ .option('-g, --gemini', 'Use Google Gemini')
118
120
  .option('-c, --continue <n>', 'Continue conversation N (1=last, 2=second-to-last)', Number.parseInt)
119
121
  .option('--cid <id>', 'Continue conversation by ID')
120
122
  .action(async (options) => {
@@ -144,6 +146,7 @@ program
144
146
  .option('-o, --openai', 'Show only OpenAI models')
145
147
  .option('-a, --anthropic', 'Show only Anthropic models')
146
148
  .option('-x, --xai', 'Show only xAI models')
149
+ .option('-g, --gemini', 'Show only Gemini models')
147
150
  .action(async (options) => {
148
151
  try {
149
152
  const config = await loadConfig()
@@ -153,8 +156,9 @@ program
153
156
  if (options.openai) providers.push('openai')
154
157
  if (options.anthropic) providers.push('anthropic')
155
158
  if (options.xai) providers.push('xai')
159
+ if (options.gemini) providers.push('gemini')
156
160
 
157
- const toShow = providers.length === 0 ? ['perplexity', 'openai', 'anthropic', 'xai'] : providers
161
+ const toShow = providers.length === 0 ? ['perplexity', 'openai', 'anthropic', 'xai', 'gemini'] : providers
158
162
 
159
163
  const results = await Promise.all(
160
164
  toShow.map(async (provider) => ({
package/lib/providers.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createAnthropic } from '@ai-sdk/anthropic'
2
+ import { createGoogleGenerativeAI } from '@ai-sdk/google'
2
3
  import { createOpenAI } from '@ai-sdk/openai'
3
4
  import { createPerplexity } from '@ai-sdk/perplexity'
4
5
  import { createXai } from '@ai-sdk/xai'
@@ -7,94 +8,38 @@ const DEFAULT_MODELS = {
7
8
  perplexity: 'sonar',
8
9
  openai: 'gpt-4o',
9
10
  anthropic: 'claude-sonnet-4-20250514',
10
- xai: 'grok-4'
11
+ xai: 'grok-4',
12
+ gemini: 'gemini-3-pro-preview'
11
13
  }
12
14
 
13
- // Perplexity doesn't have a models list API, so we hardcode these
14
- const PERPLEXITY_MODELS = [
15
- { id: 'sonar', description: 'Lightweight, cost-effective search model' },
16
- { id: 'sonar-pro', description: 'Advanced search for complex queries' },
17
- { id: 'sonar-reasoning', description: 'Chain-of-thought problem solving' },
18
- { id: 'sonar-reasoning-pro', description: 'Advanced reasoning (DeepSeek-R1)' },
19
- { id: 'sonar-deep-research', description: 'Deep research sessions' }
20
- ]
21
-
22
- // xAI doesn't have a public models list API, so we hardcode these
23
- const XAI_MODELS = [
24
- { id: 'grok-4-1-fast-reasoning', description: 'Grok 4.1 fast with reasoning' },
25
- { id: 'grok-4-1-fast-non-reasoning', description: 'Grok 4.1 fast without reasoning' },
26
- { id: 'grok-code-fast-1', description: 'Grok optimized for code' },
27
- { id: 'grok-4-fast-reasoning', description: 'Grok 4 fast with reasoning' },
28
- { id: 'grok-4-fast-non-reasoning', description: 'Grok 4 fast without reasoning' },
29
- { id: 'grok-4-0709', description: 'Grok 4 flagship model' },
30
- { id: 'grok-3-mini', description: 'Lightweight Grok 3 model' },
31
- { id: 'grok-3', description: 'Grok 3 base model' },
32
- { id: 'grok-2-vision-1212', description: 'Grok 2 with vision capabilities' },
33
- { id: 'grok-2-image-1212', description: 'Image generation model' }
34
- ]
35
-
36
- async function fetchOpenAiModels(apiKey) {
37
- const response = await fetch('https://api.openai.com/v1/models', {
38
- // biome-ignore lint/style/useNamingConvention: headers use standard capitalization
39
- headers: { Authorization: `Bearer ${apiKey}` }
40
- })
15
+ // Provider name mapping (askimo name models.dev name)
16
+ const PROVIDER_MAP = {
17
+ gemini: 'google'
18
+ }
41
19
 
20
+ async function fetchModelsFromApi() {
21
+ const response = await fetch('https://models.dev/api.json')
42
22
  if (!response.ok) {
43
- throw new Error(`OpenAI API error: ${response.status}`)
23
+ throw new Error(`models.dev API error: ${response.status}`)
44
24
  }
45
-
46
- const data = await response.json()
47
- return data.data.map((m) => ({ id: m.id, created: m.created })).sort((a, b) => b.created - a.created)
25
+ return response.json()
48
26
  }
49
27
 
50
- async function fetchAnthropicModels(apiKey) {
51
- const response = await fetch('https://api.anthropic.com/v1/models', {
52
- headers: {
53
- 'x-api-key': apiKey,
54
- 'anthropic-version': '2023-06-01'
55
- }
56
- })
28
+ async function listModels(provider) {
29
+ const apiData = await fetchModelsFromApi()
30
+ const providerKey = PROVIDER_MAP[provider] || provider
31
+ const providerData = apiData[providerKey]
57
32
 
58
- if (!response.ok) {
59
- throw new Error(`Anthropic API error: ${response.status}`)
33
+ if (!providerData) {
34
+ throw new Error(`Unknown provider: ${provider}`)
60
35
  }
61
36
 
62
- const data = await response.json()
63
- return data.data.map((m) => ({
37
+ return Object.values(providerData.models).map((m) => ({
64
38
  id: m.id,
65
- displayName: m.display_name
39
+ description: m.name
66
40
  }))
67
41
  }
68
42
 
69
- async function listModels(provider, config) {
70
- switch (provider) {
71
- case 'perplexity':
72
- return PERPLEXITY_MODELS
73
-
74
- case 'openai': {
75
- const apiKey = config.OPENAI_API_KEY
76
- if (!apiKey) {
77
- throw new Error('OPENAI_API_KEY not found in config')
78
- }
79
- return fetchOpenAiModels(apiKey)
80
- }
81
-
82
- case 'anthropic': {
83
- const apiKey = config.ANTHROPIC_API_KEY
84
- if (!apiKey) {
85
- throw new Error('ANTHROPIC_API_KEY not found in config')
86
- }
87
- return fetchAnthropicModels(apiKey)
88
- }
89
-
90
- case 'xai':
91
- return XAI_MODELS
92
-
93
- default:
94
- throw new Error(`Unknown provider: ${provider}`)
95
- }
96
- }
97
-
98
43
  function getProvider(providerName, config) {
99
44
  switch (providerName) {
100
45
  case 'perplexity': {
@@ -149,6 +94,19 @@ function getProvider(providerName, config) {
149
94
  modelName
150
95
  }
151
96
  }
97
+ case 'gemini': {
98
+ const apiKey = config.GOOGLE_GENERATIVE_AI_API_KEY
99
+ if (!apiKey) {
100
+ throw new Error('GOOGLE_GENERATIVE_AI_API_KEY not found in config')
101
+ }
102
+ const modelName = config.GEMINI_MODEL || DEFAULT_MODELS.gemini
103
+ const google = createGoogleGenerativeAI({ apiKey })
104
+ return {
105
+ model: google(modelName),
106
+ name: 'gemini',
107
+ modelName
108
+ }
109
+ }
152
110
  default:
153
111
  throw new Error(`Unknown provider: ${providerName}`)
154
112
  }
@@ -159,9 +117,10 @@ function determineProvider(options, config = {}) {
159
117
  if (options.anthropic) return 'anthropic'
160
118
  if (options.perplexity) return 'perplexity'
161
119
  if (options.xai) return 'xai'
120
+ if (options.gemini) return 'gemini'
162
121
 
163
122
  const defaultProvider = config.DEFAULT_PROVIDER?.toLowerCase()
164
- if (defaultProvider && ['perplexity', 'openai', 'anthropic', 'xai'].includes(defaultProvider)) {
123
+ if (defaultProvider && ['perplexity', 'openai', 'anthropic', 'xai', 'gemini'].includes(defaultProvider)) {
165
124
  return defaultProvider
166
125
  }
167
126
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "askimo",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "A CLI tool for communicating with AI providers (Perplexity, OpenAI, Anthropic)",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Amit Tal",
@@ -20,10 +20,6 @@
20
20
  "bin": {
21
21
  "askimo": "./index.mjs"
22
22
  },
23
- "scripts": {
24
- "test": "ava",
25
- "lint": "biome check --write"
26
- },
27
23
  "ava": {
28
24
  "files": [
29
25
  "test/**/*.mjs"
@@ -32,17 +28,22 @@
32
28
  "repository": "https://github.com/amitosdev/askimo.git",
33
29
  "homepage": "https://github.com/amitosdev/askimo#readme",
34
30
  "devDependencies": {
35
- "@biomejs/biome": "^2.3.8",
31
+ "@biomejs/biome": "^2.3.14",
36
32
  "ava": "^6.4.1"
37
33
  },
38
34
  "dependencies": {
39
- "@ai-sdk/anthropic": "^3.0.1",
40
- "@ai-sdk/openai": "^3.0.1",
41
- "@ai-sdk/perplexity": "^3.0.1",
42
- "@ai-sdk/xai": "^3.0.1",
43
- "@inquirer/input": "^5.0.2",
44
- "ai": "^6.0.3",
45
- "commander": "^14.0.2",
35
+ "@ai-sdk/anthropic": "^3.0.35",
36
+ "@ai-sdk/google": "^3.0.20",
37
+ "@ai-sdk/openai": "^3.0.25",
38
+ "@ai-sdk/perplexity": "^3.0.17",
39
+ "@ai-sdk/xai": "^3.0.46",
40
+ "@inquirer/input": "^5.0.4",
41
+ "ai": "^6.0.69",
42
+ "commander": "^14.0.3",
46
43
  "hcat": "^2.2.1"
44
+ },
45
+ "scripts": {
46
+ "test": "ava",
47
+ "lint": "biome check --write"
47
48
  }
48
- }
49
+ }
@@ -19,6 +19,11 @@ test('determineProvider returns anthropic when --anthropic flag is set', (t) =>
19
19
  t.is(result, 'anthropic')
20
20
  })
21
21
 
22
+ test('determineProvider returns gemini when --gemini flag is set', (t) => {
23
+ const result = determineProvider({ gemini: true })
24
+ t.is(result, 'gemini')
25
+ })
26
+
22
27
  test('determineProvider prefers flag over config', (t) => {
23
28
  const result = determineProvider({ openai: true }, { DEFAULT_PROVIDER: 'anthropic' })
24
29
  t.is(result, 'openai')
@@ -57,3 +62,7 @@ test('DEFAULT_MODELS contains openai model', (t) => {
57
62
  test('DEFAULT_MODELS contains anthropic model', (t) => {
58
63
  t.is(DEFAULT_MODELS.anthropic, 'claude-sonnet-4-20250514')
59
64
  })
65
+
66
+ test('DEFAULT_MODELS contains gemini model', (t) => {
67
+ t.is(DEFAULT_MODELS.gemini, 'gemini-3-pro-preview')
68
+ })