{"id":13690996,"url":"https://github.com/daleal/zum","last_synced_at":"2025-09-11T05:28:28.079Z","repository":{"id":52784680,"uuid":"339928650","full_name":"daleal/zum","owner":"daleal","description":"Stop writing scripts to interact with your APIs. Call them as CLIs instead.","archived":false,"fork":false,"pushed_at":"2021-04-19T14:56:23.000Z","size":133,"stargazers_count":87,"open_issues_count":5,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-02T11:43:48.974Z","etag":null,"topics":["api","api-client","api-development","api-wrapper","developer-tools","tooling"],"latest_commit_sha":null,"homepage":"https://zum.daleal.dev","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daleal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-18T03:48:38.000Z","updated_at":"2025-04-08T09:58:34.000Z","dependencies_parsed_at":"2022-08-23T01:10:43.317Z","dependency_job_id":null,"html_url":"https://github.com/daleal/zum","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/daleal/zum","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daleal%2Fzum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daleal%2Fzum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daleal%2Fzum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daleal%2Fzum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daleal","download_url":"https://codeload.github.com/daleal/zum/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daleal%2Fzum/sbom","scorecard":{"id":317602,"data":{"date":"2025-08-11","repo":{"name":"github.com/daleal/zum","commit":"acda5f6057c7e6029eb93a784557c38102614b6a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.1,"checks":[{"name":"Code-Review","score":1,"reason":"Found 3/30 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/linters.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Warn: no topLevel permission defined: .github/workflows/tests.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:62: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:65: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:84: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:87: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:106: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:109: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/linters.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:63: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:69: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/tests.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tests.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/daleal/zum/tests.yml/master?enable=pin","Info:   0 out of  17 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":2,"reason":"8 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-48 / GHSA-fj7x-q9j7-g6q6","Warn: Project is vulnerable to: PYSEC-2022-42986 / GHSA-43fp-rhv2-5gv8","Warn: Project is vulnerable to: PYSEC-2023-135 / GHSA-xqr8-7jwr-rhp7","Warn: Project is vulnerable to: GHSA-vqfr-h8mv-ghfj","Warn: Project is vulnerable to: PYSEC-2022-183 / GHSA-h8pj-cxx2-jfg2","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: PYSEC-2022-42969","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T00:35:23.494Z","repository_id":52784680,"created_at":"2025-08-18T00:35:23.494Z","updated_at":"2025-08-18T00:35:23.494Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274582570,"owners_count":25311644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-11T02:00:13.660Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-client","api-development","api-wrapper","developer-tools","tooling"],"created_at":"2024-08-02T17:00:38.250Z","updated_at":"2025-09-11T05:28:28.021Z","avatar_url":"https://github.com/daleal.png","language":"Python","funding_links":[],"categories":["Python","Tools"],"sub_categories":["LaTex"],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/daleal/zum\"\u003e\n        \u003cimg src=\"https://zum.daleal.dev/assets/images/zum-300x300.png\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eZum\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cem\u003e\n        Stop writing scripts to interact with your APIs. Call them as CLIs instead.\n    \u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://pypi.org/project/zum\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/zum?label=version\u0026logo=python\u0026logoColor=%23fff\u0026color=306998\" alt=\"PyPI - Version\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/daleal/zum/actions?query=workflow%3Atests\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/workflow/status/daleal/zum/tests?label=tests\u0026logo=python\u0026logoColor=%23fff\" alt=\"Tests\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codecov.io/gh/daleal/zum\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/gh/daleal/zum?label=coverage\u0026logo=codecov\u0026logoColor=ffffff\" alt=\"Coverage\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/daleal/zum/actions?query=workflow%3Alinters\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/workflow/status/daleal/zum/linters?label=linters\u0026logo=github\" alt=\"Linters\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n**Zum** (German word roughly meaning \"_to the_\" or \"_to_\" depending on the context, pronounced `/tsʊm/`) is a tool that lets you describe a web API using a [TOML](https://toml.io/en/) file and then interact with that API using your command line. This means that **the days of writing custom scripts to help you interact and develop each of your APIs are over**. Just create a `zum.toml`, describe your API and forget about maintaining more code!\n\n## Why Zum?\n\nWhile there are tools out there with goals similar to `zum`, the scopes are quite different. The common contenders are [OpenAPI](http://spec.openapis.org/oas/v3.0.3)-based tools (like [SwaggerUI](https://swagger.io/tools/swagger-ui/)) and [cURL](https://curl.se/). To me, using an OpenAPI-based documentation tool is essential on any large enough API, but the description method is **very** verbose and quite complex, so often times it is added once the API has quite a few endpoints. On the other hand, cURL gets very verbose and tedious very fast when querying APIs, so I don't like to use it when developing my APIs. As a comparison, here's a `curl` command to query a local endpoint with a JSON body:\n\n```sh\ncurl --header \"Content-Type: application/json\" \\\n    --request POST \\\n    --data '{\"name\": \"Dani\", \"city\": \"Santiago\"}' \\\n    http://localhost:8000/living-beings\n```\n\nAnd here is the `zum` command to achieve the same result:\n\n```sh\nzum create application/json Dani Santiago\n```\n\nNow, imagine having to run this command hundreads of times during API development changing only the values on the request body, for example. You can see how using cURL is **not ideal**.\n\nThe [complete documentation](https://zum.daleal.dev/docs/) is available on the [official website](https://zum.daleal.dev/).\n\n## Installation\n\nInstall using pip!\n\n```sh\npip install zum\n```\n\n## Usage\n\n### Basic Usage\n\nThe basic principle is simple:\n\n1. Describe your API using a `zum.toml` file.\n2. Use the `zum` CLI to interact with your API.\n\nWe get more _in-depth_ with how to structure the `zum.toml` file and how to use the `zum` CLI on [the complete documentation](https://zum.daleal.dev/docs/), but for now let's see a very basic example. Imagine that you are developing an API that gets the URL of [a song on YouTube](https://youtu.be/6xlsR1c8yh4). This API, for now, has only 1 endpoint: `GET /song` (clearly a [WIP](https://www.urbandictionary.com/define.php?term=Wip)). To describe your API, you would have to write a `zum.toml` file similar to this one:\n\n```toml\n[metadata]\nserver = \"http://localhost:8000\"\n\n[endpoints.dada]\nroute = \"/song\"\nmethod = \"get\"\n```\n\nNow, to get your song's URL, all you need to do is to run:\n\n```sh\nzum dada\n```\n\nNotice that, after the `zum` command, we passed an argument, that in this case was `dada`. This argument tells `zum` that it should interact with the endpoint described on the `dada` endpoint section, denoted by the header `[endpoints.dada]`. As a rule, to access an endpoint described by the header `[endpoints.{my-endpoint-name}]`, you will call the `zum` command with the `{my-endpoint-name}` argument:\n\n```sh\nzum {my-endpoint-name}\n```\n\n### `params`, `headers` and `body`\n\n**Beware!** There are some nuances on these attribute definitions, so reading [the complete documentation](https://zum.daleal.dev/docs/) is **highly recommended**.\n\n#### The `params` of an endpoint\n\nOn the previous example, the `route` was static, which means that `zum` will **always** query the same route. For some things, this might not be the best of ideas (for example, for querying entities on REST APIs), and you might want to interpolate a value on the `route` string. Let's say that there's a collection of songs, and you wanted to get the song with `id` _57_. Your endpoint definition should look like the following:\n\n```toml\n[endpoints.get-song]\nroute = \"/songs/{id}\"\nmethod = \"get\"\nparams = [\"id\"]\n```\n\nAs you can see, the element inside `params` matches the element inside the brackets on the `route`. This means that whatever parameter you pass to the `zum` CLI, it will be replaced on the `route` _on-demand_:\n\n```sh\nzum get-song 57\n```\n\nNow, `zum` will send a `GET` HTTP request to `http://localhost:8000/songs/57`. Pretty cool!\n\n#### The `headers` of an endpoint\n\nThe `headers` are defined **exactly** the same as the `params`. Let's see a small example to illustrate how to use them. Imagine that you have an API that requires [JWT](https://jwt.io/introduction) authorization to `GET` the songs of its catalog. Let's define that endpoint:\n\n```toml\n[endpoints.get-authorized-catalog]\nroute = \"/catalog\"\nmethod = \"get\"\nheaders = [\"Authorization\"]\n```\n\nNow, to acquire the catalog, we would need to run:\n\n```sh\nzum get-authorized-catalog \"Bearer super-secret-token\"\n```\n\n\u003e ⚠ **Warning**: Notice that, for the first time, we surrounded something with quotes on the CLI. The reason we did this is that, without the quotes, the console has no way of knowing if you want to pass a parameter with a space in the middle or if you want to pass multiple parameters, so it defaults to receiving the words as multiple parameters. To stop this from happening, you can surround the string in quotes, and now the whole string will be interpreted as only one parameter with the space in the middle of the string. This will be handy on future examples, so **keep it in mind**.\n\nThis will send a `GET` request to `http://localhost:8000/catalog` with the following headers:\n\n```json\n{\n    \"Authorization\": \"Bearer super-secret-token\"\n}\n```\n\nAnd now you have your authorization-protected music catalog!\n\n#### The `body` of an endpoint\n\nJust like `params` and `headers`, the `body` (the body of the request) gets defined as an array:\n\n```toml\n[endpoints.create-living-being]\nroute = \"/living-beings\"\nmethod = \"post\"\nbody = [\"name\", \"city\"]\n```\n\nTo run this endpoint, you just need to run:\n\n```sh\nzum create-living-being Dani Santiago\n```\n\nThis will send a `POST` request to `http://localhost:8000/living-beings` with the following request body:\n\n```json\n{\n    \"name\": \"Dani\",\n    \"city\": \"Santiago\"\n}\n```\n\n**Notice that you can also cast the parameters to different types**. You can read more about this on the complete documentation's section about [the request body](https://zum.daleal.dev/docs/config-file.html#the-body-of-an-endpoint).\n\n#### Combining `params`, `headers` and `body`\n\nOf course, sometimes you need to use some `params`, some `headers` **and** a `body`. For example, if you wanted to create a song inside an authorization-protected album (a _nested entity_), you would need to use the album's id as a `param`, the \"Authorization\" key inside the `headers` to get the authorization and the new song's data as the `body`. For this example, the song has a `name` (which is a string) and a `duration` in seconds (which is an integer). Let's describe this situation!\n\n```toml\n[endpoints.create-song]\nroute = \"/albums/{id}/songs\"\nmethod = \"post\"\nparams = [\"id\"]\nheaders = [\"Authorization\"]\nbody = [\n    \"name\",\n    { name = \"duration\", type = \"integer\" }\n]\n```\n\nNow, you can call the endpoint using:\n\n```sh\nzum create-song 8 \"Bearer super-secret-token\" \"Con Altura\" 161\n```\n\nThis will call `POST /albums/8/songs` with the following headers:\n\n```json\n{\n    \"Authorization\": \"Bearer super-secret-token\"\n}\n```\n\nAnd the following request body:\n\n```json\n{\n    \"name\": \"Con Altura\",\n    \"duration\": 161\n}\n```\n\nAs you can probably tell, `zum` receives the `params` first on the CLI, then the `headers` and then the `body`. In _pythonic_ terms, what `zum` does is that it kind of _unpacks_ the three arrays consecutively, something like the following:\n\n```py\narguments = [*params, *headers, *body]\nzum(arguments)\n```\n\n## Developing\n\nClone the repository:\n\n```sh\ngit clone https://github.com/daleal/zum.git\n\ncd zum\n```\n\nRecreate environment:\n\n```sh\nmake get-poetry\nmake build-env\n```\n\nRun the linters:\n\n```sh\nmake black flake8 isort mypy pylint\n```\n\nRun the tests:\n\n```sh\nmake tests\n```\n\n## Resources\n\n- [Official Website](https://zum.daleal.dev/)\n- [Issue Tracker](https://github.com/daleal/zum/issues/)\n- [Contributing Guidelines](.github/CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaleal%2Fzum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaleal%2Fzum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaleal%2Fzum/lists"}