Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
c74524b
chore: align check output with CLI wrappers
Mar 30, 2026
c67582c
fix(help): make help command AX-friendly
Mar 31, 2026
1dd401f
feat(pkg): add json format for package list
Mar 31, 2026
0595bf7
feat(pkg): show search repo metadata
Mar 31, 2026
7fda1cf
fix(pkg): accept positional search patterns
Mar 31, 2026
10de071
feat(pkg): add JSON output for package search
Mar 31, 2026
9aff00d
feat(pkg): add JSON output for pkg outdated
Mar 31, 2026
d50b006
feat(help): show snippets in search results
Mar 31, 2026
04d2444
feat(pkg): honor search limit after cache
Mar 31, 2026
4c072f9
feat(pkg): show full repo names in search results
Mar 31, 2026
d84d8cc
feat: add persistent CLI flag helpers
Apr 1, 2026
32342df
feat(pkg): accept install repo shorthand
Apr 2, 2026
27e44f0
fix(cli): reset stdin on nil override
Apr 2, 2026
fcadba0
feat(pkg): support install refs in shorthand
Apr 2, 2026
c6fae79
fix(cli): load locale sources during registration
Apr 2, 2026
9c64f23
fix(cli): respect stdin overrides in prompts
Apr 2, 2026
4e258c8
feat(cli): add runtime run helpers
Apr 2, 2026
96aef54
feat(cli): add filterable generic selection
Apr 2, 2026
f13c3bf
fix: add go check to doctor
Apr 2, 2026
a2f27b9
feat(cli): add daemon lifecycle helper
Apr 2, 2026
12496ba
feat(cli): add external daemon stop helper
Apr 2, 2026
02d4ee7
feat(cli): add string array flag helpers
Apr 2, 2026
b185012
fix(cli): accept comma-separated multi-select input
Apr 2, 2026
9fd432a
fix(cli): support comma and range multi-select input
Apr 2, 2026
4d127de
feat(cli): add security log helper
Apr 2, 2026
419e7f7
feat(cli): add formatted security log helper
Apr 2, 2026
181d954
feat(help): show topic previews
Apr 2, 2026
7dadf41
feat(cli): add string-to-string flag helpers
Apr 2, 2026
cdc7656
fix(help): add HTTP serve subcommand
Apr 2, 2026
dc30159
fix(cli): render glyph shortcodes in output
Apr 2, 2026
cf9c068
fix(cli): make width helpers rune-safe
Apr 2, 2026
e29b6e4
fix(cli): make stream width handling rune-safe
Apr 2, 2026
58f0760
fix: allow pkg update without package args
Apr 2, 2026
8b30e80
feat(cli): add ASCII glyph fallbacks for tree and tracker
Apr 2, 2026
323f408
feat(cli): add ASCII table borders
Apr 2, 2026
e259ce3
fix(cli): make stream completion idempotent
Apr 2, 2026
2a9177a
feat(pkg): add json output for pkg update
Apr 2, 2026
f376372
feat(help): show suggestions for missing topics
Apr 2, 2026
37310c7
fix(cli): avoid hanging prompts on EOF
Apr 2, 2026
a5142de
fix(cli): render glyphs in prompts and handle EOF
Apr 2, 2026
43d4bbd
fix(cli): reprompt required prompts on empty input
Apr 2, 2026
e96ea6d
fix(cli): honor default selection in multi-select
Apr 2, 2026
a035cb2
fix(cli): treat eof as empty multi-select
Apr 2, 2026
aa5c0f8
feat(help): add explicit search subcommand
Apr 2, 2026
aa537c8
fix(cli): make styled helpers nil-safe
Apr 2, 2026
5c8f08b
fix(cli): harden legacy select helpers
Apr 2, 2026
88ec926
fix(cli): strip ANSI from static frame output
Apr 2, 2026
5a73358
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
b8f3c96
fix(cli): make command registration snapshot-safe
Apr 2, 2026
be63660
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
f71bdb3
feat(cli): compile glyph shortcodes in rendered components
Apr 2, 2026
6c39c0f
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
7bf0609
fix(help): add recovery hints to help lookups
Apr 2, 2026
742b1d2
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
c3f2d6a
fix(help): add recovery hint for empty searches
Apr 2, 2026
60c9f92
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
b8bfdcf
fix(help): add next-step hints to help output
Apr 2, 2026
53b5552
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
817bdea
fix(cli): make legacy selection errors actionable
Apr 2, 2026
83d649a
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
8751348
fix(cli): remove implicit chooser defaults
Apr 2, 2026
37fdcdb
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
904a5c0
fix(cli): remove hidden chooser fallback
Apr 2, 2026
893b6d0
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
207a38e
fix(cli): improve prompt recovery hints
Apr 2, 2026
76bccc0
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
1242723
fix(pkgcmd): remove packages from registry
Apr 2, 2026
2c83745
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
4ec7341
fix(cli): surface eof for empty prompts
Apr 2, 2026
5e663d6
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
32b824a
fix(cli): style prompt recovery hints
Apr 2, 2026
125d5e7
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
8a7567c
fix(cli): send prompt recovery hints to stderr
Apr 2, 2026
c6c07f0
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
d59e6ac
fix(cli): route prompt selection hints to stderr
Apr 2, 2026
1cf8e17
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
f9bf223
fix(cli): accept eof input in multi-select
Apr 2, 2026
c02e88a
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
f8ba7be
fix(pkgcmd): send remove blockers to stderr
Apr 2, 2026
3cf13e7
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
4f7a4c3
fix(cli): route interactive ui to stderr
Apr 2, 2026
32e096c
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
6192340
fix(cli): route frame UI to stderr
Apr 2, 2026
0703a57
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
07bea81
fix(cli): render glyphs in echo and progress
Apr 2, 2026
ba456e4
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
c0cb67c
fix(cli): render glyph shortcodes in tables
Apr 2, 2026
5761524
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
e1edbc1
fix(cli): make tracker iterators snapshot-safe
Apr 2, 2026
048133c
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
11ac2c6
fix(cli): render glyphs in static renderables
Apr 2, 2026
b5e67b2
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
81be3b7
fix(cli): theme-aware semantic glyphs
Apr 2, 2026
fb914b9
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
3862b7c
fix(cli): clear chooser filters on empty input
Apr 2, 2026
9a80a60
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
50d9158
fix(cli): restore colors after ascii theme
Apr 2, 2026
3ce53ed
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
63481f1
fix(cli): render glyph shortcodes in task tracker
Apr 2, 2026
53e9fc1
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
aa07b4b
fix(cli): align public API docs with AX
Apr 2, 2026
e0aba4b
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
4e9b42e
fix(cli): make check output width-aware
Apr 2, 2026
d7c416d
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
be2e2db
fix(cli): add safe stream capture helper
Apr 2, 2026
d146c73
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
905dfae
fix(cli): align multi-select empty input with docs
Apr 2, 2026
d93504e
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
821f7d1
fix(cli): respect stdin override in Scanln
Apr 2, 2026
d65aebd
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
b1afac5
fix(cli): add explicit output setters
Apr 2, 2026
91ef8c0
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
fcf5f9c
fix(cli): make stdio routing injectable
Apr 2, 2026
3e0c9d7
Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/…
Apr 2, 2026
7b5f5e7
chore(core): verify cli RFC/AX compliance
Apr 2, 2026
8b345eb
chore(core): verify cli RFC/AX compliance status
Apr 2, 2026
050ee5b
chore(cli): align exported API docs with usage examples
Apr 2, 2026
cdae3a9
fix(cli): route stream output through injected stdout writer
Apr 2, 2026
bfc47c8
fix(cli): return early in live tracker when no pending tasks
Apr 2, 2026
6b321fe
refactor(ax): Pass 1 AX compliance sweep — banned imports, naming, tests
Mar 31, 2026
20a2e77
fix(cli): restore glyph theme after stateful tests
Apr 2, 2026
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
6 changes: 4 additions & 2 deletions cmd/core/config/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
)

// AddConfigCommands registers the 'config' command group and all subcommands.
//
// config.AddConfigCommands(rootCmd)
func AddConfigCommands(root *cli.Command) {
configCmd := cli.NewGroup("config", "Manage configuration", "")
root.AddCommand(configCmd)
Expand All @@ -17,9 +19,9 @@ func AddConfigCommands(root *cli.Command) {
}

func loadConfig() (*config.Config, error) {
cfg, err := config.New()
configuration, err := config.New()
if err != nil {
return nil, cli.Wrap(err, "failed to load config")
}
return cfg, nil
return configuration, nil
}
8 changes: 3 additions & 5 deletions cmd/core/config/cmd_get.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
package config

import (
"fmt"

"forge.lthn.ai/core/cli/pkg/cli"
)

func addGetCommand(parent *cli.Command) {
cmd := cli.NewCommand("get", "Get a configuration value", "", func(cmd *cli.Command, args []string) error {
key := args[0]

cfg, err := loadConfig()
configuration, err := loadConfig()
if err != nil {
return err
}

var value any
if err := cfg.Get(key, &value); err != nil {
if err := configuration.Get(key, &value); err != nil {
return cli.Err("key not found: %s", key)
}

fmt.Println(value)
cli.Println("%v", value)
return nil
})

Expand Down
9 changes: 4 additions & 5 deletions cmd/core/config/cmd_list.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config

import (
"fmt"
"maps"

"forge.lthn.ai/core/cli/pkg/cli"
Expand All @@ -10,23 +9,23 @@ import (

func addListCommand(parent *cli.Command) {
cmd := cli.NewCommand("list", "List all configuration values", "", func(cmd *cli.Command, args []string) error {
cfg, err := loadConfig()
configuration, err := loadConfig()
if err != nil {
return err
}

all := maps.Collect(cfg.All())
all := maps.Collect(configuration.All())
if len(all) == 0 {
cli.Dim("No configuration values set")
return nil
}

out, err := yaml.Marshal(all)
output, err := yaml.Marshal(all)
if err != nil {
return cli.Wrap(err, "failed to format config")
}

fmt.Print(string(out))
cli.Print("%s", string(output))
return nil
})

Expand Down
6 changes: 2 additions & 4 deletions cmd/core/config/cmd_path.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package config

import (
"fmt"

"forge.lthn.ai/core/cli/pkg/cli"
)

func addPathCommand(parent *cli.Command) {
cmd := cli.NewCommand("path", "Show the configuration file path", "", func(cmd *cli.Command, args []string) error {
cfg, err := loadConfig()
configuration, err := loadConfig()
if err != nil {
return err
}

fmt.Println(cfg.Path())
cli.Println("%s", configuration.Path())
return nil
})

Expand Down
4 changes: 2 additions & 2 deletions cmd/core/config/cmd_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ func addSetCommand(parent *cli.Command) {
key := args[0]
value := args[1]

cfg, err := loadConfig()
configuration, err := loadConfig()
if err != nil {
return err
}

if err := cfg.Set(key, value); err != nil {
if err := configuration.Set(key, value); err != nil {
return cli.Wrap(err, "failed to set config value")
}

Expand Down
25 changes: 17 additions & 8 deletions cmd/core/doctor/cmd_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package doctor

import (
"os/exec"
"strings"

"dappco.re/go/core"
"forge.lthn.ai/core/go-i18n"
)

Expand All @@ -26,6 +26,13 @@ func requiredChecks() []check {
args: []string{"--version"},
versionFlag: "--version",
},
{
name: i18n.T("cmd.doctor.check.go.name"),
description: i18n.T("cmd.doctor.check.go.description"),
command: "go",
args: []string{"version"},
versionFlag: "version",
},
{
name: i18n.T("cmd.doctor.check.gh.name"),
description: i18n.T("cmd.doctor.check.gh.description"),
Expand Down Expand Up @@ -84,18 +91,20 @@ func optionalChecks() []check {
}
}

// runCheck executes a tool check and returns success status and version info
func runCheck(c check) (bool, string) {
cmd := exec.Command(c.command, c.args...)
output, err := cmd.CombinedOutput()
// runCheck executes a tool check and returns success status and version info.
//
// ok, version := runCheck(check{command: "git", args: []string{"--version"}})
func runCheck(toolCheck check) (bool, string) {
proc := exec.Command(toolCheck.command, toolCheck.args...)
output, err := proc.CombinedOutput()
if err != nil {
return false, ""
}

// Extract first line as version
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
// Extract first line as version info.
lines := core.Split(core.Trim(string(output)), "\n")
if len(lines) > 0 {
return true, strings.TrimSpace(lines[0])
return true, core.Trim(lines[0])
}
return true, ""
}
22 changes: 22 additions & 0 deletions cmd/core/doctor/cmd_checks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package doctor

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRequiredChecksIncludesGo(t *testing.T) {
checks := requiredChecks()

var found bool
for _, c := range checks {
if c.command == "go" {
found = true
assert.Equal(t, "version", c.versionFlag)
break
}
}

assert.True(t, found, "required checks should include the Go compiler")
}
2 changes: 2 additions & 0 deletions cmd/core/doctor/cmd_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
)

// AddDoctorCommands registers the 'doctor' command and all subcommands.
//
// doctor.AddDoctorCommands(rootCmd)
func AddDoctorCommands(root *cobra.Command) {
doctorCmd.Short = i18n.T("cmd.doctor.short")
doctorCmd.Long = i18n.T("cmd.doctor.long")
Expand Down
61 changes: 29 additions & 32 deletions cmd/core/doctor/cmd_doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
package doctor

import (
"errors"
"fmt"

"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -32,89 +29,89 @@ func init() {
}

func runDoctor(verbose bool) error {
fmt.Println(i18n.T("common.progress.checking", map[string]any{"Item": "development environment"}))
fmt.Println()
cli.Println("%s", i18n.T("common.progress.checking", map[string]any{"Item": "development environment"}))
cli.Blank()

var passed, failed, optional int

// Check required tools
fmt.Println(i18n.T("cmd.doctor.required"))
for _, c := range requiredChecks() {
ok, version := runCheck(c)
cli.Println("%s", i18n.T("cmd.doctor.required"))
for _, toolCheck := range requiredChecks() {
ok, version := runCheck(toolCheck)
if ok {
if verbose {
fmt.Println(formatCheckResult(true, c.name, version))
cli.Println("%s", formatCheckResult(true, toolCheck.name, version))
} else {
fmt.Println(formatCheckResult(true, c.name, ""))
cli.Println("%s", formatCheckResult(true, toolCheck.name, ""))
}
passed++
} else {
fmt.Printf(" %s %s - %s\n", errorStyle.Render(cli.Glyph(":cross:")), c.name, c.description)
cli.Println(" %s %s - %s", errorStyle.Render(cli.Glyph(":cross:")), toolCheck.name, toolCheck.description)
failed++
}
}

// Check optional tools
fmt.Printf("\n%s\n", i18n.T("cmd.doctor.optional"))
for _, c := range optionalChecks() {
ok, version := runCheck(c)
cli.Println("\n%s", i18n.T("cmd.doctor.optional"))
for _, toolCheck := range optionalChecks() {
ok, version := runCheck(toolCheck)
if ok {
if verbose {
fmt.Println(formatCheckResult(true, c.name, version))
cli.Println("%s", formatCheckResult(true, toolCheck.name, version))
} else {
fmt.Println(formatCheckResult(true, c.name, ""))
cli.Println("%s", formatCheckResult(true, toolCheck.name, ""))
}
passed++
} else {
fmt.Printf(" %s %s - %s\n", dimStyle.Render(cli.Glyph(":skip:")), c.name, dimStyle.Render(c.description))
cli.Println(" %s %s - %s", dimStyle.Render(cli.Glyph(":skip:")), toolCheck.name, dimStyle.Render(toolCheck.description))
optional++
}
}

// Check GitHub access
fmt.Printf("\n%s\n", i18n.T("cmd.doctor.github"))
cli.Println("\n%s", i18n.T("cmd.doctor.github"))
if checkGitHubSSH() {
fmt.Println(formatCheckResult(true, i18n.T("cmd.doctor.ssh_found"), ""))
cli.Println("%s", formatCheckResult(true, i18n.T("cmd.doctor.ssh_found"), ""))
} else {
fmt.Printf(" %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.ssh_missing"))
cli.Println(" %s %s", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.ssh_missing"))
failed++
}

if checkGitHubCLI() {
fmt.Println(formatCheckResult(true, i18n.T("cmd.doctor.cli_auth"), ""))
cli.Println("%s", formatCheckResult(true, i18n.T("cmd.doctor.cli_auth"), ""))
} else {
fmt.Printf(" %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.cli_auth_missing"))
cli.Println(" %s %s", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.cli_auth_missing"))
failed++
}

// Check workspace
fmt.Printf("\n%s\n", i18n.T("cmd.doctor.workspace"))
cli.Println("\n%s", i18n.T("cmd.doctor.workspace"))
checkWorkspace()

// Summary
fmt.Println()
cli.Blank()
if failed > 0 {
cli.Error(i18n.T("cmd.doctor.issues", map[string]any{"Count": failed}))
fmt.Printf("\n%s\n", i18n.T("cmd.doctor.install_missing"))
cli.Println("\n%s", i18n.T("cmd.doctor.install_missing"))
printInstallInstructions()
return errors.New(i18n.T("cmd.doctor.issues_error", map[string]any{"Count": failed}))
return cli.Err("%s", i18n.T("cmd.doctor.issues_error", map[string]any{"Count": failed}))
}

cli.Success(i18n.T("cmd.doctor.ready"))
return nil
}

func formatCheckResult(ok bool, name, detail string) string {
check := cli.Check(name)
checkBuilder := cli.Check(name)
if ok {
check.Pass()
checkBuilder.Pass()
} else {
check.Fail()
checkBuilder.Fail()
}
if detail != "" {
check.Message(detail)
checkBuilder.Message(detail)
} else {
check.Message("")
checkBuilder.Message("")
}
return check.String()
return checkBuilder.String()
}
Loading