Skip to content

Commit f37cb3e

Browse files
authored
Fix CLI option bugs, add cqlsh compatibility flags, and improve docs (#75)
* Fix CLI option bugs, add cqlsh compatibility, and improve documentation - Fix default value detection using pflag.Changed() instead of == default - Add positional argument support (cqlai [host] [port]) for cqlsh compat - Implement $CQLSH_RC env var support as documented in README - Fix password prompt precedence: env var checked before interactive prompt - Add --format validation at parse time (ascii, json, csv, table) - Add --ssl flag for SSL/TLS connections without config file - Add --consistency flag with validation for batch automation - Wire --page-size through to interactive mode via ConnectionOptions - Change version short flag from -v to -V to free -v for future verbose - Pass consistency to batch mode SessionOptions (was missing) - Expand environment variable documentation from ~10 to 30+ entries - Update README and BATCH_MODE.md with new flags and usage examples * Update Japanese documentation to match English changes - Add --ssl, --consistency flags and positional args to README_jp.md CLI table - Change -v to -V for version short flag in README_jp.md - Expand env var section from ~9 to 30+ entries in README_jp.md - Add SSL, consistency, and positional arg examples to BATCH_MODE_jp.md * Fail fast parsisng args
1 parent e357088 commit f37cb3e

File tree

8 files changed

+220
-30
lines changed

8 files changed

+220
-30
lines changed

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,11 @@ cqlai
197197
### Command-Line Options
198198

199199
```bash
200-
cqlai [options]
200+
cqlai [options] [host [port]]
201201
```
202202
203+
**Note:** Positional arguments are supported for `cqlsh` compatibility. `cqlai 192.168.1.100 9042` is equivalent to `cqlai --host 192.168.1.100 --port 9042`.
204+
203205
#### Connection Options
204206
| Option | Short | Description |
205207
|--------|-------|-------------|
@@ -208,6 +210,8 @@ cqlai [options]
208210
| `--keyspace <keyspace>` | `-k` | Default keyspace (overrides config) |
209211
| `--username <username>` | `-u` | Username for authentication |
210212
| `--password <password>` | `-p` | Password for authentication* |
213+
| `--ssl` | | Enable SSL/TLS connection |
214+
| `--consistency <level>` | | Default consistency level (e.g., ONE, QUORUM, LOCAL_QUORUM) |
211215
| `--no-confirm` | | Disable confirmation prompts for destructive commands (DROP, DELETE, TRUNCATE) |
212216
| `--connect-timeout <seconds>` | | Connection timeout (default: 10) |
213217
| `--request-timeout <seconds>` | | Request timeout (default: 10) |
@@ -969,16 +973,47 @@ CQLAI searches for configuration files in the following locations:
969973
970974
### Environment Variables
971975
972-
Common environment variables:
976+
All environment variables supported by CQLAI. `CQLAI_*` variables take precedence over `CASSANDRA_*` equivalents.
977+
978+
#### Connection
973979
- `CQLAI_HOST` or `CASSANDRA_HOST` - Cassandra host
974980
- `CQLAI_PORT` or `CASSANDRA_PORT` - Cassandra port
975-
- `CQLAI_KEYSPACE` - Default keyspace
976-
- `CQLAI_USERNAME` - Authentication username
977-
- `CQLAI_PASSWORD` - Authentication password
978-
- `CQLAI_PAGE_SIZE` - Batch mode pagination size (default: 100)
981+
- `CQLAI_KEYSPACE` or `CASSANDRA_KEYSPACE` - Default keyspace
982+
- `CQLAI_USERNAME` or `CASSANDRA_USERNAME` - Authentication username
983+
- `CQLAI_PASSWORD` or `CASSANDRA_PASSWORD` - Authentication password
984+
- `CQLAI_CONNECT_TIMEOUT` - Connection timeout in seconds (default: 10)
985+
- `CQLAI_REQUEST_TIMEOUT` - Request timeout in seconds (default: 10)
979986
- `CQLAI_NO_CONFIRM` - Set to `true` or `1` to disable confirmation prompts for destructive commands
987+
- `CQLAI_DEBUG` - Set to `true` or `1` to enable debug logging
988+
989+
#### Configuration
990+
- `CQLAI_CONFIG_FILE` - Path to JSON config file (overrides default locations)
980991
- `CQLSH_RC` - Path to custom CQLSHRC file
981992
993+
#### Batch Mode
994+
- `CQLAI_EXECUTE` - CQL statement to execute (equivalent to `-e`)
995+
- `CQLAI_FILE` - CQL file to execute (equivalent to `-f`)
996+
- `CQLAI_FORMAT` - Output format: ascii, json, csv, table (default: ascii)
997+
- `CQLAI_NO_HEADER` - Set to `true` or `1` to omit column headers (CSV)
998+
- `CQLAI_FIELD_SEPARATOR` - Field separator for CSV output (default: `,`)
999+
- `CQLAI_PAGE_SIZE` - Pagination size (default: 100)
1000+
- `CQLAI_MAX_MEMORY_MB` - Maximum memory for query results in MB (default: 10)
1001+
1002+
#### AI Configuration
1003+
- `CQLAI_AI_PROVIDER` or `AI_PROVIDER` - AI provider name (mock, openai, anthropic, gemini, ollama, openrouter)
1004+
- `CQLAI_AI_API_KEY` or `AI_API_KEY` - General AI API key
1005+
- `CQLAI_AI_MODEL` or `AI_MODEL` - General AI model name
1006+
- `OPENAI_API_KEY` - OpenAI API key
1007+
- `OPENAI_MODEL` - OpenAI model name
1008+
- `ANTHROPIC_API_KEY` - Anthropic API key
1009+
- `ANTHROPIC_MODEL` - Anthropic model name
1010+
- `GEMINI_API_KEY` - Google Gemini API key
1011+
- `OLLAMA_URL` - Ollama server URL (default: `http://localhost:11434/v1`)
1012+
- `OLLAMA_MODEL` - Ollama model name
1013+
- `OPENROUTER_API_KEY` - OpenRouter API key
1014+
- `OPENROUTER_MODEL` - OpenRouter model name
1015+
- `OPENROUTER_URL` - OpenRouter API URL (default: `https://openrouter.ai/api/v1`)
1016+
9821017
### Migration from cqlsh
9831018
9841019
If you're migrating from `cqlsh`, CQLAI will automatically read your existing `~/.cassandra/cqlshrc` file. No changes are needed to start using CQLAI with your existing Cassandra configuration.

README_jp.md

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,11 @@ cqlai
186186
### コマンドラインオプション
187187

188188
```bash
189-
cqlai [options]
189+
cqlai [options] [host [port]]
190190
```
191191
192+
**注意:** `cqlsh`互換性のため、位置引数がサポートされています。`cqlai 192.168.1.100 9042``cqlai --host 192.168.1.100 --port 9042`と同等です。
193+
192194
#### 接続オプション
193195
| オプション | 短縮 | 説明 |
194196
|--------|-------|-------------|
@@ -197,6 +199,8 @@ cqlai [options]
197199
| `--keyspace <keyspace>` | `-k` | デフォルトのキースペース(設定を上書き) |
198200
| `--username <username>` | `-u` | 認証用のユーザー名 |
199201
| `--password <password>` | `-p` | 認証用のパスワード* |
202+
| `--ssl` | | SSL/TLS接続を有効化 |
203+
| `--consistency <level>` | | デフォルトの整合性レベル(例: ONE、QUORUM、LOCAL_QUORUM) |
200204
| `--no-confirm` | | 破壊的コマンド(DROP、DELETE、TRUNCATE)の確認プロンプトを無効化 |
201205
| `--connect-timeout <seconds>` | | 接続タイムアウト(デフォルト: 10) |
202206
| `--request-timeout <seconds>` | | リクエストタイムアウト(デフォルト: 10) |
@@ -221,7 +225,7 @@ cqlai [options]
221225
| オプション | 短縮 | 説明 |
222226
|--------|-------|-------------|
223227
| `--help` | `-h` | ヘルプメッセージを表示 |
224-
| `--version` | `-v` | バージョンを表示して終了 |
228+
| `--version` | `-V` | バージョンを表示して終了 |
225229
226230
### バッチモードの例
227231
@@ -774,16 +778,47 @@ CQLAIは次の場所で設定ファイルを検索します:
774778
775779
### 環境変数
776780
777-
一般的な環境変数:
781+
CQLAIがサポートするすべての環境変数です。`CQLAI_*`変数は`CASSANDRA_*`の同等変数より優先されます。
782+
783+
#### 接続
778784
- `CQLAI_HOST`または`CASSANDRA_HOST` - Cassandraホスト
779785
- `CQLAI_PORT`または`CASSANDRA_PORT` - Cassandraポート
780-
- `CQLAI_KEYSPACE` - デフォルトのキースペース
781-
- `CQLAI_USERNAME` - 認証ユーザー名
782-
- `CQLAI_PASSWORD` - 認証パスワード
783-
- `CQLAI_PAGE_SIZE` - バッチモードのページサイズ(デフォルト: 100)
786+
- `CQLAI_KEYSPACE`または`CASSANDRA_KEYSPACE` - デフォルトのキースペース
787+
- `CQLAI_USERNAME`または`CASSANDRA_USERNAME` - 認証ユーザー名
788+
- `CQLAI_PASSWORD`または`CASSANDRA_PASSWORD` - 認証パスワード
789+
- `CQLAI_CONNECT_TIMEOUT` - 接続タイムアウト(秒単位、デフォルト: 10)
790+
- `CQLAI_REQUEST_TIMEOUT` - リクエストタイムアウト(秒単位、デフォルト: 10)
784791
- `CQLAI_NO_CONFIRM` - `true`または`1`に設定して破壊的コマンドの確認プロンプトを無効化
792+
- `CQLAI_DEBUG` - `true`または`1`に設定してデバッグログを有効化
793+
794+
#### 設定
795+
- `CQLAI_CONFIG_FILE` - JSON設定ファイルへのパス(デフォルトの場所を上書き)
785796
- `CQLSH_RC` - カスタムCQLSHRCファイルへのパス
786797
798+
#### バッチモード
799+
- `CQLAI_EXECUTE` - 実行するCQLステートメント(`-e`と同等)
800+
- `CQLAI_FILE` - 実行するCQLファイル(`-f`と同等)
801+
- `CQLAI_FORMAT` - 出力形式: ascii, json, csv, table(デフォルト: ascii)
802+
- `CQLAI_NO_HEADER` - `true`または`1`に設定してカラムヘッダーを省略(CSV)
803+
- `CQLAI_FIELD_SEPARATOR` - CSV出力のフィールド区切り文字(デフォルト: `,`
804+
- `CQLAI_PAGE_SIZE` - ページネーションサイズ(デフォルト: 100)
805+
- `CQLAI_MAX_MEMORY_MB` - クエリ結果の最大メモリ(MB単位、デフォルト: 10)
806+
807+
#### AI設定
808+
- `CQLAI_AI_PROVIDER`または`AI_PROVIDER` - AIプロバイダー名(mock, openai, anthropic, gemini, ollama, openrouter)
809+
- `CQLAI_AI_API_KEY`または`AI_API_KEY` - 汎用AI APIキー
810+
- `CQLAI_AI_MODEL`または`AI_MODEL` - 汎用AIモデル名
811+
- `OPENAI_API_KEY` - OpenAI APIキー
812+
- `OPENAI_MODEL` - OpenAIモデル名
813+
- `ANTHROPIC_API_KEY` - Anthropic APIキー
814+
- `ANTHROPIC_MODEL` - Anthropicモデル名
815+
- `GEMINI_API_KEY` - Google Gemini APIキー
816+
- `OLLAMA_URL` - OllamaサーバーURL(デフォルト: `http://localhost:11434/v1`
817+
- `OLLAMA_MODEL` - Ollamaモデル名
818+
- `OPENROUTER_API_KEY` - OpenRouter APIキー
819+
- `OPENROUTER_MODEL` - OpenRouterモデル名
820+
- `OPENROUTER_URL` - OpenRouter API URL(デフォルト: `https://openrouter.ai/api/v1`
821+
787822
### cqlshからの移行
788823
789824
`cqlsh`から移行する場合、CQLAIは既存の`~/.cassandra/cqlshrc`ファイルを自動的に読み取ります。既存のCassandra設定でCQLAIの使用を開始するための変更は必要ありません。

cmd/cqlai/main.go

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ func main() {
2828
connectTimeout int
2929
requestTimeout int
3030
debug bool
31+
ssl bool
32+
consistency string
3133
execute string
3234
executeFile string
3335
format string
@@ -49,6 +51,8 @@ func main() {
4951
pflag.IntVar(&connectTimeout, "connect-timeout", 10, "Connection timeout in seconds")
5052
pflag.IntVar(&requestTimeout, "request-timeout", 10, "Request timeout in seconds")
5153
pflag.BoolVar(&debug, "debug", false, "Enable debug logging")
54+
pflag.BoolVar(&ssl, "ssl", false, "Enable SSL/TLS connection")
55+
pflag.StringVar(&consistency, "consistency", "", "Default consistency level (e.g., ONE, QUORUM, LOCAL_QUORUM)")
5256
pflag.StringVar(&configFile, "config-file", "", "Path to config file (overrides default locations)")
5357

5458
// Batch mode flags (compatible with cqlsh)
@@ -60,15 +64,47 @@ func main() {
6064
pflag.IntVar(&pageSize, "page-size", 100, "Pagination size for batch mode")
6165

6266
// Version and help flags
63-
pflag.BoolVarP(&version, "version", "v", false, "Print version and exit")
67+
pflag.BoolVarP(&version, "version", "V", false, "Print version and exit")
6468
pflag.BoolVarP(&help, "help", "h", false, "Show help message")
6569

6670
pflag.Parse()
6771

72+
// Handle positional arguments for cqlsh compatibility (cqlai [host] [port])
73+
args := pflag.Args()
74+
75+
// 1. Guard Clause: Fail fast
76+
if len(args) > 2 {
77+
fmt.Fprintf(os.Stderr, "Error: unexpected positional arguments: %v\nUsage: cqlai [options] [host [port]]\n", args[2:])
78+
os.Exit(1)
79+
}
80+
81+
// 2. Handle Host
82+
if len(args) >= 1 {
83+
if host != "" {
84+
fmt.Fprintf(os.Stderr, "Warning: positional argument %q ignored because --host was specified\n", args[0])
85+
} else {
86+
host = args[0]
87+
}
88+
}
89+
90+
// 3. Handle Port
91+
if len(args) >= 2 {
92+
if port != 0 {
93+
fmt.Fprintf(os.Stderr, "Warning: positional argument %q ignored because --port was specified\n", args[1])
94+
} else if p, err := strconv.Atoi(args[1]); err == nil {
95+
port = p
96+
} else {
97+
fmt.Fprintf(os.Stderr, "Error: invalid port number %q\n", args[1])
98+
os.Exit(1)
99+
}
100+
}
101+
68102
// Handle help flag
69103
if help {
70104
fmt.Println("cqlai - A modern Cassandra CQL shell with AI assistance")
71105
fmt.Println()
106+
fmt.Println("Usage: cqlai [options] [host [port]]")
107+
fmt.Println()
72108
pflag.PrintDefaults()
73109
os.Exit(0)
74110
}
@@ -79,6 +115,27 @@ func main() {
79115
os.Exit(0)
80116
}
81117

118+
// Validate --format value
119+
switch strings.ToLower(format) {
120+
case "ascii", "json", "csv", "table":
121+
// valid
122+
default:
123+
fmt.Fprintf(os.Stderr, "Error: invalid output format %q (valid: ascii, json, csv, table)\n", format)
124+
os.Exit(1)
125+
}
126+
127+
// Validate --consistency value if provided
128+
if consistency != "" {
129+
switch strings.ToUpper(consistency) {
130+
case "ANY", "ONE", "TWO", "THREE", "QUORUM", "ALL",
131+
"LOCAL_QUORUM", "EACH_QUORUM", "LOCAL_ONE", "SERIAL", "LOCAL_SERIAL":
132+
consistency = strings.ToUpper(consistency)
133+
default:
134+
fmt.Fprintf(os.Stderr, "Error: invalid consistency level %q (valid: ANY, ONE, TWO, THREE, QUORUM, ALL, LOCAL_QUORUM, EACH_QUORUM, LOCAL_ONE, SERIAL, LOCAL_SERIAL)\n", consistency)
135+
os.Exit(1)
136+
}
137+
}
138+
82139
// Override with environment variables if command-line flags not set
83140
// This allows users to set CQLAI_* env vars as an alternative to flags
84141
if configFile == "" {
@@ -113,14 +170,14 @@ func main() {
113170
debug = envDebug == "true" || envDebug == "1"
114171
}
115172
}
116-
if connectTimeout == 10 { // Check if still at default
173+
if !pflag.CommandLine.Changed("connect-timeout") {
117174
if envTimeout := os.Getenv("CQLAI_CONNECT_TIMEOUT"); envTimeout != "" {
118175
if t, err := strconv.Atoi(envTimeout); err == nil {
119176
connectTimeout = t
120177
}
121178
}
122179
}
123-
if requestTimeout == 10 { // Check if still at default
180+
if !pflag.CommandLine.Changed("request-timeout") {
124181
if envTimeout := os.Getenv("CQLAI_REQUEST_TIMEOUT"); envTimeout != "" {
125182
if t, err := strconv.Atoi(envTimeout); err == nil {
126183
requestTimeout = t
@@ -143,7 +200,7 @@ func main() {
143200
executeFile = envFile
144201
}
145202
}
146-
if format == "ascii" { // Check if still at default
203+
if !pflag.CommandLine.Changed("format") {
147204
if envFormat := os.Getenv("CQLAI_FORMAT"); envFormat != "" {
148205
format = envFormat
149206
}
@@ -153,20 +210,28 @@ func main() {
153210
noHeader = envNoHeader == "true" || envNoHeader == "1"
154211
}
155212
}
156-
if fieldSep == "," { // Check if still at default
213+
if !pflag.CommandLine.Changed("field-separator") {
157214
if envFieldSep := os.Getenv("CQLAI_FIELD_SEPARATOR"); envFieldSep != "" {
158215
fieldSep = envFieldSep
159216
}
160217
}
161-
if pageSize == 100 { // Check if still at default
218+
if !pflag.CommandLine.Changed("page-size") {
162219
if envPageSize := os.Getenv("CQLAI_PAGE_SIZE"); envPageSize != "" {
163220
if ps, err := strconv.Atoi(envPageSize); err == nil {
164221
pageSize = ps
165222
}
166223
}
167224
}
168225

169-
// Handle password prompting if username provided without password
226+
// Check password environment variable before interactive prompt
227+
// Precedence: CLI flag (-p) > env var > interactive prompt
228+
if password == "" {
229+
if envPass := os.Getenv("CQLAI_PASSWORD"); envPass != "" {
230+
password = envPass
231+
}
232+
}
233+
234+
// Prompt for password interactively only if still empty and username was provided
170235
if username != "" && password == "" && isTerminal() {
171236
fmt.Fprintf(os.Stderr, "Password: ")
172237
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
@@ -178,13 +243,6 @@ func main() {
178243
password = string(passwordBytes)
179244
}
180245

181-
// Also check environment variable as fallback
182-
if password == "" {
183-
if envPass := os.Getenv("CQLAI_PASSWORD"); envPass != "" {
184-
password = envPass
185-
}
186-
}
187-
188246
// Create connection options
189247
connOptions := ui.ConnectionOptions{
190248
Host: host,
@@ -197,6 +255,9 @@ func main() {
197255
RequestTimeout: requestTimeout,
198256
Debug: debug,
199257
ConfigFile: configFile,
258+
SSL: ssl,
259+
Consistency: consistency,
260+
PageSize: pageSize,
200261
}
201262

202263
// Check if we're in batch mode

docs/BATCH_MODE.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,17 @@ cqlai --host cassandra.example.com \
144144
--password mypass \
145145
--keyspace mykeyspace \
146146
-e "SELECT * FROM mytable;"
147+
148+
# With SSL and consistency level
149+
cqlai --host cassandra.example.com \
150+
--ssl \
151+
--consistency QUORUM \
152+
-e "SELECT * FROM mytable;"
153+
154+
# Using positional arguments (cqlsh compatible)
155+
cqlai cassandra.example.com 9042 \
156+
-u myuser \
157+
-e "SELECT * FROM mytable;"
147158
```
148159

149160
## Disabling Confirmation Prompts

docs/BATCH_MODE_jp.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,17 @@ cqlai --host cassandra.example.com \
144144
--password mypass \
145145
--keyspace mykeyspace \
146146
-e "SELECT * FROM mytable;"
147+
148+
# SSLと整合性レベルを指定
149+
cqlai --host cassandra.example.com \
150+
--ssl \
151+
--consistency QUORUM \
152+
-e "SELECT * FROM mytable;"
153+
154+
# 位置引数を使用(cqlsh互換)
155+
cqlai cassandra.example.com 9042 \
156+
-u myuser \
157+
-e "SELECT * FROM mytable;"
147158
```
148159

149160
##

internal/batch/executor.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ func NewExecutor(options *Options, writer io.Writer) (*Executor, error) {
8181
if options.ConnOptions.Password != "" {
8282
cfg.Password = options.ConnOptions.Password
8383
}
84+
// Override consistency from CLI flag
85+
if options.ConnOptions.Consistency != "" {
86+
cfg.Consistency = options.ConnOptions.Consistency
87+
}
88+
// Enable SSL from CLI flag (--ssl)
89+
if options.ConnOptions.SSL {
90+
if cfg.SSL == nil {
91+
cfg.SSL = &config.SSLConfig{}
92+
}
93+
cfg.SSL.Enabled = true
94+
}
8495

8596
// Use config PageSize if not specified on command line
8697
if options.PageSize == 0 && cfg.PageSize > 0 {
@@ -97,6 +108,7 @@ func NewExecutor(options *Options, writer io.Writer) (*Executor, error) {
97108
Keyspace: cfg.Keyspace,
98109
Username: cfg.Username,
99110
Password: cfg.Password,
111+
Consistency: cfg.Consistency,
100112
SSL: cfg.SSL,
101113
BatchMode: true, // Disable schema caching in batch mode
102114
ConnectTimeout: options.ConnOptions.ConnectTimeout,

0 commit comments

Comments
 (0)