clickup-cli 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. clickup_cli-1.2.0/.claude/agents/test-writer.md +39 -0
  2. clickup_cli-1.2.0/.claude/settings.json +26 -0
  3. clickup_cli-1.2.0/.claude/skills/add-command.md +75 -0
  4. clickup_cli-1.2.0/.claude/skills/clickup-cli.md +93 -0
  5. clickup_cli-1.2.0/.claude/skills/release/SKILL.md +66 -0
  6. clickup_cli-1.2.0/.github/workflows/ci.yml +31 -0
  7. clickup_cli-1.2.0/.gitignore +36 -0
  8. clickup_cli-1.2.0/.mcp.json +8 -0
  9. clickup_cli-1.2.0/CHANGELOG.md +59 -0
  10. clickup_cli-1.2.0/CLAUDE.md +76 -0
  11. clickup_cli-1.2.0/LICENSE +21 -0
  12. clickup_cli-1.2.0/PKG-INFO +204 -0
  13. clickup_cli-1.2.0/README.md +172 -0
  14. clickup_cli-1.2.0/clickup-config.example.json +12 -0
  15. clickup_cli-1.2.0/pyproject.toml +54 -0
  16. clickup_cli-1.2.0/scripts/validate-cli-output.sh +89 -0
  17. clickup_cli-1.2.0/src/clickup_cli/__init__.py +3 -0
  18. clickup_cli-1.2.0/src/clickup_cli/__main__.py +5 -0
  19. clickup_cli-1.2.0/src/clickup_cli/cli.py +176 -0
  20. clickup_cli-1.2.0/src/clickup_cli/client.py +115 -0
  21. clickup_cli-1.2.0/src/clickup_cli/commands/__init__.py +71 -0
  22. clickup_cli-1.2.0/src/clickup_cli/commands/comments.py +278 -0
  23. clickup_cli-1.2.0/src/clickup_cli/commands/docs.py +441 -0
  24. clickup_cli-1.2.0/src/clickup_cli/commands/folders.py +202 -0
  25. clickup_cli-1.2.0/src/clickup_cli/commands/init.py +153 -0
  26. clickup_cli-1.2.0/src/clickup_cli/commands/lists.py +258 -0
  27. clickup_cli-1.2.0/src/clickup_cli/commands/spaces.py +137 -0
  28. clickup_cli-1.2.0/src/clickup_cli/commands/tags.py +132 -0
  29. clickup_cli-1.2.0/src/clickup_cli/commands/tasks.py +733 -0
  30. clickup_cli-1.2.0/src/clickup_cli/commands/team.py +114 -0
  31. clickup_cli-1.2.0/src/clickup_cli/config.py +200 -0
  32. clickup_cli-1.2.0/src/clickup_cli/helpers.py +163 -0
  33. clickup_cli-1.2.0/tests/conftest.py +29 -0
  34. clickup_cli-1.2.0/tests/test_cli.py +1123 -0
  35. clickup_cli-1.2.0/tests/test_client.py +314 -0
  36. clickup_cli-1.2.0/tests/test_commands.py +1399 -0
  37. clickup_cli-1.2.0/tests/test_config.py +331 -0
  38. clickup_cli-1.2.0/tests/test_helpers.py +228 -0
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: test-writer
3
+ description: Generate pytest tests for clickup-cli commands following existing test patterns in tests/test_cli.py
4
+ ---
5
+
6
+ # Test Writer Agent
7
+
8
+ You generate pytest tests for the clickup-cli project.
9
+
10
+ ## Before Writing Tests
11
+
12
+ 1. Read `tests/conftest.py` for the test config setup pattern
13
+ 2. Read `tests/test_cli.py` for existing patterns (FakeClient, Namespace, etc.)
14
+ 3. Read the source file you're writing tests for
15
+
16
+ ## Conventions
17
+
18
+ - Use `unittest.TestCase` classes (matching existing style)
19
+ - Use `FakeClient` for API call mocking — do NOT make real HTTP calls
20
+ - Use `unittest.mock.patch` and `MagicMock` for things FakeClient doesn't cover
21
+ - Use `argparse.Namespace` to construct fake args
22
+ - Test these for every command:
23
+ - Argument parsing (parser accepts the expected args)
24
+ - `--dry-run` behavior (mutating commands return preview, don't call API)
25
+ - Normal execution with mocked responses
26
+ - Error cases (missing required args, API errors)
27
+ - All output assertions should check JSON structure
28
+ - Put tests in `tests/test_cli.py` or a new `tests/test_<group>.py` file
29
+
30
+ ## Example Pattern
31
+
32
+ ```python
33
+ class TestTasksSearch(unittest.TestCase):
34
+ def test_search_dry_run(self):
35
+ client = FakeClient(dry_run=True)
36
+ args = Namespace(query="test", space="testspace", ...)
37
+ result = cmd_tasks_search(client, args)
38
+ self.assertIn("dry_run", result)
39
+ ```
@@ -0,0 +1,26 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "filepath=\"$TOOL_INPUT_FILE_PATH$TOOL_INPUT_file_path\"; if echo \"$filepath\" | grep -qE '\\.py$'; then ruff check --fix --quiet \"$filepath\" 2>/dev/null; ruff format --quiet \"$filepath\" 2>/dev/null; fi; true"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "Edit|Write",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "filepath=\"$TOOL_INPUT_FILE_PATH$TOOL_INPUT_file_path\"; if echo \"$filepath\" | grep -qE '\\.(env|secret|pem|key)$|credentials'; then echo 'BLOCKED: refusing to edit sensitive file'; exit 1; fi"
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: add-command
3
+ description: Step-by-step workflow for adding a new CLI command to clickup-cli, with verification at each step.
4
+ ---
5
+
6
+ # Add a New CLI Command
7
+
8
+ Follow these steps in order when adding a new command or subcommand.
9
+
10
+ ## 1. Create or extend the command module
11
+
12
+ - If this is a new command group, create `src/clickup_cli/commands/<group>.py`
13
+ - If extending an existing group, add the handler function to the existing file
14
+ - Handler signature: `def cmd_<group>_<command>(client, args):`
15
+ - Return a dict (will be output as JSON)
16
+ - Mutating commands must check `client.dry_run` and return a dry-run preview instead of calling the API
17
+
18
+ ## 2. Add or extend the parser in the same module
19
+
20
+ - Each command module owns its parser via `register_parser(subparsers, F)`
21
+ - If extending an existing group, add the new subparser inside the existing `register_parser()`
22
+ - If creating a new group, add a `register_parser(subparsers, F)` function that creates the group parser and all its subparsers
23
+ - Add the subparser with:
24
+ - `description=` — what this command does
25
+ - `epilog=` — usage examples (at least 2)
26
+ - `formatter_class=F` (passed in as argument)
27
+ - Add all arguments with `help=` text
28
+ - For mutating commands, `--dry-run` is handled globally (no per-command flag needed)
29
+
30
+ ## 3. Register the handler
31
+
32
+ - Open `src/clickup_cli/commands/__init__.py`
33
+ - Import the new handler function
34
+ - Add it to the `HANDLERS` dict with key `"<group>_<command>"`
35
+
36
+ ## 3b. Wire up new command groups in cli.py (new groups only)
37
+
38
+ - If this is a new command group, open `src/clickup_cli/cli.py`
39
+ - Import `register_parser` from the new module
40
+ - Call it in `build_parser()` alongside the other `register_parser()` calls
41
+
42
+ ## 4. Write help text
43
+
44
+ - The `--help` output must be self-sufficient for an agent to use the command correctly
45
+ - Include concrete examples in the epilog
46
+ - Never use real workspace IDs, space names, or tokens in examples — use `<placeholders>`
47
+
48
+ ## 5. Add tests
49
+
50
+ - Open `tests/test_cli.py` (or create a new test file if the group is large)
51
+ - Test argument parsing (parser accepts the new args)
52
+ - Test dry-run behavior (mutating commands return dry-run preview)
53
+ - Test core logic with `FakeClient`
54
+
55
+ ## 6. Verify
56
+
57
+ Run these commands and confirm all pass before considering the work done:
58
+
59
+ ```bash
60
+ # Lint
61
+ ruff check src/ tests/
62
+
63
+ # Tests
64
+ pytest -v
65
+
66
+ # Help text renders correctly
67
+ clickup <group> <command> --help
68
+
69
+ # Validate CLI output contract
70
+ scripts/validate-cli-output.sh
71
+ ```
72
+
73
+ ## 7. Update the skill (if needed)
74
+
75
+ If the new command adds a new workflow pattern, update `.claude/skills/clickup-cli.md` with a usage example.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: clickup-cli
3
+ description: Use the clickup CLI to manage ClickUp tasks, comments, docs, folders, lists, spaces, and tags from the command line. JSON output, dry-run support.
4
+ ---
5
+
6
+ # ClickUp CLI Skill
7
+
8
+ You have access to the `clickup` CLI for managing ClickUp workspaces.
9
+
10
+ ## Discovery
11
+
12
+ Always start by discovering available commands:
13
+
14
+ ```bash
15
+ clickup --help # list all command groups
16
+ clickup <group> --help # list subcommands in a group
17
+ clickup <group> <command> --help # full usage for a specific command
18
+ ```
19
+
20
+ ## Safety
21
+
22
+ Before any mutating command (create, update, delete), use `--dry-run`:
23
+
24
+ ```bash
25
+ clickup --dry-run tasks create --space <space_name> --name "New task"
26
+ ```
27
+
28
+ Global flags (`--pretty`, `--dry-run`, `--debug`) can appear before or after the command group.
29
+
30
+ ## Output
31
+
32
+ - Successful output is always JSON on stdout
33
+ - Errors go to stderr with non-zero exit code
34
+ - Use `--pretty` for indented JSON when reading output
35
+ - Use `--full` on task list/search for raw API response
36
+
37
+ ## Common Workflows
38
+
39
+ ### Find and read a task
40
+ ```bash
41
+ clickup tasks search "login bug" --space <space_name>
42
+ clickup tasks get <task_id>
43
+ ```
44
+
45
+ ### Create a task
46
+ ```bash
47
+ clickup --dry-run tasks create --space <space_name> --name "Fix auth" --desc "Details"
48
+ clickup tasks create --space <space_name> --name "Fix auth" --desc "Details"
49
+ ```
50
+
51
+ ### Add a comment
52
+ ```bash
53
+ clickup comments add <task_id> --text "Work complete"
54
+ clickup comments add <task_id> --file report.md
55
+ ```
56
+
57
+ ### Read and edit docs
58
+ ```bash
59
+ clickup docs list --space <space_name>
60
+ clickup docs pages <doc_id>
61
+ clickup docs get-page <doc_id> <page_id>
62
+ clickup docs edit-page <doc_id> <page_id> --content-file updated.md
63
+ clickup docs edit-page <doc_id> <page_id> --content "New section" --append
64
+ ```
65
+
66
+ ### Discover workspace structure
67
+ ```bash
68
+ clickup spaces list
69
+ clickup folders list --space <space_name>
70
+ clickup lists list --folder <folder_id>
71
+ clickup spaces statuses <space_name>
72
+ ```
73
+
74
+ ## Configuration
75
+
76
+ The CLI loads config from (in order):
77
+ 1. `CLICKUP_CONFIG_PATH` env var
78
+ 2. `~/.config/clickup-cli/config.json`
79
+ 3. `clickup-config.json` in current directory
80
+
81
+ Or use environment variables only:
82
+ - `CLICKUP_API_TOKEN` (required)
83
+ - `CLICKUP_WORKSPACE_ID` (auto-detected for single-workspace accounts)
84
+
85
+ Run `clickup init` for interactive setup.
86
+
87
+ ## Key Behaviors
88
+
89
+ - `tasks get` auto-fetches comments (use `--no-comments` to skip)
90
+ - `tasks search` auto-detects task ID patterns (e.g. "PROJ-39") and applies prefix filtering
91
+ - `tasks create` checks for duplicates before creating (use `--skip-dedup` to bypass)
92
+ - Tag names are auto-lowercased (ClickUp API requirement)
93
+ - Doc ID ≠ page ID — always use `docs pages` to find page IDs first
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: release
3
+ description: Bump version, validate, tag, and build for PyPI release
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Release Workflow
8
+
9
+ Follow these steps to publish a new release of clickup-cli.
10
+
11
+ ## 1. Pre-flight checks
12
+
13
+ ```bash
14
+ ruff check src/ tests/
15
+ pytest -v
16
+ clickup --help # smoke test
17
+ ```
18
+
19
+ All must pass before proceeding.
20
+
21
+ ## 2. Bump version
22
+
23
+ The version is in `src/clickup_cli/__init__.py`. Update it following semver:
24
+ - **patch** (0.x.Y): bug fixes
25
+ - **minor** (0.X.0): new commands or features
26
+ - **major** (X.0.0): breaking changes
27
+
28
+ ## 3. Update changelog
29
+
30
+ If `CHANGELOG.md` exists, add an entry for the new version with a summary of changes since the last tag:
31
+
32
+ ```bash
33
+ git log $(git describe --tags --abbrev=0)..HEAD --oneline
34
+ ```
35
+
36
+ ## 4. Commit and tag
37
+
38
+ ```bash
39
+ git add -A
40
+ git commit -m "release: v<version>"
41
+ git tag v<version>
42
+ ```
43
+
44
+ ## 5. Build
45
+
46
+ ```bash
47
+ pip install build
48
+ python -m build
49
+ ```
50
+
51
+ Verify the dist/ output looks correct (check file sizes, version in filenames).
52
+
53
+ ## 6. Publish (requires user confirmation)
54
+
55
+ **Ask the user before running this step.**
56
+
57
+ ```bash
58
+ pip install twine
59
+ twine upload dist/*
60
+ ```
61
+
62
+ ## 7. Push
63
+
64
+ ```bash
65
+ git push origin main --tags
66
+ ```
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.9", "3.11", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: pip install -e ".[dev]"
26
+
27
+ - name: Lint
28
+ run: ruff check src/ tests/
29
+
30
+ - name: Test
31
+ run: pytest -v
@@ -0,0 +1,36 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+ env/
14
+
15
+ # Testing
16
+ .pytest_cache/
17
+ .coverage
18
+ htmlcov/
19
+
20
+ # IDE
21
+ .idea/
22
+ .vscode/
23
+ *.swp
24
+ *.swo
25
+
26
+ # Config with real tokens
27
+ clickup-config.json
28
+ .env
29
+ .env.*
30
+
31
+ # Workflow artifacts
32
+ .planning/
33
+ .audit/
34
+
35
+ # Local-only Claude Code settings (permissions, etc.)
36
+ .claude/settings.local.json
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "context7": {
4
+ "command": "npx",
5
+ "args": ["-y", "@upstash/context7-mcp@latest"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ ## 1.2.0 (2026-03-29)
4
+
5
+ - Accept flag aliases for all positional arguments — agents can now use `--task-id`, `--query`, `--doc-id`, `--page-id`, `--folder-id`, `--list-id`, `--comment-id`, `--space` instead of positional args. Both forms work; positional args are unchanged for backwards compatibility.
6
+ - Auto-infer `--space` from `--list` on `tasks create` — when `--list` is provided without `--space`, the CLI fetches the list metadata via API to resolve its parent space automatically. Eliminates the most common agent error (12+ failures in 3 days).
7
+ - Make `--space` optional on `tasks create` (was required) — now only required if `--list` is also absent.
8
+ - Expand test suite to 305 tests — full behavioral coverage for `cmd_init` (12 tests), `main()` integration (4 tests), `cmd_docs_create` content paths (3 tests), and 429 retry edge case (1 test).
9
+ - Simplify codebase: use shared `error()` in config.py, DRY up tag add/remove, task list/search epilogue, list folder/space resolution, client retry logic. -24 net lines across 7 files.
10
+
11
+ ## 1.1.3 (2026-03-29)
12
+
13
+ - Refactor: split 1633-line `build_parser()` — each command module now owns its parser via `register_parser()`
14
+ - Refactor: extract shared `_paginate_tasks()` helper, removing duplicate pagination logic in tasks list/search
15
+ - Refactor: extract `_extract_status()` and `_extract_priority()` helpers in helpers.py, deduplicating field extraction
16
+ - Clean up dead code in `client.py` (unreachable None guards, pointless variable rename)
17
+ - Expand test suite from 55 to 89 tests — comprehensive parser coverage for all 8 command groups
18
+ - Fix stale version assertion in tests (was checking 1.1.0 instead of current version)
19
+
20
+ ## 1.1.2 (2026-03-29)
21
+
22
+ - Add auto-lint hook — ruff check/format runs automatically on every Python file edit
23
+ - Add sensitive file guard — blocks edits to `.env`, `.secret`, `.pem`, `.key`, and credentials files
24
+ - Add context7 MCP server (`.mcp.json`) — live ClickUp API docs for contributors
25
+ - Add `test-writer` subagent (`.claude/agents/`) — generates pytest tests following project patterns
26
+ - Add `/release` skill — version bump, validate, tag, build, publish workflow
27
+ - Clean up permission allowlist in `.claude/settings.local.json`
28
+
29
+ ## 1.1.1 (2026-03-29)
30
+
31
+ - Add `.env` and `.env.*` to `.gitignore` for API token safety
32
+ - Add `scripts/validate-cli-output.sh` — validates all CLI help commands, error routing, and version flag
33
+ - Add `.claude/skills/add-command.md` — prescriptive dev workflow for adding new commands
34
+ - Replace hardcoded `myspace` with `<name>` / `<space_name>` placeholders in skill and README
35
+
36
+ ## 1.1.0 (2026-03-29)
37
+
38
+ - Auto-detect workspace ID for single-workspace accounts — no need to set it manually
39
+ - Fix user detection in `clickup init` — now prompts for selection in multi-member workspaces
40
+ - workspace_id is saved back to config file after auto-detection
41
+ - Add `--debug` flag — logs API requests and responses to stderr for troubleshooting
42
+ - Improve space name resolution — clear error with available names when a space isn't found in config
43
+ - Fix comment pagination — no longer hardcodes page size assumption
44
+
45
+ ## 1.0.0 (2026-03-28)
46
+
47
+ Initial public release.
48
+
49
+ - 8 command groups: tasks, comments, docs, folders, lists, spaces, team, tags
50
+ - Full task CRUD with search, move, merge
51
+ - Full comment CRUD with threading
52
+ - Docs management with page editing
53
+ - Folder and list management
54
+ - Tag management
55
+ - Workspace discovery via `clickup init`
56
+ - JSON-only stdout, errors to stderr
57
+ - Dry-run mode for all mutations
58
+ - Rate limit handling with automatic retry
59
+ - Config via JSON file or environment variables
@@ -0,0 +1,76 @@
1
+ # ClickUp CLI — Development Instructions
2
+
3
+ ## What This Is
4
+
5
+ A ClickUp CLI for developers and AI agents. JSON-only stdout, errors to stderr, dry-run on all mutations.
6
+
7
+ ## Package Structure
8
+
9
+ ```
10
+ src/clickup_cli/
11
+ ├── cli.py # root parser, dispatch, main() — delegates to command modules
12
+ ├── client.py # ClickUpClient API wrapper (rate limiting, dry-run, debug)
13
+ ├── config.py # Config loader (lazy, fallback chain, workspace auto-detect)
14
+ ├── helpers.py # output(), error(), compact_task(), etc.
15
+ └── commands/
16
+ ├── __init__.py # HANDLERS dict
17
+ ├── tasks.py # tasks parser + handlers (list/get/create/update/search/delete/move/merge)
18
+ ├── comments.py # comments parser + handlers (list/add/update/delete/thread/reply)
19
+ ├── docs.py # docs parser + handlers (list/get/create/pages/get-page/edit-page/create-page)
20
+ ├── folders.py # folders parser + handlers (list/get/create/update/delete)
21
+ ├── lists.py # lists parser + handlers (list/get/create/update/delete)
22
+ ├── spaces.py # spaces parser + handlers (list/get/statuses)
23
+ ├── tags.py # tags parser + handlers (list/add/remove)
24
+ ├── team.py # team parser + handlers (whoami/members)
25
+ └── init.py # clickup init setup command
26
+ ```
27
+
28
+ Each command module owns both its argparse parser definition (`register_parser()`) and its handler functions. `cli.py` builds the root parser and delegates to each module's `register_parser(subparsers, F)`.
29
+
30
+ ## Argument Pattern: Positional + Flag Aliases
31
+
32
+ Every positional argument (task_id, query, doc_id, page_id, etc.) also accepts a `--flag` form via `add_id_argument()` from helpers.py. This makes the CLI usable by both humans (positional) and AI agents (flags).
33
+
34
+ ```python
35
+ # In register_parser():
36
+ add_id_argument(parser, "task_id", "ClickUp task ID")
37
+
38
+ # Accepts both:
39
+ # clickup tasks get abc123
40
+ # clickup tasks get --task-id abc123
41
+ ```
42
+
43
+ Resolution happens in `cli.py` via `resolve_id_args(args)` after parsing. If both forms are provided, it errors. If neither is provided, it errors with a helpful message.
44
+
45
+ When adding new commands with ID arguments, always use `add_id_argument()` instead of `parser.add_argument()` for positional IDs.
46
+
47
+ ## Space Inference
48
+
49
+ `tasks create` auto-infers `--space` from `--list` via a lazy API lookup (`GET /v2/list/{id}`). The `_infer_space_from_list()` function in tasks.py reverse-maps the space ID to a config name. This eliminates the most common agent error.
50
+
51
+ ## Development Setup
52
+
53
+ ```bash
54
+ pip install -e ".[dev]"
55
+ pytest -v
56
+ ruff check src/ tests/
57
+ ```
58
+
59
+ ## Adding a New Command
60
+
61
+ 1. Create or extend a file in `src/clickup_cli/commands/`
62
+ 2. Add the handler function and `register_parser()` in the same module
63
+ 3. Register the handler in `commands/__init__.py` HANDLERS dict
64
+ 4. If it's a new command group, call `register_parser()` from `cli.py`'s `build_parser()`
65
+ 5. Add detailed `--help` text (description + epilog with examples)
66
+ 6. Mutating commands must support `--dry-run`
67
+ 7. All output goes to stdout as JSON, errors to stderr
68
+ 8. Add tests in `tests/`
69
+
70
+ ## Rules
71
+
72
+ - Help text must be self-sufficient — an agent should use `--help` to discover correct usage
73
+ - No workspace-specific values in help text or source code
74
+ - Stdout is always JSON. Errors and warnings go to stderr.
75
+ - Every mutation supports `--dry-run`
76
+ - Config is lazy-loaded — the `init` command works without any config file
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Efe Toker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.