Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f64473f
feat: Consolidate configuration subcommands for LLM and JIRA settings
sumansaurabh Mar 6, 2025
f22736c
feat(hook_commands): enhance post-commit hook to automatically genera…
sumansaurabh Mar 6, 2025
52ad0b6
feat: Rename and restructure documentation generation commands for cl…
sumansaurabh Mar 6, 2025
4017f74
feat: Enhance CLI tool description and help messages for improved use…
sumansaurabh Mar 6, 2025
e0a9cb1
feat: Remove obsolete snorkell-auto-documentation workflow
sumansaurabh Mar 6, 2025
00c191c
feat: Update README and enhance CLI command descriptions for clarity …
sumansaurabh Mar 6, 2025
4e92255
feat: Simplify argument names in documentation generation CLI for cla…
sumansaurabh Mar 6, 2025
f20938c
feat: Simplify argument names and improve documentation generation co…
sumansaurabh Mar 6, 2025
8eb55c2
feat: Enhance 'docgen' command description and improve help messages …
sumansaurabh Mar 6, 2025
f7a5281
feat: Refactor documentation generation logic to support file and fol…
sumansaurabh Mar 6, 2025
dbe50a9
feat: Implement logging in GitDocGenHook for better error tracking an…
sumansaurabh Mar 6, 2025
6693bf9
feat: Improve documentation generation process with enhanced logging …
sumansaurabh Mar 6, 2025
3089602
feat: Enhance documentation with detailed function descriptions and a…
sumansaurabh Mar 6, 2025
df189c9
feat: Add colored terminal output for improved visibility in doc_gen_…
sumansaurabh Mar 6, 2025
8511aaa
feat: Remove unnecessary print statements in documentation generation…
sumansaurabh Mar 6, 2025
411e599
feat: Enhance file processing with logging, progress bar, and colored…
sumansaurabh Mar 6, 2025
78baf75
feat: Refactor doc_gen_hook for improved UI output and progress tracking
sumansaurabh Mar 6, 2025
4d03e68
feat: Integrate tqdm for enhanced progress tracking in file processing
sumansaurabh Mar 6, 2025
30bcc99
feat: Update progress tracking in file analysis stages for improved c…
sumansaurabh Mar 6, 2025
ac019b3
feat: Improve error handling and progress tracking in file processing
sumansaurabh Mar 6, 2025
691ade5
feat: Enhance error reporting in API client and improve error handlin…
sumansaurabh Mar 6, 2025
0ec11d8
feat: Add repository details retrieval and refactor git folder search…
sumansaurabh Mar 6, 2025
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
19 changes: 0 additions & 19 deletions .github/workflows/snorkell-auto-documentation.yml

This file was deleted.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Penify CLI

Penify CLI is a command-line tool for managing Git hooks, generating documentation, and streamlining the development workflow. It provides functionality to install and uninstall Git post-commit hooks, generate documentation for files or folders, perform Git commits with automated message generation, and manage authentication.
Penify CLI is a command-line tool for managing Git commits, generating documentation, and streamlining the development workflow. It provides AI-powered commit message generation, JIRA integration, and documentation tools.

## Installation

Expand Down Expand Up @@ -49,7 +49,7 @@ penifycli uninstall-hook -l /path/to/git/repo
To generate documentation for files or folders:

```bash
penifycli doc-gen [options]
penifycli docgen [options]
```

Options:
Expand Down
14 changes: 8 additions & 6 deletions penify_hook/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ def send_file_for_docstring_generation(self, file_name, content, line_numbers, r
response = response.json()
return response.get('modified_content')
else:
print(f"Response: {response.status_code}")
print(f"Error: {response.text}")
return content
error_message = response.json().get('detail')
if not error_message:
error_message = response.text

raise Exception(f"API Error: {error_message}")

def generate_commit_summary(self, git_diff, instruction: str = "", repo_details = None, jira_context: dict = None):
"""Generate a commit summary by sending a POST request to the API endpoint.
Expand Down Expand Up @@ -82,9 +84,9 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details
response = response.json()
return response
else:
print(f"Response: {response.status_code}")
print(f"Error: {response.text}")
return None
# print(f"Response: {response.status_code}")
# print(f"Error: {response.text}")
raise Exception(f"API Error: {response.text}")
except Exception as e:
print(f"Error: {e}")
return None
Expand Down
6 changes: 2 additions & 4 deletions penify_hook/commands/config_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pkg_resources
from pathlib import Path
from threading import Thread
import logging

def save_llm_config(model, api_base, api_key):
"""
Expand Down Expand Up @@ -267,13 +268,10 @@ def log_message(self, format, *args):

print("Configuration completed.")

def get_token(passed_token):
def get_token():
"""
Get the token based on priority.
"""
if passed_token:
return passed_token

import os
env_token = os.getenv('PENIFY_API_TOKEN')
if env_token:
Expand Down
36 changes: 26 additions & 10 deletions penify_hook/commands/doc_commands.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,49 @@
import os
import sys
from ..folder_analyzer import FolderAnalyzerGenHook
from ..file_analyzer import FileAnalyzerGenHook
from ..git_analyzer import GitDocGenHook
from ..api_client import APIClient

def generate_doc(api_url, token, file_path=None, complete_folder_path=None, git_folder_path=None):
"""
Generates documentation based on the given parameters.
def generate_doc(api_url, token, location=None):
"""Generates documentation based on the given parameters.

This function initializes an API client using the provided API URL and
token. It then generates documentation by analyzing the specified
location, which can be a folder, a file, or the current working
directory if no location is provided. The function handles different
types of analysis based on the input location and reports any errors
encountered during the process.

Args:
api_url (str): The URL of the API to connect to for documentation generation.
token (str): The authentication token for accessing the API.
location (str?): The path to a specific file or folder to analyze.
If not provided, the current working directory is used.
"""
api_client = APIClient(api_url, token)

if file_path:
if location is None:
current_folder_path = os.getcwd()
try:
analyzer = FileAnalyzerGenHook(file_path, api_client)
analyzer = GitDocGenHook(current_folder_path, api_client)
analyzer.run()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
elif complete_folder_path:

# if location is a file
elif len(location.split('.')) > 1:
try:
analyzer = FolderAnalyzerGenHook(complete_folder_path, api_client)
analyzer = FileAnalyzerGenHook(location, api_client)
analyzer.run()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)

else:
try:
analyzer = GitDocGenHook(git_folder_path, api_client)
analyzer = FolderAnalyzerGenHook(location, api_client)
analyzer.run()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
sys.exit(1)
7 changes: 5 additions & 2 deletions penify_hook/commands/hook_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
HOOK_FILENAME = "post-commit"
HOOK_TEMPLATE = """#!/bin/sh
# This is a post-commit hook generated by penifycli.
# Automatically generates documentation for changed files after each commit.

penifycli -t {token} -gf {git_folder_path}
penifycli docgen -gf {git_folder_path} -t {token}
"""

def install_git_hook(location, token):
"""
Install a post-commit hook in the specified location.
Install a post-commit hook in the specified location that generates documentation
for changed files after each commit.
"""
hooks_dir = Path(location) / ".git/hooks"
hook_path = hooks_dir / HOOK_FILENAME
Expand All @@ -24,6 +26,7 @@ def install_git_hook(location, token):
hook_path.chmod(0o755) # Make the hook script executable

print(f"Post-commit hook installed in {hook_path}")
print(f"Documentation will now be automatically generated after each commit.")

def uninstall_git_hook(location):
"""
Expand Down
133 changes: 114 additions & 19 deletions penify_hook/file_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import os
import sys
from git import Repo
from tqdm import tqdm
import time

from penify_hook.utils import get_repo_details, recursive_search_git_folder
from .api_client import APIClient
import logging
from .ui_utils import (
format_highlight, print_info, print_success, print_warning, print_error,
print_status, create_stage_progress_bar,
update_stage, format_file_path
)

# Set up logger
logger = logging.getLogger(__name__)

class FileAnalyzerGenHook:
def __init__(self, file_path: str, api_client: APIClient):
self.file_path = file_path
self.repo_path = recursive_search_git_folder(file_path)
self.repo = Repo(self.repo_path)
self.repo_details = get_repo_details(self.repo)
self.relative_file_path = os.path.relpath(file_path)
self.api_client = api_client
self.supported_file_types = set(self.api_client.get_supported_file_types())

def process_file(self, file_path):
def process_file(self, file_path, pbar):
"""Process a file by reading its content and sending it to an API for
processing.

Expand All @@ -19,49 +37,126 @@ def process_file(self, file_path):

Args:
file_path (str): The relative path to the file that needs to be processed.
pbar (tqdm): Progress bar to update during processing.

Returns:
bool: True if the file was processed successfully, False otherwise.
"""
file_abs_path = os.path.join(os.getcwd(), file_path)
file_extension = os.path.splitext(file_path)[1].lower()


# --- STAGE 1: Validating ---
update_stage(pbar, "Validating")
if not file_extension:
print(f"File {file_path} has no extension. Skipping.")
logger.info(f"File {file_path} has no extension. Skipping.")
return False

file_extension = file_extension[1:] # Remove the leading dot

if file_extension not in self.supported_file_types:
print(f"File type {file_extension} is not supported. Skipping {file_path}.")
logger.info(f"File type {file_extension} is not supported. Skipping {file_path}.")
return False

with open(file_abs_path, 'r') as file:
content = file.read()
# Update progress bar to indicate we're moving to next stage
pbar.update(1)

# --- STAGE 2: Reading content ---
update_stage(pbar, "Reading content")
try:
with open(file_abs_path, 'r') as file:
content = file.read()
except Exception as e:
logger.error(f"Error reading file {file_path}: {str(e)}")
return False

modified_lines = [i for i in range(len(content.splitlines()))]
modified_lines = [i for i in range(len(content.splitlines()))]

# Send data to API
response = self.api_client.send_file_for_docstring_generation(file_path, content, modified_lines)
# Update progress bar to indicate we're moving to next stage
pbar.update(1)

# If the response is successful, replace the file content
with open(file_abs_path, 'w') as file:
file.write(response)
print(f"File [{self.file_path}] processed successfully.")

# --- STAGE 3: Documenting ---
update_stage(pbar, "Documenting")

response = self.api_client.send_file_for_docstring_generation(file_path, content, modified_lines, self.repo_details)

if response is None:
return False

if response == content:
logger.info(f"No changes needed for {file_path}")
return False

# Update progress bar to indicate we're moving to next stage
pbar.update(1)

# --- STAGE 4: Writing changes ---
update_stage(pbar, "Writing changes")

try:
with open(file_abs_path, 'w') as file:
file.write(response)
logger.info(f"Updated file {file_path} with generated documentation")

# Mark final stage as complete
pbar.update(1)
return True
except Exception as e:
logger.error(f"Error writing file {file_path}: {str(e)}")
return False

def print_processing(self, file_path):
"""Print a processing message for a file."""
formatted_path = format_file_path(file_path)
print(f"\n{format_highlight(f'Processing file: {formatted_path}')}")

def run(self):
"""Run the post-commit hook.

This method executes the post-commit hook by processing a specified
file. It attempts to process the file located at `self.file_path`. If an
error occurs during the processing, it catches the exception and prints
an error message indicating that the file was not processed.
an error message indicating that the file was not processed. The method
displays a progress bar and colored output to provide visual feedback on
the processing status.
"""

# Create a progress bar with appropriate stages
stages = ["Validating", "Reading content", "Documenting", "Writing changes", "Completed"]
pbar, _ = create_stage_progress_bar(stages, f"Starting documenting")

try:
self.process_file(self.file_path)
# Stage the modified file
# Print a clear indication of which file is being processed
# self.print_processing(self.file_path)

# Process the file
result = self.process_file(self.file_path, pbar)

# Ensure all stages are completed
remaining_steps = len(stages) - pbar.n
pbar.update(remaining_steps)


# Display appropriate message based on result
remaining = len(stages) - pbar.n
if remaining > 0:
pbar.update(remaining)
update_stage(pbar, "Complete")
pbar.clear()
pbar.close()

except Exception as e:
print(f"File [{self.file_path}] was not processed.")

remaining = len(stages) - pbar.n
if remaining > 0:
pbar.update(remaining)
update_stage(pbar, "Complete")
pbar.clear()
pbar.close()
print_status('error', e)
sys.exit(1)

# Ensure progress bar completes even on error
if result:
print_success(f"\n✓ Documentation updated for {self.relative_file_path}")
else:
print_success(f"\n✓ No changes needed for {self.relative_file_path}")

Loading