Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 108 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
"node": ">=20"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.115.0",
"chalk": "^5.6.2",
"commander": "^14.0.3",
"cosmiconfig": "^9.0.0",
"inquirer": "^13.2.4",
"node-fetch": "^3.3.2",
"openai": "^6.49.0",
"ora": "^9.3.0",
"simple-git": "^3.31.1",
"ts-morph": "^28.0.0"
Expand Down
4 changes: 4 additions & 0 deletions src/config/loadConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export interface GitbunConfig {
customPrompt?: string;
format?: string;
model?: string;
ai?: boolean;
interactive?: boolean;
provider?: 'ollama' | 'openai' | 'anthropic'; // add this
apiKey?: string;
Comment on lines +8 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Wire provider selection into the runtime path.

The new fields are never consumed: the supplied src/index.ts flow still calls isOllamaRunning() and enhanceCommit() directly. Selecting openai or anthropic therefore has no effect. Add provider resolution and retain Ollama only as the fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/loadConfig.ts` around lines 8 - 11, Update the src/index.ts
runtime flow around isOllamaRunning() and enhanceCommit() to resolve the
configured provider and route execution through the selected OpenAI or Anthropic
implementation; retain Ollama only when provider is unset or unsupported, and
ensure the apiKey is passed to the chosen provider as required.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)loadConfig\.ts$|package\.json|README|\.gitbunrc|smartcommit' || true

echo "== config loader candidates =="
fd -a -i 'loadConfig|config' . | sed 's#^\./##' | head -50

echo "== loadConfig.ts =="
for f in $(fd -i 'loadConfig\.ts$' .); do
  echo "--- $f"
  wc -l "$f"
  cat -n "$f"
done

echo "== searchPlaces / cosmiconfig usages =="
rg -n "cosmiconfig|searchPlaces|smartcommit|gitbunrc|\\.git" -S . --glob '!node_modules' --glob '!dist' --glob '!build' || true

echo "== package manifests =="
for f in $(fd -i 'package\.json$' .); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

Repository: nirvik34/gitbun

Length of output: 8530


🌐 Web query:

cosmiconfig 9.0 searchPlaces default .gitignore dotfiles

💡 Result:

In cosmiconfig 9.0, the searchPlaces option defines the list of filenames and paths the library checks when searching for a configuration [1][2]. Cosmiconfig does not have any built-in logic to ignore or respect.gitignore files; it simply attempts to locate and load the configuration files specified in the searchPlaces array [1][3]. The default searchPlaces configuration includes various rc-style files and a.config/ subdirectory [1][3]. The default list is as follows: [ 'package.json', .${moduleName}rc, .${moduleName}rc.json, .${moduleName}rc.yaml, .${moduleName}rc.yml, .${moduleName}rc.js, .${moduleName}rc.ts, .${moduleName}rc.mjs, .${moduleName}rc.cjs, .config/${moduleName}rc, .config/${moduleName}rc.json, .config/${moduleName}rc.yaml, .config/${moduleName}rc.yml, .config/${moduleName}rc.js, .config/${moduleName}rc.ts, .config/${moduleName}rc.mjs, .config/${moduleName}rc.cjs, ${moduleName}.config.js, ${moduleName}.config.ts, ${moduleName}.config.mjs, ${moduleName}.config.cjs ] In this list, {moduleName} is the name provided to the cosmiconfig explorer instance [1][4]. The library searches for these files in the current working directory by default (in version 9.0, the default searchStrategy is 'none', meaning it does not traverse upward by default) [5][3][6]. If you need to exclude specific files, you must manage that at the project or file system level, as cosmiconfig will attempt to load any file matching these patterns if it exists in the designated search paths [1][3].

Citations:


🌐 Web query:

cosmiconfig 9 searchPlaces default file extensions

💡 Result:

In Cosmiconfig 9, the default searchPlaces consist of a predefined list of files that the library searches for when looking for configuration [1][2]. The list includes files in the current directory and a .config/ subdirectory, using the moduleName provided to the library [1][2]. The default search places are [1][2]: * package.json (parsed for a specific property) *.${moduleName}rc *.${moduleName}rc.json *.${moduleName}rc.yaml *.${moduleName}rc.yml *.${moduleName}rc.js *.${moduleName}rc.ts *.${moduleName}rc.mjs *.${moduleName}rc.cjs *.config/${moduleName}rc *.config/${moduleName}rc.json *.config/${moduleName}rc.yaml *.config/${moduleName}rc.yml *.config/${moduleName}rc.js *.config/${moduleName}rc.ts *.config/${moduleName}rc.mjs *.config/${moduleName}rc.cjs * ${moduleName}.config.js * ${moduleName}.config.ts * ${moduleName}.config.mjs * ${moduleName}.config.cjs Each item in this list must have a corresponding loader configured in the loaders option that matches its file extension [1][2]. Common extensions are covered by default loaders, while files without extensions (e.g.,.${moduleName}rc) are handled by the noExt loader key [1][3]. Additionally, when package.json is included, Cosmiconfig parses it as JSON and loads a specific property (defaulting to the moduleName) rather than the entire file [1][2].

Citations:


Add .gitbunrc to the cosmiconfig search paths.

cosmiconfig("smartcommit") uses an explorer named smartcommit, so with the default searchPlaces it will only look for .smartcommitrc* / smartcommit.config.*; .gitbunrc will not be loaded and these new config fields are ignored. Also add a config-loading test for the documented .gitbunrc file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/loadConfig.ts` around lines 8 - 11, Update the cosmiconfig
explorer used by the configuration loader to include `.gitbunrc` in its
searchPlaces, while preserving the existing supported formats. Add a
config-loading test covering the documented `.gitbunrc` filename and verifying
its fields, including provider and apiKey, are loaded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Keep API keys out of GitbunConfig.

loadConfig() returns configuration-file contents unchanged, so apiKey?: string permits secrets to be stored in .gitbunrc, contradicting the environment-only requirement. Resolve OPENAI_API_KEY/ANTHROPIC_API_KEY in the provider factory instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/loadConfig.ts` at line 11, Remove apiKey from the GitbunConfig
type and keep loadConfig() limited to returning configuration-file contents.
Update the provider factory to resolve OPENAI_API_KEY and ANTHROPIC_API_KEY
directly from the environment instead of reading an API key from GitbunConfig.

}

/** Loads and returns user config from .gitbunrc or cosmiconfig. */
Expand Down
20 changes: 20 additions & 0 deletions src/providers/anthropic.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Anthropic from '@anthropic-ai/sdk';

export class AnthropicProvider {
private client: Anthropic;
private model: string;

constructor(apiKey: string, model = 'claude-haiku-4-5') {
this.client = new Anthropic({ apiKey });
this.model = model;
}

async generateCommitMessage(diff: string, prompt: string): Promise<string> {
const response = await this.client.messages.create({
model: this.model,
max_tokens: 200,
messages: [{ role: 'user', content: `${prompt}\n\nGit diff:\n\`\`\`\n${diff}\n\`\`\`` }],
Comment on lines +13 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
npm view `@anthropic-ai/sdk`@0.115.0 version

Repository: nirvik34/gitbun

Length of output: 310


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== file excerpt =="
sed -n '1,120p' src/providers/anthropic.provider.ts

echo
echo "== package versions / constraints =="
for f in package.json package-lock.json pnpm-lock.yaml yarn.lock; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    rg -n '"`@anthropic-ai/sdk`"|anthropic' "$f" || true
  fi
done

echo
echo "== occurrences of this provider / prompt diff usage =="
rg -n "AnthropicProvider|anthropic\\.provider|max_tokens: 200|this\\.client\\.messages\\.create|messages: \\[\\{ role: 'user', content" src package.json package-lock.json pnpm-lock.yaml yarn.lock || true

Repository: nirvik34/gitbun

Length of output: 1724


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== installed anthropic SDK type if present =="
node -e "const fs=require('fs'); const p='node_modules/@anthropic-ai/sdk/resources/messages.d.ts'; console.log(fs.existsSync(p))?fs.readFileSync(p,'utf8').slice(0,1200):p+' not present'" 2>/dev/null || true

echo
echo "== local anthropic types if present =="
if [ -d node_modules/@anthropic-ai/sdk ]; then
  find node_modules/@anthropic-ai/sdk -maxdepth 3 -type f | sed -n '1,80p'
fi

Repository: nirvik34/gitbun

Length of output: 5036


🌐 Web query:

Anthropic Messages API system parameter documentation untrusted content safety

💡 Result:

To safely handle untrusted content when using the Anthropic Messages API, you must leverage structural separation at the API level [1][2]. The Messages API is designed to distinguish between system instructions and user-provided data, which is critical for preventing prompt injection attacks [2]. Key security practices for the Messages API: 1. Use the Dedicated System Parameter: The API provides a top-level system parameter specifically for instructions that define the model's behavior, goal, or role [3][4][5]. You must use this parameter for your system instructions rather than attempting to include them within the messages array or concatenating them with user input [3][2][6]. 2. Maintain Architectural Boundary for Untrusted Input: All untrusted content—such as data from web pages, user uploads, third-party APIs, or tool outputs—must be placed within the messages array, specifically using the user role [1][7]. Do not interpolate untrusted content into the system parameter or mix it with your application's instructions [2]. For Claude 3.7 and later, the API enforces a token-level boundary that prevents user messages from syntactically overriding system instructions, provided the inputs are correctly separated at the call level [2]. 3. Handle Tool Outputs as Untrusted: When using tools, treat all tool outputs as untrusted content [7]. Keep these outputs inside tool_result blocks rather than placing them in the system prompt or plain text blocks [7]. Anthropic recommends screening tool outputs for injection attempts before passing them to the model, which can be done by using a smaller, lightweight model (e.g., Claude Haiku) to evaluate the tool output for adversarial commands before it is processed by the main agent [8]. 4. Leverage Enterprise Trust & Safety: For enterprise-tier users, additional "Trust & Safety Configuration" is available [9]. This feature allows you to inject organizational policies and audit requirements via request headers, which Anthropic's safety layer uses to evaluate both the prompt and the response [9]. If a safety violation is detected, the API will return a stop_reason of safety_violation, which must be handled programmatically in your application logic, as the API may still return a 200 OK status [9]. By strictly enforcing the separation between system-defined instructions and untrusted user/tool data, you shift the security burden from relying on model robustness alone to utilizing the API's structural protections [2].

Citations:


Pass system instructions via Anthropic’s top-level system parameter.

prompt drives the provider behavior, while diff is untrusted tool output. Keep them split: system: prompt plus a user message with the git diff only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/anthropic.provider.ts` around lines 13 - 16, Update the
Anthropic messages.create call to pass prompt through the top-level system
parameter and make the user message contain only the untrusted diff content.
Remove the prompt interpolation from the user message while preserving the
existing model and token settings.

});
return (response.content[0] as { text: string }).text.trim();
}
}
24 changes: 24 additions & 0 deletions src/providers/openai.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import OpenAI from 'openai';

export class OpenAIProvider {
private client: OpenAI;
private model: string;

constructor(apiKey: string, model = 'gpt-4o-mini') {
this.client = new OpenAI({ apiKey });
this.model = model;
}

async generateCommitMessage(diff: string, prompt: string): Promise<string> {
const response = await this.client.chat.completions.create({
model: this.model,
messages: [
{ role: 'system', content: prompt },
{ role: 'user', content: `Git diff:\n\`\`\`\n${diff}\n\`\`\`` },
Comment on lines +12 to +17

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Apply the 8,000-character diff cap consistently across cloud providers.

Both implementations interpolate the complete diff into their requests, violating the shared PR requirement.

  • src/providers/openai.provider.ts#L12-L17: truncate diff before constructing the OpenAI user message.
  • src/providers/anthropic.provider.ts#L12-L16: truncate diff before constructing the Anthropic user message.
📍 Affects 2 files
  • src/providers/openai.provider.ts#L12-L17 (this comment)
  • src/providers/anthropic.provider.ts#L12-L16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/openai.provider.ts` around lines 12 - 17, Apply the shared
8,000-character diff cap before constructing the user message in
generateCommitMessage for src/providers/openai.provider.ts (lines 12-17) and the
corresponding Anthropic request in src/providers/anthropic.provider.ts (lines
12-16). Ensure both providers interpolate only the truncated diff while
preserving the existing prompt and request structure.

],
max_tokens: 200,
temperature: 0.3,
});
return response.choices[0].message.content?.trim() ?? '';
}
}