diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..974b0c9
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,22 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-go@v5
+ with:
+ go-version: "1.26.x"
+ check-latest: true
+ - name: Build
+ run: go build ./...
+ - name: Vet
+ run: go vet ./...
+ - name: Test
+ run: go test ./...
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..f0203ae
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,103 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - "v*"
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Tag to (re)build and attach assets to (e.g. v2.1.0)"
+ required: true
+
+permissions:
+ contents: write
+
+jobs:
+ build:
+ name: Build ${{ matrix.goos }}/${{ matrix.goarch }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - goos: linux
+ goarch: amd64
+ ext: ""
+ - goos: linux
+ goarch: arm64
+ ext: ""
+ - goos: darwin
+ goarch: amd64
+ ext: ""
+ - goos: darwin
+ goarch: arm64
+ ext: ""
+ - goos: windows
+ goarch: amd64
+ ext: ".exe"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.tag || github.ref }}
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: "1.26.x"
+ check-latest: true
+
+ - name: Resolve version
+ id: ver
+ run: |
+ TAG="${{ github.event.inputs.tag || github.ref_name }}"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
+ echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
+
+ - name: Build
+ env:
+ GOOS: ${{ matrix.goos }}
+ GOARCH: ${{ matrix.goarch }}
+ CGO_ENABLED: 0
+ run: |
+ OUT="cue-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}"
+ go build -trimpath \
+ -ldflags "-s -w -X main.Version=${{ steps.ver.outputs.version }} -X main.BuildDate=${{ steps.ver.outputs.build_date }}" \
+ -o "dist/${OUT}" .
+ (cd dist && sha256sum "${OUT}" > "${OUT}.sha256")
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: cue-${{ matrix.goos }}-${{ matrix.goarch }}
+ path: dist/*
+ if-no-files-found: error
+
+ publish:
+ name: Publish release assets
+ needs: build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: dist
+ merge-multiple: true
+
+ - name: Resolve tag
+ id: ver
+ run: |
+ TAG="${{ github.event.inputs.tag || github.ref_name }}"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+
+ - name: Upload to GitHub Release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ TAG="${{ steps.ver.outputs.tag }}"
+ if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
+ gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --title "$TAG" --generate-notes
+ fi
+ gh release upload "$TAG" --repo "$GITHUB_REPOSITORY" --clobber dist/*
diff --git a/README.md b/README.md
index e8de0c4..d933c5e 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
-
πΊ Welcome to v2.0
+
πΊ Welcome to v2.2
πͺ Environment Store Management
@@ -56,14 +56,23 @@ Modern application development requires immense cognitive overhead. Should you u
The CLI distributes as a self-contained, statically linked fat binary. No complex Node/Python/Ruby dependencies are strictly required to run it!
-π§ Linux / macOS
+π§ Linux / macOS (incl. Arch Linux support)
+**One-liner install (Ubuntu / Debian / Arch / macOS):**
```bash
curl -fsSL https://raw.githubusercontent.com/GyaneshSamanta/cue/main/scripts/install.sh | bash
```
-> **Note for Arch Linux Users:** The `install.sh` script is fully compatible with Arch Linux. For optimal compatibility with future AUR packaging and dependencies, ensure you have the base development tools installed via `sudo pacman -S base-devel`.
+To pin a specific version:
+```bash
+curl -fsSL https://raw.githubusercontent.com/GyaneshSamanta/cue/main/scripts/install.sh | CUE_VERSION=v2.1.0 bash
+```
+
+**Arch Linux specifics:** The `install.sh` script works directly on Arch. We recommend having `base-devel`, `curl`, and `sudo` ready (`sudo pacman -S --needed base-devel curl`).
+
+> [!TIP]
+> **AUR packaging:** AUR support (`cue-bin`) is currently in development. For now, the direct install is the recommended path.
@@ -75,6 +84,25 @@ curl -fsSL https://raw.githubusercontent.com/GyaneshSamanta/cue/main/scripts/ins
iwr https://raw.githubusercontent.com/GyaneshSamanta/cue/main/scripts/install.ps1 -useb | iex
```
+To pin a specific version:
+```powershell
+$env:CUE_VERSION = 'v2.1.0'; iwr https://raw.githubusercontent.com/GyaneshSamanta/cue/main/scripts/install.ps1 -useb | iex
+```
+
+
+
+
+π Build from source
+
+
+Requires Go 1.26+.
+
+```bash
+git clone https://github.com/GyaneshSamanta/cue && cd cue
+go build -o cue .
+./cue --version
+```
+
@@ -118,6 +146,33 @@ During installation, it offers multiple execution engines:
---
+## π§ Local Models (Ollama, Gemma & friends)
+
+Run open-weight models on your own machine, then point Claude Code at them. Cue takes care of pulling, switching, and benchmarking.
+
+```bash
+cue model recommend # hardware-aware suggestions for your laptop
+cue model gemma # one-shot install of Google Gemma 3 (4B default)
+cue model gemma 12b # bigger Gemma for stronger quality
+cue model pull qwen2.5-coder:7b
+cue model use gemma3:4b # set as the default for Claude Code
+cue model list # see what's installed
+cue model benchmark gemma3:4b # quick speed test
+```
+
+**Gemma sizes** at a glance:
+
+| Tag | Size | Best for |
+| :--- | :--- | :--- |
+| `gemma3:1b` | ~0.8 GB | Tiny laptops, instant load |
+| `gemma3:4b` | ~3.3 GB | Default β strong general use on 8 GB+ RAM |
+| `gemma3:12b` | ~8.1 GB | High quality on 16 GB RAM / 12 GB VRAM |
+| `gemma3:27b` | ~17 GB | Best local quality on 32 GB RAM / 24 GB VRAM |
+
+> Cue requires [Ollama](https://ollama.com/download) for local models. Run `cue model gemma` and Cue will tell you exactly how to install it if it's missing.
+
+---
+
## β‘ The Ultimate Macro Glossary
Macros encapsulate best-practices and safety constraints into readable verbs. Forget raw terminal commandsβuse our semantic shortcuts.
@@ -167,6 +222,27 @@ ui.HandleError(se)
---
+## π Changelog
+
+### v2.2.0 β May 2026
+
+**New**
+- **`cue model` command** β the `internal/model` package was wired up and is now a first-class CLI feature. Run `cue model --help` to see all subcommands.
+- **Google Gemma 3 support** β `cue model gemma [1b|4b|12b|27b]` installs Google Gemma via Ollama in one command. Hardware-aware `cue model recommend` now includes all four Gemma sizes.
+- **`cue toolkit` command** β install, upgrade, remove, and inspect developer tools with automatic version-manager bootstrapping (e.g. `cue toolkit install node`). Closes the loop from `cue doctor fix --all`, which had been suggesting `cue toolkit install
` when the command didn't yet exist.
+- **Release pipeline** β added `.github/workflows/release.yml` that cross-compiles for linux/darwin/windows Γ amd64/arm64 and uploads binaries to GitHub Releases on every version tag. **This is what was causing the broken `install.sh` / `install.ps1`** β prior releases had no binary assets.
+- **CI pipeline** β added `.github/workflows/ci.yml` for build / vet / test checks on every PR.
+
+**Fixed**
+- `install.sh` and `install.ps1` now fail with a clear actionable error if no release is found, instead of silently downloading nothing.
+- Both scripts accept a `CUE_VERSION=vX.Y.Z` / `$env:CUE_VERSION` env var to pin to a specific release.
+- `cue model list/pull/gemma` now prints a friendly install hint (brew / curl / winget) if Ollama is missing, instead of an opaque exec error.
+
+**Improved**
+- README install section: explicit Arch Linux guidance, versioned-install snippets, build-from-source instructions.
+
+---
+
Built with β€οΈ by Gyanesh
diff --git a/cmd/model.go b/cmd/model.go
new file mode 100644
index 0000000..7e5a932
--- /dev/null
+++ b/cmd/model.go
@@ -0,0 +1,119 @@
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+
+ "github.com/GyaneshSamanta/cue/internal/model"
+ "github.com/GyaneshSamanta/cue/internal/ui"
+)
+
+var modelCmd = &cobra.Command{
+ Use: "model",
+ Short: "Manage local AI models (Ollama, Gemma, etc.)",
+ Long: `Manage local AI models served by Ollama, including Google Gemma.
+
+Cue treats Ollama as the canonical local-model runtime. Use this command
+to pull models, set the active model for Claude Code, benchmark inference
+speed, or get hardware-aware recommendations (including Gemma sizes).`,
+}
+
+var modelListCmd = &cobra.Command{
+ Use: "list",
+ Short: "List installed local models",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.ListModels()
+ },
+}
+
+var modelPullCmd = &cobra.Command{
+ Use: "pull ",
+ Short: "Download a model (e.g. gemma3:4b, llama3.1:8b)",
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.PullModel(args[0])
+ },
+}
+
+var modelRemoveCmd = &cobra.Command{
+ Use: "remove ",
+ Short: "Remove a downloaded model",
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.RemoveModel(args[0])
+ },
+}
+
+var modelUseCmd = &cobra.Command{
+ Use: "use ",
+ Short: "Set the default model used by Claude Code",
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.UseModel(args[0])
+ },
+}
+
+var modelRecommendCmd = &cobra.Command{
+ Use: "recommend",
+ Short: "Suggest models based on detected hardware",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.Recommend()
+ },
+}
+
+var modelBenchmarkCmd = &cobra.Command{
+ Use: "benchmark [name]",
+ Short: "Run a quick inference speed test",
+ Args: cobra.MaximumNArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ name := ""
+ if len(args) == 1 {
+ name = args[0]
+ }
+ return model.Benchmark(name)
+ },
+}
+
+var modelSearchCmd = &cobra.Command{
+ Use: "search ",
+ Short: "Search the Ollama registry",
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return model.Search(args[0])
+ },
+}
+
+var modelGemmaCmd = &cobra.Command{
+ Use: "gemma [size]",
+ Short: "Quick-install Google Gemma (sizes: 1b, 4b, 12b, 27b β default 4b)",
+ Long: `Quick-install Google Gemma 3 via Ollama.
+
+Examples:
+ cue model gemma # installs gemma3:4b (good default for 16GB RAM)
+ cue model gemma 1b # tiny β fits low-RAM laptops
+ cue model gemma 12b # quality β needs ~16GB RAM or 12GB VRAM
+ cue model gemma 27b # best β needs ~32GB RAM or 24GB VRAM
+
+After install, run 'cue model use gemma3:' to make it the default
+for Claude Code.`,
+ Args: cobra.MaximumNArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ size := "4b"
+ if len(args) == 1 {
+ size = args[0]
+ }
+ valid := map[string]bool{"1b": true, "4b": true, "12b": true, "27b": true}
+ if !valid[size] {
+ ui.PrintWarning("Unknown Gemma size '" + size + "'. Supported: 1b, 4b, 12b, 27b. Proceeding anyway.")
+ }
+ tag := "gemma3:" + size
+ if err := model.EnsureOllama(); err != nil {
+ return err
+ }
+ return model.PullModel(tag)
+ },
+}
+
+func init() {
+ modelCmd.AddCommand(modelListCmd, modelPullCmd, modelRemoveCmd, modelUseCmd,
+ modelRecommendCmd, modelBenchmarkCmd, modelSearchCmd, modelGemmaCmd)
+}
diff --git a/cmd/root.go b/cmd/root.go
index 471be3a..23112e5 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -120,6 +120,7 @@ func init() {
rootCmd.AddCommand(updateCmd)
rootCmd.AddCommand(versionMgrCmd)
rootCmd.AddCommand(doctorCmd)
+ rootCmd.AddCommand(modelCmd)
}
// Execute runs the root command.
diff --git a/internal/model/manager.go b/internal/model/manager.go
index 513884b..f196eca 100644
--- a/internal/model/manager.go
+++ b/internal/model/manager.go
@@ -12,8 +12,25 @@ import (
"github.com/GyaneshSamanta/cue/internal/ui"
)
+// EnsureOllama verifies that the Ollama runtime is installed and reachable.
+// It returns a helpful, actionable error if not β rather than letting an
+// opaque exec failure bubble up.
+func EnsureOllama() error {
+ if _, err := exec.LookPath("ollama"); err != nil {
+ ui.PrintWarning("Ollama is not installed. Install it from https://ollama.com/download")
+ ui.PrintDim(" macOS: brew install ollama")
+ ui.PrintDim(" Linux: curl -fsSL https://ollama.com/install.sh | sh")
+ ui.PrintDim(" Windows: winget install Ollama.Ollama")
+ return fmt.Errorf("ollama not found in PATH")
+ }
+ return nil
+}
+
// ListModels shows all Ollama models.
func ListModels() error {
+ if err := EnsureOllama(); err != nil {
+ return err
+ }
out, err := exec.Command("ollama", "list").CombinedOutput()
if err != nil {
return fmt.Errorf("ollama not running or not installed: %w", err)
@@ -82,6 +99,10 @@ func Recommend() error {
{"Reasoning", "deepseek-r1:8b", "5.2 GB", "Strong multi-step reasoning", ramGB >= 12},
{"Reasoning (large)", "deepseek-r1:14b", "9.1 GB", "Best reasoning under 15B", ramGB >= 24},
{"General", "llama3.1:8b", "4.7 GB", "Versatile general model", ramGB >= 12},
+ {"Gemma (tiny)", "gemma3:1b", "0.8 GB", "Google Gemma 3 β runs anywhere", true},
+ {"Gemma (default)", "gemma3:4b", "3.3 GB", "Google Gemma 3 β strong general use", ramGB >= 8},
+ {"Gemma (quality)", "gemma3:12b", "8.1 GB", "Google Gemma 3 β high quality", ramGB >= 16 || vramGB >= 12},
+ {"Gemma (large)", "gemma3:27b", "17 GB", "Google Gemma 3 β best quality", ramGB >= 32 || vramGB >= 24},
}
fmt.Println(" Recommendations:")
diff --git a/scripts/install.ps1 b/scripts/install.ps1
index 9d2904d..cb76825 100644
--- a/scripts/install.ps1
+++ b/scripts/install.ps1
@@ -27,14 +27,19 @@ $Arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" }
Write-Ok "Detected: windows/$Arch"
# Get latest version
-Write-Step "Fetching latest version..."
-try {
- $Release = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -UseBasicParsing
- $Version = $Release.tag_name -replace '^v', ''
- Write-Ok "Latest version: v$Version"
-} catch {
- $Version = "1.0.0"
- Write-Warn "Could not fetch latest version, using v$Version"
+if ($env:CUE_VERSION) {
+ $Version = $env:CUE_VERSION -replace '^v', ''
+ Write-Ok "Using requested version: v$Version"
+} else {
+ Write-Step "Fetching latest version..."
+ try {
+ $Release = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -UseBasicParsing
+ $Version = $Release.tag_name -replace '^v', ''
+ Write-Ok "Latest version: v$Version"
+ } catch {
+ Write-Err "Could not fetch latest release. Set `$env:CUE_VERSION = 'vX.Y.Z' to install a specific version, or download from https://github.com/$Repo/releases"
+ exit 1
+ }
}
# Download
diff --git a/scripts/install.sh b/scripts/install.sh
index 3a45427..49c69cb 100644
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -43,10 +43,15 @@ detect_platform() {
# Get latest release version
get_latest_version() {
- VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"v?([^"]+)".*/\1/')
+ if [ -n "${CUE_VERSION:-}" ]; then
+ VERSION="${CUE_VERSION#v}"
+ print_ok "Using requested version: v${VERSION}"
+ return
+ fi
+ VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | head -n1 | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v?([^"]+)".*/\1/')
if [ -z "$VERSION" ]; then
- VERSION="1.0.0"
- print_warn "Could not fetch latest version, using v${VERSION}"
+ print_err "Could not fetch latest release. Set CUE_VERSION=vX.Y.Z to install a specific version, or download from https://github.com/${REPO}/releases"
+ exit 1
fi
print_ok "Latest version: v${VERSION}"
}