gibson-cli 0.8.9__tar.gz → 0.8.11__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.
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/.gitignore +1 -0
- {gibson_cli-0.8.9/gibson_cli.egg-info → gibson_cli-0.8.11}/PKG-INFO +5 -3
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/README.md +1 -1
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/api/ProjectApi.py +9 -2
- gibson_cli-0.8.11/gibson/command/Deploy.py +52 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Help.py +10 -49
- gibson_cli-0.8.11/gibson/command/Studio.py +58 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Version.py +1 -1
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/CommandRouter.py +6 -6
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Configuration.py +4 -0
- gibson_cli-0.8.11/gibson/core/Select.py +270 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/mcp/server.py +7 -6
- {gibson_cli-0.8.9 → gibson_cli-0.8.11/gibson_cli.egg-info}/PKG-INFO +5 -3
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson_cli.egg-info/SOURCES.txt +4 -2
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson_cli.egg-info/requires.txt +3 -1
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/pyproject.toml +4 -2
- gibson_cli-0.8.9/venv/bin/activate_this.py +0 -59
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/.github/workflows/ci.yml +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/.pre-commit-config.yaml +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/bin/build.sh +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/bin/clean.sh +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/bin/release.sh +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/api/BaseApi.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/api/Cli.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/api/DataApi.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/bin/gibson.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/BaseCommand.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Build.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Conf.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Count.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Dev.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Forget.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Merge.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Modify.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Question.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Remove.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Show.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/Tree.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/auth/Auth.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/auth/Login.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/auth/Logout.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Api.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Base.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Code.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Model.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Models.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Schema.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Schemas.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Test.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/code/Tests.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/importer/Import.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/importer/OpenApi.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/list/Entities.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/list/List.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/list/Projects.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/mcp/McpServer.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/new/Module.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/new/New.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/new/Project.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/rename/Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/rename/Rename.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/rewrite/Rewrite.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/tests/test_command_BaseCommand.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/command/tests/test_command_Conf.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Api.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Code.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Custom.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Datastore.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Dependencies.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Dev.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Frameworks.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Modeler.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Paths.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Platform.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Project.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/Version.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/dev/Api.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/dev/Base.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/dev/Model.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/dev/Schema.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/tests/test_conf_Dependencies.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/conf/tests/test_conf_Platform.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Colors.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Completions.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Conversation.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Diff.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Env.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Memory.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/PythonPath.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/Spinner.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/TimeKeeper.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/core/utils.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/data/bash-completion.tmpl +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/data/mysql/default-ref-table.tmpl +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/data/mysql/default-table.tmpl +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/data/postgresql/default-ref-table.tmpl +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/data/postgresql/default-table.tmpl +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/db/TableExceptions.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/db/tests/test_db_TableExceptions.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/dev/Dev.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/Header.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/WorkspaceFooter.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/WorkspaceHeader.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/tests/test_display_Header.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/tests/test_display_WorkspaceFooter.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/display/tests/test_display_WorkspaceHeader.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/lang/Python.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/lang/tests/test_lang_Python.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/auth/Server.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/DataDictionary.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/EntityKeys.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/Manager.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/tests/test_code_context_schema_DataDictionary.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/tests/test_code_context_schema_EntityKeys.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/context/schema/tests/test_code_context_schema_Manager.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/Authenticator.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/BaseCustomization.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/CustomizationManager.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/Index.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/tests/test_code_customization_Authenticator.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/services/code/customization/tests/test_code_customization_BaseCustomization.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/constraints/ReferenceConstraint.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/ForeignKey.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/Index.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/IndexAttribute.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/tests/test_structure_mysql_keys_ForeignKey.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/tests/test_structure_mysql_keys_Index.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/keys/tests/test_structure_mysql_keys_IndexAttribute.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/testing.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/mysql/tests/test_structure_mysql_Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/References.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/table/ForeignKey.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/table/tests/test_structure_postgresql_table_ForeignKey.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/testing.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/postgresql/tests/test_structure_postgresql_Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/structure/tests/test_structure_Entity.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/tests/test_Env.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/tests/test_Memory.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson/tests/test_utils.py +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson_cli.egg-info/dependency_links.txt +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson_cli.egg-info/entry_points.txt +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/gibson_cli.egg-info/top_level.txt +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/ruff.toml +0 -0
- {gibson_cli-0.8.9 → gibson_cli-0.8.11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: gibson-cli
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.11
|
4
4
|
Summary: Gibson Command Line Interface
|
5
5
|
Author-email: GibsonAI <noc@gibsonai.com>
|
6
6
|
Project-URL: Homepage, https://gibsonai.com/
|
@@ -27,6 +27,8 @@ Requires-Dist: faker-sqlalchemy==0.10.2208140
|
|
27
27
|
Requires-Dist: fastapi==0.115.12
|
28
28
|
Requires-Dist: fastapi-cli==0.0.2
|
29
29
|
Requires-Dist: h11==0.14.0
|
30
|
+
Requires-Dist: harlequin==2.1.2
|
31
|
+
Requires-Dist: harlequin-mysql==1.1.0
|
30
32
|
Requires-Dist: httpcore==1.0.5
|
31
33
|
Requires-Dist: httptools==0.6.4
|
32
34
|
Requires-Dist: httpx==0.27.0
|
@@ -60,7 +62,7 @@ Requires-Dist: sniffio==1.3.1
|
|
60
62
|
Requires-Dist: sqlalchemy==1.4.41
|
61
63
|
Requires-Dist: sse-starlette==2.2.1
|
62
64
|
Requires-Dist: starlette==0.46.1
|
63
|
-
Requires-Dist: textual==0.
|
65
|
+
Requires-Dist: textual==0.89.1
|
64
66
|
Requires-Dist: tomli==2.0.1
|
65
67
|
Requires-Dist: typer==0.12.3
|
66
68
|
Requires-Dist: typing-extensions==4.13.0
|
@@ -72,7 +74,7 @@ Requires-Dist: watchfiles==1.0.5
|
|
72
74
|
Requires-Dist: websockets==12.0
|
73
75
|
Requires-Dist: yaspin==3.1.0
|
74
76
|
|
75
|
-
[](https://gibsonai.com/)
|
76
78
|
|
77
79
|
# Gibson CLI
|
78
80
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
1
3
|
from gibson.api.BaseApi import BaseApi
|
2
4
|
from gibson.core.Configuration import Configuration
|
3
5
|
|
@@ -20,8 +22,13 @@ class ProjectApi(BaseApi):
|
|
20
22
|
def lookup(self, uuid: str):
|
21
23
|
return self.get(f"{uuid}")
|
22
24
|
|
23
|
-
def deploy(self, uuid: str):
|
24
|
-
return self.post(
|
25
|
+
def deploy(self, uuid: str, databases: List[str] | None = None):
|
26
|
+
return self.post(
|
27
|
+
f"{uuid}/deploy", {"environments": databases} if databases else None
|
28
|
+
)
|
29
|
+
|
30
|
+
def diff(self, uuid: str):
|
31
|
+
return self.get(f"{uuid}/diff")
|
25
32
|
|
26
33
|
def mcp(self, uuid: str):
|
27
34
|
return self.get(f"{uuid}/mcp")
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from rich.prompt import Confirm
|
2
|
+
|
3
|
+
from gibson.api.ProjectApi import ProjectApi
|
4
|
+
from gibson.command.BaseCommand import BaseCommand
|
5
|
+
from gibson.core.Select import MultiSelect
|
6
|
+
from gibson.core.Spinner import Spinner
|
7
|
+
|
8
|
+
|
9
|
+
class Deploy(BaseCommand):
|
10
|
+
def execute(self):
|
11
|
+
self.configuration.require_login()
|
12
|
+
project_id = self.configuration.require_project_id()
|
13
|
+
project = ProjectApi(self.configuration).lookup(project_id)
|
14
|
+
databases = [database["environment"] for database in project["databases"]]
|
15
|
+
|
16
|
+
selected = MultiSelect(
|
17
|
+
title="Select the database(s) to deploy",
|
18
|
+
options=databases,
|
19
|
+
).prompt()
|
20
|
+
|
21
|
+
if not selected:
|
22
|
+
self.conversation.type("No database selected\n")
|
23
|
+
exit(1)
|
24
|
+
|
25
|
+
for database in selected:
|
26
|
+
diff = ProjectApi(self.configuration).diff(project_id)
|
27
|
+
if diff:
|
28
|
+
self.conversation.type(f"\nDiff for {database} database:\n\n")
|
29
|
+
self.conversation.type(diff)
|
30
|
+
self.conversation.newline()
|
31
|
+
|
32
|
+
if Confirm.ask(
|
33
|
+
f"After reviewing the diff, are you sure you want to deploy the {database} database?"
|
34
|
+
):
|
35
|
+
self.conversation.newline()
|
36
|
+
with Spinner(
|
37
|
+
start_text=f"Deploying {database} database...",
|
38
|
+
success_text=f"Deployed {database} database",
|
39
|
+
fail_text=f"Deployment failed for {database} database",
|
40
|
+
):
|
41
|
+
ProjectApi(self.configuration).deploy(
|
42
|
+
project_id, databases=[database]
|
43
|
+
)
|
44
|
+
|
45
|
+
else:
|
46
|
+
self.conversation.type(
|
47
|
+
f"\nSkipping deployment for {database} database\n",
|
48
|
+
)
|
49
|
+
else:
|
50
|
+
self.conversation.type(
|
51
|
+
f"\nNo changes to deploy for {database} database\n",
|
52
|
+
)
|
@@ -4,7 +4,6 @@ from rich.table import Table
|
|
4
4
|
from rich.text import Text
|
5
5
|
|
6
6
|
from gibson.command.BaseCommand import BaseCommand
|
7
|
-
from gibson.core.Memory import Memory
|
8
7
|
|
9
8
|
|
10
9
|
class Help(BaseCommand):
|
@@ -25,27 +24,26 @@ class Help(BaseCommand):
|
|
25
24
|
"auth": {
|
26
25
|
"description": "authenticate with the gibson cli",
|
27
26
|
"subcommands": ["login", "logout"],
|
28
|
-
"memory": None,
|
29
27
|
},
|
30
28
|
"build": {
|
31
29
|
"description": "create the entities in the datastore",
|
32
30
|
"subcommands": ["datastore"],
|
33
|
-
"memory": "stored",
|
34
31
|
},
|
35
32
|
"code": {
|
36
33
|
"description": "pair program with gibson",
|
37
34
|
"subcommands": ["api", "base", "entity", "models", "schemas", "tests"],
|
38
|
-
"memory": None,
|
39
35
|
},
|
40
36
|
"conf": {
|
41
37
|
"description": "set a configuration variable",
|
42
38
|
"subcommands": None,
|
43
|
-
"memory": None,
|
44
39
|
},
|
45
40
|
"count": {
|
46
41
|
"description": "show the number of entities stored",
|
47
42
|
"subcommands": ["last", "stored"],
|
48
|
-
|
43
|
+
},
|
44
|
+
"deploy": {
|
45
|
+
"description": "deploy the project database(s) with the current schema",
|
46
|
+
"subcommands": None,
|
49
47
|
},
|
50
48
|
"dev": {
|
51
49
|
"description": Text.assemble(
|
@@ -53,73 +51,55 @@ class Help(BaseCommand):
|
|
53
51
|
*dev_mode_text,
|
54
52
|
),
|
55
53
|
"subcommands": ["on", "off"],
|
56
|
-
"memory": None,
|
57
|
-
},
|
58
|
-
"forget": {
|
59
|
-
"description": "delete entities from memory",
|
60
|
-
"subcommands": ["all", "last", "stored"],
|
61
|
-
"memory": "based on user selection",
|
62
54
|
},
|
63
|
-
"help": {"description": "for help", "subcommands": None
|
55
|
+
"help": {"description": "for help", "subcommands": None},
|
64
56
|
"import": {
|
65
57
|
"description": "import entities from a datasource",
|
66
58
|
"subcommands": ["api", "mysql", "pg_dump", "openapi"],
|
67
|
-
"memory": "stored",
|
68
59
|
},
|
69
60
|
"list": {
|
70
61
|
"description": "see a list of your entities or projects",
|
71
62
|
"subcommands": ["entities", "projects"],
|
72
|
-
"memory": None,
|
73
63
|
},
|
74
64
|
"mcp": {
|
75
65
|
"description": "allows tools like Cursor to interact with your gibson project",
|
76
66
|
"subcommands": ["run"],
|
77
|
-
"memory": None,
|
78
|
-
},
|
79
|
-
"merge": {
|
80
|
-
"description": "merge last memory (recent changes) into stored project memory",
|
81
|
-
"subcommands": None,
|
82
|
-
"memory": "last -> stored",
|
83
67
|
},
|
84
68
|
"modify": {
|
85
69
|
"description": "change an entity using natural language",
|
86
70
|
"subcommands": None,
|
87
|
-
"memory": "last > stored",
|
88
71
|
},
|
89
72
|
"new": {
|
90
73
|
"description": "create something new",
|
91
74
|
"subcommands": ["project", "module", "entity"],
|
92
|
-
"memory": None,
|
93
75
|
},
|
94
76
|
"remove": {
|
95
77
|
"description": "remove an entity from the project",
|
96
78
|
"subcommands": None,
|
97
|
-
"memory": "last > stored",
|
98
79
|
},
|
99
80
|
"rename": {
|
100
81
|
"description": "rename an entity",
|
101
82
|
"subcommands": ["entity"],
|
102
|
-
"memory": "last > stored",
|
103
83
|
},
|
104
84
|
"rewrite": {
|
105
85
|
"description": "rewrite all code",
|
106
86
|
"subcommands": None,
|
107
|
-
"memory": "stored",
|
108
87
|
},
|
109
88
|
"show": {
|
110
89
|
"description": "display an entity",
|
111
90
|
"subcommands": None,
|
112
|
-
|
91
|
+
},
|
92
|
+
"studio": {
|
93
|
+
"description": "connect to your database and launch the SQL studio",
|
94
|
+
"subcommands": None,
|
113
95
|
},
|
114
96
|
"tree": {
|
115
97
|
"description": "illustrate the project layout in a tree view",
|
116
98
|
"subcommands": None,
|
117
|
-
"memory": None,
|
118
99
|
},
|
119
100
|
"q": {
|
120
|
-
"description": "
|
101
|
+
"description": "chat with gibson",
|
121
102
|
"subcommands": None,
|
122
|
-
"memory": None,
|
123
103
|
},
|
124
104
|
}
|
125
105
|
|
@@ -142,7 +122,6 @@ class Help(BaseCommand):
|
|
142
122
|
help.add_column("command", style="yellow bold", header_style="yellow bold")
|
143
123
|
help.add_column("description")
|
144
124
|
help.add_column("subcommands", header_style="magenta")
|
145
|
-
help.add_column("memory affected", style="grey50", header_style="grey50")
|
146
125
|
|
147
126
|
for subcommand, config in subcommands.items():
|
148
127
|
help.add_row(
|
@@ -155,26 +134,8 @@ class Help(BaseCommand):
|
|
155
134
|
if config["subcommands"]
|
156
135
|
else ""
|
157
136
|
),
|
158
|
-
config["memory"] or "",
|
159
137
|
)
|
160
138
|
|
161
139
|
console.print(help)
|
162
140
|
|
163
141
|
self.conversation.newline()
|
164
|
-
|
165
|
-
if self.configuration.project:
|
166
|
-
stats = Memory(self.configuration).stats()
|
167
|
-
memory = Table(
|
168
|
-
title="Memory",
|
169
|
-
show_header=True,
|
170
|
-
header_style="bold",
|
171
|
-
box=box.ROUNDED,
|
172
|
-
expand=True,
|
173
|
-
)
|
174
|
-
memory.add_column("stored", style="green", header_style="green")
|
175
|
-
memory.add_column("last", style="yellow", header_style="yellow")
|
176
|
-
memory.add_row(
|
177
|
-
f"{stats['entities']['num']} {stats['entities']['word']}",
|
178
|
-
f"{stats['last']['num']} {stats['last']['word']}",
|
179
|
-
)
|
180
|
-
console.print(memory)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from urllib.parse import urlparse
|
2
|
+
|
3
|
+
from harlequin.app import Harlequin
|
4
|
+
from harlequin.plugins import load_adapter_plugins
|
5
|
+
|
6
|
+
from gibson.api.ProjectApi import ProjectApi
|
7
|
+
from gibson.command.BaseCommand import BaseCommand
|
8
|
+
from gibson.core.Select import Select
|
9
|
+
from gibson.core.Spinner import DisappearingSpinner
|
10
|
+
|
11
|
+
|
12
|
+
class Studio(BaseCommand):
|
13
|
+
def execute(self):
|
14
|
+
self.configuration.require_login()
|
15
|
+
project_id = self.configuration.require_project_id()
|
16
|
+
|
17
|
+
project = ProjectApi(self.configuration).lookup(project_id)
|
18
|
+
|
19
|
+
choices = [database["environment"] for database in project["databases"]]
|
20
|
+
|
21
|
+
selected = Select(
|
22
|
+
title="Select a database",
|
23
|
+
options=choices,
|
24
|
+
).prompt()
|
25
|
+
|
26
|
+
if not selected:
|
27
|
+
self.conversation.type("No database selected\n")
|
28
|
+
exit(1)
|
29
|
+
|
30
|
+
database = next(
|
31
|
+
(db for db in project["databases"] if db["environment"] == selected), None
|
32
|
+
)
|
33
|
+
|
34
|
+
with DisappearingSpinner(
|
35
|
+
start_text=f"Connecting to {database['environment']} database...",
|
36
|
+
success_text=f"Connected to {database['environment']} database",
|
37
|
+
fail_text=f"Failed to connect to {database['environment']} database",
|
38
|
+
):
|
39
|
+
plugins = load_adapter_plugins()
|
40
|
+
adapter = plugins[database["datastore_type"]]
|
41
|
+
|
42
|
+
if database["datastore_type"] == "mysql":
|
43
|
+
connection = urlparse(database["connection_string"])
|
44
|
+
adapter = adapter(
|
45
|
+
conn_str=None,
|
46
|
+
host=connection.hostname,
|
47
|
+
port=connection.port,
|
48
|
+
user=connection.username,
|
49
|
+
password=connection.password,
|
50
|
+
database=connection.path.lstrip("/"),
|
51
|
+
)
|
52
|
+
else:
|
53
|
+
raise ValueError(
|
54
|
+
f"Unsupported database type: {database['datastore_type']}"
|
55
|
+
)
|
56
|
+
|
57
|
+
app = Harlequin(adapter=adapter)
|
58
|
+
app.run()
|
@@ -21,7 +21,7 @@ class Version(BaseCommand):
|
|
21
21
|
f"You are currently using version: {Colors.violet(VersionConf.num)}\n"
|
22
22
|
)
|
23
23
|
self.conversation.type(
|
24
|
-
f"Please update to the latest version by running: {Colors.command('
|
24
|
+
f"Please update to the latest version by running: {Colors.command('uv', 'tool', args='install', inputs='gibson-cli@latest')}\n"
|
25
25
|
)
|
26
26
|
else:
|
27
27
|
self.conversation.type(
|
@@ -5,13 +5,12 @@ from gibson.command.Build import Build
|
|
5
5
|
from gibson.command.code.Code import Code
|
6
6
|
from gibson.command.Conf import Conf
|
7
7
|
from gibson.command.Count import Count
|
8
|
+
from gibson.command.Deploy import Deploy
|
8
9
|
from gibson.command.Dev import Dev
|
9
|
-
from gibson.command.Forget import Forget
|
10
10
|
from gibson.command.Help import Help
|
11
11
|
from gibson.command.importer.Import import Import
|
12
12
|
from gibson.command.list.List import List
|
13
13
|
from gibson.command.mcp.McpServer import McpServer
|
14
|
-
from gibson.command.Merge import Merge
|
15
14
|
from gibson.command.Modify import Modify
|
16
15
|
from gibson.command.new.New import New
|
17
16
|
from gibson.command.Question import Question
|
@@ -19,6 +18,7 @@ from gibson.command.Remove import Remove
|
|
19
18
|
from gibson.command.rename.Rename import Rename
|
20
19
|
from gibson.command.rewrite.Rewrite import Rewrite
|
21
20
|
from gibson.command.Show import Show
|
21
|
+
from gibson.command.Studio import Studio
|
22
22
|
from gibson.command.Tree import Tree
|
23
23
|
from gibson.command.Version import Version
|
24
24
|
from gibson.core.Configuration import Configuration
|
@@ -52,10 +52,10 @@ class CommandRouter:
|
|
52
52
|
command = Conf(self.configuration)
|
53
53
|
elif sys.argv[1] == "count":
|
54
54
|
command = Count(self.configuration)
|
55
|
+
elif sys.argv[1] == "deploy":
|
56
|
+
command = Deploy(self.configuration)
|
55
57
|
elif sys.argv[1] == "dev":
|
56
58
|
command = Dev(self.configuration)
|
57
|
-
elif sys.argv[1] == "forget":
|
58
|
-
command = Forget(self.configuration)
|
59
59
|
elif sys.argv[1] == "help":
|
60
60
|
command = Help(self.configuration)
|
61
61
|
elif sys.argv[1] == "import":
|
@@ -64,8 +64,6 @@ class CommandRouter:
|
|
64
64
|
command = List(self.configuration)
|
65
65
|
elif sys.argv[1] == "mcp":
|
66
66
|
command = McpServer(self.configuration)
|
67
|
-
elif sys.argv[1] == "merge":
|
68
|
-
command = Merge(self.configuration)
|
69
67
|
elif sys.argv[1] == "modify":
|
70
68
|
command = Modify(self.configuration)
|
71
69
|
elif sys.argv[1] == "new":
|
@@ -78,6 +76,8 @@ class CommandRouter:
|
|
78
76
|
command = Rewrite(self.configuration, with_header=True)
|
79
77
|
elif sys.argv[1] == "show":
|
80
78
|
command = Show(self.configuration)
|
79
|
+
elif sys.argv[1] == "studio":
|
80
|
+
command = Studio(self.configuration)
|
81
81
|
elif sys.argv[1] == "tree":
|
82
82
|
command = Tree(self.configuration)
|
83
83
|
elif sys.argv[1] in ["q"]:
|
@@ -237,6 +237,10 @@ class Configuration:
|
|
237
237
|
return self.settings[self.project.name]
|
238
238
|
|
239
239
|
def get_project_id(self):
|
240
|
+
project_id = os.getenv("GIBSONAI_PROJECT_ID")
|
241
|
+
if project_id:
|
242
|
+
return project_id
|
243
|
+
|
240
244
|
self.require_project()
|
241
245
|
return self.project.id
|
242
246
|
|
@@ -0,0 +1,270 @@
|
|
1
|
+
from typing import Any, List, Optional
|
2
|
+
|
3
|
+
import click
|
4
|
+
from rich.align import Align
|
5
|
+
from rich.console import Group
|
6
|
+
from rich.live import Live
|
7
|
+
from rich.panel import Panel
|
8
|
+
from rich.text import Text
|
9
|
+
|
10
|
+
|
11
|
+
class BaseSelect:
|
12
|
+
"""Base class for select components with shared functionality."""
|
13
|
+
|
14
|
+
def __init__(
|
15
|
+
self,
|
16
|
+
options: List[str] = [],
|
17
|
+
title: str = "",
|
18
|
+
color: str = "bold magenta",
|
19
|
+
align: str = "left",
|
20
|
+
selection: str = "→",
|
21
|
+
highlight: str = "green",
|
22
|
+
):
|
23
|
+
self.options = options
|
24
|
+
self.index = 0
|
25
|
+
self.title = title
|
26
|
+
self.color = color
|
27
|
+
self.align = align
|
28
|
+
self.selection = selection
|
29
|
+
self.highlight = highlight
|
30
|
+
|
31
|
+
def _get_click(self) -> Optional[str]:
|
32
|
+
match click.getchar():
|
33
|
+
case "\r":
|
34
|
+
return "enter"
|
35
|
+
case "\x1b[B" | "s" | "S" | "àP":
|
36
|
+
return "down"
|
37
|
+
case "\x1b[A" | "w" | "W" | "àH":
|
38
|
+
return "up"
|
39
|
+
case "\x1b[D" | "a" | "A" | "àK":
|
40
|
+
return "left"
|
41
|
+
case "\x1b[C" | "d" | "D" | "àM":
|
42
|
+
return "right"
|
43
|
+
case "\x1b":
|
44
|
+
return "exit"
|
45
|
+
case _:
|
46
|
+
return None
|
47
|
+
|
48
|
+
def _set_index(self, key: str) -> None:
|
49
|
+
if key == "down":
|
50
|
+
self.index += 1
|
51
|
+
elif key == "up":
|
52
|
+
self.index -= 1
|
53
|
+
|
54
|
+
if self.index > len(self.options) - 1:
|
55
|
+
self.index = 0
|
56
|
+
elif self.index < 0:
|
57
|
+
self.index = len(self.options) - 1
|
58
|
+
|
59
|
+
def _clear(self) -> None:
|
60
|
+
for _ in range(len(self.options) + 5):
|
61
|
+
print("\x1b[A\x1b[K", end="")
|
62
|
+
|
63
|
+
@property
|
64
|
+
def _usage_info(self) -> Text:
|
65
|
+
"""Return usage information text for the select menu."""
|
66
|
+
return Text("Use ↑/↓ to navigate, ENTER to submit", "dim")
|
67
|
+
|
68
|
+
@property
|
69
|
+
def _layout(self) -> Group:
|
70
|
+
"""Generate the display group for the menu.
|
71
|
+
This method should be overridden by subclasses.
|
72
|
+
"""
|
73
|
+
raise NotImplementedError("Subclasses must implement _layout")
|
74
|
+
|
75
|
+
@property
|
76
|
+
def _panel_width(self) -> int:
|
77
|
+
"""Return the width of the panel."""
|
78
|
+
# Calculate minimum width needed for the usage text
|
79
|
+
usage_width = len(self._usage_info.plain) + 2
|
80
|
+
# Get the max width of options
|
81
|
+
options_width = (
|
82
|
+
max((len(option) for option in self.options), default=0) + 5
|
83
|
+
) # +5 for marker and spacing
|
84
|
+
# Use the larger of the two widths, plus some padding
|
85
|
+
return max(usage_width, options_width) + 4
|
86
|
+
|
87
|
+
def prompt(self) -> Any:
|
88
|
+
"""Display the menu and handle user input.
|
89
|
+
This method should be overridden by subclasses.
|
90
|
+
"""
|
91
|
+
raise NotImplementedError("Subclasses must implement prompt")
|
92
|
+
|
93
|
+
|
94
|
+
class Select(BaseSelect):
|
95
|
+
def __init__(
|
96
|
+
self,
|
97
|
+
options: List[str] = [],
|
98
|
+
start_index: int = 0,
|
99
|
+
title: str = "",
|
100
|
+
color: str = "bold magenta",
|
101
|
+
align: str = "left",
|
102
|
+
selection: str = "→",
|
103
|
+
highlight: str = "green",
|
104
|
+
):
|
105
|
+
super().__init__(
|
106
|
+
options=options,
|
107
|
+
title=title,
|
108
|
+
color=color,
|
109
|
+
align=align,
|
110
|
+
selection=selection,
|
111
|
+
highlight=highlight,
|
112
|
+
)
|
113
|
+
self.index = start_index
|
114
|
+
|
115
|
+
@property
|
116
|
+
def _layout(self) -> Group:
|
117
|
+
menu = Text(justify="left")
|
118
|
+
|
119
|
+
selected = Text(self.selection + " ", self.highlight)
|
120
|
+
not_selected = Text(" " * (len(self.selection) + 1))
|
121
|
+
|
122
|
+
for idx, option in enumerate(self.options):
|
123
|
+
if idx == self.index:
|
124
|
+
menu.append(
|
125
|
+
Text.assemble(selected, Text(option + "\n", self.highlight))
|
126
|
+
)
|
127
|
+
else:
|
128
|
+
menu.append(Text.assemble(not_selected, option + "\n"))
|
129
|
+
|
130
|
+
menu.rstrip()
|
131
|
+
|
132
|
+
menu = Panel(menu, padding=1, width=self._panel_width)
|
133
|
+
menu.title = Text(self.title, self.color)
|
134
|
+
menu.subtitle = self._usage_info
|
135
|
+
|
136
|
+
return Group(Align(menu, self.align))
|
137
|
+
|
138
|
+
def prompt(self) -> str:
|
139
|
+
with Live(self._layout, auto_refresh=False, screen=False) as live:
|
140
|
+
live.update(self._layout, refresh=True)
|
141
|
+
while True:
|
142
|
+
try:
|
143
|
+
key = self._get_click()
|
144
|
+
if key == "enter":
|
145
|
+
break
|
146
|
+
elif key == "exit":
|
147
|
+
exit()
|
148
|
+
|
149
|
+
self._set_index(key)
|
150
|
+
live.update(self._layout, refresh=True)
|
151
|
+
except (KeyboardInterrupt, EOFError):
|
152
|
+
exit()
|
153
|
+
|
154
|
+
self._clear()
|
155
|
+
|
156
|
+
return self.options[self.index]
|
157
|
+
|
158
|
+
|
159
|
+
class MultiSelect(BaseSelect):
|
160
|
+
def __init__(
|
161
|
+
self,
|
162
|
+
options: List[str] = [],
|
163
|
+
start_indices: Optional[List[int]] = None,
|
164
|
+
title: str = "",
|
165
|
+
color: str = "bold magenta",
|
166
|
+
align: str = "left",
|
167
|
+
selection: str = "→",
|
168
|
+
highlight: str = "green",
|
169
|
+
selected_marker: str = "●",
|
170
|
+
unselected_marker: str = "○",
|
171
|
+
):
|
172
|
+
super().__init__(
|
173
|
+
options=options,
|
174
|
+
title=title,
|
175
|
+
color=color,
|
176
|
+
align=align,
|
177
|
+
selection=selection,
|
178
|
+
highlight=highlight,
|
179
|
+
)
|
180
|
+
self.selected_indices = start_indices or []
|
181
|
+
self.selected_marker = selected_marker
|
182
|
+
self.unselected_marker = unselected_marker
|
183
|
+
|
184
|
+
def _get_click(self) -> Optional[str]:
|
185
|
+
# Override to add space key handling
|
186
|
+
match click.getchar():
|
187
|
+
case "\r":
|
188
|
+
return "enter"
|
189
|
+
case "\x1b[B" | "s" | "S" | "àP":
|
190
|
+
return "down"
|
191
|
+
case "\x1b[A" | "w" | "W" | "àH":
|
192
|
+
return "up"
|
193
|
+
case "\x1b[D" | "a" | "A" | "àK":
|
194
|
+
return "left"
|
195
|
+
case "\x1b[C" | "d" | "D" | "àM":
|
196
|
+
return "right"
|
197
|
+
case " ":
|
198
|
+
return "space"
|
199
|
+
case "\x1b":
|
200
|
+
return "exit"
|
201
|
+
case _:
|
202
|
+
return None
|
203
|
+
|
204
|
+
def _toggle_selection(self) -> None:
|
205
|
+
if self.index in self.selected_indices:
|
206
|
+
self.selected_indices.remove(self.index)
|
207
|
+
else:
|
208
|
+
self.selected_indices.append(self.index)
|
209
|
+
|
210
|
+
@property
|
211
|
+
def _usage_info(self) -> Text:
|
212
|
+
"""Return usage information text for the multi-select menu."""
|
213
|
+
return Text("Use ↑/↓ to navigate, SPACE to toggle, ENTER to submit", "dim")
|
214
|
+
|
215
|
+
@property
|
216
|
+
def _layout(self) -> Group:
|
217
|
+
menu = Text(justify="left")
|
218
|
+
|
219
|
+
cursor_selected = Text(self.selection + " ", self.highlight)
|
220
|
+
cursor_not_selected = Text(" " * (len(self.selection) + 1))
|
221
|
+
|
222
|
+
for idx, option in enumerate(self.options):
|
223
|
+
# Determine if this option is selected
|
224
|
+
marker = (
|
225
|
+
self.selected_marker
|
226
|
+
if idx in self.selected_indices
|
227
|
+
else self.unselected_marker
|
228
|
+
)
|
229
|
+
|
230
|
+
# Determine if cursor is on this item
|
231
|
+
if idx == self.index:
|
232
|
+
menu.append(
|
233
|
+
Text.assemble(
|
234
|
+
cursor_selected, Text(f"{marker} {option}\n", self.highlight)
|
235
|
+
)
|
236
|
+
)
|
237
|
+
else:
|
238
|
+
menu.append(Text.assemble(cursor_not_selected, f"{marker} {option}\n"))
|
239
|
+
|
240
|
+
menu.rstrip()
|
241
|
+
|
242
|
+
menu = Panel(menu, padding=1, width=self._panel_width)
|
243
|
+
menu.title = Text(self.title, self.color)
|
244
|
+
menu.subtitle = self._usage_info
|
245
|
+
|
246
|
+
return Group(Align(menu, self.align))
|
247
|
+
|
248
|
+
def prompt(self) -> List[str]:
|
249
|
+
with Live(self._layout, auto_refresh=False, screen=False) as live:
|
250
|
+
live.update(self._layout, refresh=True)
|
251
|
+
while True:
|
252
|
+
try:
|
253
|
+
key = self._get_click()
|
254
|
+
if key == "enter":
|
255
|
+
break
|
256
|
+
elif key == "exit":
|
257
|
+
exit()
|
258
|
+
elif key == "space":
|
259
|
+
self._toggle_selection()
|
260
|
+
live.update(self._layout, refresh=True)
|
261
|
+
else:
|
262
|
+
self._set_index(key)
|
263
|
+
live.update(self._layout, refresh=True)
|
264
|
+
except (KeyboardInterrupt, EOFError):
|
265
|
+
exit()
|
266
|
+
|
267
|
+
self._clear()
|
268
|
+
|
269
|
+
# Return the selected options
|
270
|
+
return [self.options[i] for i in self.selected_indices]
|