This commit is contained in:
retoor 2025-12-13 14:00:02 +01:00
parent c61f895135
commit 25be023b70
2 changed files with 170 additions and 94 deletions

View File

@ -9,6 +9,22 @@
## Version 0.10.0 - 2025-12-13
Updates to `rgithook.py` improve the reliability of Git hook execution. This ensures that pre-commit checks are consistently applied during development workflows.
**Changes:** 1 files, 258 lines
**Languages:** Python (258 lines)
## Version 0.9.0 - 2025-11-16
You can now export data as JSON or RSS. A command-line tool is available to extract mentions, and documentation has been added to help you use it.
**Changes:** 2 files, 209 lines
**Languages:** Markdown (8 lines), Python (201 lines)
## Version 0.8.0 - 2025-11-05 ## Version 0.8.0 - 2025-11-05
Users can now connect external tools to automate more complex tasks. Developers can integrate new tools using the updated elon.py file. Users can now connect external tools to automate more complex tasks. Developers can integrate new tools using the updated elon.py file.

View File

@ -4,7 +4,9 @@ API_URL = "https://static.molodetz.nl/rp.cgi/api/v1/chat/completions"
MODEL = "google/gemma-3-12b-it:free" MODEL = "google/gemma-3-12b-it:free"
TEMPERATURE = 1.0 TEMPERATURE = 1.0
MAX_TOKENS = None MAX_TOKENS = None
HOOK_PATH = ".git/hooks/prepare-commit-msg" HOOK_PATH_PRECOMMIT = ".git/hooks/pre-commit"
HOOK_PATH_PREPAREMSG = ".git/hooks/prepare-commit-msg"
TEMP_MSG_FILE = ".git/COMMIT_MSG_GENERATED"
import sys import sys
import subprocess import subprocess
@ -34,21 +36,27 @@ def install_hook():
return False return False
script_path = os.path.abspath(__file__) script_path = os.path.abspath(__file__)
hook_dir = os.path.dirname(HOOK_PATH) hook_dir = os.path.dirname(HOOK_PATH_PRECOMMIT)
if not os.path.exists(hook_dir): if not os.path.exists(hook_dir):
os.makedirs(hook_dir) os.makedirs(hook_dir)
if os.path.exists(HOOK_PATH): installed = False
with open(HOOK_PATH, 'r') as f:
for hook_path, mode in [(HOOK_PATH_PRECOMMIT, 'pre-commit'), (HOOK_PATH_PREPAREMSG, 'prepare-commit-msg')]:
needs_install = True
if os.path.exists(hook_path):
with open(hook_path, 'r') as f:
if script_path in f.read(): if script_path in f.read():
return False needs_install = False
with open(HOOK_PATH, 'w') as f: if needs_install:
f.write(f'#!/bin/bash\n{script_path} "$@"\n') with open(hook_path, 'w') as f:
f.write(f'#!/bin/bash\n{script_path} {mode} "$@"\n')
os.chmod(hook_path, 0o755)
installed = True
os.chmod(HOOK_PATH, 0o755) return installed
return True
except Exception: except Exception:
return False return False
@ -218,51 +226,51 @@ def generate_commit_message(diff, files):
try: try:
files_list = "\n".join([f"- {f}" for f in files[:20]]) files_list = "\n".join([f"- {f}" for f in files[:20]])
prompt = f"""You write commit messages for code changes. prompt = f"""You are a senior software engineer writing precise git commit messages following the Conventional Commits specification.
Changed files: CONTEXT:
Files modified: {len(files)}
{files_list} {files_list}
Code changes: DIFF:
{diff[:12000]} {diff[:12000]}
Write a commit message with this format: TASK:
<prefix>: <description> Analyze the changes and write a commit message that accurately describes the modifications.
Format rules: OUTPUT FORMAT:
- All lowercase for the prefix <type>: <concise description>
- Colon and space after prefix
- Start description with lowercase letter
- No period at the end
- Max 72 characters total
- Use imperative mood (Add not Added)
Choose one prefix: RULES:
- fix: for bug fixes 1. Use exactly one line per logical change
Example: fix: resolve null pointer error in user login 2. Type must be lowercase
Example: fix: correct date format in export function 3. Description starts with lowercase verb in imperative mood
4. No trailing punctuation
5. Maximum 72 characters per line
6. Group related changes under a single type when appropriate
7. Order by importance (most significant change first)
- feat: for new features TYPES (select the most appropriate):
Example: feat: add dark mode toggle to settings - feat: new functionality or capability added to the codebase
Example: feat: implement search filter for products - fix: correction of a bug or erroneous behavior
- refactor: code restructuring without changing external behavior
- perf: performance optimization or improvement
- docs: documentation additions or modifications
- test: test additions or modifications
- build: build system or dependency changes
- ci: continuous integration configuration changes
- style: formatting changes (whitespace, semicolons, etc.)
- chore: routine maintenance tasks
- docs: for documentation changes ANALYSIS GUIDELINES:
Example: docs: update api endpoint descriptions - Identify the primary purpose of the change
Example: docs: add setup guide for development - Distinguish between new features and modifications to existing ones
- Recognize bug fixes by error handling, null checks, or condition corrections
- Note refactoring by structural changes without behavior modification
- Detect performance work by optimization patterns or caching additions
- perf: for performance improvements OUTPUT:
Example: perf: reduce database query time by 40% Provide only the commit message lines, no explanations or additional text."""
Example: perf: optimize image loading with lazy load
- refactor: for code restructuring
Example: refactor: simplify user validation logic
Example: refactor: extract common functions to utils
- maintenance: for routine updates and maintenance
Example: maintenance: update dependencies to latest versions
Example: maintenance: clean up unused imports and files
Reply with ONLY the commit message, nothing else."""
message = call_ai(prompt) message = call_ai(prompt)
if not message: if not message:
@ -270,18 +278,28 @@ Reply with ONLY the commit message, nothing else."""
message = message.strip().strip('"').strip("'") message = message.strip().strip('"').strip("'")
prefixes = ['fix:', 'feat:', 'docs:', 'perf:', 'refactor:', 'maintenance:'] lines = message.split('\n')
if not any(message.startswith(p) for p in prefixes): processed_lines = []
message = f"feat: {message}" prefixes = ['feat:', 'fix:', 'refactor:', 'perf:', 'docs:', 'test:', 'build:', 'ci:', 'style:', 'chore:']
for line in lines:
line = line.strip()
if not line:
continue
if not any(line.startswith(p) for p in prefixes):
line = f"chore: {line}"
processed_lines.append(line)
return message if not processed_lines:
return generate_fallback_message(files)
return '\n'.join(processed_lines)
except Exception: except Exception:
return generate_fallback_message(files) return generate_fallback_message(files)
def generate_fallback_message(files): def generate_fallback_message(files):
try: try:
if not files: if not files:
return "feat: update project" return "chore: update project"
exts = set() exts = set()
for f in files: for f in files:
@ -290,10 +308,10 @@ def generate_fallback_message(files):
exts.add(ext[1:]) exts.add(ext[1:])
if exts: if exts:
return f"feat: update {', '.join(sorted(exts)[:3])} files" return f"chore: update {', '.join(sorted(exts)[:3])} files"
return "feat: update project files" return "chore: update project files"
except Exception: except Exception:
return "feat: update project" return "chore: update project"
def create_git_tag(version): def create_git_tag(version):
try: try:
@ -334,20 +352,32 @@ def update_changelog(version, commit_message, stats, files):
lang_summary = ", ".join(lang_stats) if lang_stats else "No code changes" lang_summary = ", ".join(lang_stats) if lang_stats else "No code changes"
functional_desc_prompt = f"""You write changelog entries for software releases. functional_desc_prompt = f"""You are a technical writer creating changelog entries for a software release.
Commit message: {commit_message} COMMIT SUMMARY:
Changed files: {", ".join(files[:10])} {commit_message}
Write a short functional description of what changed for users or developers. AFFECTED FILES:
Use simple clear words, be direct, max 2 sentences. {", ".join(files[:10])}
Focus on what it does, not how.
Reply with ONLY the description text.""" TASK:
Write a concise changelog entry describing the functional impact of these changes.
REQUIREMENTS:
1. Maximum two sentences
2. Focus on user-visible or developer-relevant changes
3. Use present tense, active voice
4. Be specific about what functionality is added, modified, or fixed
5. Avoid implementation details unless architecturally significant
6. No marketing language or superlatives
OUTPUT:
Provide only the changelog description text, no formatting or prefixes."""
functional_desc = call_ai(functional_desc_prompt) functional_desc = call_ai(functional_desc_prompt)
if not functional_desc: if not functional_desc:
functional_desc = commit_message.split(':', 1)[1].strip() if ':' in commit_message else commit_message first_line = commit_message.split('\n')[0]
functional_desc = first_line.split(':', 1)[1].strip() if ':' in first_line else first_line
entry_lines = [ entry_lines = [
f"## Version {version} - {today}", f"## Version {version} - {today}",
@ -382,6 +412,48 @@ Reply with ONLY the description text."""
except Exception: except Exception:
pass pass
def run_pre_commit():
diff = get_git_diff()
files = get_changed_files()
if not diff and not files:
sys.exit(0)
stats = analyze_diff_stats(diff)
commit_message = generate_commit_message(diff, files)
current_version, source = get_version()
new_version = update_version(current_version, source)
update_changelog(new_version, commit_message, stats, files)
safe_run(['git', 'add', 'CHANGELOG.md'])
with open(TEMP_MSG_FILE, 'w') as f:
f.write(f"{new_version}\n{commit_message}")
sys.exit(0)
def run_prepare_commit_msg(commit_msg_file):
if not os.path.exists(TEMP_MSG_FILE):
sys.exit(0)
with open(TEMP_MSG_FILE, 'r') as f:
content = f.read()
lines = content.split('\n', 1)
new_version = lines[0] if lines else "0.1.0"
commit_message = lines[1] if len(lines) > 1 else "chore: update project"
with open(commit_msg_file, 'w') as f:
f.write(commit_message + '\n')
create_git_tag(new_version)
os.remove(TEMP_MSG_FILE)
sys.exit(0)
def main(): def main():
try: try:
if len(sys.argv) < 2: if len(sys.argv) < 2:
@ -391,35 +463,23 @@ def main():
print("Git hook already installed") print("Git hook already installed")
sys.exit(0) sys.exit(0)
commit_msg_file = sys.argv[1] mode = sys.argv[1]
current_version, source = get_version()
new_version = update_version(current_version, source)
diff = get_git_diff()
files = get_changed_files()
stats = analyze_diff_stats(diff)
commit_message = generate_commit_message(diff, files)
update_changelog(new_version, commit_message, stats, files)
safe_run(['git', 'add', 'CHANGELOG.md'])
create_git_tag(new_version)
with open(commit_msg_file, 'w') as f:
f.write(commit_message + '\n')
if mode == 'pre-commit':
run_pre_commit()
elif mode == 'prepare-commit-msg':
if len(sys.argv) >= 3:
run_prepare_commit_msg(sys.argv[2])
sys.exit(0)
else:
sys.exit(0) sys.exit(0)
except Exception: except Exception:
try: try:
if len(sys.argv) >= 2: if os.path.exists(TEMP_MSG_FILE):
with open(sys.argv[1], 'w') as f: os.remove(TEMP_MSG_FILE)
f.write("feat: update project\n")
except Exception: except Exception:
pass pass
sys.exit(0) sys.exit(0)
if __name__ == '__main__': if __name__ == '__main__':
main() main()