diff --git a/Gopkg.lock b/Gopkg.lock
index dbb0427c..2dfbec21 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -25,6 +25,18 @@
   packages = ["."]
   revision = "88b7bfd34643dce0c28a6b797652d6b5026091af"
 
+[[projects]]
+  name = "github.com/fatih/color"
+  packages = ["."]
+  revision = "507f6050b8568533fb3f5504de8e5205fa62a114"
+  version = "v1.6.0"
+
+[[projects]]
+  name = "github.com/fsnotify/fsnotify"
+  packages = ["."]
+  revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
+  version = "v1.4.7"
+
 [[projects]]
   name = "github.com/go-ole/go-ole"
   packages = [
@@ -57,6 +69,65 @@
   ]
   revision = "044f3332f2e8c38cfbb56bab29f65b4245bbe76b"
 
+[[projects]]
+  branch = "master"
+  name = "github.com/hashicorp/hcl"
+  packages = [
+    ".",
+    "hcl/ast",
+    "hcl/parser",
+    "hcl/printer",
+    "hcl/scanner",
+    "hcl/strconv",
+    "hcl/token",
+    "json/parser",
+    "json/scanner",
+    "json/token"
+  ]
+  revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
+
+[[projects]]
+  name = "github.com/inconshreveable/mousetrap"
+  packages = ["."]
+  revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
+  version = "v1.0"
+
+[[projects]]
+  name = "github.com/magiconair/properties"
+  packages = ["."]
+  revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
+  version = "v1.7.6"
+
+[[projects]]
+  name = "github.com/mattn/go-colorable"
+  packages = ["."]
+  revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
+  version = "v0.0.9"
+
+[[projects]]
+  name = "github.com/mattn/go-isatty"
+  packages = ["."]
+  revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
+  version = "v0.0.3"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/mitchellh/go-homedir"
+  packages = ["."]
+  revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/mitchellh/mapstructure"
+  packages = ["."]
+  revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
+
+[[projects]]
+  name = "github.com/pelletier/go-toml"
+  packages = ["."]
+  revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
+  version = "v1.1.0"
+
 [[projects]]
   name = "github.com/pmezard/go-difflib"
   packages = ["difflib"]
@@ -94,6 +165,49 @@
   revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
   version = "v1.0.5"
 
+[[projects]]
+  name = "github.com/spf13/afero"
+  packages = [
+    ".",
+    "mem"
+  ]
+  revision = "63644898a8da0bc22138abf860edaf5277b6102e"
+  version = "v1.1.0"
+
+[[projects]]
+  name = "github.com/spf13/cast"
+  packages = ["."]
+  revision = "8965335b8c7107321228e3e3702cab9832751bac"
+  version = "v1.2.0"
+
+[[projects]]
+  name = "github.com/spf13/cobra"
+  packages = [
+    ".",
+    "cobra",
+    "cobra/cmd"
+  ]
+  revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
+  version = "v0.0.2"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/spf13/jwalterweatherman"
+  packages = ["."]
+  revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
+
+[[projects]]
+  name = "github.com/spf13/pflag"
+  packages = ["."]
+  revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
+  version = "v1.0.1"
+
+[[projects]]
+  name = "github.com/spf13/viper"
+  packages = ["."]
+  revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
+  version = "v1.0.2"
+
 [[projects]]
   name = "github.com/stretchr/testify"
   packages = ["assert"]
@@ -130,6 +244,25 @@
   ]
   revision = "6f686a352de66814cdd080d970febae7767857a3"
 
+[[projects]]
+  name = "golang.org/x/text"
+  packages = [
+    "internal/gen",
+    "internal/triegen",
+    "internal/ucd",
+    "transform",
+    "unicode/cldr",
+    "unicode/norm"
+  ]
+  revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
+  version = "v0.3.0"
+
+[[projects]]
+  name = "gopkg.in/yaml.v2"
+  packages = ["."]
+  revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
+  version = "v2.2.1"
+
 [[projects]]
   branch = "master"
   name = "sourcegraph.com/sourcegraph/go-diff"
@@ -145,6 +278,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "fc3abded9121a32e3fc9de887b9f1dd7a4d64ee58e169053e9120881d6c54658"
+  inputs-digest = "e734f3b5571fe0e9119d20d9e9a7f8ee2b2f316686cf194307a3e5fb3327c85b"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/cmd/golangci-lint/main.go b/cmd/golangci-lint/main.go
index d04721e7..2dac78a9 100644
--- a/cmd/golangci-lint/main.go
+++ b/cmd/golangci-lint/main.go
@@ -1,43 +1,16 @@
 package main
 
 import (
-	"context"
 	"log"
-	"os"
-	"path/filepath"
 
-	"github.com/golangci/golangci-lint/pkg/config"
-	"github.com/golangci/golangci-lint/pkg/golinters"
-	"github.com/golangci/golangci-shared/pkg/executors"
+	"github.com/golangci/golangci-lint/internal/commands"
 )
 
 func main() {
-	if err := run(); err != nil {
+	log.SetFlags(0) // don't print time
+
+	e := commands.NewExecutor()
+	if err := e.Execute(); err != nil {
 		panic(err)
 	}
 }
-
-func run() error {
-	var cfg config.Config
-	config.ReadFromCommandLine(&cfg)
-
-	linters := golinters.GetSupportedLinters()
-	ctx := context.Background()
-
-	ex, err := os.Executable()
-	if err != nil {
-		return err
-	}
-	exPath := filepath.Dir(ex)
-	exec := executors.NewShell(exPath)
-
-	for _, linter := range linters {
-		res, err := linter.Run(ctx, exec)
-		if err != nil {
-			return err
-		}
-		log.Print(res)
-	}
-
-	return nil
-}
diff --git a/internal/commands/executor.go b/internal/commands/executor.go
new file mode 100644
index 00000000..c20694b9
--- /dev/null
+++ b/internal/commands/executor.go
@@ -0,0 +1,27 @@
+package commands
+
+import (
+	"github.com/golangci/golangci-lint/pkg/config"
+	"github.com/spf13/cobra"
+)
+
+type Executor struct {
+	rootCmd *cobra.Command
+
+	cfg *config.Config
+}
+
+func NewExecutor() *Executor {
+	e := &Executor{
+		cfg: config.NewDefault(),
+	}
+
+	e.initRoot()
+	e.initRun()
+
+	return e
+}
+
+func (e Executor) Execute() error {
+	return e.rootCmd.Execute()
+}
diff --git a/internal/commands/root.go b/internal/commands/root.go
new file mode 100644
index 00000000..3cae660b
--- /dev/null
+++ b/internal/commands/root.go
@@ -0,0 +1,18 @@
+package commands
+
+import (
+	"github.com/spf13/cobra"
+)
+
+func (e *Executor) initRoot() {
+	rootCmd := &cobra.Command{
+		Use:   "golangci-lint",
+		Short: "golangci-lint is a smart linters runner.",
+		Long:  `Smart, fast linters runner. Run it in cloud for every GitHub pull request on https://golangci.com`,
+		Run: func(cmd *cobra.Command, args []string) {
+			_ = cmd.Help()
+		},
+	}
+	rootCmd.PersistentFlags().BoolVarP(&e.cfg.Common.IsVerbose, "verbose", "v", false, "verbose output")
+	e.rootCmd = rootCmd
+}
diff --git a/internal/commands/run.go b/internal/commands/run.go
new file mode 100644
index 00000000..532359ea
--- /dev/null
+++ b/internal/commands/run.go
@@ -0,0 +1,92 @@
+package commands
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/fatih/color"
+	"github.com/golangci/golangci-lint/pkg/config"
+	"github.com/golangci/golangci-lint/pkg/golinters"
+	"github.com/golangci/golangci-lint/pkg/result"
+	"github.com/golangci/golangci-shared/pkg/executors"
+	"github.com/spf13/cobra"
+)
+
+func (e *Executor) initRun() {
+	var runCmd = &cobra.Command{
+		Use:   "run",
+		Short: "Run linters",
+		Run:   e.executeRun,
+	}
+	e.rootCmd.AddCommand(runCmd)
+
+	runCmd.Flags().StringVarP(&e.cfg.Run.OutFormat, "out-format", "",
+		config.OutFormatColoredLineNumber,
+		fmt.Sprintf("Format of output: %s", strings.Join(config.OutFormats, "|")))
+}
+
+func (e Executor) executeRun(cmd *cobra.Command, args []string) {
+	f := func() error {
+		linters := golinters.GetSupportedLinters()
+		ctx := context.Background()
+
+		ex, err := os.Executable()
+		if err != nil {
+			return err
+		}
+		exPath := filepath.Dir(ex)
+		exec := executors.NewShell(exPath)
+
+		issues := []result.Issue{}
+		for _, linter := range linters {
+			res, err := linter.Run(ctx, exec)
+			if err != nil {
+				return err
+			}
+			issues = append(issues, res.Issues...)
+		}
+
+		if err = outputIssues(e.cfg.Run.OutFormat, issues); err != nil {
+			return fmt.Errorf("can't output %d issues: %s", len(issues), err)
+		}
+
+		return nil
+	}
+
+	if err := f(); err != nil {
+		panic(err)
+	}
+}
+
+func outputIssues(format string, issues []result.Issue) error {
+	if format == config.OutFormatLineNumber || format == config.OutFormatColoredLineNumber {
+		if len(issues) == 0 {
+			outStr := "Congrats! No issues were found."
+			if format == config.OutFormatColoredLineNumber {
+				outStr = color.GreenString(outStr)
+			}
+			log.Print(outStr)
+		}
+
+		for _, i := range issues {
+			log.Printf("%s:%d: %s", i.File, i.LineNumber, i.Text)
+		}
+		return nil
+	}
+
+	if format == config.OutFormatJSON {
+		outputJSON, err := json.Marshal(issues)
+		if err != nil {
+			return err
+		}
+		log.Print(string(outputJSON))
+		return nil
+	}
+
+	return fmt.Errorf("unknown output format %q", format)
+}
diff --git a/pkg/config/command_line.go b/pkg/config/command_line.go
deleted file mode 100644
index a99e47b5..00000000
--- a/pkg/config/command_line.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package config
-
-import "flag"
-
-func ReadFromCommandLine(cfg *Config) {
-	flag.Parse()
-	paths := flag.Args()
-	if len(paths) != 0 {
-		cfg.Paths = paths
-	}
-}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 90579090..99a18262 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,5 +1,34 @@
 package config
 
-type Config struct {
-	Paths []string
+type OutFormat string
+
+const (
+	OutFormatJSON              = "json"
+	OutFormatLineNumber        = "line-number"
+	OutFormatColoredLineNumber = "colored-line-number"
+)
+
+var OutFormats = []string{OutFormatColoredLineNumber, OutFormatLineNumber, OutFormatJSON}
+
+type Common struct {
+	IsVerbose bool
+}
+
+type Run struct {
+	Paths     []string
+	OutFormat string
+}
+
+type Config struct {
+	Common Common
+	Run    Run
+}
+
+func NewDefault() *Config {
+	return &Config{
+		Run: Run{
+			Paths:     []string{"./..."},
+			OutFormat: OutFormatColoredLineNumber,
+		},
+	}
 }
diff --git a/pkg/golinters/utils.go b/pkg/golinters/utils.go
index c44947d5..38f27d74 100644
--- a/pkg/golinters/utils.go
+++ b/pkg/golinters/utils.go
@@ -3,7 +3,6 @@ package golinters
 import (
 	"context"
 	"fmt"
-	"log"
 	"path"
 	"path/filepath"
 
@@ -38,7 +37,6 @@ func processPaths(root string, paths []string, maxPaths int) ([]string, error) {
 func getPathsForGoProject(root string) (*ProjectPaths, error) {
 	excludeDirs := []string{"vendor", "testdata", "examples", "Godeps"}
 	pr := fsutils.NewPathResolver(excludeDirs, []string{".go"})
-	log.Printf("root is %q, paths are %q", root, path.Join(root, "..."))
 	paths, err := pr.Resolve(path.Join(root, "..."))
 	if err != nil {
 		return nil, fmt.Errorf("can't resolve paths: %s", err)