gitpt 1.3.0 → 1.6.1

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 (62) hide show
  1. package/README.md +7 -0
  2. package/dist/commands/commit/context/buildPrompt.d.ts +4 -0
  3. package/dist/commands/commit/context/buildPrompt.js +13 -0
  4. package/dist/commands/commit/context/summaryPrompt.d.ts +2 -0
  5. package/dist/commands/commit/context/summaryPrompt.js +17 -0
  6. package/dist/commands/commit/generateCommitMessage.js +8 -26
  7. package/dist/commands/commit/index.js +4 -2
  8. package/dist/commands/commit/summarizeDiff.d.ts +1 -0
  9. package/dist/commands/commit/summarizeDiff.js +172 -0
  10. package/dist/commands/middleware/setupMiddleware/defaultModels.d.ts +8 -0
  11. package/dist/commands/middleware/setupMiddleware/defaultModels.js +11 -0
  12. package/dist/commands/middleware/setupMiddleware/index.js +58 -24
  13. package/dist/commands/pr/context/userPrompt.d.ts +1 -1
  14. package/dist/commands/pr/context/userPrompt.js +3 -1
  15. package/dist/commands/pr/generatePRDetails.js +7 -14
  16. package/dist/commands/reset.d.ts +3 -0
  17. package/dist/commands/reset.js +26 -0
  18. package/dist/config.d.ts +6 -10
  19. package/dist/config.js +25 -20
  20. package/dist/index.js +6 -0
  21. package/dist/llm/client.d.ts +24 -0
  22. package/dist/llm/index.d.ts +3 -2
  23. package/dist/llm/index.js +4 -9
  24. package/dist/llm/providers/anthropic/index.d.ts +9 -0
  25. package/dist/llm/providers/anthropic/index.js +31 -0
  26. package/dist/llm/providers/apiKey.d.ts +3 -0
  27. package/dist/llm/providers/apiKey.js +40 -0
  28. package/dist/llm/providers/apple/client.d.ts +3 -0
  29. package/dist/llm/providers/apple/client.js +87 -0
  30. package/dist/llm/providers/apple/index.d.ts +13 -0
  31. package/dist/llm/providers/apple/index.js +77 -0
  32. package/dist/llm/providers/apple/models.d.ts +14 -0
  33. package/dist/llm/providers/apple/models.js +21 -0
  34. package/dist/llm/providers/base.d.ts +30 -0
  35. package/dist/llm/providers/base.js +36 -0
  36. package/dist/llm/providers/local/index.d.ts +11 -0
  37. package/dist/llm/providers/local/index.js +96 -0
  38. package/dist/llm/providers/openai/index.d.ts +10 -0
  39. package/dist/llm/providers/openai/index.js +16 -0
  40. package/dist/llm/providers/openaiCompatible.d.ts +15 -0
  41. package/dist/llm/providers/openaiCompatible.js +69 -0
  42. package/dist/llm/providers/openrouter/index.d.ts +9 -0
  43. package/dist/llm/providers/openrouter/index.js +16 -0
  44. package/dist/llm/registry.d.ts +8 -0
  45. package/dist/llm/registry.js +43 -0
  46. package/dist/{commands/middleware/setupMiddleware → llm/setup}/getAvailableModels.d.ts +1 -0
  47. package/dist/{commands/middleware/setupMiddleware → llm/setup}/getAvailableModels.js +3 -3
  48. package/dist/{commands/middleware/setupMiddleware → llm/setup}/selectModel.d.ts +1 -1
  49. package/dist/{commands/middleware/setupMiddleware → llm/setup}/selectModel.js +13 -3
  50. package/dist/llm/setup/types.js +1 -0
  51. package/dist/llm/tokenCount.d.ts +3 -0
  52. package/dist/llm/tokenCount.js +4 -0
  53. package/dist/services/git/getStagedChanges.js +1 -1
  54. package/package.json +6 -2
  55. package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.d.ts +0 -1
  56. package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.js +0 -39
  57. package/dist/commands/middleware/setupMiddleware/setupLocalLLM.d.ts +0 -5
  58. package/dist/commands/middleware/setupMiddleware/setupLocalLLM.js +0 -60
  59. package/dist/commands/middleware/setupMiddleware/setupOpenRouter.d.ts +0 -2
  60. package/dist/commands/middleware/setupMiddleware/setupOpenRouter.js +0 -66
  61. /package/dist/{commands/middleware/setupMiddleware/types.js → llm/client.js} +0 -0
  62. /package/dist/{commands/middleware/setupMiddleware → llm/setup}/types.d.ts +0 -0
@@ -0,0 +1,9 @@
1
+ import { GitPTConfig } from "../../../config.js";
2
+ import { OpenAICompatibleProvider } from "../openaiCompatible.js";
3
+ export declare class OpenRouterProvider extends OpenAICompatibleProvider {
4
+ static readonly id = "openrouter";
5
+ static readonly label = "OpenRouter (remote)";
6
+ static readonly baseURL = "https://openrouter.ai/api/v1";
7
+ static setup(existingConfig: GitPTConfig): Promise<GitPTConfig>;
8
+ protected baseURL(): string;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { OPENROUTER_API_URL } from "../../index.js";
2
+ import { OpenAICompatibleProvider, setupApiKeyProvider, } from "../openaiCompatible.js";
3
+ export class OpenRouterProvider extends OpenAICompatibleProvider {
4
+ static id = "openrouter";
5
+ static label = "OpenRouter (remote)";
6
+ static baseURL = OPENROUTER_API_URL;
7
+ static setup(existingConfig) {
8
+ return setupApiKeyProvider(existingConfig, {
9
+ baseURL: OpenRouterProvider.baseURL,
10
+ label: OpenRouterProvider.label,
11
+ });
12
+ }
13
+ baseURL() {
14
+ return OpenRouterProvider.baseURL;
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ import type { Provider, ProviderClass } from "./providers/base.js";
2
+ export declare const PROVIDERS: ProviderClass[];
3
+ export declare const getProviderClass: (id: string | undefined) => ProviderClass | undefined;
4
+ export declare const getProvider: () => Provider;
5
+ export declare const validateConfig: () => {
6
+ isValid: boolean;
7
+ errors: string[];
8
+ };
@@ -0,0 +1,43 @@
1
+ import { getConfig } from "../config.js";
2
+ import { AnthropicProvider } from "./providers/anthropic/index.js";
3
+ import { getApiKey } from "./providers/apiKey.js";
4
+ import { AppleProvider } from "./providers/apple/index.js";
5
+ import { LocalProvider } from "./providers/local/index.js";
6
+ import { OpenAIProvider } from "./providers/openai/index.js";
7
+ import { OpenRouterProvider } from "./providers/openrouter/index.js";
8
+ export const PROVIDERS = [
9
+ AppleProvider,
10
+ OpenRouterProvider,
11
+ OpenAIProvider,
12
+ AnthropicProvider,
13
+ LocalProvider,
14
+ ];
15
+ export const getProviderClass = (id) => PROVIDERS.find((p) => p.id === id);
16
+ export const getProvider = () => {
17
+ const { provider, model } = getConfig();
18
+ const ProviderImpl = getProviderClass(provider);
19
+ if (!ProviderImpl) {
20
+ throw new Error(`Unknown provider: ${provider ?? "(none)"}. Run "gitpt setup".`);
21
+ }
22
+ return new ProviderImpl(model ?? "");
23
+ };
24
+ export const validateConfig = () => {
25
+ const { provider, customLLMEndpoint, model } = getConfig();
26
+ const errors = [];
27
+ const spec = getProviderClass(provider);
28
+ if (!spec) {
29
+ errors.push("No provider configured. Run 'gitpt setup'.");
30
+ }
31
+ else {
32
+ if (spec.requiresApiKey && !getApiKey()) {
33
+ errors.push(`API key is required for ${spec.label}.`);
34
+ }
35
+ if (spec.requiresEndpoint && !customLLMEndpoint) {
36
+ errors.push(`Custom endpoint is required for ${spec.label}.`);
37
+ }
38
+ }
39
+ if (!model) {
40
+ errors.push("Model is required.");
41
+ }
42
+ return { isValid: errors.length === 0, errors };
43
+ };
@@ -1,4 +1,5 @@
1
1
  import { Model } from "openai/resources/models";
2
2
  export declare const getAvailableModels: (options?: {
3
3
  baseURLOverride?: string;
4
+ apiKey?: string;
4
5
  }) => Promise<Model[]>;
@@ -1,7 +1,7 @@
1
- import { getLLMClient } from "../../../llm/index.js";
1
+ import { getLLMClient } from "../index.js";
2
2
  export const getAvailableModels = async (options) => {
3
- const { baseURLOverride } = options || {};
4
- let modelsList = await getLLMClient({ baseURLOverride }).models.list();
3
+ const { baseURLOverride, apiKey } = options || {};
4
+ let modelsList = await getLLMClient({ baseURLOverride, apiKey }).models.list();
5
5
  const modelsData = modelsList.data;
6
6
  while (modelsList.hasNextPage()) {
7
7
  modelsList = await modelsList.getNextPage();
@@ -2,4 +2,4 @@ import { Model } from "./types.js";
2
2
  /**
3
3
  * Show a list of models to select from
4
4
  */
5
- export declare const selectModel: (models: Model[], existingModel?: string) => Promise<string>;
5
+ export declare const selectModel: (models: Model[], existingModel?: string, notes?: string[]) => Promise<string>;
@@ -1,11 +1,14 @@
1
+ import chalk from "chalk";
1
2
  import inquirer from "inquirer";
2
3
  /**
3
4
  * Show a list of models to select from
4
5
  */
5
- export const selectModel = async (models, existingModel) => {
6
+ export const selectModel = async (models, existingModel, notes) => {
6
7
  const modelChoices = models.map((model) => ({
7
8
  name: model.name
8
- ? `${model.name} (Context: ${model.context_length})`
9
+ ? model.context_length
10
+ ? `${model.name} (Context: ${model.context_length})`
11
+ : model.name
9
12
  : model.id,
10
13
  value: model.id,
11
14
  }));
@@ -13,12 +16,19 @@ export const selectModel = async (models, existingModel) => {
13
16
  name: "Other (specify model identifier)",
14
17
  value: "custom",
15
18
  });
19
+ const choices = [...modelChoices];
20
+ if (notes && notes.length > 0) {
21
+ choices.push(new inquirer.Separator(" "));
22
+ for (const note of notes) {
23
+ choices.push(new inquirer.Separator(chalk.gray(note)));
24
+ }
25
+ }
16
26
  const answers = await inquirer.prompt([
17
27
  {
18
28
  type: "list",
19
29
  name: "modelChoice",
20
30
  message: "Select an AI model:",
21
- choices: modelChoices,
31
+ choices,
22
32
  default: () => {
23
33
  const currentIndex = modelChoices.findIndex((choice) => choice.value === existingModel);
24
34
  return currentIndex >= 0 ? currentIndex : 0;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const RESERVED_OUTPUT_TOKENS = 1024;
2
+ export declare const countTokens: (text: string) => number;
3
+ export declare const getContextWindow: () => Promise<number>;
@@ -0,0 +1,4 @@
1
+ import { getProvider } from "./registry.js";
2
+ export const RESERVED_OUTPUT_TOKENS = 1024;
3
+ export const countTokens = (text) => getProvider().countTokens(text);
4
+ export const getContextWindow = () => getProvider().getContextWindow();
@@ -2,7 +2,7 @@ import chalk from "chalk";
2
2
  import { execSync } from "child_process";
3
3
  export const getStagedChanges = () => {
4
4
  try {
5
- return execSync("git diff --staged").toString();
5
+ return execSync("git diff --staged --function-context").toString();
6
6
  }
7
7
  catch (error) {
8
8
  console.error(chalk.red("Error getting staged changes:"), error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitpt",
3
- "version": "1.3.0",
3
+ "version": "1.6.1",
4
4
  "description": "CLI tool that helps you write commit messages & pull request descriptions using AI",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -16,6 +16,9 @@
16
16
  "typecheck": "tsc --noEmit",
17
17
  "build": "tsc",
18
18
  "test": "npm run typecheck",
19
+ "test:apple": "npm run build && node tests/apple-foundation-models/run.mjs",
20
+ "bench:apple": "npm run build && node tests/apple-foundation-models/benchmark.mjs",
21
+ "check:prompt": "npm run build && node tests/snapshots/check.mjs",
19
22
  "prepublishOnly": "npm run build"
20
23
  },
21
24
  "repository": {
@@ -50,6 +53,7 @@
50
53
  "configstore": "^7.0.0",
51
54
  "inquirer": "^12.5.2",
52
55
  "node-fetch": "^3.3.2",
53
- "openai": "^4.100.0"
56
+ "openai": "^4.100.0",
57
+ "ora": "^8.2.0"
54
58
  }
55
59
  }
@@ -1 +0,0 @@
1
- export declare const getOrUpdateApiKey: (existingApiKey?: string) => Promise<string>;
@@ -1,39 +0,0 @@
1
- import inquirer from "inquirer";
2
- import { maskApiKey } from "../../../utils/maskApiKey.js";
3
- export const getOrUpdateApiKey = async (existingApiKey) => {
4
- // For setup command, handle API key selection
5
- if (existingApiKey) {
6
- // If we have an existing API key, ask if the user wants to keep it or use a new one
7
- const useExistingKeyAnswer = await inquirer.prompt([
8
- {
9
- type: "list",
10
- name: "useExistingKey",
11
- message: "OpenRouter API key:",
12
- choices: [
13
- {
14
- name: `Use existing key (${maskApiKey(existingApiKey)})`,
15
- value: true,
16
- },
17
- { name: "Enter a new API key", value: false },
18
- ],
19
- },
20
- ]);
21
- if (useExistingKeyAnswer.useExistingKey) {
22
- return existingApiKey;
23
- }
24
- }
25
- // If no existing key or user wants a new one, prompt for a new API key
26
- const apiKeyAnswer = await inquirer.prompt([
27
- {
28
- type: "input",
29
- name: "apiKey",
30
- message: "Enter your OpenRouter API key:",
31
- validate: (input) => {
32
- if (!input)
33
- return "API key is required";
34
- return true;
35
- },
36
- },
37
- ]);
38
- return apiKeyAnswer.apiKey;
39
- };
@@ -1,5 +0,0 @@
1
- import { GitPTConfig } from "../../../config.js";
2
- /**
3
- * Set up a local LLM configuration
4
- */
5
- export declare const setupLocalLLM: (existingConfig: GitPTConfig) => Promise<GitPTConfig>;
@@ -1,60 +0,0 @@
1
- import chalk from "chalk";
2
- import inquirer from "inquirer";
3
- import { saveConfig } from "../../../config.js";
4
- import { getAvailableModels } from "./getAvailableModels.js";
5
- import { selectModel } from "./selectModel.js";
6
- /**
7
- * Set up a local LLM configuration
8
- */
9
- export const setupLocalLLM = async (existingConfig) => {
10
- console.log(chalk.blue("Local LLM Setup"));
11
- const endpointAnswer = await inquirer.prompt([
12
- {
13
- type: "input",
14
- name: "localLLMEndpoint",
15
- message: "Enter local LLM API endpoint (e.g., http://127.0.0.1:1234):",
16
- default: existingConfig.customLLMEndpoint || "http://127.0.0.1:1234",
17
- validate: (input) => {
18
- if (!input)
19
- return "API endpoint is required";
20
- if (!input.startsWith("http://") && !input.startsWith("https://")) {
21
- return "Must be a valid URL starting with http:// or https://";
22
- }
23
- return true;
24
- },
25
- },
26
- ]);
27
- console.log(chalk.gray("Trying to fetch available models from local LLM server..."));
28
- const models = await getAvailableModels({
29
- baseURLOverride: endpointAnswer.localLLMEndpoint,
30
- });
31
- let selectedModel;
32
- if (models.length > 0) {
33
- console.log(chalk.green(`✓ Found ${models.length} models available on your local LLM server`));
34
- selectedModel = await selectModel(models, existingConfig.model);
35
- }
36
- else {
37
- console.log(chalk.yellow("Could not fetch models from local LLM server, please enter model name manually"));
38
- const modelAnswer = await inquirer.prompt([
39
- {
40
- type: "input",
41
- name: "model",
42
- message: "Enter model name to use with local endpoint:",
43
- default: existingConfig.model,
44
- validate: (input) => (input ? true : "Model name is required"),
45
- },
46
- ]);
47
- selectedModel = modelAnswer.model;
48
- }
49
- const updatedConfig = {
50
- ...existingConfig,
51
- provider: "local",
52
- model: selectedModel,
53
- customLLMEndpoint: endpointAnswer.localLLMEndpoint,
54
- };
55
- // Save the config
56
- saveConfig(updatedConfig);
57
- console.log(chalk.green("✓ Local LLM configuration saved"));
58
- console.log(chalk.gray("Testing connection to local LLM..."));
59
- return updatedConfig;
60
- };
@@ -1,2 +0,0 @@
1
- import { GitPTConfig } from "../../../config.js";
2
- export declare const setupOpenRouter: (existingConfig: GitPTConfig) => Promise<GitPTConfig>;
@@ -1,66 +0,0 @@
1
- import chalk from "chalk";
2
- import inquirer from "inquirer";
3
- import { OPENROUTER_API_URL } from "../../../llm/index.js";
4
- import { saveConfig } from "../../../config.js";
5
- import { getAvailableModels } from "./getAvailableModels.js";
6
- import { getOrUpdateApiKey } from "./getOrUpdateApiKey.js";
7
- import { selectModel } from "./selectModel.js";
8
- export const setupOpenRouter = async (existingConfig) => {
9
- // Get API key - either the existing one or a new one
10
- const apiKey = await getOrUpdateApiKey(existingConfig.apiKey);
11
- if (!apiKey) {
12
- console.error(chalk.red("API key is required for OpenRouter."));
13
- process.exit(1);
14
- }
15
- // Update the config with the potentially new API key
16
- const updatedConfig = { ...existingConfig, apiKey };
17
- // Display current model if any
18
- if (updatedConfig.model) {
19
- console.log("Current model:", chalk.yellow(updatedConfig.model));
20
- console.log("");
21
- }
22
- try {
23
- console.log(chalk.gray("Fetching available models from OpenRouter..."));
24
- const models = await getAvailableModels({
25
- baseURLOverride: OPENROUTER_API_URL,
26
- });
27
- if (models.length > 0) {
28
- console.log(chalk.green(`✓ Found ${models.length} models available with your API key`));
29
- }
30
- else {
31
- console.log(chalk.yellow("No models found from OpenRouter. Please specify a model manually."));
32
- }
33
- const selectedModel = await selectModel(models, updatedConfig.model);
34
- const finalConfig = {
35
- ...updatedConfig,
36
- model: selectedModel,
37
- useLocalLLM: false,
38
- };
39
- // Save the config
40
- saveConfig(finalConfig);
41
- console.log(chalk.green(`✓ Model set to: ${chalk.yellow(selectedModel)}`));
42
- return finalConfig;
43
- }
44
- catch (error) {
45
- console.error(chalk.yellow(`Error fetching models: ${error}`));
46
- // Fallback to manual input if API call fails
47
- const modelAnswer = await inquirer.prompt([
48
- {
49
- type: "input",
50
- name: "model",
51
- message: "Enter model identifier:",
52
- validate: (input) => input ? true : "Model identifier is required",
53
- },
54
- ]);
55
- const selectedModel = modelAnswer.model;
56
- const finalConfig = {
57
- ...updatedConfig,
58
- model: selectedModel,
59
- useLocalLLM: false,
60
- };
61
- // Save the config
62
- saveConfig(finalConfig);
63
- console.log(chalk.green(`✓ Model set to: ${chalk.yellow(selectedModel)}`));
64
- return finalConfig;
65
- }
66
- };