Compare commits

..

1 Commits

Author SHA1 Message Date
acereca b874441f06 replace use of pydantic_settings w/ dataclass 2026-01-02 12:52:08 +01:00
24 changed files with 314 additions and 2397 deletions
-39
View File
@@ -1,42 +1,3 @@
# mrpy.nvim
NeoVim plugin, integrating a clickup to gitlab workflow (1 task to 1 issue to 1 merge request)
## Install
### Lazy.nvim
#### From Source
```lua
return {
dir = "<path/to/cloned/repo>",
dev = true,
dependencies = {
"nvim-lua/plenary.nvim",
},
}
```
## Setup
### Shell Configuration
#### Environment Variables
CLICKUP_AUTH
: user api token for clickup
CLICKUP_USER_ID
: user id for clickup
CLICKUP_WORKSPACE_ID
: workspace id for clickup
## Commands
### `:MrPy`
- start task selector of user's tasks (not done/closed)
View File
-109
View File
@@ -1,109 +0,0 @@
from collections.abc import Iterator
from dataclasses import dataclass, field
from json import loads
from pydantic import BaseModel
from requests import HTTPError, get
from .common import EnvVar, JSONDataMap, JSONData, JSONDataList, JSONDataScalar
class ClickupStatus(BaseModel):
status: str
@property
def status_symbol(self) -> str:
match self.status:
case "backlog":
return "󰧟"
case "selected for development":
return "󱥸"
case "in progress":
return "󰝦"
case "in review":
return "󰬫"
case "done":
return "󰻃"
case "closed":
return "󰄴"
case "on hold":
return "󰏦"
case _:
return self.status
class ClickupTask(BaseModel):
id: str
name: str
markdown_description: str
status: ClickupStatus
class ClickupList(BaseModel):
task_count: int
@dataclass
class ClickupSession:
auth_key: str = field(
default_factory=EnvVar(
"CLICKUP_AUTH",
"clickup auth token is required to be set",
)
)
workspace_id: str = field(
default_factory=EnvVar(
"CLICKUP_WORKSPACE_ID",
"clickup workspace id is required to be set",
)
)
user_id: str = field(
default_factory=EnvVar(
"CLICKUP_USER_ID",
"clickup user id is required to be set",
)
)
base_url: str = "https://api.clickup.com/api/v2"
def _get(self, endpoint: str, **query_params: str) -> JSONDataMap:
with get(
self.base_url + endpoint,
query_params,
headers={
"accept": "application/json",
"Authorization": self.auth_key,
},
) as resp:
return resp.json()
def get_tasks(self, **filters: str) -> Iterator[ClickupTask]:
pix = 0
while True:
partial_data = self._get(
f"/team/{self.workspace_id}/task",
**{
"subtasks": "true",
"include_markdown_description": "true",
"include_closed": "true",
"assignees[]": self.user_id,
"page": pix,
}
| filters,
).get("tasks", [])
pix += 1
for inst in partial_data:
yield ClickupTask.model_validate(inst)
if len(partial_data) < 100:
break
def get_task(self, task_id: str) -> ClickupTask:
return ClickupTask.model_validate(
self._get(
f"/task/{task_id}",
include_markdown_description="true",
),
)
def get_list(self, list_id: str) -> ClickupList:
return ClickupList.model_validate(self._get(f"/list/{list_id}"))
-32
View File
@@ -1,32 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from os import environ
from typing import TypeAlias
JSONDataScalar: TypeAlias = str | None | float | bool
JSONDataList: TypeAlias = list["JSONDataScalar | JSONDataMap | JSONDataList"]
JSONDataMap: TypeAlias = dict[str, "JSONDataScalar | JSONDataList | JSONDataMap"]
JSONData: TypeAlias = "JSONDataMap | JSONDataList"
@dataclass
class EnvVar:
"""
Environment Variable fetcher for use in dataclass ``field(default_factory=...)``
>>> @dataclass
>>> class SomeDataclass:
... field_name: str = field(default_factory=EnvVar("SOME_VAR_NAME", "err msg"))
"""
var_name: str
err_msg: str = ""
def __call__(self) -> str:
try:
return environ[self.var_name]
except KeyError as e:
e.add_note(self.err_msg)
raise
-133
View File
@@ -1,133 +0,0 @@
from dataclasses import dataclass, field
from typing import Any
from pydantic import BaseModel, Field
from requests import get
from requests.compat import quote
from eta.common import EnvVar, JSONDataMap
class GitlabProject(BaseModel):
name: str
name_with_namespace: str
path_with_namespace: str
web_url: str
id: int
issues: list["GitlabIssue"] = Field(default_factory=list, init=False)
merge_requests: list["GitlabMergeRequest"] = Field(default_factory=list, init=False)
class GitlabMilestone(BaseModel):
id: int
title: str
state: str
class GitlabIssue(BaseModel):
title: str
description: str | None
id: int
project_id: int
iid: int
state: str
labels: list[str]
milestone: GitlabMilestone | None
# web_url: str
class GitlabMergeRequest(BaseModel):
id: int
iid: int
description: str | None
draft: bool
labels: list[str]
milestone: GitlabMilestone | None
source_branch: str
state: str
target_branch: str
project_id: int
title: str
web_url: str
@dataclass
class GitlabSession:
auth_key: str = field(
default_factory=EnvVar(
"GITLAB_AUTH",
"gitlab auth token is required to be set",
)
)
base_url: str = "https://git.extoll.de/api/v4"
def _get(self, endpoint: str, **query_params: str) -> JSONDataMap:
with get(
self.base_url + endpoint,
query_params,
headers={
"accept": "application/json",
"PRIVATE-TOKEN": self.auth_key,
},
verify=False,
) as resp:
return resp.json()
def get_projects(self, **filters: str) -> list[GitlabProject]:
ret: list[GitlabProject] = []
page = 1
while projs := self._get(
"/projects",
active="true",
simple="true",
per_page="100",
page=str(page),
):
ret.extend(GitlabProject.model_validate(proj) for proj in projs)
page = page + 1
if len(projs) < 100:
break
return ret
def get_issues(self, projects: list[GitlabProject]) -> list[GitlabIssue]:
ret: list[GitlabIssue] = []
pids = {p.id for p in projects}
page = 1
while True:
issues = self._get(
"/issues",
per_page="100",
page=str(page),
)
ret.extend(
GitlabIssue.model_validate(iss) for iss in issues if int(iss["project_id"]) in pids
)
page = page + 1
if len(issues) < 100:
break
return ret
def get_merge_requests(self, projects: list[GitlabProject]) -> list[GitlabMergeRequest]:
ret: list[GitlabMergeRequest] = []
pids = {p.id for p in projects}
page = 1
while True:
merge_requests = self._get(
"/merge_requests",
simple="true",
per_page="100",
page=str(page),
)
ret.extend(
GitlabMergeRequest.model_validate(mr)
for mr in merge_requests
if int(mr["project_id"]) in pids
)
page = page + 1
if len(merge_requests) < 100:
break
return ret
-435
View File
@@ -1,435 +0,0 @@
from enum import Enum, IntEnum
import re
from lsprotocol.types import (
INITIALIZED,
TEXT_DOCUMENT_CODE_ACTION,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_DOCUMENT_SYMBOL,
TEXT_DOCUMENT_HOVER,
TEXT_DOCUMENT_INLAY_HINT,
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
WORKSPACE_INLAY_HINT_REFRESH,
CodeAction,
CodeActionKind,
CodeActionOptions,
CodeActionParams,
Command,
CompletionItem,
CompletionItemKind,
CompletionItemLabelDetails,
CompletionOptions,
CompletionParams,
DocumentSymbol,
DocumentSymbolParams,
Hover,
HoverParams,
InitializedParams,
InlayHint,
InlayHintParams,
MarkupContent,
MarkupKind,
NotebookDocumentSyncOptions,
Position,
Range,
SemanticTokens,
SemanticTokensLegend,
SemanticTokensParams,
SymbolKind,
TextDocumentSyncKind,
WorkDoneProgressBegin,
WorkDoneProgressEnd,
WorkDoneProgressReport,
)
from pygls.lsp.server import LanguageServer
from eta.gitlab import GitlabProject, GitlabSession
from .clickup import ClickupSession, ClickupTask
GL_PRJ_PATTERN = r"#project\/(?P<ns>((\w+)/)+)(?P<prj>\w+)"
GL_ID_PATTERN = r"#(?P<idt>mr|issue)\/(?P<sid>\d+)"
CU_PATTERN = r"#task/(?P<id>\w{8,})"
class CustomServer(LanguageServer):
cu_cache: dict[str, ClickupTask]
cu_session: ClickupSession
gl_cache: dict[str, GitlabProject]
gl_session: GitlabSession
def __init__(
self,
name: str,
version: str,
text_document_sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental,
notebook_document_sync: NotebookDocumentSyncOptions | None = None,
) -> None:
super().__init__(name, version, text_document_sync_kind, notebook_document_sync)
self.cu_cache = {}
self.cu_session = ClickupSession()
self.gl_cache = {}
self.gl_session = GitlabSession()
async def update_task_cache(self) -> None:
self.cu_cache = {}
tasks = self.cu_session.get_tasks()
for ti, t in enumerate(tasks):
self.cu_cache[t.id] = t
self.protocol.progress.report(
"startup",
WorkDoneProgressReport(
message="ClickUp Tasks", percentage=int(100 * (1 + ti) / len(tasks))
),
)
async def update_proj_cache(self) -> None:
self.gl_cache = {}
projs = self.gl_session.get_projects()
for pi, p in enumerate(projs):
self.gl_cache[p.path_with_namespace] = p
self.protocol.progress.report(
"startup",
WorkDoneProgressReport(
message="Gitlab Prjects", percentage=int(100 * (1 + pi) / len(projs))
),
)
await self.update_issue_cache(projs)
await self.update_mr_cache(projs)
async def update_issue_cache(self, prjs: list[GitlabProject]) -> None:
p_by_id = {p.id: p for p in prjs}
issues = self.gl_session.get_issues(prjs)
for issue in issues:
self.gl_cache[p_by_id[issue.project_id].path_with_namespace].issues.append(issue)
async def update_mr_cache(self, prjs: list[GitlabProject]) -> None:
p_by_id = {p.id: p for p in prjs}
mrs = self.gl_session.get_merge_requests(prjs)
for mr in mrs:
self.gl_cache[p_by_id[mr.project_id].path_with_namespace].merge_requests.append(mr)
server = CustomServer("eta-server", "0.1.0")
@server.feature(INITIALIZED)
async def on_init(params: InitializedParams) -> None:
server.protocol.progress.begin(
"startup", WorkDoneProgressBegin("Caching ", percentage=0, cancellable=True)
)
await server.update_task_cache()
await server.update_proj_cache()
server.protocol.progress.end("startup", WorkDoneProgressEnd(message="Done Caching"))
@server.feature(TEXT_DOCUMENT_DOCUMENT_SYMBOL)
async def list_ids(params: DocumentSymbolParams) -> list[DocumentSymbol]:
return [
DocumentSymbol(
t.id,
SymbolKind.Enum,
Range(Position(i, 0), Position(i, 0)),
Range(Position(i, 0), Position(i, 0)),
detail=t.name,
)
for i, t in enumerate(server.cu_cache.values())
]
@server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=["/", "!", "#"]))
async def complete_cu_ids(params: CompletionParams) -> list[CompletionItem]:
doc = server.workspace.get_text_document(params.text_document.uri)
line = doc.lines[params.position.line]
if not line[: params.position.character].strip().endswith(("/", "!", "#")):
return []
prev = (
line[: params.position.character]
.strip()
.removesuffix("/")
.removesuffix("!")
.removesuffix("#")
.split("#")[-1]
)
if prev.endswith(("task", "cu", "clickup")):
return [
CompletionItem(
f"{t.name}",
CompletionItemLabelDetails(detail=f" #{t.id}"),
kind=CompletionItemKind.Constant,
insert_text=f"{t.id}",
)
for t in server.cu_cache.values()
]
if prev.endswith(("project", "gitlab", "gl")):
return [
CompletionItem(
p.path_with_namespace,
detail=f" {p.path_with_namespace}",
kind=CompletionItemKind.Enum,
)
for p in server.gl_cache.values()
]
prj = next(re.finditer(GL_PRJ_PATTERN, line), None)
if prj is None:
return [
CompletionItem(
f"{line}: {prj}",
)
]
if prev.endswith("issue"):
return [
CompletionItem(
i.title,
insert_text=f"{i.iid}",
detail=f"#{i.iid}: {i.title} {' '.join('[' + t + ']' for t in i.labels)}",
documentation=i.description,
kind=CompletionItemKind.EnumMember,
)
for p in server.gl_cache.values()
for i in p.issues
if i.state == "opened"
and p.path_with_namespace == f"{prj.group('ns')}{prj.group('prj')}"
]
if prev.endswith("mr"):
return [
CompletionItem(
f"!{m.title} ({p.path_with_namespace}!{m.iid})",
insert_text=f"{m.iid}",
detail=f"!{m.iid}: {m.title} {' '.join('[' + t + ']' for t in m.labels)}",
documentation=m.description,
kind=CompletionItemKind.EnumMember,
)
for p in server.gl_cache.values()
for m in p.merge_requests
if m.state == "opened"
and p.path_with_namespace.casefold()
== f"{prj.group('ns')}{prj.group('prj')}".casefold()
]
return []
@server.feature(TEXT_DOCUMENT_INLAY_HINT)
async def inlay_info(params: InlayHintParams) -> list[InlayHint]:
ret: list[InlayHint] = []
for lid, line in enumerate(server.workspace.text_documents[params.text_document.uri].lines):
# for m in re.finditer(GL_PATTERN, line):
# ns = m.group("ns")
# prj = m.group("prj")
# idt = m.group("styp")
# id = m.group("sid")
#
# if p := server.gl_cache.get(f"{ns}{prj}"):
# if not idt:
# pass
# elif idt == "!" and (mr := next(m for m in p.merge_requests if str(m.iid) == id)):
# ret.append(
# InlayHint(
# Position(line=lid, character=m.end()),
# label=f"({mr.title} | {mr.state})",
# padding_right=True,
# padding_left=True,
# )
# )
# elif idt == "#" and (issue := next(i for i in p.issues if str(i.iid) == id)):
# ret.append(
# InlayHint(
# Position(line=lid, character=m.end()),
# label=f"({issue.title} | {issue.state})",
# padding_right=True,
# padding_left=True,
# )
# )
for m in re.finditer(CU_PATTERN, line):
id = m.group(1)
if t := server.cu_cache.get(id):
ret.append(
InlayHint(
Position(line=lid, character=m.start()),
label=f"{t.status.status_symbol}",
padding_left=False,
padding_right=False,
)
)
ret.append(
InlayHint(
Position(line=lid, character=m.end()),
label=f"{t.name}",
padding_left=True,
padding_right=False,
)
)
return ret
@server.command("recache_gl")
async def recache_gl(*_) -> None:
await server.update_proj_cache()
await server.protocol.send_request_async(WORKSPACE_INLAY_HINT_REFRESH, None)
@server.command("recache_cu")
async def recache_cu(*_) -> None:
await server.update_task_cache()
await server.protocol.send_request_async(WORKSPACE_INLAY_HINT_REFRESH, None)
@server.feature(
TEXT_DOCUMENT_CODE_ACTION,
CodeActionOptions(code_action_kinds=[CodeActionKind.QuickFix]),
)
def code_actions(params: CodeActionParams) -> list[CodeAction]:
return [
CodeAction(
"Re-Cache GitLab Project Info",
kind=CodeActionKind.QuickFix,
command=Command("recache gl", "recache_gl"),
),
CodeAction(
"Re-Cache ClickUp Task Info",
kind=CodeActionKind.QuickFix,
command=Command("recache cu", "recache_cu"),
),
]
@server.feature(TEXT_DOCUMENT_HOVER)
def on_hover(params: HoverParams) -> Hover | None:
doc = server.workspace.get_text_document(params.text_document.uri)
line = doc.lines[params.position.line]
# prj_match = list(re.finditer(GL_PRJ_PATTERN, line))[0]
# ns = prj_match.group("ns")
# prj = prj_match.group("prj")
# id_match = list(re.finditer(GL_ID_PATTERN, line))
#
# if p := server.gl_cache.get(f"{ns}{prj}"):
# for m in id_match:
# if params.position.character >= m.start() and params.position.character < m.end():
# idt = m.group("idt")
# id = m.group("sid")
#
# if not idt:
# pass
# elif idt == "mr" and (mr := next(m for m in p.merge_requests if str(m.iid) == id)):
# return Hover(
# MarkupContent(
# kind=MarkupKind.Markdown,
# value=f"# {mr.title}\n\n{mr.description or ''}",
# ),
# range=Range(
# Position(line=params.position.line, character=m.start()),
# Position(line=params.position.line, character=m.end()),
# ),
# )
# elif idt == "issue" and (issue := next(i for i in p.issues if str(i.iid) == id)):
# return Hover(
# MarkupContent(
# kind=MarkupKind.Markdown,
# value=f"# {issue.title}\n\n{issue.description or ''}",
# ),
# range=Range(
# Position(line=params.position.line, character=m.start()),
# Position(line=params.position.line, character=m.end()),
# ),
# )
for m in re.finditer(CU_PATTERN, line):
if params.position.character >= m.start() and params.position.character < m.end():
id = m.group("id")
if t := server.cu_cache.get(id):
return Hover(
MarkupContent(
kind=MarkupKind.Markdown,
value=f"# {t.name} - 󱇯 {t.status.status}\n\n{t.markdown_description}",
),
range=Range(
Position(line=params.position.line, character=m.start()),
Position(line=params.position.line, character=m.end()),
),
)
else:
return Hover(
MarkupContent(
kind=MarkupKind.Markdown,
value=f"# {id}: {m.group(0)}\n\n{list(server.cu_cache)}",
),
range=Range(
Position(line=params.position.line, character=m.start()),
Position(line=params.position.line, character=m.end()),
),
)
return None
def token_offset(rest: list[int], current: tuple[int, int]) -> tuple[int, int]:
lines = rest[::5]
# offsets = rest[:-4:-5]
last_line = sum(lines)
return (current[0] - last_line, current[1])
class TaskModifiers(Enum):
Backlog = "backlog"
Selected = "selected for development"
Progress = "in progress"
Review = "in review"
Done = "done"
Closed = "closed"
@server.feature(
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
SemanticTokensLegend(["cuTask"], list(t.name for t in TaskModifiers)),
)
def sem_tokens(params: SemanticTokensParams) -> SemanticTokens:
ret: list[int] = []
doc = server.workspace.get_text_document(params.text_document.uri)
for lix, line in enumerate(doc.lines):
# ms = re.finditer(GL_PATTERN, line)
#
# for m in ms:
# idt = m.group("styp")
# id = m.group("sid")
# rel_line, rel_char = token_offset(ret, (lix, m.start()))
#
# if not idt:
# ret.extend([rel_line, rel_char, len(m.group(0)), 0, 0])
# elif idt == "!":
# ret.extend([rel_line, rel_char, len(m.group(0)), 2, 0])
# elif idt == "#":
# ret.extend([rel_line, rel_char, len(m.group(0)), 2, 0])
for m in re.finditer(CU_PATTERN, line):
id = m.group("id")
rel_line, rel_char = token_offset(ret, (lix, m.start()))
if t := server.cu_cache.get(id):
ret.extend(
[
rel_line,
rel_char,
len(m.group(0)),
0,
list(t.value for t in TaskModifiers).index(t.status.status),
]
)
return SemanticTokens(data=ret)
def main() -> None:
server.start_io()
-105
View File
@@ -1,105 +0,0 @@
local curl = require("plenary.curl")
local helpers = require("eta.helpers")
local M = {}
---@class eta.clickup.Session: eta.Session
---@field user string
---@field workspace string
---@field default_reviewer string
---@class eta.clickup.Ref
---@field name string
---@field id string
---@class eta.clickup.Dep
---@field task_id string
---@field depends_on string
---@class eta.clickup.Task
---@field id string
---@field name string
---@field tags? table[]
---@field locations? table[]
---@field list? eta.clickup.Ref
---@field parent? string | nil
---@field dependencies? eta.clickup.Dep[]
---@field [string] string
---@param self eta.clickup.Session
---@return eta.clickup.Task[]
M.latest_tasks = function(self)
local ret = helpers.request("get", self, "/team/" .. self.workspace .. "/task",
{ subtasks = "true", include_markdown_description = "true", ['assignees[]'] = self.user })
if ret then
return ret.tasks
end
return {}
end
---@param self eta.clickup.Session
---@param id string
---@return eta.clickup.Task
M.task = function(self, id)
local ret = helpers.request("get", self, "/task/" .. id, { include_markdown_description = "true" }) or {}
return ret
end
---@param self eta.clickup.Session
---@param id string
M.task_relations = function(self, id)
local ret = helpers.request("get", self, "/task/" .. id .. "/dependency", {}) or {}
return ret
end
M.insert_ref = function()
local pos = vim.api.nvim_win_get_cursor(0)
local row = pos[1] - 1
local col = pos[2]
local tasks = M.latest_tasks(require("plugin.eta").clickup_session)
---@type SelectionItem[]
local items = {}
for tix, t in ipairs(tasks) do
if string.sub(t.name, -7, -1) ~= "Absence" then
local preview_frontmatter = ""
---@type SelectionItem
local prepared = {
idx = tix,
id = t.id,
text = t.name .. t.id .. t.markdown_description,
name = t.name,
tags = require("plugin.eta").retrieve_subkeys(t.tags, { "name" }),
status = t.status.status,
parent = t.parent,
list = t.list.name,
description = t.markdown_description,
preview = {
ft = "markdown",
},
action = M._on_select_task
}
for _, k in ipairs({ "id", "name", "status", "tags", "parent" }) do
preview_frontmatter = preview_frontmatter .. "\n" .. k .. ": " .. vim.json.encode(prepared[k])
end
prepared.preview.text = "---" .. preview_frontmatter .. "\n---\n" .. t.markdown_description
table.insert(items, #items + 1, prepared)
end
end
require("snacks.picker").pick({
title = "Select Task",
format = require("plugin.eta")._item_format,
preview = "preview",
confirm = function(picker, item)
picker:close()
vim.api.nvim_buf_set_text(0, row, col, row, col,
{ "[#" .. item.id .. "](https://app.clickup.com/t/" .. item.id .. ")" })
end,
items = items
})
end
return M
-149
View File
@@ -1,149 +0,0 @@
local notify = require("snacks.notifier").notify
local helpers = require("eta.helpers")
local M = {}
---@class eta.gitlab.Session: eta.Session
---@field auth string personal access token `PRIVATE_TOKEN: <auth_token>`
---@class eta.gitlab.Namespace
---@field full_path string
---@class eta.gitlab.Project
---@field id number
---@field name string
---@field path_with_namespace string
---@field tag_list string[]
---@field text? string
---@field namespace eta.gitlab.Namespace
---@field preview? {ft: string, text: string}
---@class eta.gitlab.Milestone
---@field id number
---@field title string
---@class eta.gitlab.Assignee
---@field username string
---@field id number
---@class eta.gitlab.Issue
---@field id number
---@field milestone eta.gitlab.Milestone
---@field title string
---@field assignees eta.gitlab.Assignee[]
---@field description string
---@field labels string[]
---@param session eta.gitlab.Session
---@return eta.gitlab.Project[]
M.possible_projects = function(session)
return helpers.request("get", session, "/projects", {simple="true", per_page="100"}) or {}
end
---@param prj eta.gitlab.Project
M._project_format = function(prj)
local ret = {}
ret[#ret + 1] = { prj.namespace.full_path .. "/", "SnacksPickerComment" }
ret[#ret + 1] = { prj.name }
return ret
end
---@param self eta.gitlab.Session
---@return eta.gitlab.Project | nil
M.update_current_project = function(self)
local cmd = "git remote -v | grep fetch | cut -f2 | cut -d' ' -f1 | cut -d':' -f2"
local handle, _ = io.popen(cmd, 'r')
if not handle then return nil end
local repo = handle:read("*a")
handle:close()
local prjs = M.possible_projects(self)
for _, prj in ipairs(prjs) do
if prj.path_with_namespace == repo then
M.active_project = prj
notify("selected " .. prj.path_with_namespace, "info", { title = "ETA", style = 'fancy' })
return prj
end
end
end
---@type eta.gitlab.Project
M.active_project = nil
---@param picker snacks.Picker
---@param item eta.gitlab.Project
M._on_project_select = function(picker, item)
M.active_project = item
picker:close()
notify("selected " .. item.path_with_namespace, "info", { title = "ETA", style = 'fancy' })
end
---@param self eta.gitlab.Session
M.select_project = function(self)
local prjs = M.possible_projects(self)
require("snacks.picker").pick({
title = "Select Project",
format = M._project_format,
preview = "preview",
confirm = M._on_project_select,
items = prjs
})
end
---@param self eta.gitlab.Session
---@return eta.gitlab.Issue[]
M.active_issues = function(self)
if not M.active_project then
if not M.update_current_project(self) then
M.select_project(self)
end
end
return request("get", self, "/projects/" .. tostring(M.active_project.id) .. "/issues", {})
end
---@param self eta.gitlab.Session
---@param name string
---@param clickup_task_id string
---@param tags string[]
M.new_issue = function(self, name, clickup_task_id, tags)
if not M.active_project then
if not M.update_current_project(self) then
M.select_project(self)
end
end
local description = "%23" .. clickup_task_id
local title = name:gsub("%s", "%20")
return request("post", self, "/projects/" ..
tostring(M.active_project.id) ..
"/issues", {
title=title, description=description, labels=table.concat(tags, ",")
})
end
---@param self eta.gitlab.Session
---@param name string
---@param issue number
---@param clickup_task_id string
---@param tags string[]
M.new_mr = function(self, name, issue, clickup_task_id, tags)
if not M.active_project then
if not M.update_current_project(self) then
M.select_project(self)
end
end
local description = "Closes %23" .. tostring(issue) .. " | %23" .. clickup_task_id
local title = name:gsub("%s", "%20")
return request("post", self, "/projects/" .. tostring(M.active_project.id) .. "/merge_requests", {
title=title, description=description, labels=table.concat(tags, ",")
})
end
return M
-80
View File
@@ -1,80 +0,0 @@
local curl = require("plenary.curl")
local M = {}
---@class eta.Session
---@field auth string
---@field base_url string
---@param method `get` | `post`
---@param session eta.Session
---@param endpoint string
---@param params {[string]: string}
---@returns table | nil
M.request = function(method, session, endpoint, params)
local url = session.base_url .. endpoint
local resp = nil
if method == "get" then
local param_list = {}
for param_name, param_value in pairs(params) do
table.insert(param_list, param_name .. "=" .. param_value)
end
if #param_list then
url = url .. "?" .. table.concat(param_list, "&")
end
resp = curl.get(url, {
headers = {
-- ['PRIVATE-TOKEN'] = session.auth,
['Authorization'] = session.auth,
["content-type"] = "application/json",
}
})
elseif method == "post" then
resp = curl.post(url, {
headers = {
-- ['PRIVATE-TOKEN'] = session.auth,
['Authorization'] = session.auth,
["content-type"] = "application/json",
},
body = vim.json.encode(params)
})
else
return nil
end
if resp.status == 200 then
local prjs = vim.json.decode(resp.body)
for _, prj in ipairs(prjs) do
local fcmd = "echo '" .. vim.json.encode(prj) .. "' | jq"
local fhandle, _ = io.popen(fcmd, 'r')
if not fhandle then goto continue end
local formatted = fhandle:read("*a")
fhandle:close()
prj.text = prj.path_with_namespace
prj.preview = {
ft = "json",
text = formatted
}
::continue::
end
return prjs
end
error("failed http request: " .. tostring(resp.status) .. " (" .. resp.body .. ", " .. url .. ")")
end
--- @param str string
--- @param char? string defaults to " "
--- @return string[]
M.split = function(str, char)
char = char or " "
--- @type string[]
local ret = {}
for part in str:gmatch("[^" .. char .. "]+") do
table.insert(ret, part)
end
return ret
end
return M
-350
View File
@@ -1,350 +0,0 @@
local curl = require("plenary.curl")
local notifier = require("snacks.notifier")
local notify = require("snacks.notifier").notify
local gitlab = require("eta.gitlab")
local clickup = require("eta.clickup")
local helpers = require("eta.helpers")
local nio = require("nio")
local M = {}
---@param data table
---@return string | nil
M.table_to_yaml = function(data)
local json = vim.json.encode(data)
-- Nutzt 'yq' um JSON zu YAML zu konvertieren
local handle = io.popen("echo '" .. json .. "' | yq -y")
local result = handle:read("*a")
handle:close()
if not result then
print("Error: yq not installed or got invalid json data")
return nil
end
return result
end
---@param yaml_string string
---@return table | nil
M.yaml_to_table = function(yaml_string)
-- 1. YAML String in yq einspeisen und JSON ausgeben lassen
-- Das Flag -o=json sorgt für die Konvertierung
local temp_file = os.tmpname()
local temp_file_handle = io.open(temp_file, "w+b")
if not temp_file_handle then
print("Error: could not open temp file")
print(" temp_file: " .. temp_file)
return nil
end
temp_file_handle:write(yaml_string)
temp_file_handle:flush()
temp_file_handle:close()
local cmd = "cat " .. temp_file .. " | yq"
local handle, errres = io.popen(cmd, 'r')
local json_result = handle:read("*a")
handle:close()
-- 2. Fehlerprüfung
if json_result == "" then
print("Error: yq not installed or got invalid yaml data")
print(" cmd: " .. cmd)
print(" err: " .. errres)
return nil
end
-- 3. JSON in Lua-Table umwandeln
local ok, table_result = pcall(vim.json.decode, json_result)
if not ok then
print("Error: failed to decode json")
print(" cmd: " .. cmd)
print(" json: " .. json_result)
return nil
end
os.remove(temp_file)
return table_result
end
---@type eta.clickup.Session
M.clickup_session = nil
---@type eta.gitlab.Session
M.gitlab_session = nil
---@param task eta.clickup.Task
---@return eta.clickup.Task
M._update_task = function(task)
local update_table = {}
for _, k in ipairs({ "name", "markdown_content", "status" }) do
if task[k] then
update_table[k] = task[k]
end
end
local last_task = clickup.task(M.clickup_session, task.id)
if task.status == "in review" and last_task.status.status == "in progress" then
local resp = curl.post(M.clickup_session.base_url .. "/task/" .. task.id .. "/comment", {
headers = {
['Authorization'] = M.clickup_session.auth,
["accept"] = "application/json",
["content-type"] = "application/json",
},
body = vim.json.encode({
comment_text = "pls review",
assignee = M.clickup_session.default_reviewer,
notify_all = "false",
})
})
if resp.status ~= 200 then
error("failed to comment " .. task.id .. "\n(" .. resp.body .. ")")
end
end
local resp = curl.put(M.clickup_session.base_url .. "/task/" .. task.id, {
headers = {
['Authorization'] = M.clickup_session.auth,
["accept"] = "application/json",
["content-type"] = "application/json",
},
body = vim.json.encode(update_table)
})
if resp.status ~= 200 then
error("failed to update " .. task.id .. "\n(" .. resp.body .. ")")
end
return vim.json.decode(resp.body)
end
---@param args vim.api.keyset.create_autocmd.callback_args
M._on_tempbuf_write = function(args)
local lines = vim.api.nvim_buf_get_lines(args.buf, 0, -1, false)
---@type string[]
local parts = {}
---@type string[]
local current = {}
for _, line in ipairs(lines) do
if line == "---" then
if #current then
table.insert(parts, table.concat(current, "\n"))
current = {}
end
else
table.insert(current, line)
end
end
table.insert(parts, table.concat(current, "\n"))
---@type eta.clickup.Task
local data = M.yaml_to_table(parts[2]) or {}
data['markdown_content'] = string.gsub(parts[3], "%-%-%-\n", "")
M._update_task(data)
vim.api.nvim_set_option_value("modified", false, { buf = args.buf })
local notif_id = notify("updated task #" .. data.id, "info", { title = "ETA", style = 'fancy' })
end
---@class SelectionItem
---@field status string
---@field parent string | nil
---@field list string
---@field name string
---@field description string
---@field tags string[]
---@field id string
---@param picker snacks.Picker | nil
---@param item SelectionItem
M._on_select_task = function(picker, item)
local notif_id = notify("opening task #" .. item.id, "info", { title = "ETA", style = 'fancy' })
local new_name = "[ClickUp] " .. item.name
local new_buf_no = vim.api.nvim_create_buf(true, false)
local status, _ = pcall(vim.api.nvim_buf_set_name, new_buf_no, new_name)
if status then
-- vim.api.nvim_set_option_value("buftype", "nofile", { buf = new_buf_no })
vim.api.nvim_set_option_value("filetype", "markdown", { buf = new_buf_no })
vim.api.nvim_create_autocmd({ "BufWriteCmd" }, { buffer = new_buf_no, callback = M._on_tempbuf_write })
local content = { "---" }
local to_dump = {}
for _, k in ipairs({ "id", "name", "status", "tags", "list", "parent", "dependencies" }) do
to_dump[k] = item[k]
end
local yaml_string = M.table_to_yaml(to_dump) or ""
for line in yaml_string:gmatch("([^\n]*)\n?") do
table.insert(content, line)
end
-- table.insert(content, "}")
table.insert(content, "---")
for line in string.gmatch(item.description, "[^\r\n]+") do
table.insert(content, line)
end
vim.api.nvim_buf_set_lines(new_buf_no, 0, 0, false, content)
vim.api.nvim_set_option_value("modified", false, { buf = new_buf_no })
vim.api.nvim_win_set_buf(0, new_buf_no)
else
vim.api.nvim_buf_delete(new_buf_no, {})
if picker then
picker:close()
end
local to_open = vim.fn.bufnr(new_name)
vim.api.nvim_win_set_buf(0, to_open)
end
end
---@param item SelectionItem
M._item_format = function(item, _)
local ret = {}
local hl = "SnacksPickerComment"
if item.status == "in progress" then
hl = "@method"
elseif item.status == "selected for development" then
hl = "@constant.builtin"
elseif item.status == "in review" then
hl = "@keyword"
elseif item.status == "done" then
hl = "@variable.builtin"
end
if item.parent ~= vim.NIL then
ret[#ret + 1] = { "󰘍 ", "SnacksPickerComment" }
end
ret[#ret + 1] = { item.name, hl }
for _, v in ipairs(item.tags) do
ret[#ret + 1] = { " #" .. v, "SnacksPickerComment" }
end
ret[#ret + 1] = {
" 󰻾" .. item.id,
"SnacksPickerComment"
}
return ret
end
---@param list table[]
---@param keys string[]
M.retrieve_subkeys = function(list, keys)
local ret = {}
local interm = list
for _, key in ipairs(keys) do
ret = {}
for _, item in ipairs(interm) do
table.insert(ret, item[key])
end
interm = ret
end
return ret
end
---@param id string
M.open_task = function(id)
if id:match("[a-z0-9]+") then
local notif_id = notify("opening #" .. id, "info", { timeout = 1000, title = "ETA", style = 'fancy' })
local t = clickup.task(M.clickup_session, id)
---@type SelectionItem
local item = {
idx = nil,
id = t.id,
text = t.name .. t.id .. t.markdown_description,
name = t.name,
tags = M.retrieve_subkeys(t.tags, { "name" }),
status = t.status.status,
parent = t.parent,
list = t.list.name,
description = t.markdown_description,
preview = {
ft = "markdown",
},
action = nil
}
pcall(M._on_select_task, nil, item)
else
error("invalid id format")
end
end
---@param data vim.api.keyset.create_user_command.command_args
M.select_task = function(data)
local notif_id = notify("fetching tasks", "warn", { timeout = 10000, title = "ETA", style = 'fancy' })
local args = data.fargs
if args[1] == "start" then
return
end
if not pcall(M.open_task, vim.fn.expand("<cword>")) then
---@type SelectionItem[]
local items = {}
local tasks = clickup.latest_tasks(M.clickup_session)
for tix, t in ipairs(tasks) do
if string.sub(t.name, -7, -1) ~= "Absence" then
local preview_frontmatter = ""
local deps = {}
for _, dependency in ipairs(t.dependencies) do
table.insert(deps, dependency.depends_on)
end
---@type SelectionItem
local prepared = {
idx = tix,
id = t.id,
text = t.name .. t.id .. t.markdown_description,
name = t.name,
tags = M.retrieve_subkeys(t.tags, { "name" }),
status = t.status.status,
parent = t.parent,
list = t.list.name,
description = t.markdown_description,
dependencies = deps,
preview = {
ft = "markdown",
},
action = M._on_select_task
}
for _, k in ipairs({ "id", "name", "status", "tags", "parent" }) do
preview_frontmatter = preview_frontmatter .. "\n" .. k .. ": " .. vim.json.encode(prepared[k])
end
prepared.preview.text = "---" .. preview_frontmatter .. "\n---\n" .. t.markdown_description
table.insert(items, #items + 1, prepared)
end
end
require("snacks.notifier").hide(notif_id)
require("snacks.picker").pick({
title = "Select Task",
format = M._item_format,
preview = "preview",
confirm = M._on_select_task,
items = items
})
end
end
---@param data vim.api.keyset.create_user_command.command_args
M.mr_from_task = function(data)
if data.args then
notify(data.args, "info", { title = "ETA" })
end
end
return M
-40
View File
@@ -1,40 +0,0 @@
local notify = require("snacks.notifier").notify
local helpers = require("eta.helpers")
local M = {}
---@class eta.solidtime.Session: eta.Session
---@field auth string personal access token in the form of `Bearer <TOKEN>`
---@field org string organization id
---@class eta.solidtime.User
---@field name string
---@field id string
---@param session eta.solidtime.Session
---@return eta.solidtime.User
M.get_user = function(session)
return helpers.request(
"get",
session,
"/users/me",
{}
).data
end
---@param session eta.solidtime.Session
---@param task eta.clickup.Task
---@param project? string
M.start_new = function(session, task, project)
local req = helpers.request(
"post",
session,
"/organizations/" .. session.org .. "/time-entries",
{
billable = false,
member_id = M.get_user(session).id,
start = os.date("%Y-%m-%dT%H:%M:%SZ")
}
)
end
return M
-79
View File
@@ -1,79 +0,0 @@
-- 1. Map each status to a distinct color, Nerd Font icon, and styling
local status_configs = {
["backlog"] = { fg = "#7f8c8d", icon = "", bold = false }, -- Clipboard List
["selected for development"] = { fg = "#3498db", icon = "󰓾 ", bold = false }, -- Target / Bullseye
["in progress"] = { fg = "#f1c40f", icon = "󱐋 ", bold = true }, -- Lightning Bolt
["in review"] = { fg = "#9b59b6", icon = "󰈈 ", bold = true }, -- Eye / Review
["on hold"] = { fg = "#e67e22", icon = "󰏤 ", bold = false }, -- Pause Circle
["done"] = { fg = "#2ecc71", icon = "󰄬 ", bold = true }, -- Checkmark
["closed"] = { fg = "#555555", icon = "󰅙 ", strikethrough = true }, -- Blocked / Minus Circle
}
-- 2. Dynamically build the Neovim Highlight Groups
for status, config in pairs(status_configs) do
local hl_name = "TaskStatus_" .. status:gsub(" ", "_")
vim.api.nvim_set_hl(0, hl_name, {
fg = config.fg,
bold = config.bold,
strikethrough = config.strikethrough
})
config.hl_group = hl_name
end
-- Subtle gray style for the task title
vim.api.nvim_set_hl(0, "TaskTitle", { fg = "#abb2bf", italic = true })
-- 3. Create a unique namespace
local task_hint_ns = vim.api.nvim_create_namespace("custom_task_inlay_hints")
-- 4. Hijack the native LSP Inlay Hint Renderer
local native_inlay_handler = vim.lsp.handlers["textDocument/inlayHint"]
vim.lsp.handlers["textDocument/inlayHint"] = function(err, result, ctx, config)
if err or not result then
return native_inlay_handler(err, result, ctx, config)
end
local standard_hints = {}
local bufnr = ctx.bufnr or 0
if vim.api.nvim_buf_is_loaded(bufnr) then
vim.api.nvim_buf_clear_namespace(bufnr, task_hint_ns, 0, -1)
end
for _, hint in ipairs(result) do
local raw_label = ""
if type(hint.label) == "string" then
raw_label = hint.label
elseif type(hint.label) == "table" then
for _, part in ipairs(hint.label) do
raw_label = raw_label .. part.value
end
end
-- Match format: [<status>] <title>
local status, title = raw_label:match("^%[(.-)%]%s*(.*)$")
local norm_status = status and status:lower() or ""
if status_configs[norm_status] then
local cfg = status_configs[norm_status]
-- Multi-colored array using the clean Nerd Font icons
local virtual_text_chunks = {
{ " ── ", "Comment" },
{ cfg.icon, cfg.hl_group }, -- Nerd Font Symbol
{ "[" .. status:upper() .. "] ", cfg.hl_group }, -- Colored Status text
{ title, "TaskTitle" } -- Muted task name
}
vim.api.nvim_buf_set_extmark(bufnr, task_hint_ns, hint.position.line, hint.position.character, {
virt_text = virtual_text_chunks,
virt_text_pos = "inline", -- Swap to "eol" if it disrupts typing alignment
})
else
table.insert(standard_hints, hint)
end
end
return native_inlay_handler(err, standard_hints, ctx, config)
end
-169
View File
@@ -1,169 +0,0 @@
local eta = require("eta")
local eta_clickup = require("eta.clickup")
vim.api.nvim_create_user_command("ETA", eta.select_task, { nargs = "?" })
---@type eta.clickup.Session
eta.clickup_session = {
auth = os.getenv("CLICKUP_AUTH") or "",
user = os.getenv("CLICKUP_USER_ID") or "",
workspace = os.getenv("CLICKUP_WORKSPACE_ID") or "",
base_url = "https://api.clickup.com/api/v2",
default_reviewer = os.getenv("CLICKUP_DEFAULT_REVIEWER") or "",
}
---@type eta.gitlab.Session
eta.gitlab_session = {
auth = os.getenv("GITLAB_AUTH") or "",
base_url = "https://git.extoll.de/api/v4",
}
---@type eta.solidtime.Session
eta.solidtime_session = {
auth = os.getenv("SOLIDTIME_AUTH") or "",
org = os.getenv("SOLIDTIME_ORG") or "",
base_url = os.getenv("SOLIDTIME_URL") or "",
user = os.getenv("SOLIDTIME_USER") or "",
}
eta.ext = {
ns = vim.api.nvim_create_namespace("eta"),
}
vim.api.nvim_set_hl(0, "EtaHlTagKey", { fg = "#a6adc8", bg = "bg", bold = true })
vim.api.nvim_set_hl(0, "EtaHlTagVal", { bg = "bg", fg = "#a6adc8" })
local special_val_hl_cases = {
fail = "#f38ba8",
failed = "#f38ba8",
wontfix = "#f38ba8",
error = "#f38ba8",
err = "#f38ba8",
problem = "#f38ba8",
success = "#a6e3a1",
succeeded = "#a6e3a1",
ok = "#a6e3a1",
running = "#f9e2af",
in_progress = "#f9e2af",
progress = "#f9e2af",
warning = "#f9e2af",
warn = "#f9e2af",
in_review = "#94e2d5",
review = "#94e2d5",
selected = "#eba0ac",
selected_for_development = "#eba0ac",
backlog = "#7f849c",
}
local special_key_hl_cases = {
project = "#fab387",
mr = "#fab387",
issue = "#fab387",
task = "#b4befe",
lib = "#89b4fa",
library = "#89b4fa",
cell = "#74c7ec",
run = "#89dceb",
}
for special, special_hl in pairs(special_val_hl_cases) do
vim.api.nvim_set_hl(0, "EtaHlTagVal" .. special, { bg = "bg", fg = special_hl })
vim.api.nvim_set_hl(0, "EtaHlTagKey" .. special, { bg = "bg", fg = special_hl })
end
for special, special_hl in pairs(special_key_hl_cases) do
vim.api.nvim_set_hl(0, "EtaHlTagVal" .. special, { bg = "bg", fg = special_hl })
vim.api.nvim_set_hl(0, "EtaHlTagKey" .. special, { bg = "bg", fg = special_hl })
end
vim.api.nvim_create_autocmd({ "CursorHold" }, {
callback = function(args)
local eid = 1;
for lix, line in ipairs(vim.api.nvim_buf_get_lines(args.buf, 0, -1, false)) do
for match in line:gmatch("#[a-z0-9_-]+/%S+") do
local offset, mend = line:find(match, 1, true)
local start, colend = line:find("/%s*", offset)
local val = line:sub(colend + 1 or 0, mend):gsub("%W", "_")
local key = line:sub(offset + 1, start - 1):gsub("%W", "_")
local vhl = "EtaHlTagVal"
local khl = "EtaHlTagKey"
for case, _ in pairs(special_key_hl_cases) do
if case == key then
vhl = vhl .. key
khl = khl .. key
end
end
for case, _ in pairs(special_val_hl_cases) do
if case == val then
vhl = vhl .. val
khl = khl .. val
end
end
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, colend or 0, {
id = eid,
end_row = lix - 1,
end_col = mend,
hl_group = { "EtaHlTagVal", vhl },
})
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, offset - 1, {
id = eid + 1,
end_row = lix - 1,
end_col = colend - 1,
hl_group = { "EtaHlTagKey", khl },
})
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, start - 1, {
id = eid + 2,
end_row = lix - 1,
end_col = colend,
conceal = " ",
hl_group = { khl },
})
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, offset - 1, {
id = eid + 3,
end_row = lix - 1,
end_col = offset,
conceal = " ",
hl_group = { khl },
})
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, mend or 0, {
id = eid + 4,
end_row = lix - 1,
end_col = mend,
conceal = "",
hl_group = { khl },
priority = 200,
})
for special_key, special_key_repl in pairs({
mr = "",
issue = "",
task = "",
created = "󰙴 ",
closed = "󰸞 ",
project = "󰏖 ",
branch = "󰘬",
lib = "󱍵 ",
library = "󱍵 ",
cell = "󰌕 ",
run = "󰫍",
}) do
if key == special_key then
vim.api.nvim_buf_set_extmark(args.buf, eta.ext.ns, lix - 1, offset, {
id = eid + 5,
end_row = lix - 1,
end_col = start,
conceal = special_key_repl,
hl_group = { khl },
priority = 200,
})
end
end
eid = eid + 6
end
end
end
})
+6 -20
View File
@@ -1,25 +1,11 @@
[build-system]
requires = ['setuptools>=57', "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "eta-lsp"
name = "mrpy-nvim"
version = "0.1.0"
description = "EXTOLL Task Access"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
"pygls~=2.0",
"pydantic~=2.12",
"requests~=2.32",
"pydantic-settings~=2.12",
"pynvim~=0.6.0",
"requests>=2.32.5",
]
authors = [
{ name = "Patrick Nisble", email = "acereca@outlook.de"},
]
[project.scripts]
eta-lsp = "eta.main:main"
[tool.setuptools.packages.find]
include = ['eta']
namespaces = false
+85 -160
View File
@@ -1,105 +1,97 @@
from collections.abc import Sequence
from dataclasses import dataclass, field, fields
from dataclasses import dataclass, field
from json import loads
from os import environ
from typing import Any, Callable, Self, TypedDict
from urllib.error import HTTPError
from typing import Any, Callable
from .hints import JSONDataMap
from .requests import delete, get, put, post
from .env import EnvVar
from pydantic import AliasPath, BaseModel, Field, field_validator
from urllib.request import Request, urlopen
class TaskRespCheckItemDict(TypedDict):
name: str
resolved: int
def get(
url: str,
query_params: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
with urlopen(
Request(
url + ("?" + "&".join(f"{k}={v}" for k, v in query_params.items()))
if query_params
else "",
headers=headers or {},
method="GET",
),
) as resp:
return resp.read().decode("utf-8")
class TaskRespCheckDict(TypedDict):
name: str
items: list[TaskRespCheckItemDict]
def put(
url: str,
data: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
with urlopen(
Request(
url,
str(data).encode(),
headers=headers or {},
method="PUT",
),
) as resp:
return resp.read().decode("utf-8")
class TaskRespDict(TypedDict):
class ClickupTask(BaseModel):
"""fields marked with `exclude=True` will not be pushed for updates"""
name: str
markdown_description: str
status: dict[str, str]
status: str = Field(validation_alias=AliasPath("status", "status"))
id: str
assignees: list[dict[str, str]]
tags: list[dict[str, str]]
parent: str | None
locations: list[dict[str, str]]
checklists: list[TaskRespCheckDict]
list: dict[str, str]
@dataclass(kw_only=True)
class ClickupTask:
"""fields marked with ``repr=False`` will not be pushed for updates"""
name: str
status: str
id: str | None = field(default=None, repr=False)
assignees: list[str] | None = field(default=None, repr=False)
tags: list[str] | None = field(default=None, repr=False)
parent_list: str = field(repr=False)
locations: list[str] | None = field(default=None, repr=False)
checklists: dict[str, dict[str, bool]] | None = field(default=None, repr=False)
parent: str | None = field(default=None, repr=False)
markdown_content: str
id: str = Field(exclude=True)
assignees: list[str] = Field(exclude=True)
tags: list[str] = Field(exclude=True)
parent: str | None = Field(None, exclude=True)
parent_list: str = Field(validation_alias=AliasPath("list", "id"), exclude=True)
locations: list[str] = Field(exclude=True)
checklists: dict[str, bool] = Field(exclude=True)
@field_validator("checklists", mode="before")
@classmethod
def from_resp_data(cls, resp_data_raw: dict[str, Any]) -> Self:
resp_data = TaskRespDict(**resp_data_raw)
return cls(
name=resp_data["name"],
markdown_content=resp_data["markdown_description"],
status=resp_data["status"]["status"],
id=resp_data["id"],
assignees=cls.convert_assignees(resp_data["assignees"]),
tags=cls.convert_simple_list_with_names(resp_data["tags"]),
parent=resp_data.get("parent"),
parent_list=resp_data["list"]["id"],
locations=cls.convert_simple_list_with_names(resp_data["locations"]),
checklists={
tlist["name"]: {t["name"]: bool(t["resolved"]) for t in tlist["items"]}
for tlist in resp_data["checklists"]
},
)
@classmethod
def convert_checklists(cls, content: Sequence[Any]) -> dict[str, bool]:
def convert_checklists(cls, content: list[Any]) -> dict[str, bool]:
return {
entry["name"]: entry["resolved"]
for checklist in content
for entry in checklist["items"]
}
@field_validator("assignees", mode="before")
@classmethod
def convert_assignees(cls, content: Sequence[str | dict[str, str]]) -> list[str]:
def convert_assignees(cls, content: list[str | dict[str, Any]]) -> list[str]:
return [(e if isinstance(e, str) else e["username"]) for e in content]
@field_validator("tags", "locations", mode="before")
@classmethod
def convert_simple_list_with_names(
cls,
content: Sequence[str | dict[str, str]],
content: list[str | dict[str, Any]],
) -> list[str]:
return [(e if isinstance(e, str) else e["name"]) for e in content]
@property
def updateables(self) -> dict[str, Any]:
return {f.name: getattr(self, f.name, None) for f in fields(type(self)) if f.repr}
return {
k: getattr(self, k, None)
for k, v in type(self).model_fields.items()
if not v.exclude
}
@property
def showables(self) -> dict[str, Any]:
return {
dcf.name: getattr(self, dcf.name, ...)
for dcf in fields(self)
if getattr(self, dcf.name, ...) is not None
k: getattr(self, k, None)
for k in type(self).model_fields
if k != "markdown_description"
}
@property
@@ -109,54 +101,34 @@ class ClickupTask:
if self.parent:
ret += " \033[32m 󰙅 "
ret += self.name
if self.tags:
ret += "".join(f" #{t}" for t in self.tags)
ret += f" (#{self.id})"
ret += f"{self.name} (#{self.id})"
return ret
def get_env_var(var_name: str) -> Callable[[], str]:
return lambda var=var_name: environ[var]
@dataclass
class ClickupSession:
auth_key: str = field(
default_factory=EnvVar(
"CLICKUP_AUTH",
"clickup auth token is required to be set",
)
)
workspace_id: str = field(
default_factory=EnvVar(
"CLICKUP_WORKSPACE_ID",
"clickup workspace id is required to be set",
)
)
user_id: str = field(
default_factory=EnvVar(
"CLICKUP_USER_ID",
"clickup user id is required to be set",
)
)
auth_key: str = field(default_factory=get_env_var("CLICKUP_AUTH"))
workspace_id: str = field(default_factory=get_env_var("CLICKUP_WORKSPACE_ID"))
user_id: str = field(default_factory=get_env_var("CLICKUP_USER_ID"))
base_url: str = "https://api.clickup.com/api/v2"
def _get(self, endpoint: str, **query_params: str) -> JSONDataMap:
try:
raw_data = get(
self.base_url + endpoint,
query_params,
headers={
"accept": "application/json",
"Authorization": self.auth_key,
},
)
return loads(raw_data)
except HTTPError as e:
e.add_note(e.url)
raise
def _get(self, endpoint: str, **query_params: Any) -> dict[str, Any]:
raw_data = get(
self.base_url + endpoint,
query_params,
headers={
"accept": "application/json",
"Authorization": self.auth_key,
},
)
return loads(raw_data)
def _put(self, endpoint: str, **body_params: str) -> JSONDataMap:
def _put(self, endpoint: str, **body_params: Any) -> dict[str, Any]:
raw_data = put(
self.base_url + endpoint,
body_params,
@@ -168,63 +140,16 @@ class ClickupSession:
)
return loads(raw_data)
def _post(self, endpoint: str, **body_params: str) -> JSONDataMap:
raw_data = post(
self.base_url + endpoint,
body_params,
headers={
"accept": "application/json",
"content-type": "application/json",
"Authorization": self.auth_key,
},
)
return loads(raw_data)
def _delete(self, endpoint: str) -> JSONDataMap:
raw_data = delete(
self.base_url + endpoint,
headers={
"accept": "application/json",
"content-type": "application/json",
"Authorization": self.auth_key,
},
)
return loads(raw_data)
def get_tasks(self, **filters: str) -> list[ClickupTask]:
def get_tasks(self) -> list[ClickupTask]:
data = self._get(
f"/team/{self.workspace_id}/task",
**{
"subtask": "true",
"include_markdown_description": "true",
"assignees[]": self.user_id,
}
| filters,
).get("tasks", [])
if isinstance(data, list):
return [ClickupTask.from_resp_data(t) for t in data if isinstance(t, dict)]
return []
f"/team/{self.workspace_id}/task?subtasks=true&include_markdown_description=true&assignees[]={self.user_id}"
)
return [ClickupTask.model_validate(t) for t in data["tasks"]]
def get_task(self, task_id: str) -> ClickupTask:
return ClickupTask.from_resp_data(
self._get(
f"/task/{task_id}",
include_markdown_description="true",
),
return ClickupTask.model_validate(
self._get(f"/task/{task_id}?include_markdown_description=true")
)
def update(self, data: ClickupTask) -> None:
ret_task = self._put(f"/task/{data.id}", **data.updateables)
current_tags: set[str] = set(ret_task["tags"])
new_tags = set(data.tags)
for del_tag in current_tags - new_tags:
self._delete(f"/task/{data.id}/tag/{del_tag}")
for add_tag in new_tags - current_tags:
self._post(f"/task/{data.id}/tag/{add_tag}")
def create(self, data: ClickupTask) -> str:
ret_task = self._post(f"/list/{data.parent_list}/task", **data.updateables)
return str(ret_task["id"])
_ = self._put(f"/task/{data.id}", **data.updateables)
-24
View File
@@ -1,24 +0,0 @@
from dataclasses import dataclass
from os import environ
@dataclass
class EnvVar:
"""
Environment Variable fetcher for use in dataclass ``field(default_factory=...)``
>>> @dataclass
>>> class SomeDataclass:
... field_name: str = field(default_factory=EnvVar("SOME_VAR_NAME", "err msg"))
"""
var_name: str
err_msg: str = ""
def __call__(self) -> str:
try:
return environ[self.var_name]
except KeyError as e:
e.add_note(self.err_msg)
raise
-99
View File
@@ -1,99 +0,0 @@
from dataclasses import dataclass, field, fields
from functools import cached_property
from json import loads
from typing import Any
from pynvim import Nvim
from .requests import request
from .ui import Select
from .clickup import ClickupTask
from .env import EnvVar
@dataclass
class GitlabMergeRequest:
@property
def updateables(self) -> dict[str, Any]:
return {f.name: getattr(self, f.name, None) for f in fields(type(self)) if f.repr}
@dataclass(kw_only=True)
class GitlabIssue:
project: int | str = field(repr=False)
"""project id or url encoded path to project, e.g. `ope%2Fopepipeline`"""
title: str
assignee_id: int | None = None
description: str | None = None
labels: list[str] | None = None
milestone_id: int | None = None
@property
def updateables(self) -> dict[str, Any]:
return {f.name: getattr(self, f.name, None) for f in fields(type(self)) if f.repr}
@dataclass
class GitlabSession:
auth_key: str = field(
default_factory=EnvVar(
"GITLAB_AUTH",
"gitlab autho token is required to be set",
),
)
user_id: str = field(
default_factory=EnvVar(
"GITLAB_USER_ID",
"gitlab user id is required to be set",
),
)
base_url: str = "https://git.extoll.de/api/v4"
def _authed_request(
self,
method: str,
url: str,
data: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
return request(
url,
method,
data=data,
headers=(headers or {}) | {"PRIVATE_TOKEN": self.auth_key},
)
@cached_property
def projects(self) -> list[str]:
return [
p["path_with_namespace"]
for p in loads(self._authed_request("GET", self.base_url + "/projects"))
]
def new_issue(self, nvim: Nvim, clickup_task: ClickupTask) -> GitlabIssue:
issue_new = GitlabIssue(
project=Select(nvim, self.projects)(),
title=clickup_task.name,
assignee_id=int(self.user_id),
description=f"#{clickup_task.id}",
)
self._authed_request(
"POST",
self.base_url + f"/projects/{issue_new.project}/issues",
data=issue_new.updateables,
)
return issue_new
def new_mr(self, from_issue: GitlabIssue) -> GitlabMergeRequest:
mr_new = GitlabMergeRequest()
self._authed_request(
"POST",
self.base_url + f"/projects/{from_issue.project}/merge_requests",
data=mr_new.updateables,
)
return mr_new
+4 -8
View File
@@ -1,8 +1,4 @@
from __future__ import annotations
from typing import TypeAlias
JSONDataScalar: TypeAlias = str | None | float | bool
JSONDataList: TypeAlias = list["JSONDataScalar | JSONDataMap | JSONDataList"]
JSONDataMap: TypeAlias = dict[str, "JSONDataScalar | JSONDataList | JSONDataMap"]
JSONData: TypeAlias = "JSONDataMap | JSONDataList"
type JSONDataScalar = str | None | float | bool
type JSONDataList = list[JSONDataScalar | "JSONDataMap" | "JSONDataList"]
type JSONDataMap = dict[str, JSONDataScalar | JSONDataList | "JSONDataMap"]
type JSONData = JSONDataMap | JSONDataList
+21 -81
View File
@@ -1,13 +1,9 @@
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import Any
from pynvim import Nvim, command, plugin
from pynvim.api import Buffer
from .ui import Select
from .clickup import ClickupSession, ClickupTask
from .clickup import ClickupSession
from .hints import JSONData
from .yaml import load, dump
@@ -32,20 +28,18 @@ class MrPyPlugin:
"assignees": usernames_from_objs,
}
def select_task_id(self, tags: set[str] | None = None) -> None:
tasks = self.clickup.get_tasks(**({"tags[]": "&tags[]=".join(tags)} if tags else {}))
def select_task_id(self) -> None:
tasks = self.clickup.get_tasks()
task_names_by_id = [
{
"idx": tix + 1,
"id": t.id,
"text": t.short,
"name": t.name,
"tags": t.tags,
"status": t.status,
"is_child": bool(t.parent),
"preview": {
"text": dump(t.showables),
"ft": "yaml",
"text": f"---\n{dump(t.showables)}---\n{t.markdown_description}",
"ft": "markdown",
},
"action": f":Mrpy {t.id}",
}
@@ -76,12 +70,8 @@ class MrPyPlugin:
ret[#ret + 1] = { item.name, hl }
for _, v in ipairs(item.tags) do
ret[#ret + 1] = { " #" .. v, "SnacksPickerComment" }
end
ret[#ret + 1] = {
" 󰻾" .. item.id,
" (#" .. item.id .. ")",
"SnacksPickerComment"
}
return ret
@@ -101,36 +91,36 @@ class MrPyPlugin:
task = self.clickup.get_task(task_id)
temp_buf: Buffer = self.nvim.api.create_buf(True, False)
self.nvim.api.buf_set_name(temp_buf, f"[ClickUp] {task.name}")
self.nvim.buffers[temp_buf.number].options["filetype"] = "yaml"
self.nvim.buffers[temp_buf.number].options["filetype"] = "markdown"
self.nvim.api.create_autocmd(
["BufWriteCmd"],
{"buffer": temp_buf.number, "command": "MrpyPush " + str(temp_buf.number)},
)
content = [
f"# yaml-language-server: $schema={Path(__file__).parent.parent.parent.parent}/schema.json ",
"---",
]
content = ["---"]
content.extend(
dump(
task.showables,
).splitlines()
)
content.append("---")
content.extend(task.markdown_description.splitlines())
self.nvim.api.buf_set_lines(temp_buf, 0, 0, False, content)
self.nvim.buffers[temp_buf.number].options["modified"] = False
self.nvim.api.win_set_buf(0, temp_buf)
@command("Mrpy", nargs="?")
def entry(self, args: Sequence[str] = ()) -> None:
match args:
case (str() as task_id,):
try:
self.open_task_buffer(task_id)
except:
self.select_task_id({task_id})
case tags:
self.select_task_id(set(tags))
self.open_task_buffer(task_id)
case ():
self.select_task_id()
case _:
pass
@command("MrpyPush", nargs="?")
def on_vbuf_write(self, args: Sequence[str] = ()) -> None:
match args:
case "0", *_:
@@ -142,64 +132,14 @@ class MrPyPlugin:
try:
a = self.nvim.buffers[buf_no][0:-1]
fm = "\n".join(a)
_, fm, *text = "\n".join(a).split("---\n")
data = load(fm)
assert isinstance(data, dict)
data["markdown_content"] = data["markdown_content"].strip() + "\n"
data["markdown_description"] = "\n\n".join(text)
self.clickup.update(ClickupTask(**data))
self.nvim.err_write(str(data) + "\n")
# self.clickup.update(data)
self.nvim.buffers[buf_no].options["modified"] = False
except ValueError:
pass
def on_new_task(self) -> None:
task = ClickupTask(name="", status="backlog", markdown_content="", parent_list="")
temp_buf: Buffer = self.nvim.api.create_buf(True, False)
self.nvim.api.buf_set_name(temp_buf, f"[ClickUp] New Task")
self.nvim.buffers[temp_buf.number].options["filetype"] = "yaml"
self.nvim.api.create_autocmd(
["BufWriteCmd"],
{"buffer": temp_buf.number, "command": "MrpyPushNew " + str(temp_buf.number)},
)
content = [
f"# yaml-language-server: $schema={Path(__file__).parent.parent.parent.parent}/schema.json ",
"---",
]
content.extend(
dump(
task.showables,
).splitlines()
)
self.nvim.api.buf_set_lines(temp_buf, 0, 0, False, content)
self.nvim.buffers[temp_buf.number].options["modified"] = False
self.nvim.api.win_set_buf(0, temp_buf)
def on_new_vbuf_write(self, buf_no: Sequence[Any] = ()) -> None:
buf_no = int(buf_no[0])
try:
a = self.nvim.buffers[buf_no][0:-1]
fm = "\n".join(a)
data = load(fm)
assert isinstance(data, dict)
data["markdown_content"] = data["markdown_content"].strip() + "\n"
new_task = ClickupTask(**data)
new_task_id = self.clickup.create(new_task)
self.nvim.buffers[buf_no].options["modified"] = False
self.nvim.command("bdelete " + str(buf_no))
self.entry([new_task_id])
except ValueError:
pass
def handle_test(self, choice: Any) -> None:
self.nvim.err_write(str(choice))
def test(self) -> None:
self.nvim.ui.select(["a", "b"], {"prompt": "hi"}, self.handle_test)
-64
View File
@@ -1,64 +0,0 @@
from json import dumps
from ssl import SSLContext, create_default_context
from urllib.error import HTTPError
from urllib.request import Request, urlopen
from urllib.parse import urlencode
def request(
url: str,
method: str,
query_params: dict[str, str] | None = None,
data: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
try:
with urlopen(
Request(
url
+ (
("?" + "&".join(f"{k}={v}" for k, v in query_params.items()))
if query_params
else ""
),
dumps(data).encode() if data else None,
headers=headers or {},
method=method,
),
context=create_default_context(),
) as resp:
return resp.read().decode("utf-8")
except HTTPError as e:
e.add_note(e.url)
raise
def get(
url: str,
query_params: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
return request(url, "GET", query_params=query_params, headers=headers)
def post(
url: str,
data: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
return request(url, "POST", data=data, headers=headers)
def put(
url: str,
data: dict[str, str] | None = None,
headers: dict[str, str] | None = None,
) -> str:
return request(url, "PUT", data=data, headers=headers)
def delete(
url: str,
headers: dict[str, str] | None = None,
) -> str:
return request(url, "DELETE", headers=headers)
-17
View File
@@ -1,17 +0,0 @@
from dataclasses import dataclass
from pynvim import Nvim
@dataclass
class Select:
nvim: Nvim
options: list[str]
def __call__(self) -> str:
return self.nvim.call(
"""
return vim.fn.inputlist(...)
""",
self.options,
async_=False,
)
+7 -49
View File
@@ -14,44 +14,20 @@ def dump_scalar(entry: hints.JSONDataScalar) -> str:
case False:
return "false\n"
case _:
return f"{entry}\n"
return f"{entry:s}\n"
def dump(obj: hints.JSONDataMap) -> str:
def dump(obj: dict[str, hints.JSONDataScalar | list[hints.JSONDataScalar]]) -> str:
ret = ""
for key, value in obj.items():
ret += key
ret += ":"
match value:
case []:
ret += " []\n"
case {}:
ret += " {}\n"
case "":
ret += ' ""\n'
case list() as entries:
ret += "\n"
for entry in entries:
match entry:
case dict():
subdump = dump(entry)
ret += "\n".join(" " + l for l in subdump.splitlines())
case list():
pass
case _:
ret += f" - {dump_scalar(entry)}"
case dict() as substruct:
ret += "\n"
subdump = dump(substruct)
ret += "\n".join(" " + l for l in subdump.splitlines()) + "\n"
case str() as mlstring if key == "markdown_content":
if mlstring.strip() == "" or dump_scalar(mlstring).strip() == "null":
ret += ' ""\n'
else:
indented = "\n".join((" " + l) for l in dump_scalar(mlstring).splitlines())
ret += f" >\n{indented}\n"
ret += f" - {dump_scalar(entry)}"
case entry:
ret += f" {dump_scalar(entry)}"
@@ -77,10 +53,6 @@ def load(content: str) -> hints.JSONData:
... key10:
... - str
... - "str"
... key11: >
... * a
... * b:
... * c
... '''
>>> yaml_parsed = load(yaml)
>>> compare = {
@@ -94,8 +66,8 @@ def load(content: str) -> hints.JSONData:
... 'key8': False,
... 'key9': [1, 23.2],
... 'key10': ['str', 'str'],
... 'key11': "* a\\n* b:\\n * c",
... }
>>> {k: yaml_parsed.get(k, None) for k in compare if compare[k] != yaml_parsed[k]}
{}
"""
@@ -103,34 +75,20 @@ def load(content: str) -> hints.JSONData:
ret = {}
key = None
value = None
mlstring = False
for line in sub(r"(:( >)?)[\s\n]*", r"\1\n ", dedent(content).strip()).splitlines():
if line.startswith("#") or line.strip() == "---":
continue
elif line.endswith(":"):
for line in sub(r":[\s\n]*", ":\n ", dedent(content).strip()).splitlines():
if line.endswith(":"):
if key:
ret[key] = value
value = None
mlstring = False
key = line.removesuffix(":")
elif line.endswith(": >"):
if key:
ret[key] = value
value = None
mlstring = True
key = line.removesuffix(": >")
elif line.startswith(" -") and not mlstring:
elif line.startswith(" -"):
value = value or []
try:
parsed = loads(line.removeprefix(" -").strip())
except:
parsed = loads('"' + line.removeprefix(" -").strip() + '"')
value.append(parsed)
elif line.startswith(" ") and mlstring:
value = value or ""
value += line.removeprefix(" ") + "\n"
else:
try:
value = loads(line.strip())
-44
View File
@@ -1,44 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "ETA yaml frontmatter",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"backlog",
"selected for development",
"in progress",
"in review",
"on hold",
"done",
"closed"
]
},
"markdown_content": {
"type": "string"
},
"id": {
"type": "string",
"pattern": "^[a-z0-9]+$"
},
"parent_list": {
"anyOf": [
{
"const": 900400316794,
"description": "OPE Tooling"
}
]
}
},
"required": [
"name",
"status",
"markdown_content",
"parent_list"
],
"title": "clickup task",
"type": "object"
}
Generated
+190 -110
View File
@@ -1,69 +1,31 @@
version = 1
revision = 3
requires-python = ">=3.11"
requires-python = ">=3.12"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "attrs"
version = "25.4.0"
source = { registry = "https://pypi.extoll.de/" }
sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
]
[[package]]
name = "cattrs"
version = "25.3.0"
source = { registry = "https://pypi.extoll.de/" }
dependencies = [
{ name = "attrs" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6e/00/2432bb2d445b39b5407f0a90e01b9a271475eea7caf913d7a86bcb956385/cattrs-25.3.0.tar.gz", hash = "sha256:1ac88d9e5eda10436c4517e390a4142d88638fe682c436c93db7ce4a277b884a", size = 509321, upload-time = "2025-10-07T12:26:08.737Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/2b/a40e1488fdfa02d3f9a653a61a5935ea08b3c2225ee818db6a76c7ba9695/cattrs-25.3.0-py3-none-any.whl", hash = "sha256:9896e84e0a5bf723bc7b4b68f4481785367ce07a8a02e7e9ee6eb2819bc306ff", size = 70738, upload-time = "2025-10-07T12:26:06.603Z" },
]
[[package]]
name = "certifi"
version = "2026.1.4"
source = { registry = "https://pypi.extoll.de/" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
version = "2025.11.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
{ url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.4"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
{ url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
{ url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
{ url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
{ url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
{ url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
{ url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
{ url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
{ url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
{ url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
{ url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
{ url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
{ url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
{ url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
@@ -116,48 +78,120 @@ wheels = [
]
[[package]]
name = "eta-lsp"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "pydantic" },
{ name = "pygls" },
{ name = "requests" },
]
[package.metadata]
requires-dist = [
{ name = "pydantic", specifier = "~=2.12" },
{ name = "pygls", specifier = "~=2.0" },
{ name = "requests", specifier = "~=2.32" },
name = "greenlet"
version = "3.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" },
{ url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" },
{ url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" },
{ url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" },
{ url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" },
{ url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" },
{ url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" },
{ url = "https://files.pythonhosted.org/packages/6c/79/3912a94cf27ec503e51ba493692d6db1e3cd8ac7ac52b0b47c8e33d7f4f9/greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39", size = 301964, upload-time = "2025-12-04T14:36:58.316Z" },
{ url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" },
{ url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" },
{ url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" },
{ url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" },
{ url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" },
{ url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" },
{ url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" },
{ url = "https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38", size = 301833, upload-time = "2025-12-04T14:32:23.929Z" },
{ url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" },
{ url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" },
{ url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" },
{ url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" },
{ url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" },
{ url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" },
{ url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" },
{ url = "https://files.pythonhosted.org/packages/7c/9a/9030e6f9aa8fd7808e9c31ba4c38f87c4f8ec324ee67431d181fe396d705/greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170", size = 305387, upload-time = "2025-12-04T14:26:51.063Z" },
{ url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" },
{ url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" },
{ url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" },
{ url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" },
{ url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" },
{ url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" },
{ url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" },
]
[[package]]
name = "idna"
version = "3.11"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "lsprotocol"
version = "2025.0.0"
source = { registry = "https://pypi.extoll.de/" }
name = "mrpy-nvim"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "attrs" },
{ name = "cattrs" },
{ name = "pydantic-settings" },
{ name = "pynvim" },
{ name = "requests" },
{ name = "ruamel-yaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/26/67b84e6ec1402f0e6764ef3d2a0aaf9a79522cc1d37738f4e5bb0b21521a/lsprotocol-2025.0.0.tar.gz", hash = "sha256:e879da2b9301e82cfc3e60d805630487ac2f7ab17492f4f5ba5aaba94fe56c29", size = 74896, upload-time = "2025-06-17T21:30:18.156Z" }
[package.metadata]
requires-dist = [
{ name = "pydantic-settings", specifier = "~=2.12" },
{ name = "pynvim", specifier = "~=0.6.0" },
{ name = "requests", specifier = ">=2.32.5" },
{ name = "ruamel-yaml", specifier = "~=0.18.17" },
]
[[package]]
name = "msgpack"
version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/f0/92f2d609d6642b5f30cb50a885d2bf1483301c69d5786286500d15651ef2/lsprotocol-2025.0.0-py3-none-any.whl", hash = "sha256:f9d78f25221f2a60eaa4a96d3b4ffae011b107537facee61d3da3313880995c7", size = 76250, upload-time = "2025-06-17T21:30:19.455Z" },
{ url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" },
{ url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" },
{ url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" },
{ url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" },
{ url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" },
{ url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" },
{ url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" },
{ url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" },
{ url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" },
{ url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" },
{ url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" },
{ url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" },
{ url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" },
{ url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" },
{ url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" },
{ url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" },
{ url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" },
{ url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" },
{ url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" },
{ url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" },
{ url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" },
{ url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" },
{ url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" },
{ url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" },
{ url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" },
{ url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" },
{ url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" },
{ url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" },
{ url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" },
{ url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" },
{ url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" },
{ url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" },
{ url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" },
{ url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" },
{ url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" },
{ url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" },
]
[[package]]
name = "pydantic"
version = "2.12.5"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
@@ -172,26 +206,12 @@ wheels = [
[[package]]
name = "pydantic-core"
version = "2.41.5"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
{ url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
{ url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
{ url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
{ url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
{ url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
{ url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
{ url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
{ url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
{ url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
{ url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
{ url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
{ url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
{ url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
@@ -248,42 +268,52 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
{ url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
{ url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
{ url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
{ url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
{ url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
{ url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
{ url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
{ url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
{ url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
{ url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
{ url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
]
[[package]]
name = "pygls"
version = "2.0.0"
source = { registry = "https://pypi.extoll.de/" }
name = "pydantic-settings"
version = "2.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "cattrs" },
{ name = "lsprotocol" },
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/87/50/2bfc32f3acbc8941042919b59c9f592291127b55d7331b72e67ce7b62f08/pygls-2.0.0.tar.gz", hash = "sha256:99accd03de1ca76fe1e7e317f0968ebccf7b9955afed6e2e3e188606a20b4f07", size = 55796, upload-time = "2025-10-17T19:22:47.925Z" }
sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/09/14feafc13bebb9c85b29b374889c1549d3700cb572f2d43a1bb940d70315/pygls-2.0.0-py3-none-any.whl", hash = "sha256:b4e54bba806f76781017ded8fd07463b98670f959042c44170cd362088b200cc", size = 69533, upload-time = "2025-10-17T19:22:46.63Z" },
{ url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" },
]
[[package]]
name = "pynvim"
version = "0.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "platform_python_implementation != 'PyPy'" },
{ name = "msgpack" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/d7/c4412e6219661fd8689cdd9553988f8ea38c151067d70c49436977688aa9/pynvim-0.6.0.tar.gz", hash = "sha256:0ffcb879322d08f9e9061e1123dd58ba3a5ccfbd4999bb1157bac525822aa590", size = 51478, upload-time = "2025-09-07T18:35:37.601Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/2d/c28b9293975ed9ee05e52f1d316cb402ba6edb232baa0497754a70182ca2/pynvim-0.6.0-py3-none-any.whl", hash = "sha256:29f92ff3fde9e52c263e6bfd7f30345be05bfc7b5c21484da1cc38a8d8e5e98f", size = 47633, upload-time = "2025-09-07T18:35:36.348Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
]
[[package]]
name = "requests"
version = "2.32.5"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
@@ -295,10 +325,60 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "ruamel-yaml"
version = "0.18.17"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ruamel-yaml-clib", marker = "python_full_version < '3.15' and platform_python_implementation == 'CPython'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3a/2b/7a1f1ebcd6b3f14febdc003e658778d81e76b40df2267904ee6b13f0c5c6/ruamel_yaml-0.18.17.tar.gz", hash = "sha256:9091cd6e2d93a3a4b157ddb8fabf348c3de7f1fb1381346d985b6b247dcd8d3c", size = 149602, upload-time = "2025-12-17T20:02:55.757Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/fe/b6045c782f1fd1ae317d2a6ca1884857ce5c20f59befe6ab25a8603c43a7/ruamel_yaml-0.18.17-py3-none-any.whl", hash = "sha256:9c8ba9eb3e793efdf924b60d521820869d5bf0cb9c6f1b82d82de8295e290b9d", size = 121594, upload-time = "2025-12-17T20:02:07.657Z" },
]
[[package]]
name = "ruamel-yaml-clib"
version = "0.2.15"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ea/97/60fda20e2fb54b83a61ae14648b0817c8f5d84a3821e40bfbdae1437026a/ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600", size = 225794, upload-time = "2025-11-16T16:12:59.761Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/72/4b/5fde11a0722d676e469d3d6f78c6a17591b9c7e0072ca359801c4bd17eee/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff", size = 149088, upload-time = "2025-11-16T16:13:22.836Z" },
{ url = "https://files.pythonhosted.org/packages/85/82/4d08ac65ecf0ef3b046421985e66301a242804eb9a62c93ca3437dc94ee0/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2", size = 134553, upload-time = "2025-11-16T16:13:24.151Z" },
{ url = "https://files.pythonhosted.org/packages/b9/cb/22366d68b280e281a932403b76da7a988108287adff2bfa5ce881200107a/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1", size = 737468, upload-time = "2025-11-16T20:22:47.335Z" },
{ url = "https://files.pythonhosted.org/packages/71/73/81230babf8c9e33770d43ed9056f603f6f5f9665aea4177a2c30ae48e3f3/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60", size = 753349, upload-time = "2025-11-16T16:13:26.269Z" },
{ url = "https://files.pythonhosted.org/packages/61/62/150c841f24cda9e30f588ef396ed83f64cfdc13b92d2f925bb96df337ba9/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9", size = 788211, upload-time = "2025-11-16T16:13:27.441Z" },
{ url = "https://files.pythonhosted.org/packages/30/93/e79bd9cbecc3267499d9ead919bd61f7ddf55d793fb5ef2b1d7d92444f35/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642", size = 743203, upload-time = "2025-11-16T16:13:28.671Z" },
{ url = "https://files.pythonhosted.org/packages/8d/06/1eb640065c3a27ce92d76157f8efddb184bd484ed2639b712396a20d6dce/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690", size = 747292, upload-time = "2025-11-16T20:22:48.584Z" },
{ url = "https://files.pythonhosted.org/packages/a5/21/ee353e882350beab65fcc47a91b6bdc512cace4358ee327af2962892ff16/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a", size = 771624, upload-time = "2025-11-16T16:13:29.853Z" },
{ url = "https://files.pythonhosted.org/packages/57/34/cc1b94057aa867c963ecf9ea92ac59198ec2ee3a8d22a126af0b4d4be712/ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144", size = 100342, upload-time = "2025-11-16T16:13:31.067Z" },
{ url = "https://files.pythonhosted.org/packages/b3/e5/8925a4208f131b218f9a7e459c0d6fcac8324ae35da269cb437894576366/ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc", size = 119013, upload-time = "2025-11-16T16:13:32.164Z" },
{ url = "https://files.pythonhosted.org/packages/17/5e/2f970ce4c573dc30c2f95825f2691c96d55560268ddc67603dc6ea2dd08e/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb", size = 147450, upload-time = "2025-11-16T16:13:33.542Z" },
{ url = "https://files.pythonhosted.org/packages/d6/03/a1baa5b94f71383913f21b96172fb3a2eb5576a4637729adbf7cd9f797f8/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471", size = 133139, upload-time = "2025-11-16T16:13:34.587Z" },
{ url = "https://files.pythonhosted.org/packages/dc/19/40d676802390f85784235a05788fd28940923382e3f8b943d25febbb98b7/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25", size = 731474, upload-time = "2025-11-16T20:22:49.934Z" },
{ url = "https://files.pythonhosted.org/packages/ce/bb/6ef5abfa43b48dd55c30d53e997f8f978722f02add61efba31380d73e42e/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a", size = 748047, upload-time = "2025-11-16T16:13:35.633Z" },
{ url = "https://files.pythonhosted.org/packages/ff/5d/e4f84c9c448613e12bd62e90b23aa127ea4c46b697f3d760acc32cb94f25/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf", size = 782129, upload-time = "2025-11-16T16:13:36.781Z" },
{ url = "https://files.pythonhosted.org/packages/de/4b/e98086e88f76c00c88a6bcf15eae27a1454f661a9eb72b111e6bbb69024d/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d", size = 736848, upload-time = "2025-11-16T16:13:37.952Z" },
{ url = "https://files.pythonhosted.org/packages/0c/5c/5964fcd1fd9acc53b7a3a5d9a05ea4f95ead9495d980003a557deb9769c7/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf", size = 741630, upload-time = "2025-11-16T20:22:51.718Z" },
{ url = "https://files.pythonhosted.org/packages/07/1e/99660f5a30fceb58494598e7d15df883a07292346ef5696f0c0ae5dee8c6/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51", size = 766619, upload-time = "2025-11-16T16:13:39.178Z" },
{ url = "https://files.pythonhosted.org/packages/36/2f/fa0344a9327b58b54970e56a27b32416ffbcfe4dcc0700605516708579b2/ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec", size = 100171, upload-time = "2025-11-16T16:13:40.456Z" },
{ url = "https://files.pythonhosted.org/packages/06/c4/c124fbcef0684fcf3c9b72374c2a8c35c94464d8694c50f37eef27f5a145/ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6", size = 118845, upload-time = "2025-11-16T16:13:41.481Z" },
{ url = "https://files.pythonhosted.org/packages/3e/bd/ab8459c8bb759c14a146990bf07f632c1cbec0910d4853feeee4be2ab8bb/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef", size = 147248, upload-time = "2025-11-16T16:13:42.872Z" },
{ url = "https://files.pythonhosted.org/packages/69/f2/c4cec0a30f1955510fde498aac451d2e52b24afdbcb00204d3a951b772c3/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf", size = 133764, upload-time = "2025-11-16T16:13:43.932Z" },
{ url = "https://files.pythonhosted.org/packages/82/c7/2480d062281385a2ea4f7cc9476712446e0c548cd74090bff92b4b49e898/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000", size = 730537, upload-time = "2025-11-16T20:22:52.918Z" },
{ url = "https://files.pythonhosted.org/packages/75/08/e365ee305367559f57ba6179d836ecc3d31c7d3fdff2a40ebf6c32823a1f/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4", size = 746944, upload-time = "2025-11-16T16:13:45.338Z" },
{ url = "https://files.pythonhosted.org/packages/a1/5c/8b56b08db91e569d0a4fbfa3e492ed2026081bdd7e892f63ba1c88a2f548/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c", size = 778249, upload-time = "2025-11-16T16:13:46.871Z" },
{ url = "https://files.pythonhosted.org/packages/6a/1d/70dbda370bd0e1a92942754c873bd28f513da6198127d1736fa98bb2a16f/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043", size = 737140, upload-time = "2025-11-16T16:13:48.349Z" },
{ url = "https://files.pythonhosted.org/packages/5b/87/822d95874216922e1120afb9d3fafa795a18fdd0c444f5c4c382f6dac761/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524", size = 741070, upload-time = "2025-11-16T20:22:54.151Z" },
{ url = "https://files.pythonhosted.org/packages/b9/17/4e01a602693b572149f92c983c1f25bd608df02c3f5cf50fd1f94e124a59/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e", size = 765882, upload-time = "2025-11-16T16:13:49.526Z" },
{ url = "https://files.pythonhosted.org/packages/9f/17/7999399081d39ebb79e807314de6b611e1d1374458924eb2a489c01fc5ad/ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa", size = 102567, upload-time = "2025-11-16T16:13:50.78Z" },
{ url = "https://files.pythonhosted.org/packages/d2/67/be582a7370fdc9e6846c5be4888a530dcadd055eef5b932e0e85c33c7d73/ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467", size = 122847, upload-time = "2025-11-16T16:13:51.807Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
@@ -307,7 +387,7 @@ wheels = [
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.extoll.de/" }
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
@@ -318,9 +398,9 @@ wheels = [
[[package]]
name = "urllib3"
version = "2.6.3"
source = { registry = "https://pypi.extoll.de/" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
version = "2.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
{ url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" },
]