diff --git a/mrpy/__init__.py b/eta/__init__.py similarity index 100% rename from mrpy/__init__.py rename to eta/__init__.py diff --git a/eta/clickup.py b/eta/clickup.py new file mode 100644 index 0000000..c0a2a08 --- /dev/null +++ b/eta/clickup.py @@ -0,0 +1,68 @@ +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 ClickupTask(BaseModel): + id: str + name: str + + +@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) -> list[ClickupTask]: + data = self._get( + f"/team/{self.workspace_id}/task", + **{ + "subtasks": "true", + "include_markdown_description": "true", + "assignees[]": self.user_id, + } + | filters, + ).get("tasks", []) + if isinstance(data, list): + return [ClickupTask.model_validate(t) for t in data if isinstance(t, dict)] + + return [] + + def get_task(self, task_id: str) -> ClickupTask: + return ClickupTask.model_validate( + self._get( + f"/task/{task_id}", + include_markdown_description="true", + ), + ) diff --git a/eta/common.py b/eta/common.py new file mode 100644 index 0000000..c3e7b8e --- /dev/null +++ b/eta/common.py @@ -0,0 +1,32 @@ +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 diff --git a/eta/main.py b/eta/main.py new file mode 100644 index 0000000..cdbb5e6 --- /dev/null +++ b/eta/main.py @@ -0,0 +1,90 @@ +from lsprotocol.types import ( + TEXT_DOCUMENT_COMPLETION, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, + CompletionItem, + CompletionItemKind, + CompletionItemLabelDetails, + CompletionOptions, + CompletionParams, + DocumentSymbol, + DocumentSymbolParams, + NotebookDocumentSyncOptions, + Position, + Range, + SymbolKind, + TextDocumentSyncKind, + WorkDoneProgressBegin, + WorkDoneProgressEnd, + WorkDoneProgressReport, +) +from pygls.lsp.server import LanguageServer + +from .clickup import ClickupSession, ClickupTask + + +class CustomServer(LanguageServer): + cache: dict[str, ClickupTask] + cu_session: ClickupSession + + 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.cache = {} + self.cu_session = ClickupSession() + self.update_task_cache() + + def update_task_cache(self) -> None: + self.protocol.progress.begin( + "startup", WorkDoneProgressBegin("Fetching Cache ...", percentage=0, cancellable=True) + ) + self.cache = {} + tasks = self.cu_session.get_ta() + for ti, t in enumerate(tasks): + self.cache[t.id] = t + self.protocol.progress.report( + "startup", + WorkDoneProgressReport( + message="Fetched Cache", percentage=int(100 * (1 + ti) / len(tasks)) + ), + ) + + self.protocol.progress.end("startup", WorkDoneProgressEnd(message="Done Caching")) + + +server = CustomServer("mrpy-server", "0.1.0") + + +@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.cache.values()) + ] + + +@server.feature(TEXT_DOCUMENT_COMPLETION) +async def complete_cu_ids(params: CompletionParams) -> list[CompletionItem]: + return [ + CompletionItem( + t.name, + CompletionItemLabelDetails(detail=f" #{t.id}"), + kind=CompletionItemKind.Constant, + insert_text=f"[{t.name} #{t.id}](https://app.clickup.com/t/{t.id})", + ) + for t in server.cache.values() + ] + + +def main() -> None: + server.start_io() diff --git a/lua/eta/clickup.lua b/lua/eta/clickup.lua new file mode 100644 index 0000000..28146b1 --- /dev/null +++ b/lua/eta/clickup.lua @@ -0,0 +1,103 @@ +local curl = require("plenary.curl") +local helpers = require("eta.helpers") +local M = {} + +---@class eta.clickup.Session: eta.Session +---@field user string +---@field workspace 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 diff --git a/lua/eta/gitlab.lua b/lua/eta/gitlab.lua new file mode 100644 index 0000000..01dbf5f --- /dev/null +++ b/lua/eta/gitlab.lua @@ -0,0 +1,149 @@ +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: ` + +---@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 diff --git a/lua/eta/helpers.lua b/lua/eta/helpers.lua new file mode 100644 index 0000000..6b651ff --- /dev/null +++ b/lua/eta/helpers.lua @@ -0,0 +1,63 @@ +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 param_list = {} + for param_name, param_value in pairs(params) do + table.insert(param_list, param_name .. "=" .. param_value) + end + local url = session.base_url .. endpoint + if #param_list then + url = url .. "?" .. table.concat(param_list, "&") + end + local resp = nil + if method == "get" then + 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", + }}) + 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 + + print("failed http request: " .. tostring(resp.status) .. " (" .. resp.body .. ", " .. url .. ")") + return nil +end + +return M diff --git a/lua/mrpy.lua b/lua/eta/init.lua similarity index 72% rename from lua/mrpy.lua rename to lua/eta/init.lua index 9687c31..da4fdb3 100644 --- a/lua/mrpy.lua +++ b/lua/eta/init.lua @@ -1,5 +1,7 @@ local curl = require("plenary.curl") local notify = require("snacks.notifier").notify +local gitlab = require("eta.gitlab") +local clickup = require("eta.clickup") local M = {} ---@param data table @@ -8,7 +10,7 @@ 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 -P e '. ' -") + local handle = io.popen("echo '" .. json .. "' | yq -y") local result = handle:read("*a") handle:close() @@ -35,12 +37,11 @@ M.yaml_to_table = function(yaml_string) temp_file_handle:flush() temp_file_handle:close() - local cmd = "yq '.' " .. temp_file + local cmd = "cat " .. temp_file .. " | yq" local handle, errres = io.popen(cmd, 'r') local json_result = handle:read("*a") handle:close() - os.remove(temp_file) -- 2. Fehlerprüfung if json_result == "" then @@ -59,53 +60,19 @@ M.yaml_to_table = function(yaml_string) return nil end + os.remove(temp_file) return table_result end ----@class ClickupSession ----@field auth string ----@field user string ----@field workspace string ----@field base_url string +---@type eta.clickup.Session +M.clickup_session = nil ----@type ClickupSession -M.session = nil +---@type eta.gitlab.Session +M.gitlab_session = nil ----@class ClickupRef ----@field name string ----@field id string ----@class ClickupTask ----@field id string ----@field name string ----@field tags? table[] ----@field locations? table[] ----@field list? ClickupRef ----@field parent? string | nil ----@field [string] string - ----@return ClickupTask[] -M.latest_tasks = function() - local resp = curl.get(M.session.base_url .. - "/team/" .. M.session.workspace .. "/task?subtasks=true&include_markdown_description=true&assignees[]=" .. - M.session.user, { - headers = { - ['Authorization'] = M.session.auth, - ["accept"] = "application/json", - ["content-type"] = "application/json", - } - }) - - if resp.status == 200 then - return vim.json.decode(resp.body).tasks - end - - print("failed http request: " .. tostring(resp.status) .. " (" .. resp.body .. ")") - return {} -end - ----@param task ClickupTask ----@return ClickupTask +---@param task eta.clickup.Task +---@return eta.clickup.Task M._update_task = function(task) local update_table = {} @@ -115,9 +82,9 @@ M._update_task = function(task) end end - local resp = curl.put(M.session.base_url .. "/task/" .. task.id, { + local resp = curl.put(M.clickup_session.base_url .. "/task/" .. task.id, { headers = { - ['Authorization'] = M.session.auth, + ['Authorization'] = M.clickup_session.auth, ["accept"] = "application/json", ["content-type"] = "application/json", }, @@ -149,14 +116,16 @@ M._on_tempbuf_write = function(args) end end table.insert(parts, table.concat(current, "\n")) - ---@type ClickupTask - local data = vim.json.decode(parts[2]) + + ---@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="MrPy", style='fancy'}) + local notif_id = notify("updated task #" .. data.id, "info", { title = "ETA", style = 'fancy' }) end ---@class SelectionItem @@ -168,9 +137,10 @@ end ---@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="MrPy", style='fancy'}) + 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) @@ -179,14 +149,16 @@ M._on_select_task = function(picker, item) -- 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 = { "---", "{" } - for _, k in ipairs({ "id", "name", "status", "tags", "list", "parent" }) do - if content[#content] ~= "{" then - content[#content] = content[#content] .. "," - end - table.insert(content, ' "' .. k .. '": ' .. vim.json.encode(item[k])) + local content = { "---"} + local to_dump = {} + for _, k in ipairs({ "id", "name", "status", "tags", "list", "parent", "dependencies" }) do + to_dump[k] = item[k] end - table.insert(content, "}") + 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) @@ -196,7 +168,11 @@ M._on_select_task = function(picker, item) vim.api.nvim_win_set_buf(0, new_buf_no) else vim.api.nvim_buf_delete(new_buf_no, {}) - picker:close() + 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 @@ -249,11 +225,38 @@ M.retrieve_subkeys = function(list, keys) 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="MrPy", style='fancy'}) - vim.schedule(function() - local tasks = M.latest_tasks() + local notif_id = notify("fetching tasks", "warn", { timeout = 10000, title = "ETA", style = 'fancy' }) + if not pcall(M.open_task,vim.fn.expand("")) then + local tasks = clickup.latest_tasks(M.clickup_session) ---@type SelectionItem[] local items = {} @@ -262,6 +265,11 @@ M.select_task = function(data) 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, @@ -273,13 +281,14 @@ M.select_task = function(data) 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 + for _, k in ipairs({ "id", "name", "status", "tags", "parent"}) do preview_frontmatter = preview_frontmatter .. "\n" .. k .. ": " .. vim.json.encode(prepared[k]) end @@ -296,7 +305,15 @@ M.select_task = function(data) confirm = M._on_select_task, items = items }) - end) + 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 diff --git a/mrpy/main.py b/mrpy/main.py deleted file mode 100644 index 047ac14..0000000 --- a/mrpy/main.py +++ /dev/null @@ -1,2 +0,0 @@ -def main() -> None: - pass diff --git a/plugin/eta.lua b/plugin/eta.lua new file mode 100644 index 0000000..67c9859 --- /dev/null +++ b/plugin/eta.lua @@ -0,0 +1,18 @@ +local eta = require("eta") +local eta_clickup = require("eta.clickup") + +vim.api.nvim_create_user_command("ETA", eta.select_task, {}) + +---@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", +} + +---@type eta.gitlab.Session +eta.gitlab_session = { + auth = os.getenv("GITLAB_AUTH") or "", + base_url = "https://git.extoll.de/api/v4", +} diff --git a/plugin/mrpy.lua b/plugin/mrpy.lua deleted file mode 100644 index 2e4334e..0000000 --- a/plugin/mrpy.lua +++ /dev/null @@ -1,10 +0,0 @@ -local mrpy = require("mrpy") - -vim.api.nvim_create_user_command("MrPy", mrpy.select_task, {}) - -mrpy.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", -} diff --git a/pyproject.toml b/pyproject.toml index bc9e72f..eb86779 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,21 +3,23 @@ requires = ['setuptools>=57', "wheel"] build-backend = "setuptools.build_meta" [project] -name = "mrpy-lsp" +name = "eta-lsp" version = "0.1.0" -description = "Clickup/Gitlab LSP" +description = "EXTOLL Task Access" readme = "README.md" requires-python = ">=3.11" dependencies = [ "pygls~=2.0", + "pydantic~=2.12", + "requests~=2.32", ] authors = [ { name = "Patrick Nisble", email = "acereca@outlook.de"}, ] [project.scripts] -mrpy-lsp = "mrpy.main:main" +eta-lsp = "eta.main:main" -[tool.setuptools.package.find] +[tool.setuptools.packages.find] include = ['mrpy'] namespaces = false diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..20891fe --- /dev/null +++ b/schema.json @@ -0,0 +1,44 @@ +{ + "$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" +}