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
6 changes: 6 additions & 0 deletions src/conf/Setting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ void Setting::initialize(QMap<Id, QString> &keys) {
keys[Id::ShowChangedFilesInSingleView] = "doubletreeview/single";
keys[Id::ShowChangedFilesMultiColumn] = "doubletreeview/listviewmulticolumn";
keys[Id::HideUntracked] = "untracked.hide";
keys[Id::AiServiceUrl] = "ai/service/url";
keys[Id::EnableAiCommitMessages] = "ai/commit/enable";
keys[Id::AiCommitModel] = "ai/commit/model";
keys[Id::AiCommitTemperature] = "ai/commit/temperature";
keys[Id::AiCommitSystemMessage] = "ai/commit/system_message";
keys[Id::AiCommitPromptMessage] = "ai/commit/prompt_message";
}

void Prompt::initialize(QMap<Kind, QString> &keys) {
Expand Down
48 changes: 48 additions & 0 deletions src/conf/Setting.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@
#include <QObject>
#include <QMap>

// AI Commit Message Generation Constants
namespace AiCommitConstants {

inline constexpr bool enabled = true;
inline constexpr const char *DefaultServiceUrl = "https://api.mistral.ai/v1";
inline constexpr const char *DefaultModel = "mistral-tiny";
inline constexpr double DefaultTemperature = 0.2;

inline constexpr const char *DefaultSystemMessage =
"You are a helpful assistant that generates concise git commit messages "
"following conventional commits format. "
"Start directly with the commit message without any prefixes like 'Commit "
"Message:', 'Git', or markdown code blocks. "
"Follow the format: type(scope): subject\n\nbody";

inline constexpr const char *DefaultPromptMessage =
"Generate a concise, professional git commit message based on the "
"following changes:\n\n"
"Changed files:\n"
"{{CHANGED_FILES}}\n"
"Additions:\n"
"{{ADDITIONS}}\n"
"Deletions:\n"
"{{DELETIONS}}\n"
"File changes:\n"
"{{FILE_CHANGES}}\n"
"Total changes: +{{TOTAL_ADDITIONS}} lines, -{{TOTAL_DELETIONS}} "
"lines\n\n"
"Follow git commit message conventions:\n"
"- First line: short summary (50-72 chars max)\n"
"- Body: detailed explanation (72 chars per line max)\n"
"- Use imperative mood (e.g., 'Fix bug' not 'Fixed bug')\n"
"- Be concise but descriptive\n"
"- Start directly with the commit message (no prefixes like 'Commit "
"Message:' nor markdown in the title)\n"
"- Follow format: type(scope): subject\\n\\nbody\n"
"- Consider the commit message idea if available\n"
"\n"
"Generate the commit message:";

} // namespace AiCommitConstants

template <class T> class SettingsTempl {
public:
template <typename TId> static QString key(const TId id) {
Expand Down Expand Up @@ -68,6 +110,12 @@ class Setting : public SettingsTempl<Setting> {
ShowChangedFilesInSingleView,
HideUntracked,
Language,
AiServiceUrl,
EnableAiCommitMessages,
AiCommitModel,
AiCommitTemperature,
AiCommitSystemMessage,
AiCommitPromptMessage,
};
Q_ENUM(Id)

Expand Down
18 changes: 10 additions & 8 deletions src/cred/Cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,28 @@

Cache::Cache() {}

bool Cache::get(const QString &url, QString &username, QString &password) {
CredentialHelper::Result Cache::get(const QString &url, QString &username,
QString &password) {
if (!mCache.contains(url))
return false;
return CredentialHelper::Result::ERROR(QStringLiteral(""));

const QMap<QString, QString> &map = mCache[url];
if (map.isEmpty())
return false;
return CredentialHelper::Result::ERROR(QStringLiteral(""));

if (username.isEmpty())
username = map.keys().first();

if (!map.contains(username))
return false;
return CredentialHelper::Result::ERROR(QStringLiteral(""));

password = map.value(username);
return true;
return CredentialHelper::Result::OK();
}

bool Cache::store(const QString &url, const QString &username,
const QString &password) {
CredentialHelper::Result Cache::store(const QString &url,
const QString &username,
const QString &password) {
mCache[url][username] = password;
return true;
return CredentialHelper::Result::OK();
}
7 changes: 4 additions & 3 deletions src/cred/Cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ class Cache : public CredentialHelper {
public:
Cache();

bool get(const QString &url, QString &username, QString &password) override;
CredentialHelper::Result get(const QString &url, QString &username,
QString &password) override;

bool store(const QString &url, const QString &username,
const QString &password) override;
CredentialHelper::Result store(const QString &url, const QString &username,
const QString &password) override;

private:
QMap<QString, QMap<QString, QString>> mCache;
Expand Down
27 changes: 23 additions & 4 deletions src/cred/CredentialHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,33 @@
// securely storing passwords associated with host accounts.
class CredentialHelper : public QObject {
public:
struct Result {
bool success;
QString error;

static Result OK() {
return Result{
.success = true,
.error = QString(),
};
}

static Result ERROR(const QString &error) {
return Result{
.success = false,
.error = error,
};
}
};

// Username can be supplied by the caller to lookup a specific
// account or filled in by the helper to get the first account
// for the given host. Password will be filled in on success.
virtual bool get(const QString &url, QString &username,
QString &password) = 0;
virtual Result get(const QString &url, QString &username,
QString &password) = 0;

virtual bool store(const QString &url, const QString &username,
const QString &password) = 0;
virtual Result store(const QString &url, const QString &username,
const QString &password) = 0;

// Get the correct helper for the current platform.
static CredentialHelper *instance();
Expand Down
Loading
Loading