Skip to content

Commit 1faa083

Browse files
authored
feat(commands): add login, config, commit, and docgen command handler… (#36)
* feat(commands): add login, config, commit, and docgen command handlers with argument parsing * feat(commit): enhance commit message generation with LLM and JIRA * fix(main): update command handling to use subcommands instead of commands * feat(config): implement subcommands for LLM and JIRA configuration with argument parsing * feat(config): refine config command parser and improve error handling * fix(config): reorder imports for config command functionality * feat(config): add endpoint to retrieve current LLM configuration and enhance UI for displaying settings * feat(jira): add endpoint to retrieve current JIRA configuration and enhance UI for displaying settings * refactor(login): streamline login handling by importing necessary constants and commands * feat(docgen): enhance documentation generation command with install/uninstall hook options * fix(file_analyzer): improve warning messages for unsupported file extensions
1 parent aac632f commit 1faa083

10 files changed

Lines changed: 444 additions & 206 deletions

File tree

penify_hook/commands/config_commands.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,26 @@ def do_GET(self):
125125
content = f.read()
126126

127127
self.wfile.write(content.encode())
128+
elif self.path == "/get_config":
129+
self.send_response(200)
130+
self.send_header("Content-type", "application/json")
131+
self.end_headers()
132+
133+
# Get current LLM configuration
134+
current_config = get_llm_config()
135+
136+
if current_config:
137+
response = {
138+
"success": True,
139+
"config": current_config
140+
}
141+
else:
142+
response = {
143+
"success": False,
144+
"message": "No configuration found"
145+
}
146+
147+
self.wfile.write(json.dumps(response).encode())
128148
else:
129149
self.send_response(404)
130150
self.send_header("Content-type", "text/plain")
@@ -186,7 +206,6 @@ def config_jira_web():
186206
"""
187207
Open a web browser interface for configuring JIRA settings.
188208
"""
189-
# Similar implementation to config_llm_web but for JIRA settings
190209
redirect_port = random.randint(30000, 50000)
191210
server_url = f"http://localhost:{redirect_port}"
192211

@@ -208,6 +227,26 @@ def do_GET(self):
208227
content = f.read()
209228

210229
self.wfile.write(content.encode())
230+
elif self.path == "/get_config":
231+
self.send_response(200)
232+
self.send_header("Content-type", "application/json")
233+
self.end_headers()
234+
235+
# Get current JIRA configuration
236+
current_config = get_jira_config()
237+
238+
if current_config:
239+
response = {
240+
"success": True,
241+
"config": current_config
242+
}
243+
else:
244+
response = {
245+
"success": False,
246+
"message": "No JIRA configuration found"
247+
}
248+
249+
self.wfile.write(json.dumps(response).encode())
211250
else:
212251
self.send_response(404)
213252
self.send_header("Content-type", "text/plain")

penify_hook/commit_command.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import argparse
2+
3+
4+
5+
def setup_commit_parser(parser):
6+
commit_parser_description = """
7+
It generates smart commit messages. By default, it will just generate just the Title of the commit message.
8+
1. If you have not configured LLM, it will give an error. You either need to configure LLM or use the API key.
9+
2. If you have not configured JIRA. It will not enhance the commit message with JIRA issue details.
10+
3. For more information, visit https://docs.penify.dev/
11+
"""
12+
parser.help = "Generate smart commit messages using local-LLM(no login required)."
13+
parser.description = commit_parser_description
14+
parser.formatter_class = argparse.RawDescriptionHelpFormatter
15+
16+
# Add the message argument with better help
17+
parser.add_argument("-m", "--message", required=False, help="Commit with contextual commit message.", default="N/A")
18+
parser.add_argument("-e", "--terminal", action="store_true", help="Open edit terminal before committing.")
19+
parser.add_argument("-d", "--description", action="store_false", help="It will generate commit message with title and description.", default=False)
20+
21+
def handle_commit(args):
22+
from penify_hook.commands.commit_commands import commit_code
23+
from penify_hook.commands.config_commands import get_jira_config, get_llm_config, get_token
24+
from penify_hook.constants import API_URL
25+
26+
# Only import dependencies needed for commit functionality here
27+
open_terminal = args.terminal
28+
generate_description = args.description
29+
print(f"Generate Description: {generate_description}")
30+
# Try to get from config
31+
llm_config = get_llm_config()
32+
llm_model = llm_config.get('model')
33+
llm_api_base = llm_config.get('api_base')
34+
llm_api_key = llm_config.get('api_key')
35+
token = get_token()
36+
37+
38+
39+
# Try to get from config
40+
jira_config = get_jira_config()
41+
jira_url = jira_config.get('url')
42+
jira_user = jira_config.get('username')
43+
jira_api_token = jira_config.get('api_token')
44+
45+
46+
commit_code(API_URL, token, args.message, open_terminal, generate_description,
47+
llm_model, llm_api_base, llm_api_key,
48+
jira_url, jira_user, jira_api_token)

penify_hook/config_command.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
3+
4+
def setup_config_parser(parent_parser):
5+
# Config subcommand: Create subparsers for config types
6+
parser = parent_parser.add_subparsers(title="config_type", dest="config_type")
7+
8+
# Config subcommand: llm
9+
llm_config_parser = parser.add_parser("llm", help="Configure LLM settings.")
10+
llm_config_parser.add_argument("--model", required=True, help="LLM model to use")
11+
llm_config_parser.add_argument("--api-base", help="API base URL for the LLM service")
12+
llm_config_parser.add_argument("--api-key", help="API key for the LLM service")
13+
14+
# Config subcommand: llm-web
15+
parser.add_parser("llm-web", help="Configure LLM settings through a web interface")
16+
17+
# Config subcommand: jira
18+
jira_config_parser = parser.add_parser("jira", help="Configure JIRA settings.")
19+
jira_config_parser.add_argument("--url", required=True, help="JIRA base URL")
20+
jira_config_parser.add_argument("--username", required=True, help="JIRA username or email")
21+
jira_config_parser.add_argument("--api-token", required=True, help="JIRA API token")
22+
jira_config_parser.add_argument("--verify", action="store_true", help="Verify JIRA connection")
23+
24+
# Config subcommand: jira-web
25+
parser.add_parser("jira-web", help="Configure JIRA settings through a web interface")
26+
27+
# Add all other necessary arguments for config command
28+
29+
def handle_config(args):
30+
# Only import dependencies needed for config functionality here
31+
from penify_hook.commands.config_commands import save_llm_config
32+
from penify_hook.jira_client import JiraClient # Import moved here
33+
from penify_hook.commands.config_commands import config_jira_web, config_llm_web, save_jira_config
34+
35+
if args.config_type == "llm":
36+
save_llm_config(args.model, args.api_base, args.api_key)
37+
print(f"LLM configuration set: Model={args.model}, API Base={args.api_base or 'default'}")
38+
39+
elif args.config_type == "llm-web":
40+
config_llm_web()
41+
42+
elif args.config_type == "jira":
43+
save_jira_config(args.url, args.username, args.api_token)
44+
print(f"JIRA configuration set: URL={args.url}, Username={args.username}")
45+
46+
# Verify connection if requested
47+
if args.verify:
48+
if JiraClient:
49+
jira_client = JiraClient(
50+
jira_url=args.url,
51+
jira_user=args.username,
52+
jira_api_token=args.api_token
53+
)
54+
if jira_client.is_connected():
55+
print("JIRA connection verified successfully!")
56+
else:
57+
print("Failed to connect to JIRA. Please check your credentials.")
58+
else:
59+
print("JIRA package not installed. Cannot verify connection.")
60+
61+
elif args.config_type == "jira-web":
62+
config_jira_web()
63+
64+
else:
65+
print("Please specify a config type: llm, llm-web, jira, or jira-web")
66+
return 1
67+
68+
return 0

penify_hook/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
API_URL = 'https://production-gateway.snorkell.ai/api'
2+
DASHBOARD_URL = "https://dashboard.penify.dev/auth/localhost/login"

penify_hook/docgen_command.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import logging
2+
import os
3+
4+
5+
6+
# Define the docgen description text
7+
docgen_description = """Generate code documentation using Penify.
8+
9+
This command requires you to be logged in to your Penify account.
10+
You can generate documentation for:
11+
- Current Git diff (default)
12+
- Specific file
13+
- Specific folder
14+
"""
15+
16+
def setup_docgen_parser(parser):
17+
# We don't need to create a new docgen_parser since it's passed as a parameter
18+
docgen_subparsers = parser.add_subparsers(title="docgen_subcommand", dest="docgen_subcommand")
19+
20+
# Docgen main options (for direct documentation generation)
21+
parser.add_argument("-l", "--location", help="[Optional] Path to the folder or file to Generate Documentation. By default it will pick the root directory.", default=None)
22+
23+
# Subcommand: install-hook (as part of docgen)
24+
install_hook_parser = docgen_subparsers.add_parser("install-hook", help="Install the Git post-commit hook.")
25+
install_hook_parser.add_argument("-l", "--location", required=False,
26+
help="Location in which to install the Git hook. Defaults to current directory.",
27+
default=os.getcwd())
28+
29+
# Subcommand: uninstall-hook (as part of docgen)
30+
uninstall_hook_parser = docgen_subparsers.add_parser("uninstall-hook", help="Uninstall the Git post-commit hook.")
31+
uninstall_hook_parser.add_argument("-l", "--location", required=False,
32+
help="Location from which to uninstall the Git hook. Defaults to current directory.",
33+
default=os.getcwd())
34+
35+
def handle_docgen(args):
36+
# Only import dependencies needed for docgen functionality here
37+
from penify_hook.commands.config_commands import get_token
38+
import sys
39+
from penify_hook.commands.doc_commands import generate_doc
40+
from penify_hook.commands.hook_commands import install_git_hook, uninstall_git_hook
41+
from penify_hook.constants import API_URL
42+
43+
token = get_token()
44+
if not token:
45+
logging.error("Error: Unable to authenticate. Please run 'penifycli login'.")
46+
sys.exit(1)
47+
48+
if args.docgen_subcommand == "install-hook":
49+
install_git_hook(args.location, token)
50+
51+
elif args.docgen_subcommand == "uninstall-hook":
52+
uninstall_git_hook(args.location)
53+
54+
else: # Direct documentation generation
55+
generate_doc(API_URL, token, args.location)

penify_hook/file_analyzer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ def process_file(self, file_path, pbar):
4646
# --- STAGE 1: Validating ---
4747
update_stage(pbar, "Validating")
4848
if not file_extension:
49-
logger.info(f"File {file_path} has no extension. Skipping.")
49+
print_warning(f" Empty extension is not supported. Skipping '{self.relative_file_path}'.")
5050
return False
5151

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

5454
if file_extension not in self.supported_file_types:
55-
logger.info(f"File type {file_extension} is not supported. Skipping {file_path}.")
55+
print_warning(f" File type '{file_extension}' is not supported. Skipping '{self.relative_file_path}'.")
5656
return False
5757

5858
# Update progress bar to indicate we're moving to next stage

penify_hook/login_command.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def setup_login_parser(parser):
2+
parser.add_argument("--token", help="Specify API token directly")
3+
# Add all other necessary arguments for login command
4+
5+
def handle_login(args):
6+
from penify_hook.constants import API_URL, DASHBOARD_URL
7+
from penify_hook.commands.auth_commands import login
8+
9+
10+
# Only import dependencies needed for login functionality here
11+
return login(API_URL, DASHBOARD_URL)

0 commit comments

Comments
 (0)