cmt-cli 0.2.0__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cmt_cli-1.0.1/.github/workflows/test.yml +30 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/.gitignore +3 -1
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/PKG-INFO +2 -1
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/README.md +1 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/pyproject.toml +8 -13
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/ai/cache.py +2 -2
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/ai/openai.py +26 -7
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/ai/prompt.py +7 -1
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/cli.py +57 -12
- cmt_cli-1.0.1/src/cmt/core/__init__.py +4 -0
- cmt_cli-1.0.1/src/cmt/core/logging.py +18 -0
- {cmt_cli-0.2.0/src/cmt/config → cmt_cli-1.0.1/src/cmt/core}/settings.py +9 -10
- cmt_cli-1.0.1/src/cmt/enums/config.py +6 -0
- cmt_cli-1.0.1/src/cmt/schemas/config.py +6 -0
- cmt_cli-1.0.1/src/cmt/schemas/general.py +7 -0
- cmt_cli-1.0.1/src/cmt/services/__init__.py +3 -0
- cmt_cli-1.0.1/src/cmt/services/pypi.py +34 -0
- cmt_cli-1.0.1/tests/__init__.py +0 -0
- cmt_cli-0.2.0/.github/workflows/test.yml +0 -27
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/.python-version +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/LICENSE +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/art/cmt-cli.svg +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/ai/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/ai/provider.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/analysis/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/analysis/analyzer.py +0 -0
- {cmt_cli-0.2.0/src/cmt/config → cmt_cli-1.0.1/src/cmt/enums}/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/exceptions.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/git/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/git/repository.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/models/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/models/changes.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/models/suggestion.py +0 -0
- {cmt_cli-0.2.0/tests → cmt_cli-1.0.1/src/cmt/schemas}/__init__.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/src/cmt/utils.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/tests/unit/test_repository.py +0 -0
- {cmt_cli-0.2.0 → cmt_cli-1.0.1}/uv.lock +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- 'src/**'
|
|
8
|
+
- 'tests/**'
|
|
9
|
+
- 'pyproject.toml'
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v3
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --all-groups
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: uv run pytest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmt-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: AI-powered Git commit message generator
|
|
5
5
|
Project-URL: Homepage, https://github.com/bijaydas/cmt
|
|
6
6
|
Project-URL: Documentation, https://github.com/bijaydas/cmt#readme
|
|
@@ -33,6 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
<a href="https://pypi.org/project/cmt-cli/"><img src="https://img.shields.io/pypi/pyversions/cmt-cli.svg" alt="Python versions"></a>
|
|
34
34
|
<a href="https://github.com/bijaydas/cmt/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
|
35
35
|
<img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Alpha status">
|
|
36
|
+
<img src="https://img.shields.io/badge/tests-passing-green.svg" alt="Tests">
|
|
36
37
|
</p>
|
|
37
38
|
|
|
38
39
|
<p>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<a href="https://pypi.org/project/cmt-cli/"><img src="https://img.shields.io/pypi/pyversions/cmt-cli.svg" alt="Python versions"></a>
|
|
10
10
|
<a href="https://github.com/bijaydas/cmt/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
|
11
11
|
<img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Alpha status">
|
|
12
|
+
<img src="https://img.shields.io/badge/tests-passing-green.svg" alt="Tests">
|
|
12
13
|
</p>
|
|
13
14
|
|
|
14
15
|
<p>
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cmt-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.1"
|
|
4
4
|
description = "AI-powered Git commit message generator"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
license = { text = "MIT" }
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
|
-
authors = [
|
|
10
|
-
{ name = "Bijay Das", email = "me@bijaydas.com" },
|
|
11
|
-
]
|
|
9
|
+
authors = [{ name = "Bijay Das", email = "me@bijaydas.com" }]
|
|
12
10
|
dependencies = [
|
|
13
11
|
"langchain>=1.3.18",
|
|
14
12
|
"langchain-openai>=1.6.0",
|
|
@@ -39,10 +37,7 @@ build-backend = "hatchling.build"
|
|
|
39
37
|
keywords = ["ai", "git", "commit", "message", "generator"]
|
|
40
38
|
|
|
41
39
|
[dependency-groups]
|
|
42
|
-
dev = [
|
|
43
|
-
"pytest>=9.1.1",
|
|
44
|
-
"ruff>=0.16.5",
|
|
45
|
-
]
|
|
40
|
+
dev = ["pytest>=9.1.1", "ruff>=0.16.5"]
|
|
46
41
|
[tool.ruff]
|
|
47
42
|
line-length = 100
|
|
48
43
|
target-version = "py312"
|
|
@@ -50,11 +45,11 @@ src = ["src"]
|
|
|
50
45
|
|
|
51
46
|
[tool.ruff.lint]
|
|
52
47
|
select = [
|
|
53
|
-
"E",
|
|
54
|
-
"F",
|
|
55
|
-
"I",
|
|
56
|
-
"UP",
|
|
57
|
-
"B",
|
|
48
|
+
"E", # pycodestyle errors
|
|
49
|
+
"F", # pyflakes
|
|
50
|
+
"I", # isort (import sorting)
|
|
51
|
+
"UP", # pyupgrade (modern syntax)
|
|
52
|
+
"B", # flake8-bugbear (common bugs)
|
|
58
53
|
"SIM", # flake8-simplify
|
|
59
54
|
]
|
|
60
55
|
ignore = []
|
|
@@ -2,13 +2,13 @@ import hashlib
|
|
|
2
2
|
import json
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from cmt.
|
|
5
|
+
from cmt.core.settings import settings
|
|
6
6
|
from cmt.models.suggestion import CommitSuggestion
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CommitMessageCache:
|
|
10
10
|
def __init__(self):
|
|
11
|
-
self.cache_dir = Path(
|
|
11
|
+
self.cache_dir = Path(settings.CACHE_DIR)
|
|
12
12
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
@@ -1,21 +1,29 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import cast
|
|
3
|
+
|
|
1
4
|
from langchain.agents import create_agent
|
|
5
|
+
from langchain.agents.middleware.types import InputAgentState
|
|
6
|
+
from langchain_core.messages import BaseMessage, get_buffer_string
|
|
2
7
|
from langchain_openai import ChatOpenAI
|
|
3
8
|
from pydantic import SecretStr
|
|
4
9
|
|
|
5
10
|
from cmt.ai.cache import CommitMessageCache
|
|
6
11
|
from cmt.ai.prompt import COMMIT_PROMPT, COMMIT_SYSTEM_PROMPT
|
|
7
12
|
from cmt.ai.provider import AIProvider
|
|
8
|
-
from cmt.
|
|
13
|
+
from cmt.core.settings import settings
|
|
9
14
|
from cmt.models.changes import AnalysisResult, StagedChangeSet, StagedFile
|
|
10
15
|
from cmt.models.suggestion import CommitSuggestion
|
|
11
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
12
19
|
|
|
13
20
|
class OpenAIProvider(AIProvider):
|
|
14
21
|
def __init__(self) -> None:
|
|
15
|
-
settings = Settings()
|
|
16
22
|
self.config = settings.get()
|
|
17
23
|
|
|
18
|
-
def _build_prompt(
|
|
24
|
+
def _build_prompt(
|
|
25
|
+
self, changes: StagedChangeSet, analysis: AnalysisResult
|
|
26
|
+
) -> list[BaseMessage]:
|
|
19
27
|
staged_files = self._process_files(changes.files)
|
|
20
28
|
staged_diffs = changes.diff
|
|
21
29
|
|
|
@@ -37,12 +45,14 @@ class OpenAIProvider(AIProvider):
|
|
|
37
45
|
|
|
38
46
|
return output.strip()
|
|
39
47
|
|
|
40
|
-
def _invoke(self, prompt:
|
|
48
|
+
def _invoke(self, prompt: list[BaseMessage]) -> CommitSuggestion:
|
|
41
49
|
model = ChatOpenAI(
|
|
42
50
|
model=self.config.model,
|
|
43
51
|
api_key=SecretStr(self.config.api_key),
|
|
44
52
|
timeout=30,
|
|
45
|
-
|
|
53
|
+
)
|
|
54
|
+
logger.info(
|
|
55
|
+
"Invoking %s model with prompt:\n%s", self.config.model, get_buffer_string(prompt)
|
|
46
56
|
)
|
|
47
57
|
|
|
48
58
|
agent = create_agent(
|
|
@@ -51,7 +61,7 @@ class OpenAIProvider(AIProvider):
|
|
|
51
61
|
response_format=CommitSuggestion,
|
|
52
62
|
)
|
|
53
63
|
|
|
54
|
-
result = agent.invoke(
|
|
64
|
+
result = agent.invoke(cast(InputAgentState, prompt))
|
|
55
65
|
|
|
56
66
|
return result["structured_response"]
|
|
57
67
|
|
|
@@ -64,13 +74,22 @@ class OpenAIProvider(AIProvider):
|
|
|
64
74
|
cached_message = cache.get(change_set.diff, self.config.model)
|
|
65
75
|
|
|
66
76
|
if cached_message:
|
|
67
|
-
|
|
77
|
+
suggestion = CommitSuggestion(
|
|
68
78
|
message=cached_message.message, description=cached_message.description
|
|
69
79
|
)
|
|
80
|
+
logger.info(
|
|
81
|
+
"Returning cached commit suggestion for the following changes:\n\n%s\n\n"
|
|
82
|
+
"Commit message: %s",
|
|
83
|
+
change_set.diff,
|
|
84
|
+
suggestion.message,
|
|
85
|
+
)
|
|
86
|
+
return suggestion
|
|
70
87
|
|
|
71
88
|
suggestion = self._invoke(prompt)
|
|
89
|
+
logger.info("Generated new commit suggestion.\n%s", suggestion)
|
|
72
90
|
|
|
73
91
|
cache.set(change_set.diff, self.config.model, suggestion)
|
|
92
|
+
logger.info("Cached new commit suggestion.")
|
|
74
93
|
|
|
75
94
|
return suggestion
|
|
76
95
|
|
|
@@ -59,7 +59,13 @@ Renamed: {renamed_files}
|
|
|
59
59
|
Generate the commit message now.
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
COMMIT_PROMPT1 = ChatPromptTemplate.from_messages(
|
|
63
|
+
[
|
|
64
|
+
("human", COMMIT_DATA_PROMPT),
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
COMMIT_PROMPT = ChatPromptTemplate(
|
|
63
69
|
[
|
|
64
70
|
("human", COMMIT_DATA_PROMPT),
|
|
65
71
|
]
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
2
3
|
from importlib.metadata import version
|
|
3
4
|
|
|
4
5
|
import typer
|
|
5
6
|
|
|
6
7
|
from cmt.ai.openai import OpenAIProvider
|
|
7
8
|
from cmt.analysis.analyzer import Analyzer
|
|
8
|
-
from cmt.
|
|
9
|
+
from cmt.core import settings, setup_logging
|
|
10
|
+
from cmt.enums.config import ConfigTask
|
|
9
11
|
from cmt.exceptions import CmtError
|
|
10
12
|
from cmt.git.repository import Repository
|
|
13
|
+
from cmt.schemas.config import AIConfig
|
|
14
|
+
from cmt.services import PyPIService
|
|
11
15
|
from cmt.utils import edit_with_vim
|
|
12
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
13
19
|
app = typer.Typer(
|
|
14
20
|
add_completion=False,
|
|
15
21
|
)
|
|
@@ -17,7 +23,7 @@ app = typer.Typer(
|
|
|
17
23
|
|
|
18
24
|
def version_callback(value: bool) -> None:
|
|
19
25
|
if value:
|
|
20
|
-
typer.echo(f"cmt-cli version: {version(
|
|
26
|
+
typer.echo(f"cmt-cli version: {version(settings.APP_NAME)}")
|
|
21
27
|
raise typer.Exit()
|
|
22
28
|
|
|
23
29
|
|
|
@@ -26,7 +32,9 @@ def main(
|
|
|
26
32
|
ctx: typer.Context,
|
|
27
33
|
flag: bool = typer.Option(False, "--version", callback=version_callback, is_eager=True),
|
|
28
34
|
) -> None:
|
|
35
|
+
setup_logging()
|
|
29
36
|
if ctx.invoked_subcommand is None:
|
|
37
|
+
logger.info("No subcommand invoked, displaying help.")
|
|
30
38
|
typer.echo(ctx.get_help())
|
|
31
39
|
raise typer.Exit(code=0)
|
|
32
40
|
|
|
@@ -39,11 +47,15 @@ def suggest() -> None:
|
|
|
39
47
|
analyzer = Analyzer()
|
|
40
48
|
|
|
41
49
|
if not repository.is_git_repository():
|
|
50
|
+
logger.error("Not a git repository.")
|
|
42
51
|
raise CmtError("Not a git repository.")
|
|
43
52
|
|
|
44
53
|
staged_files = repository.get_staged_changes()
|
|
45
54
|
|
|
46
55
|
if not staged_files.files:
|
|
56
|
+
logger.warning(
|
|
57
|
+
"No staged files found. Please stage your changes before running this command."
|
|
58
|
+
)
|
|
47
59
|
typer.echo(
|
|
48
60
|
"No staged files found. Please stage your changes before running this command."
|
|
49
61
|
)
|
|
@@ -56,6 +68,7 @@ def suggest() -> None:
|
|
|
56
68
|
commit = open_ai.generate_commit_message(staged_files, analysis_result)
|
|
57
69
|
commit_command = OpenAIProvider.commit_command(commit)
|
|
58
70
|
|
|
71
|
+
logger.info("Suggested commit:\n\n%s", commit_command)
|
|
59
72
|
typer.echo(f"Suggested commit:\n\n{commit_command}")
|
|
60
73
|
|
|
61
74
|
while True:
|
|
@@ -74,7 +87,10 @@ def suggest() -> None:
|
|
|
74
87
|
typer.echo(f"Edited commit:\n\n{commit_command}")
|
|
75
88
|
|
|
76
89
|
if action == "y":
|
|
90
|
+
logger.info("Committing with message:\n\n%s", commit_command)
|
|
77
91
|
result = repository.commit(commit_command)
|
|
92
|
+
|
|
93
|
+
logger.info("Commit result:\n\n%s", result.stdout)
|
|
78
94
|
typer.echo(f"Commit result:\n\n{result.stdout}")
|
|
79
95
|
break
|
|
80
96
|
|
|
@@ -84,32 +100,27 @@ def suggest() -> None:
|
|
|
84
100
|
except typer.Exit:
|
|
85
101
|
raise
|
|
86
102
|
except KeyboardInterrupt:
|
|
103
|
+
logger.info("Operation cancelled by user.")
|
|
87
104
|
typer.echo("\nOperation cancelled by user.", err=True)
|
|
88
105
|
raise typer.Exit(code=1) from None
|
|
89
106
|
except Exception as e:
|
|
107
|
+
logger.error("Unexpected error: %s", e)
|
|
90
108
|
typer.echo(f"Unexpected error: {e}", err=True)
|
|
91
109
|
raise typer.Exit(code=1) from None
|
|
92
110
|
|
|
93
111
|
|
|
94
|
-
class ConfigTask(StrEnum):
|
|
95
|
-
set = "set"
|
|
96
|
-
get = "get"
|
|
97
|
-
|
|
98
|
-
|
|
99
112
|
@app.command()
|
|
100
113
|
def config(task: ConfigTask) -> None:
|
|
101
114
|
"""Configure cmt-cli configuration for OpenAI API key and model."""
|
|
102
115
|
if task == ConfigTask.set:
|
|
103
|
-
settings = Settings()
|
|
104
|
-
|
|
105
116
|
open_ai_key = typer.prompt("Enter your OpenAI API key", default=None)
|
|
106
117
|
open_ai_model = typer.prompt(
|
|
107
118
|
"Enter your OpenAI model", default=settings.OPEN_AI_DEFAULT_MODEL
|
|
108
119
|
)
|
|
109
|
-
|
|
120
|
+
logger.info("Configuration updated with OpenAI API key and model.")
|
|
121
|
+
settings.set(AIConfig(api_key=open_ai_key, model=open_ai_model))
|
|
110
122
|
|
|
111
123
|
if task == ConfigTask.get:
|
|
112
|
-
settings = Settings()
|
|
113
124
|
setting_values = settings.get()
|
|
114
125
|
|
|
115
126
|
if setting_values.api_key:
|
|
@@ -118,8 +129,42 @@ def config(task: ConfigTask) -> None:
|
|
|
118
129
|
if setting_values.model:
|
|
119
130
|
typer.echo(f"OpenAI model: {setting_values.model}")
|
|
120
131
|
|
|
132
|
+
logger.info("Retrieved current configuration.")
|
|
133
|
+
|
|
121
134
|
raise typer.Exit(code=0)
|
|
122
135
|
|
|
123
136
|
|
|
137
|
+
@app.command()
|
|
138
|
+
def update() -> None:
|
|
139
|
+
"""Update cmt-cli to the latest version."""
|
|
140
|
+
logger.info("Starting update process for %s.", settings.APP_NAME)
|
|
141
|
+
|
|
142
|
+
current_version = version(settings.APP_NAME)
|
|
143
|
+
logger.info("Current %s version: %s", settings.APP_NAME, current_version)
|
|
144
|
+
|
|
145
|
+
pypi_service = PyPIService()
|
|
146
|
+
latest_info_response = pypi_service.any_update(current_version=current_version)
|
|
147
|
+
|
|
148
|
+
if latest_info_response.updated_required:
|
|
149
|
+
logger.info(
|
|
150
|
+
"Update required: current version %s, latest version %s",
|
|
151
|
+
latest_info_response.current_version,
|
|
152
|
+
latest_info_response.latest_version,
|
|
153
|
+
)
|
|
154
|
+
typer.echo(
|
|
155
|
+
f"A new version of {settings.APP_NAME} is available: "
|
|
156
|
+
f"{latest_info_response.latest_version}.\n\n"
|
|
157
|
+
f"upgrade {settings.APP_NAME} using your package manager.",
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
logger.info(
|
|
161
|
+
"No update required. You are using the latest version: %s",
|
|
162
|
+
latest_info_response.current_version,
|
|
163
|
+
)
|
|
164
|
+
typer.echo(f"You are using the latest version of {settings.APP_NAME}")
|
|
165
|
+
|
|
166
|
+
raise typer.Exit(code=0)
|
|
167
|
+
|
|
168
|
+
|
|
124
169
|
if __name__ == "__main__":
|
|
125
170
|
app()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from logging.handlers import RotatingFileHandler
|
|
3
|
+
|
|
4
|
+
from cmt.core.settings import settings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def setup_logging():
|
|
8
|
+
settings.LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
9
|
+
|
|
10
|
+
handler = RotatingFileHandler(
|
|
11
|
+
settings.LOGS_DIR / f"{settings.APP_NAME}.log", maxBytes=1024 * 1024, backupCount=9
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(
|
|
15
|
+
level=logging.DEBUG,
|
|
16
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
17
|
+
handlers=[handler],
|
|
18
|
+
)
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
from configparser import ConfigParser
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
|
|
6
4
|
from cmt.exceptions import ConfigurationError
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Config(BaseModel):
|
|
10
|
-
api_key: str
|
|
11
|
-
model: str
|
|
5
|
+
from cmt.schemas.config import AIConfig
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class Settings:
|
|
9
|
+
APP_NAME: str = "cmt-cli"
|
|
15
10
|
CONFIG_DIR: Path = Path.home() / ".config" / "cmt"
|
|
16
11
|
CACHE_DIR: Path = CONFIG_DIR / "cache"
|
|
12
|
+
LOGS_DIR: Path = CONFIG_DIR / "logs"
|
|
17
13
|
|
|
18
14
|
CONFIG_FILE: Path = CONFIG_DIR / "config.ini"
|
|
19
15
|
OPEN_AI_DEFAULT_MODEL: str = "gpt-4o-mini"
|
|
20
16
|
|
|
21
|
-
def set(self, openai_config:
|
|
17
|
+
def set(self, openai_config: AIConfig):
|
|
22
18
|
self.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
23
19
|
|
|
24
20
|
config = ConfigParser()
|
|
@@ -35,7 +31,7 @@ class Settings:
|
|
|
35
31
|
with open(self.CONFIG_FILE, "w") as f:
|
|
36
32
|
config.write(f)
|
|
37
33
|
|
|
38
|
-
def get(self) ->
|
|
34
|
+
def get(self) -> AIConfig:
|
|
39
35
|
if not self.CONFIG_FILE.exists():
|
|
40
36
|
raise FileNotFoundError(
|
|
41
37
|
"Configuration file not found. Run `cmt config set` to set it up."
|
|
@@ -57,4 +53,7 @@ class Settings:
|
|
|
57
53
|
"Model not found in the configuration. Run `cmt config set` to set it up."
|
|
58
54
|
)
|
|
59
55
|
|
|
60
|
-
return
|
|
56
|
+
return AIConfig(api_key=api_key, model=model)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
settings = Settings()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from cmt.core import settings
|
|
6
|
+
from cmt.schemas.general import VersionInfo
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PyPIService:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._base_url = "https://pypi.org/pypi"
|
|
14
|
+
|
|
15
|
+
def _get_package_info(self) -> dict:
|
|
16
|
+
url = f"{self._base_url}/{settings.APP_NAME}/json"
|
|
17
|
+
response = httpx.get(url)
|
|
18
|
+
|
|
19
|
+
logger.debug("Received response with status code: %s", response.status_code)
|
|
20
|
+
response.raise_for_status()
|
|
21
|
+
|
|
22
|
+
return response.json()
|
|
23
|
+
|
|
24
|
+
def any_update(self, current_version: str) -> VersionInfo:
|
|
25
|
+
latest_info = self._get_package_info()
|
|
26
|
+
latest_version = latest_info["info"]["version"]
|
|
27
|
+
|
|
28
|
+
logger.debug("Current version: %s, Latest version: %s", current_version, latest_version)
|
|
29
|
+
|
|
30
|
+
return VersionInfo(
|
|
31
|
+
current_version=current_version,
|
|
32
|
+
latest_version=latest_version,
|
|
33
|
+
updated_required=latest_version != current_version,
|
|
34
|
+
)
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
name: Tests
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- name: Checkout code
|
|
15
|
-
uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
- name: Install uv
|
|
18
|
-
uses: astral-sh/setup-uv@v3
|
|
19
|
-
with:
|
|
20
|
-
python-version: "3.12"
|
|
21
|
-
enable-cache: true
|
|
22
|
-
|
|
23
|
-
- name: Install dependencies
|
|
24
|
-
run: uv sync --all-groups
|
|
25
|
-
|
|
26
|
-
- name: Run tests
|
|
27
|
-
run: uv run pytest
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|