Skip to content

Commit 4a8a684

Browse files
feat: Add 20 new themes and CLI update commands
- Introduce 20 new 2026 design trends themes - Implement `kairo version` command - Add `k
1 parent 8aedb9b commit 4a8a684

6 files changed

Lines changed: 479 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.1]
9+
10+
### Added
11+
- **20 New Themes (2026 Design Trends)**:
12+
- Dark themes: `obsidian_bloom`, `neon_reef`, `carbon_sunset`, `vanta_aurora`, `plasma_grape`, `midnight_jade`, `synthwave_minimal`, `graphite_matcha`
13+
- Light themes: `cloud_dancer`, `sakura_sand`, `olive_mist`, `terracotta_air`, `vanilla_sky`, `peach_fuzz_neo`, `coastal_drift`, `matcha_latte`
14+
- Hybrid themes: `digital_lavender`, `neo_mint_system`, `sunset_gradient_pro`, `forest_sanctuary`
15+
- **Version Management**: `kairo version` command to display installed version
16+
- **Update Command**: `kairo update` command for one-step updates via `go install github.com/programmersd21/kairo/cmd/kairo@latest`
17+
18+
### Changed
19+
- Updated theme registry to 32 total themes (12 legacy + 20 new)
20+
821
## [1.1.0]
922

1023
### Added

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@ kairo api update --id <task-id> --status done
7474
kairo api --json '{"action": "create", "payload": {"title": "API Task", "tags": ["bot"]}}'
7575
```
7676

77+
### Other CLI Commands
78+
79+
```bash
80+
# Check installed version
81+
kairo version
82+
83+
# Update to the latest version
84+
kairo update
85+
86+
# Export tasks
87+
kairo export --format json --out tasks.json
88+
kairo export --format markdown --out tasks.md
89+
90+
# Import tasks
91+
kairo import --format json --in tasks.json
92+
93+
# Sync with Git (if configured)
94+
kairo sync
95+
```
96+
7797
---
7898

7999
## 🔌 Plugins (Lua)

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.0
1+
1.1.1

cmd/kairo/main.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"io"
9+
"net/http"
810
"os"
11+
"os/exec"
912
"os/signal"
1013
"path/filepath"
1114
"strings"
@@ -84,6 +87,15 @@ func main() {
8487
os.Exit(2)
8588
}
8689
return
90+
case "version":
91+
runVersion()
92+
return
93+
case "update":
94+
if err := runUpdate(ctx); err != nil {
95+
fmt.Fprintln(os.Stderr, "kairo update:", err)
96+
os.Exit(2)
97+
}
98+
return
8799
}
88100
}
89101

@@ -231,3 +243,146 @@ func runImport(ctx context.Context, repo *storage.Repository, args []string) err
231243
}
232244
return nil
233245
}
246+
247+
func runVersion() {
248+
version := getCurrentVersion()
249+
fmt.Printf("kairo version %s\n", version)
250+
}
251+
252+
func getCurrentVersion() string {
253+
// Try current working directory first (most common for development/running)
254+
if data, err := os.ReadFile("VERSION.txt"); err == nil {
255+
return strings.TrimSpace(string(data))
256+
}
257+
258+
// Try repository root relative to binary location
259+
if ex, err := os.Executable(); err == nil {
260+
versionPath := filepath.Join(filepath.Dir(ex), "VERSION.txt")
261+
if data, err := os.ReadFile(versionPath); err == nil {
262+
return strings.TrimSpace(string(data))
263+
}
264+
265+
// Try going up from the binary directory (for development builds)
266+
versionPath = filepath.Join(filepath.Dir(ex), "..", "VERSION.txt")
267+
if data, err := os.ReadFile(versionPath); err == nil {
268+
return strings.TrimSpace(string(data))
269+
}
270+
}
271+
272+
// If not found in expected locations, try home config directory
273+
homeDir, _ := os.UserHomeDir()
274+
altPath := filepath.Join(homeDir, ".config", "kairo", "VERSION.txt")
275+
if data, err := os.ReadFile(altPath); err == nil {
276+
return strings.TrimSpace(string(data))
277+
}
278+
279+
// Fallback to default
280+
return "1.0.0"
281+
}
282+
283+
type GitHubRelease struct {
284+
TagName string `json:"tag_name"`
285+
Draft bool `json:"draft"`
286+
Prerelease bool `json:"prerelease"`
287+
}
288+
289+
func getLatestGitHubRelease(ctx context.Context) (string, error) {
290+
// Query GitHub API for the latest release
291+
url := "https://api.github.com/repos/programmersd21/kairo/releases/latest"
292+
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
293+
if err != nil {
294+
return "", err
295+
}
296+
297+
req.Header.Set("Accept", "application/vnd.github.v3+json")
298+
299+
client := &http.Client{}
300+
resp, err := client.Do(req)
301+
if err != nil {
302+
return "", err
303+
}
304+
defer func() {
305+
_ = resp.Body.Close()
306+
}()
307+
308+
if resp.StatusCode != http.StatusOK {
309+
return "", fmt.Errorf("GitHub API returned status %d", resp.StatusCode)
310+
}
311+
312+
body, err := io.ReadAll(resp.Body)
313+
if err != nil {
314+
return "", err
315+
}
316+
317+
var release GitHubRelease
318+
if err := json.Unmarshal(body, &release); err != nil {
319+
return "", err
320+
}
321+
322+
// Remove 'v' prefix if present
323+
version := strings.TrimPrefix(release.TagName, "v")
324+
return version, nil
325+
}
326+
327+
func compareVersions(current, latest string) int {
328+
// Parse semantic versions: x.y.z
329+
currentParts := strings.Split(strings.TrimPrefix(current, "v"), ".")
330+
latestParts := strings.Split(strings.TrimPrefix(latest, "v"), ".")
331+
332+
// Pad with zeros if needed
333+
for len(currentParts) < 3 {
334+
currentParts = append(currentParts, "0")
335+
}
336+
for len(latestParts) < 3 {
337+
latestParts = append(latestParts, "0")
338+
}
339+
340+
// Simple numeric comparison
341+
for i := 0; i < 3; i++ {
342+
var curr, last int
343+
_, _ = fmt.Sscanf(currentParts[i], "%d", &curr)
344+
_, _ = fmt.Sscanf(latestParts[i], "%d", &last)
345+
346+
if last > curr {
347+
return -1 // latest is newer
348+
}
349+
if last < curr {
350+
return 1 // current is newer
351+
}
352+
}
353+
return 0 // equal
354+
}
355+
356+
func runUpdate(ctx context.Context) error {
357+
currentVersion := getCurrentVersion()
358+
fmt.Printf("Checking for updates (current: %s)...\n", currentVersion)
359+
360+
latestVersion, err := getLatestGitHubRelease(ctx)
361+
if err != nil {
362+
return fmt.Errorf("failed to fetch latest release: %w", err)
363+
}
364+
365+
cmp := compareVersions(currentVersion, latestVersion)
366+
if cmp >= 0 {
367+
// Already on latest or newer version
368+
fmt.Printf("✓ Updated to latest version %s\n", currentVersion)
369+
return nil
370+
}
371+
372+
// There's a newer version available
373+
fmt.Printf("Found newer version: %s\n", latestVersion)
374+
fmt.Println("Updating kairo...")
375+
376+
// Execute: go install github.com/programmersd21/kairo/cmd/kairo@latest
377+
cmd := exec.CommandContext(ctx, "go", "install", "github.com/programmersd21/kairo/cmd/kairo@latest")
378+
cmd.Stdout = os.Stdout
379+
cmd.Stderr = os.Stderr
380+
cmd.Stdin = os.Stdin
381+
382+
if err := cmd.Run(); err != nil {
383+
return fmt.Errorf("failed to update: %w", err)
384+
}
385+
386+
fmt.Printf("✓ Kairo updated to version %s successfully!\n", latestVersion)
387+
return nil
388+
}

internal/lua/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (e *Engine) SetupKairoAPI(L *lua.LState) {
5555
L.SetField(kairo, "notify", L.NewFunction(e.luaNotify))
5656

5757
// Meta
58-
L.SetField(kairo, "version", lua.LString("1.0.1"))
58+
L.SetField(kairo, "version", lua.LString("1.1.1"))
5959

6060
// Set as global
6161
L.SetGlobal("kairo", kairo)

0 commit comments

Comments
 (0)