Skip to content
Merged
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
19 changes: 11 additions & 8 deletions penify_hook/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,18 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details
return None

def get_supported_file_types(self) -> list[str]:
"""Retrieve the supported file types from the API.

"""Retrieve supported file types from the API or return a default list."""
url = self.api_url+"/v1/cli/supported_languages"
response = requests.get(url)
if response.status_code == 200:
response = response.json()
return response
else:
return ["py", "js", "ts", "java", "kt", "cs", "c"]
This function sends a request to the API endpoint
`/v1/file/supported_languages` to obtain a list of supported file types.
If the API call is successful (status code 200), it parses the JSON
response and returns the list of supported file types. If the API call
fails, it returns a default list of common file types.

Returns:
list[str]: A list of supported file types, either from the API or a default set.
"""
return ["py", "js", "ts", "java", "kt", "cs", "c", 'cpp', 'go', 'php', 'tsx','jsx']

def generate_commit_summary_with_llm(self, diff, message, generate_description: bool, repo_details, llm_client : LLMClient, jira_context=None):
"""Generates a commit summary using a local LLM client; falls back to API on
Expand Down
38 changes: 25 additions & 13 deletions penify_hook/commands/auth_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def save_credentials(api_key):
f.write(f"{key}={value}\n")

print(f"API token saved to {env_file}")
return True
# return True
except Exception as e:
print(f"Error saving to .env file: {str(e)}")
# Fall back to saving in .penify global config
Expand Down Expand Up @@ -92,10 +92,13 @@ def login(api_url, dashboard_url):
redirect_port = random.randint(30000, 50000)
redirect_url = f"http://localhost:{redirect_port}/callback"

full_login_url = f"{dashboard_url}?redirectUri={urllib.parse.quote(redirect_url)}"
full_login_url = f"{dashboard_url}?redirectUri={urllib.parse.quote(redirect_url)}&autoClose=true"

print(f"Opening login page in your default web browser: {full_login_url}")
webbrowser.open(full_login_url)
# Open browser with autoraise=True to ensure window is brought to front
# Note: window.close() in JavaScript can only close windows that were opened by JavaScript
# We'll pass an autoClose parameter to the dashboard URL so the server can handle closing
webbrowser.open(full_login_url, autoraise=True)

class TokenHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
Expand All @@ -113,15 +116,17 @@ def do_GET(self):
response = """
<html>
<head>
<script>
setTimeout(function() {
window.location.href = 'https://dashboard.penify.dev';
}, 5000);
</script>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding-top: 50px; }
.message { margin: 20px 0; }
.note { font-size: 0.9em; color: #666; margin-top: 30px; }
</style>
</head>
<body>
<h1>Login Successful!</h1>
<p>You will be redirected to the Penify dashboard in 5 seconds. You can also close this window and return to the CLI.</p>
<p class="message">API keys have been fetched and saved.</p>
<p class="message">You can close this window and return to the CLI.</p>
<p class="note">(The browser window cannot be closed automatically for security reasons)</p>
</body>
</html>
"""
Expand All @@ -133,7 +138,7 @@ def do_GET(self):
if api_key:
save_credentials(api_key)
print("API keys fetched and saved successfully.")
print("You'll be redirected to the Penify dashboard. You can continue using the CLI.")
print("Please close the browser window and continue using the CLI.")
else:
print("Failed to fetch API keys.")
else:
Expand All @@ -142,9 +147,17 @@ def do_GET(self):
self.end_headers()
response = """
<html>
<head>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding-top: 50px; }
.error { color: #d9534f; }
.message { margin: 20px 0; }
</style>
</head>
<body>
<h1>Login Failed</h1>
<p>Please try again.</p>
<h1 class="error">Login Failed</h1>
<p class="message">Please try again.</p>
<p class="message">You can close this window and return to the CLI.</p>
</body>
</html>
"""
Expand All @@ -155,7 +168,6 @@ def do_GET(self):
thread = Thread(target=self.server.shutdown)
thread.daemon = True
thread.start()

def log_message(self, format, *args):
# Suppress log messages
"""Suppress log messages."""
Expand Down
16 changes: 0 additions & 16 deletions penify_hook/commands/doc_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,6 @@ def generate_doc(api_url, token, location=None):
"""
t1 = time.time()
from ..api_client import APIClient
print(f"Time taken to laod APIClinet: {time.time() - t1:.2f} seconds")
"""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 location is None:
current_folder_path = os.getcwd()
Expand Down
53 changes: 5 additions & 48 deletions penify_hook/commit_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,7 @@ def __init__(self, repo_path: str, api_client: APIClient, llm_client=None, jira_
self.jira_client: JiraClient = jira_client # Add JIRA client as an optional parameter

def get_summary(self, instruction: str, generate_description: bool) -> dict:
"""Generate a summary for the commit based on the staged changes.

This function retrieves the differences of the staged changes in the repository
and generates a commit summary using the provided instruction. If there are no
changes staged for commit, an exception is raised. If a JIRA client is
connected, it will attempt to extract issue keys from the current branch and
use them to fetch context. The summary can be generated either with a Language
Model (LLM) client or through the API client.

Args:
instruction (str): A string containing instructions for generating the commit summary.
generate_description (bool): Whether to include detailed descriptions in the summary.

Raises:
ValueError: If there are no changes staged for commit.
"""
"""Generate a summary for the commit based on the staged changes."""
diff = self.repo.git.diff('--cached')
if not diff:
raise ValueError("No changes to commit")
Expand Down Expand Up @@ -64,20 +49,8 @@ def get_summary(self, instruction: str, generate_description: bool) -> dict:


def run(self, msg: Optional[str], edit_commit_message: bool, generate_description: bool):
"""Run the post-commit hook.

This method processes the modified files from the last commit, stages them, and
creates an auto-commit with an optional message. It also handles JIRA
integration if available. If there is an error generating the commit summary,
an exception is raised.

Args:
msg (Optional[str]): An optional message to include in the commit.
edit_commit_message (bool): A flag indicating whether to open the git commit
edit terminal after committing.
generate_description (bool): A flag indicating whether to include a description
in the commit message.
"""
"""Run the post-commit hook and create an auto-commit with optional JIRA
integration."""
summary: dict = self.get_summary(msg, True)
if not summary:
raise Exception("Error generating commit summary")
Expand All @@ -102,23 +75,7 @@ def run(self, msg: Optional[str], edit_commit_message: bool, generate_descriptio

def process_jira_integration(self, title: str, description: str, msg: str) -> tuple:
# Look for JIRA issue keys in commit message, title, description and user message
"""Process JIRA integration by extracting issue keys from commit message
components and branch name.

This function looks for JIRA issue keys in the provided commit title,
description, original user message, and the active branch name. It uses these
keys to update the commit message with JIRA information and adds comments to
the corresponding JIRA issues. If no keys are found, it logs a warning.

Args:
title (str): The generated commit title.
description (str): The generated commit description.
msg (str): The original user message that might contain JIRA references.

Returns:
tuple: A tuple containing the updated commit title and description with included JIRA
information.
"""
"""Process JIRA integration to update commit message with issue keys."""
issue_keys = []
if self.jira_client:
# Extract from message content
Expand Down Expand Up @@ -156,7 +113,7 @@ def process_jira_integration(self, title: str, description: str, msg: str) -> tu
return title, description

def _amend_commit(self):
"""Amends the last commit message in the repository."""
"""Open the default git editor to amend the last commit message."""
try:
# Change to the repository directory
os.chdir(self.repo_path)
Expand Down
14 changes: 2 additions & 12 deletions penify_hook/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def setup_config_parser(parent_parser):

# Config subcommand: Create subparsers for config types
"""Set up configuration parsers with subcommands for LLM and JIRA settings."""
"""Set up configuration parsers for different types of configurations."""
parser = parent_parser.add_subparsers(title="config_type", dest="config_type")

# Config subcommand: llm
Expand Down Expand Up @@ -34,17 +34,7 @@ def handle_config(args):



"""Handle configuration settings based on the specified config type.

This function processes different types of configurations such as LLM (Language
Model) and JIRA. It saves configurations, sets up web-based configurations, and
verifies JIRA connections. Depending on the `args.config_type`, it imports
necessary modules, handles configuration saving or setup, and optionally
verifies JIRA connectivity.

Args:
args (argparse.Namespace): Command-line arguments containing the type of configuration to handle.
"""
"""Handle configuration settings based on the specified config type."""
if args.config_type == "llm-cmd":
from penify_hook.commands.config_commands import save_llm_config
save_llm_config(args.model, args.api_base, args.api_key)
Expand Down
2 changes: 1 addition & 1 deletion penify_hook/constants.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
API_URL = 'http://localhost:8000/api'
API_URL = 'https://production.gateway.snorkell.ai/api'
DASHBOARD_URL = "https://dashboard.penify.dev/auth/localhost/login"
1 change: 1 addition & 0 deletions penify_hook/file_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def run(self):
"""
stages = ["Validating", "Reading content", "Documenting", "Writing changes", "Completed"]
pbar, _ = create_stage_progress_bar(stages, f"Starting documenting")
logger.debug(f"Processing file: {self.file_path}")

try:
# Print a clear indication of which file is being processed
Expand Down
Loading