2026-01-30 00:22:47 +00:00
|
|
|
package gocmd
|
|
|
|
|
|
|
|
|
|
import (
|
refactor(cli): move commands from cmd/ to pkg/ with self-registration
Implements defence in depth through build variants - only compiled code
exists in the binary. Commands now self-register via cli.RegisterCommands()
in their init() functions, mirroring the i18n.RegisterLocales() pattern.
Structure changes:
- cmd/{ai,build,ci,dev,docs,doctor,go,php,pkg,sdk,setup,test,vm}/ → pkg/*/cmd_*.go
- cmd/core_dev.go, cmd/core_ci.go → cmd/variants/{full,ci,php,minimal}.go
- Added pkg/cli/commands.go with RegisterCommands API
- Updated pkg/cli/runtime.go to attach registered commands
Build variants:
- go build → full (21MB, all 13 command groups)
- go build -tags ci → ci (18MB, build/ci/sdk/doctor)
- go build -tags php → php (14MB, php/doctor)
- go build -tags minimal → minimal (11MB, doctor only)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 21:55:55 +00:00
|
|
|
"errors"
|
2026-01-30 00:22:47 +00:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
2026-02-16 00:30:41 +00:00
|
|
|
"forge.lthn.ai/core/cli/pkg/cli"
|
|
|
|
|
"forge.lthn.ai/core/cli/pkg/i18n"
|
2026-01-30 00:22:47 +00:00
|
|
|
)
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
var (
|
|
|
|
|
installVerbose bool
|
|
|
|
|
installNoCgo bool
|
|
|
|
|
)
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
func addGoInstallCommand(parent *cli.Command) {
|
|
|
|
|
installCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "install [path]",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Install Go binary",
|
|
|
|
|
Long: "Install Go binary to $GOPATH/bin",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
// Get install path from args or default to current dir
|
|
|
|
|
installPath := "./..."
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
|
installPath = args[0]
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Detect if we're in a module with cmd/ subdirectories or a root main.go
|
|
|
|
|
if installPath == "./..." {
|
|
|
|
|
if _, err := os.Stat("core.go"); err == nil {
|
|
|
|
|
installPath = "."
|
|
|
|
|
} else if entries, err := os.ReadDir("cmd"); err == nil && len(entries) > 0 {
|
|
|
|
|
installPath = "./cmd/..."
|
|
|
|
|
} else if _, err := os.Stat("main.go"); err == nil {
|
|
|
|
|
installPath = "."
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.Progress("install"))
|
|
|
|
|
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("path")), installPath)
|
2026-01-30 00:47:54 +00:00
|
|
|
if installNoCgo {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("cgo")), "disabled")
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
cmdArgs := []string{"install"}
|
|
|
|
|
if installVerbose {
|
|
|
|
|
cmdArgs = append(cmdArgs, "-v")
|
|
|
|
|
}
|
|
|
|
|
cmdArgs = append(cmdArgs, installPath)
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", cmdArgs...)
|
|
|
|
|
if installNoCgo {
|
|
|
|
|
execCmd.Env = append(os.Environ(), "CGO_ENABLED=0")
|
|
|
|
|
}
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
if err := execCmd.Run(); err != nil {
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s\n", errorStyle.Render(i18n.T("i18n.fail.install", "binary")))
|
2026-01-30 00:47:54 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
// Show where it was installed
|
|
|
|
|
gopath := os.Getenv("GOPATH")
|
|
|
|
|
if gopath == "" {
|
|
|
|
|
home, _ := os.UserHomeDir()
|
|
|
|
|
gopath = filepath.Join(home, "go")
|
|
|
|
|
}
|
|
|
|
|
binDir := filepath.Join(gopath, "bin")
|
|
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.install")), binDir)
|
2026-01-30 00:47:54 +00:00
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 22:43:39 +00:00
|
|
|
installCmd.Flags().BoolVarP(&installVerbose, "verbose", "v", false, "Verbose output")
|
|
|
|
|
installCmd.Flags().BoolVar(&installNoCgo, "no-cgo", false, "Disable CGO")
|
2026-01-30 00:47:54 +00:00
|
|
|
|
|
|
|
|
parent.AddCommand(installCmd)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
func addGoModCommand(parent *cli.Command) {
|
|
|
|
|
modCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "mod",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Module management",
|
|
|
|
|
Long: "Go module management commands",
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// tidy
|
2026-01-31 11:39:19 +00:00
|
|
|
tidyCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "tidy",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Run go mod tidy",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "mod", "tidy")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// download
|
2026-01-31 11:39:19 +00:00
|
|
|
downloadCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "download",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Download module dependencies",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "mod", "download")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// verify
|
2026-01-31 11:39:19 +00:00
|
|
|
verifyCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "verify",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Verify module checksums",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "mod", "verify")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// graph
|
2026-01-31 11:39:19 +00:00
|
|
|
graphCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "graph",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Print module dependency graph",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "mod", "graph")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modCmd.AddCommand(tidyCmd)
|
|
|
|
|
modCmd.AddCommand(downloadCmd)
|
|
|
|
|
modCmd.AddCommand(verifyCmd)
|
|
|
|
|
modCmd.AddCommand(graphCmd)
|
|
|
|
|
parent.AddCommand(modCmd)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-31 11:39:19 +00:00
|
|
|
func addGoWorkCommand(parent *cli.Command) {
|
|
|
|
|
workCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "work",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Workspace management",
|
|
|
|
|
Long: "Go workspace management commands",
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// sync
|
2026-01-31 11:39:19 +00:00
|
|
|
syncCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "sync",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Sync workspace modules",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "work", "sync")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// init
|
2026-01-31 11:39:19 +00:00
|
|
|
initCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "init",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Initialise a new workspace",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
execCmd := exec.Command("go", "work", "init")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
if err := execCmd.Run(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// Auto-add current module if go.mod exists
|
|
|
|
|
if _, err := os.Stat("go.mod"); err == nil {
|
|
|
|
|
execCmd = exec.Command("go", "work", "use", ".")
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-01-30 00:22:47 +00:00
|
|
|
|
|
|
|
|
// use
|
2026-01-31 11:39:19 +00:00
|
|
|
useCmd := &cli.Command{
|
2026-01-30 00:47:54 +00:00
|
|
|
Use: "use [modules...]",
|
2026-01-30 22:43:39 +00:00
|
|
|
Short: "Add modules to workspace",
|
2026-01-31 11:39:19 +00:00
|
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
2026-01-30 00:47:54 +00:00
|
|
|
if len(args) == 0 {
|
|
|
|
|
// Auto-detect modules
|
|
|
|
|
modules := findGoModules(".")
|
|
|
|
|
if len(modules) == 0 {
|
2026-01-30 22:43:39 +00:00
|
|
|
return errors.New("no Go modules found")
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
2026-01-30 00:47:54 +00:00
|
|
|
for _, mod := range modules {
|
|
|
|
|
execCmd := exec.Command("go", "work", "use", mod)
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
if err := execCmd.Run(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-31 11:39:19 +00:00
|
|
|
cli.Print("%s %s\n", successStyle.Render(i18n.T("i18n.done.add")), mod)
|
2026-01-30 00:47:54 +00:00
|
|
|
}
|
|
|
|
|
return nil
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-30 00:47:54 +00:00
|
|
|
cmdArgs := append([]string{"work", "use"}, args...)
|
|
|
|
|
execCmd := exec.Command("go", cmdArgs...)
|
|
|
|
|
execCmd.Stdout = os.Stdout
|
|
|
|
|
execCmd.Stderr = os.Stderr
|
|
|
|
|
return execCmd.Run()
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
workCmd.AddCommand(syncCmd)
|
|
|
|
|
workCmd.AddCommand(initCmd)
|
|
|
|
|
workCmd.AddCommand(useCmd)
|
|
|
|
|
parent.AddCommand(workCmd)
|
2026-01-30 00:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findGoModules(root string) []string {
|
|
|
|
|
var modules []string
|
feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs
Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.
Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.
Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add collect, config, crypt, plugin packages and fix all lint issues
Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)
Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.
Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
|
|
|
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
2026-01-30 00:22:47 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if info.Name() == "go.mod" && path != "go.mod" {
|
|
|
|
|
modules = append(modules, filepath.Dir(path))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return modules
|
|
|
|
|
}
|