Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 5 additions & 23 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,15 @@ import (
"github.com/spf13/cobra"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "tmpo",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Short: "Minimal CLI time tracker for developers",
Long: `tmpo - Set the tmpo

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
A minimal, developer-friendly time tracking tool that lives in your terminal.
Track time effortlessly with automatic project detection and simple commands.`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
Expand All @@ -31,15 +23,5 @@ func Execute() {
}

func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.

// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tmpo.yaml)")

// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.Version = "0.1.0"
}


78 changes: 57 additions & 21 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,71 @@ package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/DylanDevelops/tmpo/internal/storage"
"github.com/spf13/cobra"
)

// startCmd represents the start command
var startCmd = &cobra.Command{
Use: "start",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("start called")
Use: "start [description]",
Short: "Start tracking time",
Long: `Start a new time tracking session for the current project.`,
Run: func(cmd* cobra.Command, args []string) {
db, err := storage.Initialize()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

defer db.Close()

running, err := db.GetRunningEntry()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

if running != nil {
fmt.Fprintf(os.Stderr, "Error: Already tracking time for `%s\n", running.ProjectName)
fmt.Println("Use 'tmpo stop' to stop the current session first.")

os.Exit(1)
}

cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current directory: %v\n", err)

os.Exit(1)
}

projectName := filepath.Base(cwd)

description := ""
if len(args) > 0 {
description = args[0]
}

entry, err := db.CreateEntry(projectName, description)

if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

fmt.Printf("[tmpo] Started tracking time for '%s'\n", entry.ProjectName)

if description != "" {
fmt.Printf(" Description: %s\n", description)
}
},
}

func init() {
rootCmd.AddCommand(startCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// startCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
59 changes: 39 additions & 20 deletions cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,54 @@ package cmd

import (
"fmt"
"os"
"time"

"github.com/DylanDevelops/tmpo/internal/storage"
"github.com/spf13/cobra"
)

// statusCmd represents the status command
var statusCmd = &cobra.Command{
Use: "status",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("status called")
Short: "Show current tracking status",
Long: `Display information about the currently running time tracking session.`,

Run: func(cmd* cobra.Command, args []string) {
db, err := storage.Initialize()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

defer db.Close()

running, err := db.GetRunningEntry()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

if running == nil {
fmt.Println("[tmpo] Not currently tracking time")
fmt.Println("\nUse 'tmpo start' to begin tracking")

return
}

duration := time.Since(running.StartTime)

fmt.Printf("[tmpo] Currently tracking: %s\n", running.ProjectName)
fmt.Printf(" Started: %s\n", running.StartTime.Format("3:04 PM"))
fmt.Printf(" Duration: %s\n", formatDuration(duration))

if running.Description != "" {
fmt.Printf(" Description: %s\n", running.Description)
}
},
}

func init() {
rootCmd.AddCommand(statusCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// statusCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// statusCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
75 changes: 57 additions & 18 deletions cmd/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,74 @@ package cmd

import (
"fmt"
"os"
"time"

"github.com/DylanDevelops/tmpo/internal/storage"
"github.com/spf13/cobra"
)

// stopCmd represents the stop command
var stopCmd = &cobra.Command{
Use: "stop",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("stop called")
Short: "Stop tracking time",
Long: `Stop the currently running time tracking session.`,
Run: func(cmd* cobra.Command, args []string) {
db, err := storage.Initialize()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

defer db.Close()

running, err := db.GetRunningEntry()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

if running == nil {
fmt.Println("No active time tracking session.")

os.Exit(0)
}

err = db.StopEntry(running.ID)
if(err != nil) {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

os.Exit(1)
}

duration := time.Since(running.StartTime)

fmt.Printf("[tmpo] Stopped tracking '%s'\n", running.ProjectName)
fmt.Printf(" Total Duration: %s\n", formatDuration(duration))
},
}

func init() {
rootCmd.AddCommand(stopCmd)
// formatDuration formats d into a concise, human-readable string using hours, minutes and seconds.
// It returns "<h>h <m>m <s>s" when the duration is at least one hour, "<m>m <s>s" when the duration
// is at least one minute but less than an hour, and "<s>s" for durations under one minute.
// Hours, minutes and seconds are derived from d using integer truncation (no fractional parts).
// This function is intended for non-negative durations; behavior for negative durations is unspecified.
func formatDuration(d time.Duration) string {
hours := int(d.Hours())
minutes := int(d.Minutes()) % 60
seconds := int(d.Seconds()) % 60

// Here you will define your flags and configuration settings.
if hours > 0 {
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
} else if minutes > 0 {
return fmt.Sprintf("%dm %ds", minutes, seconds)
}

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// stopCmd.PersistentFlags().String("foo", "", "A help for foo")
return fmt.Sprintf("%ds", seconds)
}

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// stopCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
func init() {
rootCmd.AddCommand(stopCmd)
}
19 changes: 5 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@ module github.com/DylanDevelops/tmpo
go 1.25.5

require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/mattn/go-sqlite3 v1.14.32
github.com/spf13/cobra v1.10.2
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-sqlite3 v1.14.32 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
)
23 changes: 0 additions & 23 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Empty file removed internal/commands/root.go
Empty file.
Loading