gut-cli 0.1.14 → 0.1.15
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/dist/index.js +41 -18
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +300 -0
- package/dist/lib/index.js +583 -0
- package/dist/lib/index.js.map +1 -0
- package/package.json +17 -2
package/dist/index.js
CHANGED
|
@@ -114,6 +114,9 @@ async function getKeytar() {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
async function saveApiKey(provider, apiKey) {
|
|
117
|
+
if (provider === "ollama") {
|
|
118
|
+
throw new Error("Ollama does not require an API key");
|
|
119
|
+
}
|
|
117
120
|
const keytar = await getKeytar();
|
|
118
121
|
if (!keytar) {
|
|
119
122
|
throw new Error("Keychain not available. Set environment variable instead.");
|
|
@@ -121,6 +124,9 @@ async function saveApiKey(provider, apiKey) {
|
|
|
121
124
|
await keytar.setPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider], apiKey);
|
|
122
125
|
}
|
|
123
126
|
async function getApiKey(provider) {
|
|
127
|
+
if (provider === "ollama") {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
124
130
|
const envKey = process.env[ENV_VAR_MAP[provider]];
|
|
125
131
|
if (envKey) return envKey;
|
|
126
132
|
const fallbackKey = process.env[FALLBACK_ENV_MAP[provider]];
|
|
@@ -130,6 +136,9 @@ async function getApiKey(provider) {
|
|
|
130
136
|
return keytar.getPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider]);
|
|
131
137
|
}
|
|
132
138
|
async function deleteApiKey(provider) {
|
|
139
|
+
if (provider === "ollama") {
|
|
140
|
+
throw new Error("Ollama does not use an API key");
|
|
141
|
+
}
|
|
133
142
|
const keytar = await getKeytar();
|
|
134
143
|
if (!keytar) {
|
|
135
144
|
throw new Error("Keychain not available.");
|
|
@@ -137,20 +146,22 @@ async function deleteApiKey(provider) {
|
|
|
137
146
|
return keytar.deletePassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider]);
|
|
138
147
|
}
|
|
139
148
|
async function listProviders() {
|
|
140
|
-
const
|
|
149
|
+
const apiKeyProviders = ["gemini", "openai", "anthropic"];
|
|
141
150
|
const results = await Promise.all(
|
|
142
|
-
|
|
151
|
+
apiKeyProviders.map(async (provider) => ({
|
|
143
152
|
provider,
|
|
144
153
|
hasKey: !!await getApiKey(provider)
|
|
145
154
|
}))
|
|
146
155
|
);
|
|
156
|
+
results.push({ provider: "ollama", hasKey: true });
|
|
147
157
|
return results;
|
|
148
158
|
}
|
|
149
159
|
function getProviderDisplayName(provider) {
|
|
150
160
|
const names = {
|
|
151
161
|
gemini: "Google Gemini",
|
|
152
162
|
openai: "OpenAI",
|
|
153
|
-
anthropic: "Anthropic Claude"
|
|
163
|
+
anthropic: "Anthropic Claude",
|
|
164
|
+
ollama: "Ollama (Local)"
|
|
154
165
|
};
|
|
155
166
|
return names[provider];
|
|
156
167
|
}
|
|
@@ -270,6 +281,7 @@ import { generateText, generateObject } from "ai";
|
|
|
270
281
|
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
271
282
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
272
283
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
284
|
+
import { createOllama } from "ollama-ai-provider";
|
|
273
285
|
import { z } from "zod";
|
|
274
286
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
275
287
|
import { join as join2, dirname } from "path";
|
|
@@ -410,29 +422,45 @@ function applyTemplate(userTemplate, templateName, variables) {
|
|
|
410
422
|
var DEFAULT_MODELS = {
|
|
411
423
|
gemini: "gemini-2.0-flash",
|
|
412
424
|
openai: "gpt-4o-mini",
|
|
413
|
-
anthropic: "claude-sonnet-4-20250514"
|
|
425
|
+
anthropic: "claude-sonnet-4-20250514",
|
|
426
|
+
ollama: "llama3.2"
|
|
414
427
|
};
|
|
415
428
|
async function getModel(options) {
|
|
416
|
-
const apiKey = await getApiKey(options.provider);
|
|
417
|
-
if (!apiKey) {
|
|
418
|
-
throw new Error(
|
|
419
|
-
`No API key found for ${options.provider}. Run: gut auth login --provider ${options.provider}`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
429
|
const modelName = options.model || DEFAULT_MODELS[options.provider];
|
|
430
|
+
async function resolveApiKey() {
|
|
431
|
+
if (options.apiKey) return options.apiKey;
|
|
432
|
+
return getApiKey(options.provider);
|
|
433
|
+
}
|
|
434
|
+
if (options.provider !== "ollama") {
|
|
435
|
+
const apiKey = await resolveApiKey();
|
|
436
|
+
if (!apiKey) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`No API key found for ${options.provider}. Run: gut auth login --provider ${options.provider}`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
423
442
|
switch (options.provider) {
|
|
424
443
|
case "gemini": {
|
|
444
|
+
const apiKey = await resolveApiKey();
|
|
425
445
|
const google = createGoogleGenerativeAI({ apiKey });
|
|
426
446
|
return google(modelName);
|
|
427
447
|
}
|
|
428
448
|
case "openai": {
|
|
449
|
+
const apiKey = await resolveApiKey();
|
|
429
450
|
const openai = createOpenAI({ apiKey });
|
|
430
451
|
return openai(modelName);
|
|
431
452
|
}
|
|
432
453
|
case "anthropic": {
|
|
454
|
+
const apiKey = await resolveApiKey();
|
|
433
455
|
const anthropic = createAnthropic({ apiKey });
|
|
434
456
|
return anthropic(modelName);
|
|
435
457
|
}
|
|
458
|
+
case "ollama": {
|
|
459
|
+
const ollama = createOllama({
|
|
460
|
+
baseURL: options.ollamaBaseUrl || "http://localhost:11434/api"
|
|
461
|
+
});
|
|
462
|
+
return ollama(modelName);
|
|
463
|
+
}
|
|
436
464
|
}
|
|
437
465
|
}
|
|
438
466
|
async function generateCommitMessage(diff, options, template) {
|
|
@@ -614,11 +642,12 @@ async function searchCommits(query, commits, options, maxResults = 5, template)
|
|
|
614
642
|
schema: CommitSearchSchema,
|
|
615
643
|
prompt
|
|
616
644
|
});
|
|
617
|
-
const enrichedMatches = result.object.matches.map((match) => {
|
|
645
|
+
const enrichedMatches = result.object.matches.map((match, index) => {
|
|
618
646
|
const commit = commits.find((c) => c.hash.startsWith(match.hash));
|
|
619
647
|
if (!commit) {
|
|
620
648
|
return null;
|
|
621
649
|
}
|
|
650
|
+
const relevance = index === 0 ? "high" : index < 3 ? "medium" : "low";
|
|
622
651
|
return {
|
|
623
652
|
hash: commit.hash,
|
|
624
653
|
message: commit.message,
|
|
@@ -626,15 +655,9 @@ async function searchCommits(query, commits, options, maxResults = 5, template)
|
|
|
626
655
|
email: commit.email,
|
|
627
656
|
date: commit.date,
|
|
628
657
|
reason: match.reason,
|
|
629
|
-
relevance
|
|
630
|
-
// First results are most relevant
|
|
658
|
+
relevance
|
|
631
659
|
};
|
|
632
660
|
}).filter((m) => m !== null);
|
|
633
|
-
enrichedMatches.forEach((match, index) => {
|
|
634
|
-
if (index === 0) match.relevance = "high";
|
|
635
|
-
else if (index < 3) match.relevance = "medium";
|
|
636
|
-
else match.relevance = "low";
|
|
637
|
-
});
|
|
638
661
|
return {
|
|
639
662
|
matches: enrichedMatches,
|
|
640
663
|
summary: result.object.summary
|