qbraid-cli 0.10.9a0__tar.gz → 0.11.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.
Potentially problematic release.
This version of qbraid-cli might be problematic. Click here for more details.
- {qbraid_cli-0.10.9a0/qbraid_cli.egg-info → qbraid_cli-0.11.0}/PKG-INFO +2 -3
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/pyproject.toml +4 -7
- qbraid_cli-0.11.0/qbraid_cli/_version.py +18 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/configure/app.py +87 -0
- qbraid_cli-0.11.0/qbraid_cli/configure/claude_config.py +215 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0/qbraid_cli.egg-info}/PKG-INFO +2 -3
- qbraid_cli-0.11.0/qbraid_cli.egg-info/SOURCES.txt +50 -0
- qbraid_cli-0.11.0/tests/test_version.py +151 -0
- qbraid_cli-0.10.9a0/.env.example +0 -4
- qbraid_cli-0.10.9a0/.github/ISSUE_TEMPLATE/bug_report.yml +0 -34
- qbraid_cli-0.10.9a0/.github/ISSUE_TEMPLATE/feature_request.yml +0 -25
- qbraid_cli-0.10.9a0/.github/PULL_REQUEST_TEMPLATE.md +0 -3
- qbraid_cli-0.10.9a0/.github/dependabot.yml +0 -15
- qbraid_cli-0.10.9a0/.github/workflows/bump-version.yml +0 -71
- qbraid_cli-0.10.9a0/.github/workflows/ci-pr-target.yml +0 -81
- qbraid_cli-0.10.9a0/.github/workflows/docs-pr.yml +0 -128
- qbraid_cli-0.10.9a0/.github/workflows/docs.yml +0 -41
- qbraid_cli-0.10.9a0/.github/workflows/format.yml +0 -30
- qbraid_cli-0.10.9a0/.github/workflows/main.yml +0 -84
- qbraid_cli-0.10.9a0/.github/workflows/pre-release.yml +0 -38
- qbraid_cli-0.10.9a0/.github/workflows/publish.yml +0 -30
- qbraid_cli-0.10.9a0/.github/workflows/tag-on-merge.yml +0 -36
- qbraid_cli-0.10.9a0/.gitignore +0 -167
- qbraid_cli-0.10.9a0/.readthedocs.yml +0 -23
- qbraid_cli-0.10.9a0/CLAUDE.md +0 -904
- qbraid_cli-0.10.9a0/CONTRIBUTING.md +0 -128
- qbraid_cli-0.10.9a0/MANIFEST.IN +0 -6
- qbraid_cli-0.10.9a0/MCP_IMPLEMENTATION_PROGRESS.md +0 -329
- qbraid_cli-0.10.9a0/Makefile +0 -58
- qbraid_cli-0.10.9a0/docs/Makefile +0 -21
- qbraid_cli-0.10.9a0/docs/_static/favicon.ico +0 -0
- qbraid_cli-0.10.9a0/docs/_static/logo.png +0 -0
- qbraid_cli-0.10.9a0/docs/_static/style/custom.css +0 -269
- qbraid_cli-0.10.9a0/docs/_static/style/s4defs-roles.css +0 -329
- qbraid_cli-0.10.9a0/docs/conf.py +0 -75
- qbraid_cli-0.10.9a0/docs/index.rst +0 -134
- qbraid_cli-0.10.9a0/docs/make.bat +0 -36
- qbraid_cli-0.10.9a0/docs/requirements.txt +0 -7
- qbraid_cli-0.10.9a0/qbraid_cli.egg-info/SOURCES.txt +0 -130
- qbraid_cli-0.10.9a0/tests/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/account/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/account/test_account_credits.py +0 -86
- qbraid_cli-0.10.9a0/tests/admin/test_headers.py +0 -232
- qbraid_cli-0.10.9a0/tests/configure/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/configure/test_configure_commands.py +0 -117
- qbraid_cli-0.10.9a0/tests/configure/test_configure_default.py +0 -61
- qbraid_cli-0.10.9a0/tests/configure/test_configure_prompt_for_config.py +0 -54
- qbraid_cli-0.10.9a0/tests/configure/test_configure_set.py +0 -37
- qbraid_cli-0.10.9a0/tests/configure/test_configure_validate_input.py +0 -65
- qbraid_cli-0.10.9a0/tests/conftest.py +0 -35
- qbraid_cli-0.10.9a0/tests/devices/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/devices/test_devices_get.py +0 -278
- qbraid_cli-0.10.9a0/tests/devices/test_devices_list.py +0 -74
- qbraid_cli-0.10.9a0/tests/devices/test_devices_validations.py +0 -72
- qbraid_cli-0.10.9a0/tests/envs/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/envs/test_envs_activate.py +0 -42
- qbraid_cli-0.10.9a0/tests/envs/test_envs_activate_find_shell_rc.py +0 -54
- qbraid_cli-0.10.9a0/tests/envs/test_envs_activate_print_command.py +0 -38
- qbraid_cli-0.10.9a0/tests/envs/test_envs_activate_pyenv.py +0 -70
- qbraid_cli-0.10.9a0/tests/envs/test_envs_create_from_yaml.py +0 -108
- qbraid_cli-0.10.9a0/tests/envs/test_envs_list.py +0 -37
- qbraid_cli-0.10.9a0/tests/envs/test_envs_remove.py +0 -34
- qbraid_cli-0.10.9a0/tests/files/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/files/test_files_commands.py +0 -148
- qbraid_cli-0.10.9a0/tests/jobs/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_disable.py +0 -51
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_enable.py +0 -69
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_get.py +0 -179
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_list.py +0 -50
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_state.py +0 -32
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_toggle_braket_confirm.py +0 -52
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_toggle_braket_disable.py +0 -100
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_toggle_braket_enable.py +0 -107
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_toggle_braket_get_data.py +0 -185
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_validate_get_state.py +0 -94
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_validate_handle_state.py +0 -60
- qbraid_cli-0.10.9a0/tests/jobs/test_jobs_validate_library.py +0 -35
- qbraid_cli-0.10.9a0/tests/kernels/__init__.py +0 -0
- qbraid_cli-0.10.9a0/tests/kernels/test_kernels_list.py +0 -82
- qbraid_cli-0.10.9a0/tests/mcp/__init__.py +0 -2
- qbraid_cli-0.10.9a0/tests/mcp/test_mcp_list.py +0 -219
- qbraid_cli-0.10.9a0/tests/mcp/test_mcp_serve.py +0 -393
- qbraid_cli-0.10.9a0/tests/mcp/test_mcp_status.py +0 -48
- qbraid_cli-0.10.9a0/tests/resources/envs/correct.yaml +0 -16
- qbraid_cli-0.10.9a0/tests/resources/envs/icon.png +0 -0
- qbraid_cli-0.10.9a0/tests/resources/envs/incorrect.yaml +0 -4
- qbraid_cli-0.10.9a0/tools/bump_version.py +0 -28
- qbraid_cli-0.10.9a0/tools/create_dev_build.sh +0 -88
- qbraid_cli-0.10.9a0/tools/install_wheel_extras.sh +0 -67
- qbraid_cli-0.10.9a0/tools/split_md.py +0 -185
- qbraid_cli-0.10.9a0/tools/split_rst.py +0 -119
- qbraid_cli-0.10.9a0/tools/stamp_pre_release.py +0 -36
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/LICENSE +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/README.md +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/account/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/account/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/admin/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/admin/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/admin/headers.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/admin/validation.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/chat/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/chat/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/configure/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/configure/actions.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/devices/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/devices/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/devices/validation.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/envs/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/envs/activate.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/envs/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/envs/create.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/envs/data_handling.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/exceptions.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/files/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/files/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/handlers.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/jobs/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/jobs/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/jobs/toggle_braket.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/jobs/validation.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/kernels/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/kernels/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/main.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/mcp/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/mcp/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/mcp/serve.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/pip/__init__.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/pip/app.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/pip/hooks.py +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli/py.typed +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli.egg-info/dependency_links.txt +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli.egg-info/entry_points.txt +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli.egg-info/requires.txt +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/qbraid_cli.egg-info/top_level.txt +0 -0
- {qbraid_cli-0.10.9a0 → qbraid_cli-0.11.0}/setup.cfg +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Command Line Interface for interacting with all parts of the qBraid platform.
|
|
5
5
|
Author-email: qBraid Development Team <contact@qbraid.com>
|
|
6
|
-
License: Proprietary
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
7
|
Project-URL: Homepage, https://docs.qbraid.com/cli/user-guide/overview
|
|
8
8
|
Project-URL: Documentation, https://docs.qbraid.com/cli/api-reference/qbraid
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/qBraid/community/issues
|
|
@@ -12,7 +12,6 @@ Keywords: qbraid,cli,quantum,cloud
|
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Natural Language :: English
|
|
15
|
-
Classifier: License :: Other/Proprietary License
|
|
16
15
|
Classifier: Intended Audience :: System Administrators
|
|
17
16
|
Classifier: Operating System :: Microsoft :: Windows
|
|
18
17
|
Classifier: Operating System :: POSIX :: Linux
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=42", "wheel"
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qbraid-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
8
8
|
description = "Command Line Interface for interacting with all parts of the qBraid platform."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "qBraid Development Team", email = "contact@qbraid.com" }]
|
|
11
|
-
license =
|
|
11
|
+
license = "LicenseRef-Proprietary"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
12
13
|
keywords = ["qbraid", "cli", "quantum", "cloud"]
|
|
13
14
|
classifiers = [
|
|
14
15
|
"Development Status :: 5 - Production/Stable",
|
|
15
16
|
"Intended Audience :: Developers",
|
|
16
17
|
"Natural Language :: English",
|
|
17
|
-
"License :: Other/Proprietary License",
|
|
18
18
|
"Intended Audience :: System Administrators",
|
|
19
19
|
"Operating System :: Microsoft :: Windows",
|
|
20
20
|
"Operating System :: POSIX :: Linux",
|
|
@@ -50,9 +50,6 @@ dev = ["isort", "black", "pytest", "pytest-cov", "pytest-asyncio"]
|
|
|
50
50
|
[project.scripts]
|
|
51
51
|
qbraid = "qbraid_cli.main:app"
|
|
52
52
|
|
|
53
|
-
[tool.setuptools_scm]
|
|
54
|
-
write_to = "qbraid_cli/_version.py"
|
|
55
|
-
|
|
56
53
|
[tool.black]
|
|
57
54
|
line-length = 100
|
|
58
55
|
target-version = ["py39", "py310", "py311", "py312"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Module defining package version.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
__version__ = importlib.metadata.version("qbraid-cli")
|
|
13
|
+
except Exception: # pylint: disable=broad-exception-caught # pragma: no cover
|
|
14
|
+
__version__ = "dev"
|
|
15
|
+
|
|
16
|
+
__version_tuple__ = tuple(int(part) if part.isdigit() else part for part in __version__.split("."))
|
|
17
|
+
|
|
18
|
+
__all__ = ["__version__", "__version_tuple__"]
|
|
@@ -131,5 +131,92 @@ def configure_magic():
|
|
|
131
131
|
console.print(f"\n\t{in_1}\n\n\t{in_2}\n")
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
@configure_app.command(name="claude")
|
|
135
|
+
def configure_claude(
|
|
136
|
+
action: str = typer.Argument(
|
|
137
|
+
...,
|
|
138
|
+
help="Action to perform: 'add', 'remove', or 'show'",
|
|
139
|
+
),
|
|
140
|
+
overwrite: bool = typer.Option(
|
|
141
|
+
False,
|
|
142
|
+
"--overwrite",
|
|
143
|
+
help="Overwrite existing MCP server configuration",
|
|
144
|
+
),
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Manage qBraid MCP server in Claude Desktop configuration.
|
|
148
|
+
|
|
149
|
+
Add, remove, or show the qBraid MCP server in your Claude Desktop config
|
|
150
|
+
(claude_desktop_config.json).
|
|
151
|
+
"""
|
|
152
|
+
# pylint: disable-next=import-outside-toplevel
|
|
153
|
+
from qbraid_cli.configure.claude_config import (
|
|
154
|
+
add_qbraid_mcp_server,
|
|
155
|
+
get_claude_config_path,
|
|
156
|
+
get_qbraid_mcp_server_config,
|
|
157
|
+
remove_qbraid_mcp_server,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
console = Console()
|
|
161
|
+
|
|
162
|
+
# Validate action
|
|
163
|
+
valid_actions = ["add", "remove", "show"]
|
|
164
|
+
if action not in valid_actions:
|
|
165
|
+
console.print(
|
|
166
|
+
f"[red]Error:[/red] Invalid action '{action}'. "
|
|
167
|
+
f"Must be one of: {', '.join(valid_actions)}"
|
|
168
|
+
)
|
|
169
|
+
raise typer.Exit(1)
|
|
170
|
+
|
|
171
|
+
# Check if Claude config exists (except for show which can work without it)
|
|
172
|
+
if action != "show":
|
|
173
|
+
config_path = get_claude_config_path()
|
|
174
|
+
if config_path is None:
|
|
175
|
+
console.print(
|
|
176
|
+
"[yellow]Warning:[/yellow] Claude Desktop config file not found.\n"
|
|
177
|
+
"Expected locations:\n"
|
|
178
|
+
" - macOS: ~/Library/Application Support/Claude/claude_desktop_config.json\n"
|
|
179
|
+
" - Windows: %APPDATA%\\Claude\\claude_desktop_config.json\n"
|
|
180
|
+
" - Linux: ~/.config/Claude/claude_desktop_config.json\n\n"
|
|
181
|
+
"Creating new config file..."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Perform action
|
|
185
|
+
if action == "add":
|
|
186
|
+
success, message = add_qbraid_mcp_server(overwrite=overwrite)
|
|
187
|
+
if success:
|
|
188
|
+
console.print(f"[green]Success:[/green] {message}")
|
|
189
|
+
console.print(
|
|
190
|
+
"\n[yellow]Note:[/yellow] You may need to restart Claude Desktop "
|
|
191
|
+
"for changes to take effect."
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
console.print(f"[red]Error:[/red] {message}")
|
|
195
|
+
raise typer.Exit(1)
|
|
196
|
+
|
|
197
|
+
elif action == "remove":
|
|
198
|
+
success, message = remove_qbraid_mcp_server()
|
|
199
|
+
if success:
|
|
200
|
+
console.print(f"[green]Success:[/green] {message}")
|
|
201
|
+
console.print(
|
|
202
|
+
"\n[yellow]Note:[/yellow] You may need to restart Claude Desktop "
|
|
203
|
+
"for changes to take effect."
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
console.print(f"[red]Error:[/red] {message}")
|
|
207
|
+
raise typer.Exit(1)
|
|
208
|
+
|
|
209
|
+
elif action == "show":
|
|
210
|
+
server_config = get_qbraid_mcp_server_config()
|
|
211
|
+
if server_config is None:
|
|
212
|
+
console.print("No qBraid MCP server found in Claude Desktop configuration.")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
console.print("\n[cyan]qBraid MCP Server Configuration:[/cyan]\n")
|
|
216
|
+
console.print(f" Command: {server_config.get('command', 'N/A')}")
|
|
217
|
+
console.print(f" Args: {server_config.get('args', [])}")
|
|
218
|
+
console.print()
|
|
219
|
+
|
|
220
|
+
|
|
134
221
|
if __name__ == "__main__":
|
|
135
222
|
configure_app()
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Utility functions for managing Claude Desktop configuration.
|
|
6
|
+
|
|
7
|
+
This module provides cross-platform support for locating and updating
|
|
8
|
+
the claude_desktop_config.json file used by Claude Desktop.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import shutil
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_claude_config_path() -> Optional[Path]:
|
|
20
|
+
"""
|
|
21
|
+
Get the path to the Claude Desktop configuration file.
|
|
22
|
+
|
|
23
|
+
Returns the platform-specific path to claude_desktop_config.json:
|
|
24
|
+
- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
25
|
+
- Windows: %APPDATA%\\Claude\\claude_desktop_config.json
|
|
26
|
+
- Linux: ~/.config/Claude/claude_desktop_config.json
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Path to the config file if it exists, None otherwise
|
|
30
|
+
"""
|
|
31
|
+
system = platform.system()
|
|
32
|
+
|
|
33
|
+
if system == "Darwin": # macOS
|
|
34
|
+
config_path = Path.home() / "Library" / "Application Support" / "Claude"
|
|
35
|
+
elif system == "Windows":
|
|
36
|
+
appdata = os.environ.get("APPDATA")
|
|
37
|
+
if not appdata:
|
|
38
|
+
return None
|
|
39
|
+
config_path = Path(appdata) / "Claude"
|
|
40
|
+
elif system == "Linux":
|
|
41
|
+
config_path = Path.home() / ".config" / "Claude"
|
|
42
|
+
else:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
config_file = config_path / "claude_desktop_config.json"
|
|
46
|
+
return config_file if config_file.exists() else None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_claude_config() -> dict:
|
|
50
|
+
"""
|
|
51
|
+
Load the Claude Desktop configuration file.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Configuration dictionary. If file doesn't exist, returns empty dict.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
json.JSONDecodeError: If config file exists but contains invalid JSON
|
|
58
|
+
OSError: If there are permission issues reading the file
|
|
59
|
+
"""
|
|
60
|
+
config_path = get_claude_config_path()
|
|
61
|
+
|
|
62
|
+
if config_path is None or not config_path.exists():
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
66
|
+
return json.load(f)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def save_claude_config(config: dict) -> Path:
|
|
70
|
+
"""
|
|
71
|
+
Save the Claude Desktop configuration file.
|
|
72
|
+
|
|
73
|
+
Creates the configuration directory if it doesn't exist.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
config: Configuration dictionary to save
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Path to the saved configuration file
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
OSError: If there are permission issues or the platform is unsupported
|
|
83
|
+
"""
|
|
84
|
+
system = platform.system()
|
|
85
|
+
|
|
86
|
+
if system == "Darwin": # macOS
|
|
87
|
+
config_dir = Path.home() / "Library" / "Application Support" / "Claude"
|
|
88
|
+
elif system == "Windows":
|
|
89
|
+
appdata = os.environ.get("APPDATA")
|
|
90
|
+
if not appdata:
|
|
91
|
+
raise OSError("APPDATA environment variable not set")
|
|
92
|
+
config_dir = Path(appdata) / "Claude"
|
|
93
|
+
elif system == "Linux":
|
|
94
|
+
config_dir = Path.home() / ".config" / "Claude"
|
|
95
|
+
else:
|
|
96
|
+
raise OSError(f"Unsupported platform: {system}")
|
|
97
|
+
|
|
98
|
+
# Create directory if it doesn't exist
|
|
99
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
config_file = config_dir / "claude_desktop_config.json"
|
|
102
|
+
|
|
103
|
+
# Write config with pretty formatting
|
|
104
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
105
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
106
|
+
f.write("\n") # Add trailing newline
|
|
107
|
+
|
|
108
|
+
return config_file
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def add_qbraid_mcp_server(overwrite: bool = False) -> tuple[bool, str]:
|
|
112
|
+
"""
|
|
113
|
+
Add qBraid MCP server configuration to Claude Desktop config.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
overwrite: If True, overwrite existing qbraid entry
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Tuple of (success: bool, message: str)
|
|
120
|
+
"""
|
|
121
|
+
# Get the full path to qbraid executable
|
|
122
|
+
qbraid_path = shutil.which("qbraid")
|
|
123
|
+
if not qbraid_path:
|
|
124
|
+
return (
|
|
125
|
+
False,
|
|
126
|
+
"Could not find qbraid executable in PATH. "
|
|
127
|
+
"Please ensure qbraid-cli is installed and accessible.",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
config = load_claude_config()
|
|
132
|
+
except json.JSONDecodeError as e:
|
|
133
|
+
return False, f"Invalid JSON in Claude config file: {e}"
|
|
134
|
+
except OSError as e:
|
|
135
|
+
return False, f"Error reading Claude config file: {e}"
|
|
136
|
+
|
|
137
|
+
# Initialize mcpServers section if it doesn't exist
|
|
138
|
+
if "mcpServers" not in config:
|
|
139
|
+
config["mcpServers"] = {}
|
|
140
|
+
|
|
141
|
+
# Determine server name
|
|
142
|
+
server_name = "qbraid"
|
|
143
|
+
|
|
144
|
+
# Check if server already exists
|
|
145
|
+
if server_name in config["mcpServers"] and not overwrite:
|
|
146
|
+
return (
|
|
147
|
+
False,
|
|
148
|
+
f"MCP server '{server_name}' already exists in config. "
|
|
149
|
+
"Use --overwrite to replace it.",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Build server configuration with full path to qbraid
|
|
153
|
+
server_config = {"command": qbraid_path, "args": ["mcp", "serve"]}
|
|
154
|
+
|
|
155
|
+
# Add server to config
|
|
156
|
+
config["mcpServers"][server_name] = server_config
|
|
157
|
+
|
|
158
|
+
# Save config
|
|
159
|
+
try:
|
|
160
|
+
config_path = save_claude_config(config)
|
|
161
|
+
action = "Updated" if server_name in config["mcpServers"] else "Added"
|
|
162
|
+
return True, f"{action} MCP server '{server_name}' in [cyan]{config_path}[/cyan]"
|
|
163
|
+
except OSError as e:
|
|
164
|
+
return False, f"Error saving Claude config file: {e}"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def remove_qbraid_mcp_server() -> tuple[bool, str]:
|
|
168
|
+
"""
|
|
169
|
+
Remove qBraid MCP server configuration from Claude Desktop config.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Tuple of (success: bool, message: str)
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
config = load_claude_config()
|
|
176
|
+
except json.JSONDecodeError as e:
|
|
177
|
+
return False, f"Invalid JSON in Claude config file: {e}"
|
|
178
|
+
except OSError as e:
|
|
179
|
+
return False, f"Error reading Claude config file: {e}"
|
|
180
|
+
|
|
181
|
+
if "mcpServers" not in config:
|
|
182
|
+
return False, "No mcpServers section found in Claude config"
|
|
183
|
+
|
|
184
|
+
server_name = "qbraid"
|
|
185
|
+
|
|
186
|
+
if server_name not in config["mcpServers"]:
|
|
187
|
+
return False, f"MCP server '{server_name}' not found in config"
|
|
188
|
+
|
|
189
|
+
# Remove server
|
|
190
|
+
del config["mcpServers"][server_name]
|
|
191
|
+
|
|
192
|
+
# Save config
|
|
193
|
+
try:
|
|
194
|
+
config_path = save_claude_config(config)
|
|
195
|
+
return True, f"Removed MCP server '{server_name}' from [cyan]{config_path}[/cyan]"
|
|
196
|
+
except OSError as e:
|
|
197
|
+
return False, f"Error saving Claude config file: {e}"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_qbraid_mcp_server_config() -> Optional[dict]:
|
|
201
|
+
"""
|
|
202
|
+
Get qBraid MCP server configuration from Claude Desktop config.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Server configuration dict if found, None otherwise
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
config = load_claude_config()
|
|
209
|
+
except (json.JSONDecodeError, OSError):
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
if "mcpServers" not in config:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
return config["mcpServers"].get("qbraid")
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Command Line Interface for interacting with all parts of the qBraid platform.
|
|
5
5
|
Author-email: qBraid Development Team <contact@qbraid.com>
|
|
6
|
-
License: Proprietary
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
7
|
Project-URL: Homepage, https://docs.qbraid.com/cli/user-guide/overview
|
|
8
8
|
Project-URL: Documentation, https://docs.qbraid.com/cli/api-reference/qbraid
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/qBraid/community/issues
|
|
@@ -12,7 +12,6 @@ Keywords: qbraid,cli,quantum,cloud
|
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Natural Language :: English
|
|
15
|
-
Classifier: License :: Other/Proprietary License
|
|
16
15
|
Classifier: Intended Audience :: System Administrators
|
|
17
16
|
Classifier: Operating System :: Microsoft :: Windows
|
|
18
17
|
Classifier: Operating System :: POSIX :: Linux
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
qbraid_cli/__init__.py
|
|
5
|
+
qbraid_cli/_version.py
|
|
6
|
+
qbraid_cli/exceptions.py
|
|
7
|
+
qbraid_cli/handlers.py
|
|
8
|
+
qbraid_cli/main.py
|
|
9
|
+
qbraid_cli/py.typed
|
|
10
|
+
qbraid_cli.egg-info/PKG-INFO
|
|
11
|
+
qbraid_cli.egg-info/SOURCES.txt
|
|
12
|
+
qbraid_cli.egg-info/dependency_links.txt
|
|
13
|
+
qbraid_cli.egg-info/entry_points.txt
|
|
14
|
+
qbraid_cli.egg-info/requires.txt
|
|
15
|
+
qbraid_cli.egg-info/top_level.txt
|
|
16
|
+
qbraid_cli/account/__init__.py
|
|
17
|
+
qbraid_cli/account/app.py
|
|
18
|
+
qbraid_cli/admin/__init__.py
|
|
19
|
+
qbraid_cli/admin/app.py
|
|
20
|
+
qbraid_cli/admin/headers.py
|
|
21
|
+
qbraid_cli/admin/validation.py
|
|
22
|
+
qbraid_cli/chat/__init__.py
|
|
23
|
+
qbraid_cli/chat/app.py
|
|
24
|
+
qbraid_cli/configure/__init__.py
|
|
25
|
+
qbraid_cli/configure/actions.py
|
|
26
|
+
qbraid_cli/configure/app.py
|
|
27
|
+
qbraid_cli/configure/claude_config.py
|
|
28
|
+
qbraid_cli/devices/__init__.py
|
|
29
|
+
qbraid_cli/devices/app.py
|
|
30
|
+
qbraid_cli/devices/validation.py
|
|
31
|
+
qbraid_cli/envs/__init__.py
|
|
32
|
+
qbraid_cli/envs/activate.py
|
|
33
|
+
qbraid_cli/envs/app.py
|
|
34
|
+
qbraid_cli/envs/create.py
|
|
35
|
+
qbraid_cli/envs/data_handling.py
|
|
36
|
+
qbraid_cli/files/__init__.py
|
|
37
|
+
qbraid_cli/files/app.py
|
|
38
|
+
qbraid_cli/jobs/__init__.py
|
|
39
|
+
qbraid_cli/jobs/app.py
|
|
40
|
+
qbraid_cli/jobs/toggle_braket.py
|
|
41
|
+
qbraid_cli/jobs/validation.py
|
|
42
|
+
qbraid_cli/kernels/__init__.py
|
|
43
|
+
qbraid_cli/kernels/app.py
|
|
44
|
+
qbraid_cli/mcp/__init__.py
|
|
45
|
+
qbraid_cli/mcp/app.py
|
|
46
|
+
qbraid_cli/mcp/serve.py
|
|
47
|
+
qbraid_cli/pip/__init__.py
|
|
48
|
+
qbraid_cli/pip/app.py
|
|
49
|
+
qbraid_cli/pip/hooks.py
|
|
50
|
+
tests/test_version.py
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Unit tests for qbraid_cli._version module.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from unittest.mock import patch
|
|
9
|
+
|
|
10
|
+
import qbraid_cli._version as version_module
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_version_is_string():
|
|
14
|
+
"""Test that __version__ is a string."""
|
|
15
|
+
assert isinstance(version_module.__version__, str)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_version_not_empty():
|
|
19
|
+
"""Test that __version__ is not empty."""
|
|
20
|
+
assert len(version_module.__version__) > 0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_version_format():
|
|
24
|
+
"""Test that __version__ follows semantic versioning or is 'dev'."""
|
|
25
|
+
version = version_module.__version__
|
|
26
|
+
if version != "dev":
|
|
27
|
+
# Should have at least major.minor format
|
|
28
|
+
parts = version.split(".")
|
|
29
|
+
assert len(parts) >= 2, "Version should have at least major.minor"
|
|
30
|
+
# First two parts should be numeric
|
|
31
|
+
assert parts[0].isdigit(), "Major version should be numeric"
|
|
32
|
+
assert parts[1].split("-")[0].isdigit(), "Minor version should be numeric"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_version_tuple_is_tuple():
|
|
36
|
+
"""Test that __version_tuple__ is a tuple."""
|
|
37
|
+
assert isinstance(version_module.__version_tuple__, tuple)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_version_tuple_not_empty():
|
|
41
|
+
"""Test that __version_tuple__ is not empty."""
|
|
42
|
+
assert len(version_module.__version_tuple__) > 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_version_tuple_structure():
|
|
46
|
+
"""Test that __version_tuple__ has expected structure."""
|
|
47
|
+
version_tuple = version_module.__version_tuple__
|
|
48
|
+
# Should have at least 2 elements (major, minor)
|
|
49
|
+
assert len(version_tuple) >= 2
|
|
50
|
+
# First two elements should be integers (major, minor)
|
|
51
|
+
if version_module.__version__ != "dev":
|
|
52
|
+
assert isinstance(version_tuple[0], int), "Major version should be int"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_version_tuple_matches_version_string():
|
|
56
|
+
"""Test that __version_tuple__ components match __version__ string."""
|
|
57
|
+
version = version_module.__version__
|
|
58
|
+
version_tuple = version_module.__version_tuple__
|
|
59
|
+
|
|
60
|
+
parts = version.split(".")
|
|
61
|
+
for i, part in enumerate(parts):
|
|
62
|
+
if i < len(version_tuple):
|
|
63
|
+
if part.isdigit():
|
|
64
|
+
assert version_tuple[i] == int(part), f"Tuple element {i} should match version part"
|
|
65
|
+
else:
|
|
66
|
+
assert version_tuple[i] == part, f"Tuple element {i} should match version part"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_module_exports():
|
|
70
|
+
"""Test that __all__ contains expected exports."""
|
|
71
|
+
assert hasattr(version_module, "__all__")
|
|
72
|
+
assert "__version__" in version_module.__all__
|
|
73
|
+
assert "__version_tuple__" in version_module.__all__
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_version_fallback_on_metadata_error():
|
|
77
|
+
"""Test that __version__ falls back to 'dev' when metadata is not available."""
|
|
78
|
+
# pylint: disable=import-outside-toplevel,reimported
|
|
79
|
+
import sys
|
|
80
|
+
|
|
81
|
+
with patch("importlib.metadata.version", side_effect=Exception("Metadata not found")):
|
|
82
|
+
# Remove from sys.modules to force reload
|
|
83
|
+
if "qbraid_cli._version" in sys.modules:
|
|
84
|
+
del sys.modules["qbraid_cli._version"]
|
|
85
|
+
|
|
86
|
+
# Import again (will use mocked metadata.version)
|
|
87
|
+
import qbraid_cli._version as reloaded_version
|
|
88
|
+
|
|
89
|
+
# Should fall back to 'dev'
|
|
90
|
+
assert reloaded_version.__version__ == "dev"
|
|
91
|
+
|
|
92
|
+
# Clean up - reload the original module
|
|
93
|
+
if "qbraid_cli._version" in sys.modules:
|
|
94
|
+
del sys.modules["qbraid_cli._version"]
|
|
95
|
+
import qbraid_cli._version # noqa: F401 # pylint: disable=unused-import
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_version_tuple_handles_dev():
|
|
99
|
+
"""Test that __version_tuple__ correctly handles 'dev' version."""
|
|
100
|
+
# pylint: disable=import-outside-toplevel,reimported
|
|
101
|
+
import sys
|
|
102
|
+
|
|
103
|
+
with patch("importlib.metadata.version", side_effect=Exception("Metadata not found")):
|
|
104
|
+
# Remove from sys.modules to force reload
|
|
105
|
+
if "qbraid_cli._version" in sys.modules:
|
|
106
|
+
del sys.modules["qbraid_cli._version"]
|
|
107
|
+
|
|
108
|
+
# Import again (will use mocked metadata.version)
|
|
109
|
+
import qbraid_cli._version as reloaded_version
|
|
110
|
+
|
|
111
|
+
# Should fall back to 'dev'
|
|
112
|
+
assert reloaded_version.__version__ == "dev"
|
|
113
|
+
assert reloaded_version.__version_tuple__ == ("dev",)
|
|
114
|
+
|
|
115
|
+
# Clean up
|
|
116
|
+
if "qbraid_cli._version" in sys.modules:
|
|
117
|
+
del sys.modules["qbraid_cli._version"]
|
|
118
|
+
import qbraid_cli._version # noqa: F401 # pylint: disable=unused-import
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_version_tuple_handles_prerelease():
|
|
122
|
+
"""Test that __version_tuple__ correctly handles pre-release versions."""
|
|
123
|
+
# Example: "0.10.9.dev" or "0.10.9-beta.1"
|
|
124
|
+
test_version = "0.10.9.dev"
|
|
125
|
+
parts = test_version.split(".")
|
|
126
|
+
version_tuple = tuple(int(part) if part.isdigit() else part for part in parts)
|
|
127
|
+
|
|
128
|
+
# Should have 4 elements: (0, 10, 9, 'dev')
|
|
129
|
+
assert len(version_tuple) == 4
|
|
130
|
+
assert version_tuple[0] == 0
|
|
131
|
+
assert version_tuple[1] == 10
|
|
132
|
+
assert version_tuple[2] == 9
|
|
133
|
+
assert version_tuple[3] == "dev"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_version_comparison():
|
|
137
|
+
"""Test that version tuples can be compared."""
|
|
138
|
+
# Create sample version tuples
|
|
139
|
+
v1 = (0, 10, 8)
|
|
140
|
+
v2 = (0, 10, 9)
|
|
141
|
+
v3 = (1, 0, 0)
|
|
142
|
+
|
|
143
|
+
assert v1 < v2
|
|
144
|
+
assert v2 < v3
|
|
145
|
+
assert v1 < v3
|
|
146
|
+
|
|
147
|
+
# Test with current version tuple if it's not 'dev'
|
|
148
|
+
if version_module.__version__ != "dev":
|
|
149
|
+
current = version_module.__version_tuple__[:3] # Take major.minor.patch
|
|
150
|
+
# Should be comparable to tuples - test reflexivity
|
|
151
|
+
assert current == current # pylint: disable=comparison-with-itself
|
qbraid_cli-0.10.9a0/.env.example
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Bug report 🚩
|
|
2
|
-
description: Create a report to help us improve
|
|
3
|
-
title: "[BUG] "
|
|
4
|
-
labels: [bug]
|
|
5
|
-
|
|
6
|
-
body:
|
|
7
|
-
- type: markdown
|
|
8
|
-
attributes:
|
|
9
|
-
value: "Thanks for taking the time to fill out our bug report form."
|
|
10
|
-
|
|
11
|
-
- type: textarea
|
|
12
|
-
attributes:
|
|
13
|
-
label: Environment
|
|
14
|
-
value: |
|
|
15
|
-
- **qBraid-CORE version**:
|
|
16
|
-
- **qBraid-CLI version**:
|
|
17
|
-
- **Python version**:
|
|
18
|
-
- **Operating system**:
|
|
19
|
-
validations:
|
|
20
|
-
required: true
|
|
21
|
-
|
|
22
|
-
- type: textarea
|
|
23
|
-
attributes:
|
|
24
|
-
label: What happened?
|
|
25
|
-
description: Please provide a detailed description of the bug, accompanied by a minimal code example that demonstrates how the error(s) can be reproduced.
|
|
26
|
-
validations:
|
|
27
|
-
required: true
|
|
28
|
-
|
|
29
|
-
- type: textarea
|
|
30
|
-
attributes:
|
|
31
|
-
label: Suggestions (Optional)
|
|
32
|
-
description: We warmly welcome any recommendations on potential fixes, insights, or considerations that contributors should keep in mind when working to resolve this issue.
|
|
33
|
-
validations:
|
|
34
|
-
required: false
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
name: Feature request 💡
|
|
2
|
-
description: Suggest an idea for this project
|
|
3
|
-
title: "[FEATURE] "
|
|
4
|
-
labels: [enhancement]
|
|
5
|
-
|
|
6
|
-
body:
|
|
7
|
-
- type: markdown
|
|
8
|
-
attributes:
|
|
9
|
-
value: "Thank you for taking the time to suggest a feature. Please fill out the details below to help us understand your idea better."
|
|
10
|
-
|
|
11
|
-
- type: textarea
|
|
12
|
-
id: feature-description
|
|
13
|
-
attributes:
|
|
14
|
-
label: Feature Description
|
|
15
|
-
description: "Describe the feature you'd like and the problem it solves. Include any specific use cases that illustrate its benefits."
|
|
16
|
-
placeholder: "Please describe the feature in detail."
|
|
17
|
-
validations:
|
|
18
|
-
required: true
|
|
19
|
-
|
|
20
|
-
- type: textarea
|
|
21
|
-
attributes:
|
|
22
|
-
label: Implementation (Optional)
|
|
23
|
-
description: "Do you have an idea for how this could be implemented? Please include those details here."
|
|
24
|
-
validations:
|
|
25
|
-
required: false
|