Skip to content

Commit e535826

Browse files
committed
Add skill descriptions for connection commands
1 parent 893c706 commit e535826

5 files changed

Lines changed: 79 additions & 24 deletions

File tree

skills/hotdata-cli/SKILL.md

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: hotdata-cli
3-
description: Use this skill when the user wants to run hotdata CLI commands, query the HotData API, list workspaces, list connections, list tables, manage datasets, execute SQL queries, or interact with the hotdata service. Activate when the user says "run hotdata", "query hotdata", "list workspaces", "list connections", "list tables", "list datasets", "create a dataset", "upload a dataset", "execute a query", or asks you to use the hotdata CLI.
3+
description: Use this skill when the user wants to run hotdata CLI commands, query the HotData API, list workspaces, list connections, create connections, list tables, manage datasets, execute SQL queries, or interact with the hotdata service. Activate when the user says "run hotdata", "query hotdata", "list workspaces", "list connections", "create a connection", "list tables", "list datasets", "create a dataset", "upload a dataset", "execute a query", or asks you to use the hotdata CLI.
44
version: 0.1.3
55
---
66

@@ -36,7 +36,66 @@ Returns workspaces with `public_id`, `name`, `active`, `favorite`, `provision_st
3636
```
3737
hotdata connections list [--workspace-id <workspace_id>] [--format table|json|yaml]
3838
```
39-
Routes via API gateway using `X-Workspace-Id` header.
39+
Returns `id`, `name`, `source_type` for each connection in the workspace.
40+
41+
### Create a Connection
42+
43+
#### Step 1 — Discover available connection types
44+
```
45+
hotdata connections create list [--workspace-id <workspace_id>] [--format table|json|yaml]
46+
```
47+
Returns all available connection types with `name` and `label`.
48+
49+
#### Step 2 — Inspect the schema for a specific type
50+
```
51+
hotdata connections create list <name> [--workspace-id <workspace_id>] [--format json]
52+
```
53+
Returns `config` and `auth` JSON Schema objects describing all required and optional fields for that connection type. Use `--format json` to get the full schema detail.
54+
55+
- `config` — connection configuration fields (host, port, database, etc.). May be `null` for services that need no configuration.
56+
- `auth` — authentication fields (password, token, credentials, etc.). May be `null` for services that need no authentication. May be a `oneOf` with multiple authentication method options.
57+
58+
#### Step 3 — Create the connection
59+
```
60+
hotdata connections create \
61+
--name "my-connection" \
62+
--type <source_type> \
63+
--config '<json object>' \
64+
[--workspace-id <workspace_id>]
65+
```
66+
67+
The `--config` JSON object must contain all **required** fields from `config` plus the **auth fields** merged in at the top level. Auth fields are not nested — they sit alongside config fields in the same object.
68+
69+
Example for PostgreSQL (required: `host`, `port`, `user`, `database` + auth field `password`):
70+
```
71+
hotdata connections create \
72+
--name "my-postgres" \
73+
--type postgres \
74+
--config '{"host":"db.example.com","port":5432,"user":"myuser","database":"mydb","password":"..."}'
75+
```
76+
77+
**Security: never expose credentials in plain text.** Passwords, tokens, API keys, and any field with `"format": "password"` in the schema must never be hardcoded as literal strings in CLI commands. Always use one of these safe approaches:
78+
79+
- Read from an environment variable:
80+
```
81+
--config "{\"host\":\"db.example.com\",\"port\":5432,\"user\":\"myuser\",\"database\":\"mydb\",\"password\":\"$DB_PASSWORD\"}"
82+
```
83+
- Read a credential from a file and inject it:
84+
```
85+
--config "{\"token\":\"$(cat ~/.secrets/my-token)\"}"
86+
```
87+
88+
**Field-building rules from the schema:**
89+
90+
- Include all fields listed in `config.required` — these are mandatory.
91+
- Include optional config fields only if the user provides values for them.
92+
- For `auth` with a single method (no `oneOf`): include all `auth.required` fields in the config object.
93+
- For `auth` with `oneOf`: pick one authentication method and include only its required fields.
94+
- Fields with `"format": "password"` are credentials — apply the security rules above.
95+
- Fields with `"type": "integer"` must be JSON numbers, not strings (e.g. `"port": 5432` not `"port": "5432"`).
96+
- Fields with `"type": "boolean"` must be JSON booleans (e.g. `"use_tls": true`).
97+
- Fields with `"type": "array"` must be JSON arrays (e.g. `"spreadsheet_ids": ["abc", "def"]`).
98+
- Nested `oneOf` fields must be a JSON object including a `"type"` discriminator field matching the chosen variant's `const` value.
4099

41100
### List Tables and Columns
42101
```
@@ -136,3 +195,19 @@ hotdata init # Create ~/.hotdata/config.yml
136195
```
137196
hotdata query "SELECT 1"
138197
```
198+
199+
## Workflow: Creating a Connection
200+
201+
1. List available connection types:
202+
```
203+
hotdata connections create list
204+
```
205+
2. Inspect the schema for the desired type:
206+
```
207+
hotdata connections create list <type_name> --format json
208+
```
209+
3. Collect required config and auth field values from the user or environment. **Never hardcode credentials — use env vars or files.**
210+
4. Create the connection:
211+
```
212+
hotdata connections create --name "my-connection" --type <type_name> --config '<json>'
213+
```

src/command.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,6 @@ pub enum ConnectionsCommands {
357357
#[arg(long)]
358358
config: Option<String>,
359359

360-
/// Reference to a secret by ID for authentication
361-
#[arg(long, conflicts_with = "secret_name")]
362-
secret_id: Option<String>,
363-
364-
/// Reference to a secret by name for authentication
365-
#[arg(long, conflicts_with = "secret_id")]
366-
secret_name: Option<String>,
367-
368360
/// Output format
369361
#[arg(long, default_value = "table", value_parser = ["table", "json", "yaml"])]
370362
format: String,

src/connections.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,6 @@ pub fn create(
163163
name: &str,
164164
source_type: &str,
165165
config: &str,
166-
secret_id: Option<&str>,
167-
secret_name: Option<&str>,
168166
format: &str,
169167
) {
170168
let profile_config = match crate::config::load("default") {
@@ -191,17 +189,11 @@ pub fn create(
191189
}
192190
};
193191

194-
let mut body = serde_json::json!({
192+
let body = serde_json::json!({
195193
"name": name,
196194
"source_type": source_type,
197195
"config": config_value,
198196
});
199-
if let Some(id) = secret_id {
200-
body["secret_id"] = serde_json::json!(id);
201-
}
202-
if let Some(sn) = secret_name {
203-
body["secret_name"] = serde_json::json!(sn);
204-
}
205197

206198
let url = format!("{}/connections", profile_config.api_url);
207199
let client = reqwest::blocking::Client::new();

src/connections_new.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ struct ConnectionTypeSummary {
1010
}
1111

1212
struct ConnectionTypeDetail {
13-
name: String,
1413
config_schema: Option<Value>,
1514
auth: Option<Value>,
1615
}
@@ -75,7 +74,6 @@ fn fetch_detail(workspace_id: &str, name: &str) -> ConnectionTypeDetail {
7574
}
7675
let body: Value = resp.json().unwrap_or_else(|e| { eprintln!("error: {e}"); std::process::exit(1) });
7776
ConnectionTypeDetail {
78-
name: body["name"].as_str().unwrap_or(name).to_string(),
7977
config_schema: if body["config_schema"].is_null() { None } else { Some(body["config_schema"].clone()) },
8078
auth: if body["auth"].is_null() { None } else { Some(body["auth"].clone()) },
8179
}

src/main.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ fn main() {
9696
let workspace_id = resolve_workspace(workspace_id);
9797
connections_new::run(&workspace_id)
9898
}
99-
ConnectionsCommands::Create { command, workspace_id, name, source_type, config, secret_id, secret_name, format } => {
99+
ConnectionsCommands::Create { command, workspace_id, name, source_type, config, format } => {
100100
match command {
101101
Some(ConnectionsCreateCommands::List { name, workspace_id, format }) => {
102102
let workspace_id = resolve_workspace(workspace_id);
@@ -121,8 +121,6 @@ fn main() {
121121
&name.unwrap(),
122122
&source_type.unwrap(),
123123
&config.unwrap(),
124-
secret_id.as_deref(),
125-
secret_name.as_deref(),
126124
&format,
127125
)
128126
}

0 commit comments

Comments
 (0)