Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b4cb1f1
docs: add RFC 0008 and specifications for AI TUI mode
zx06 Aug 5, 2026
07e8840
feat: add AIConfig and resolution logic to internal/config
zx06 Aug 5, 2026
bad7ba7
feat: add AI client, prompt builder and service in internal/ai
zx06 Aug 5, 2026
a603c5a
feat: add interactive Bubbletea TUI engine in internal/tui
zx06 Aug 5, 2026
bdcb849
feat: add xsql-ai binary entrypoint and xsql ai subcommand
zx06 Aug 5, 2026
b57e3b6
test: add E2E tests for AI service and TUI terminal mode
zx06 Aug 5, 2026
32e9d67
fix: register db drivers for xsql and xsql-ai binaries
zx06 Aug 5, 2026
f14f8a5
style: upgrade TUI aesthetics with Lipgloss rounded table, tags and c…
zx06 Aug 5, 2026
8a35d27
feat: auto execute initial prompt on TUI startup
zx06 Aug 5, 2026
d471e37
fix: prevent TUI table border alignment breakage with single-line cel…
zx06 Aug 5, 2026
9c9d280
feat: add full untruncated vertical view toggle (Ctrl+V) for query re…
zx06 Aug 5, 2026
9c98c3d
docs: add Ctrl+V vertical view toggle shortcut in docs/ai.md
zx06 Aug 5, 2026
74f7e84
feat: support Shift+Tab shortcut to toggle between Auto-Execute and M…
zx06 Aug 5, 2026
dea22cc
docs: add Shift+Tab shortcut description in docs/ai.md
zx06 Aug 5, 2026
7700df8
feat: implement strict single-line no-wrap table with horizontal colu…
zx06 Aug 5, 2026
a87acb5
docs: add arrow keys horizontal column scrolling description in docs/…
zx06 Aug 5, 2026
b504ab2
feat: support PgUp/PgDn/Up/Down viewport scrolling and limit single t…
zx06 Aug 5, 2026
3e3eb20
docs: add PgUp/PgDn viewport scrolling shortcut in docs/ai.md
zx06 Aug 5, 2026
31fcc9a
feat: support PgUp/PgDn row pagination for large query results in TUI…
zx06 Aug 5, 2026
de505f8
docs: update PgUp/PgDn row pagination shortcut description in docs/ai.md
zx06 Aug 5, 2026
bf37e16
feat: support Tab key focus switching and independent scrolling for h…
zx06 Aug 5, 2026
e9d2ef8
docs: add Tab table focus switching shortcut description in docs/ai.md
zx06 Aug 5, 2026
f7358bc
feat: refactor TUI keybindings with scoped SQL approval controls and …
zx06 Aug 5, 2026
8ef7d67
docs: update refactored scoped shortcuts in docs/ai.md
zx06 Aug 5, 2026
a6ad5d0
refactor: remove xsql ai subcommand and keep xsql-ai as a standalone …
zx06 Aug 5, 2026
5ab28a1
fix: resolve SQL editing focus and Enter key execution flow in TUI
zx06 Aug 5, 2026
89cf6a1
fix: handle CJK wide character display width with runewidth to preven…
zx06 Aug 5, 2026
ae3a263
refactor: migrate OpenAI client to official SDK and Tool Call execute…
zx06 Aug 6, 2026
fa5b25e
fix: resolve linter version error and E2E mock server URL hang
zx06 Aug 6, 2026
33efdb6
fix: adapt TUI color styles for light theme and fix linter issues
zx06 Aug 6, 2026
16f179c
fix: format Go files according to goimports spec
zx06 Aug 6, 2026
e174830
fix: remove SQL box background to prevent color block mismatch
zx06 Aug 6, 2026
8094f6a
refactor: overhaul TUI visual design with Catppuccin palette, Pill ba…
zx06 Aug 6, 2026
2af7c81
refactor: polish TUI layout, table color harmony, header bar and disa…
zx06 Aug 6, 2026
38bc36e
fix: remove repeating prompt symbol from textarea lines
zx06 Aug 6, 2026
bb6aee2
feat: integrate goja JS engine and Session DataStore for AI data anal…
zx06 Aug 6, 2026
183327c
feat: add AI auto-fix retry loop for JS analysis and inject ES6 polyf…
zx06 Aug 6, 2026
bad3f07
refactor: specify ES5 JS syntax in system prompt and remove manual po…
zx06 Aug 6, 2026
047301f
feat: implement proactive AI intent evaluation and automatic post-que…
zx06 Aug 6, 2026
a82230f
fix: unescape stringified JSON in JS engine and collapse long JS code…
zx06 Aug 6, 2026
e48f691
feat: synthesize JS computation results into human-readable Markdown …
zx06 Aug 6, 2026
1a24823
refactor: enforce Agent Loop Invariant ensuring final turn is always …
zx06 Aug 6, 2026
85e7e4b
refactor: implement generic ReAct Agent Loop driven by chat history a…
zx06 Aug 6, 2026
a2b27d6
feat: implement collapsible tool call items with toggle interaction i…
zx06 Aug 6, 2026
63747d3
feat: implement render_table Tool Call empowering AI to control UI ta…
zx06 Aug 6, 2026
77b5644
feat: add export_data Tool Call with human-in-the-loop interactive co…
zx06 Aug 6, 2026
02a1cc9
fix: eliminate duplicate SQL status lines and embed execution metrics…
zx06 Aug 6, 2026
e657dd4
feat: embed table widgets inside foldable tool call containers and ad…
zx06 Aug 6, 2026
3ff3364
docs: update AI documentation and RFC 0011 to reflect 4 Tool Calls an…
zx06 Aug 6, 2026
4807424
refactor: unify Tab/ShiftTab and Ctrl+P/N navigation across Tool Call…
zx06 Aug 6, 2026
24d9cfb
refactor: restore ShiftTab for mode toggle and simplify navigation ke…
zx06 Aug 6, 2026
b31db4d
refactor: remove redundant render_table tool call in favor of host-si…
zx06 Aug 6, 2026
a059f08
feat: add SQL and JS syntax highlighting with Chroma and Markdown ren…
zx06 Aug 6, 2026
ff6d0e5
feat: implement unified interactive options list card for SQL preview…
zx06 Aug 6, 2026
daeac02
refactor: clarify option 2 labels for SQL editing and export prompt a…
zx06 Aug 6, 2026
e021cec
fix: implement adaptive high-contrast syntax highlighting for light a…
zx06 Aug 6, 2026
a7da4f2
fix: eliminate duplicate SQL summary line in expanded ToolCallItem view
zx06 Aug 6, 2026
252c86a
fix: add defensive raw code interception and enforce strict tool call…
zx06 Aug 6, 2026
49eadcb
refactor: simplify system prompt template to focus purely on essentia…
zx06 Aug 6, 2026
8ecef19
refactor: simplify prompt input area with single-line prompt symbol a…
zx06 Aug 6, 2026
984b681
refactor: support multiline prompt input, display raw tool outputs, r…
zx06 Aug 6, 2026
af8e85e
feat: support fallback to default profile in xsql-ai
zx06 Aug 6, 2026
4051768
refactor: rewrite multiline prompt textarea editing and rendering
zx06 Aug 6, 2026
13ad668
fix: remove multi-line prompt symbol duplication and restrict soft ne…
zx06 Aug 6, 2026
76792ad
feat: implement double Ctrl+C exit, Esc clear prompt, and Ctrl+P prof…
zx06 Aug 6, 2026
6890d35
refactor: explicitly format and highlight target database dialect in …
zx06 Aug 6, 2026
6bacd9d
fix: pre-resolve all profile configurations so profile switching main…
zx06 Aug 6, 2026
a2bdfa6
test: add unit tests for cmd/xsql-ai, tui render, service, and model …
zx06 Aug 6, 2026
335dee0
fix: resolve golangci-lint version compatibility and expand test cove…
zx06 Aug 6, 2026
d223a76
fix: update .golangci.yml for golangci-lint v2 schema and expand test…
zx06 Aug 6, 2026
e982a58
fix: resolve all staticcheck, errcheck, and ineffassign issues for go…
zx06 Aug 6, 2026
ddfbbe4
test: boost patch unit test coverage for export, tui, components, and…
zx06 Aug 6, 2026
0cf62cf
test: increase test coverage across all new AI TUI modules above 80%
zx06 Aug 6, 2026
6bb0709
build: add xsql-ai binary release target to goreleaser and CI workflow
zx06 Aug 6, 2026
f418f2d
feat: register ai subcommand to main xsql CLI
zx06 Aug 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,24 @@ jobs:
working-directory: webui

- name: Build Linux
run: GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o xsql-linux-amd64 ./cmd/xsql
run: |
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o xsql-linux-amd64 ./cmd/xsql
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o xsql-ai-linux-amd64 ./cmd/xsql-ai

- name: Build Windows
run: GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o xsql-windows-amd64.exe ./cmd/xsql
run: |
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o xsql-windows-amd64.exe ./cmd/xsql
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o xsql-ai-windows-amd64.exe ./cmd/xsql-ai

- name: Build macOS (amd64)
run: GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o xsql-darwin-amd64 ./cmd/xsql
run: |
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o xsql-darwin-amd64 ./cmd/xsql
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o xsql-ai-darwin-amd64 ./cmd/xsql-ai

- name: Build macOS (arm64)
run: GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o xsql-darwin-arm64 ./cmd/xsql
run: |
GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o xsql-darwin-arm64 ./cmd/xsql
GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o xsql-ai-darwin-arm64 ./cmd/xsql-ai

- name: Upload artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ coverage*.txt
coverage*.html
coverage.out
stats_coverage.out
bin/
35 changes: 19 additions & 16 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
version: "2"
version: "2"

run:
timeout: 5m
tests: false

linters:
default: standard
settings:
errcheck:
exclude-functions:
- (*database/sql.Rows).Close
- (*database/sql.DB).Close
- (*github.com/zx06/xsql/internal/ssh.Client).Close
- (io.Closer).Close

formatters:
enable:
- goimports
settings:
goimports:
local-prefixes:
- github.com/zx06/xsql
errcheck:
exclude-functions:
- (*database/sql.Rows).Close
- (*database/sql.DB).Close
- (*github.com/zx06/xsql/internal/ssh.Client).Close
- (io.Closer).Close
- (*os.File).Close
- (*net/http.Response.Body).Close
- io.Copy

formatters:
enable:
- goimports
settings:
goimports:
local-prefixes:
- github.com/zx06/xsql
181 changes: 100 additions & 81 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,86 +1,105 @@
version: 2
project_name: xsql
version: 2

project_name: xsql

before:
hooks:
- npm --prefix webui ci
- npm --prefix webui run build
- go mod tidy

builds:
- id: xsql
main: ./cmd/xsql
binary: xsql
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.ShortCommit}}
- -X main.date={{.Date}}

archives:
- id: default
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip
files:
- README.md
- LICENSE*
- docs/*

checksum:
name_template: "checksums.txt"

snapshot:
version_template: "{{ incpatch .Version }}-next"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
- "Merge pull request"
- "Merge branch"

release:
github:
owner: zx06
name: xsql
draft: false
prerelease: auto
name_template: "v{{.Version}}"

brews:
- repository:
owner: zx06
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
directory: Formula
homepage: "https://github.com/zx06/xsql"
description: "AI-first cross-database CLI tool with SSH proxy support"
license: "MIT"
install: |
bin.install "xsql"
test: |
system "#{bin}/xsql", "version"

scoops:
- repository:
owner: zx06
name: scoop-bucket
token: "{{ .Env.SCOOP_BUCKET_TOKEN }}"
homepage: "https://github.com/zx06/xsql"
description: "AI-first cross-database CLI tool with SSH proxy support"
license: "MIT"

builds:
- id: xsql
main: ./cmd/xsql
binary: xsql
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.ShortCommit}}
- -X main.date={{.Date}}

- id: xsql-ai
main: ./cmd/xsql-ai
binary: xsql-ai
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.ShortCommit}}
- -X main.date={{.Date}}

archives:
- id: default
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip
files:
- README.md
- LICENSE*
- docs/*

checksum:
name_template: "checksums.txt"

snapshot:
version_template: "{{ incpatch .Version }}-next"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
- "Merge pull request"
- "Merge branch"

release:
github:
owner: zx06
name: xsql
draft: false
prerelease: auto
name_template: "v{{.Version}}"

brews:
- repository:
owner: zx06
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
directory: Formula
homepage: "https://github.com/zx06/xsql"
description: "AI-first cross-database CLI tool with SSH proxy support"
license: "MIT"
install: |
bin.install "xsql"
bin.install "xsql-ai"
test: |
system "#{bin}/xsql", "version"

scoops:
- repository:
owner: zx06
name: scoop-bucket
token: "{{ .Env.SCOOP_BUCKET_TOKEN }}"
homepage: "https://github.com/zx06/xsql"
description: "AI-first cross-database CLI tool with SSH proxy support"
license: "MIT"
107 changes: 107 additions & 0 deletions cmd/xsql-ai/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package main

import (
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"

"github.com/zx06/xsql/internal/ai"
"github.com/zx06/xsql/internal/config"
_ "github.com/zx06/xsql/internal/db/mysql"

Check warning on line 13 in cmd/xsql-ai/main.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a comment explaining why this blank import is needed.

See more on https://sonarcloud.io/project/issues?id=zx06_xsql&issues=AZ_SLP8yvpef94Z-7_4r&open=AZ_SLP8yvpef94Z-7_4r&pullRequest=65
_ "github.com/zx06/xsql/internal/db/pg"

Check warning on line 14 in cmd/xsql-ai/main.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a comment explaining why this blank import is needed.

See more on https://sonarcloud.io/project/issues?id=zx06_xsql&issues=AZ_SLP8yvpef94Z-7_4s&open=AZ_SLP8yvpef94Z-7_4s&pullRequest=65
"github.com/zx06/xsql/internal/secret"
"github.com/zx06/xsql/internal/tui"
)

type AIFlags struct {
ConfigPath string
Profile string
Model string
BaseURL string
APIKey string
UnsafeAllowWrite bool
Prompt string
}

var newProgramFunc = func(model tea.Model) *tea.Program {
return tea.NewProgram(model, tea.WithAltScreen())
}

func newRootCmd() *cobra.Command {
flags := &AIFlags{}

rootCmd := &cobra.Command{
Use: "xsql-ai [PROMPT]",
Short: "xsql AI interactive database query tool (TUI)",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
flags.Prompt = strings.Join(args, " ")
}
return runAI(cmd, flags)
},
}

rootCmd.Flags().StringVar(&flags.ConfigPath, "config", "", "Config file path (YAML)")
rootCmd.Flags().StringVarP(&flags.Profile, "profile", "p", "", "Profile name (default: 'default')")
rootCmd.Flags().StringVar(&flags.Model, "model", "", "AI model name (default: gpt-4o)")
rootCmd.Flags().StringVar(&flags.BaseURL, "base-url", "", "AI service base URL")
rootCmd.Flags().StringVar(&flags.APIKey, "api-key", "", "AI service API key")
rootCmd.Flags().BoolVar(&flags.UnsafeAllowWrite, "unsafe-allow-write", false, "Allow write operations (bypasses read-only protection)")
rootCmd.Flags().StringVar(&flags.Prompt, "prompt", "", "Initial prompt for AI query")

return rootCmd
}

func main() {
rootCmd := newRootCmd()
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}

func runAI(cmd *cobra.Command, flags *AIFlags) error {
opts := config.Options{
ConfigPath: flags.ConfigPath,
CLIProfile: flags.Profile,
CLIProfileSet: flags.Profile != "",
CLIAIModel: flags.Model,
CLIAIModelSet: cmd.Flags().Changed("model"),
CLIAIBaseURL: flags.BaseURL,
CLIAIBaseURLSet: cmd.Flags().Changed("base-url"),
CLIAIAPIKey: flags.APIKey,
CLIAIAPIKeySet: cmd.Flags().Changed("api-key"),
}

resolved, xe := config.Resolve(opts)
if xe != nil {
return fmt.Errorf("config error [%s]: %s", xe.Code, xe.Message)
}
if resolved.ProfileName == "" || resolved.Profile.DB == "" {
return fmt.Errorf("config error [XSQL_CFG_INVALID]: no profile specified and no 'default' profile found in config")
}

// Resolve API key if keyring reference or plaintext
apiKey := resolved.AI.APIKey
if secret.IsKeyringRef(apiKey) {
resolvedKey, xe := secret.Resolve(apiKey, secret.Options{AllowPlaintext: true})
if xe == nil {
apiKey = resolvedKey
}
}
resolved.AI.APIKey = apiKey

aiClient := ai.NewClient(resolved.AI, nil)
aiService := ai.NewService(resolved.AI, aiClient)

model := tui.NewModel(opts, resolved, aiService, flags.Prompt, flags.UnsafeAllowWrite)

p := newProgramFunc(model)
if _, err := p.Run(); err != nil {
return fmt.Errorf("error running TUI: %w", err)
}

return nil
}
Loading
Loading