Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/cli/RFC.md fully. Find ONE feature ...' (#50) from agent/read---spec-code-core-go-cli-rfc-md-full into dev
Some checks are pending
Security Scan / security (push) Waiting to run
Some checks are pending
Security Scan / security (push) Waiting to run
This commit is contained in:
commit
66568f9641
9 changed files with 81 additions and 25 deletions
12
go.sum
12
go.sum
|
|
@ -6,13 +6,10 @@ forge.lthn.ai/core/go-i18n v0.1.7 h1:aHkAoc3W8fw3RPNvw/UszQbjyFWXHszzbZgty3SwyAA
|
|||
forge.lthn.ai/core/go-i18n v0.1.7/go.mod h1:0VDjwtY99NSj2iqwrI09h5GUsJeM9s48MLkr+/Dn4G8=
|
||||
forge.lthn.ai/core/go-inference v0.1.7 h1:9Dy6v03jX5ZRH3n5iTzlYyGtucuBIgSe+S7GWvBzx9Q=
|
||||
forge.lthn.ai/core/go-inference v0.1.7/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.6/go.mod h1:3MSuQZuzhCi6aefECQ/LxhM8ooVLam1KgEvgeEjYZVc=
|
||||
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
|
||||
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||
|
|
@ -23,12 +20,10 @@ github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF
|
|||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
|
|
@ -36,7 +31,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
|
@ -69,7 +63,6 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
|
|||
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/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
|
|
@ -77,8 +70,6 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
|
|||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
|
|
@ -87,9 +78,6 @@ golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
|||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
|
|||
|
|
@ -76,9 +76,7 @@ func TestRender_ColorEnabled_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUseASCII_Good(t *testing.T) {
|
||||
// Save original state
|
||||
original := ColorEnabled()
|
||||
defer SetColorEnabled(original)
|
||||
restoreThemeAndColors(t)
|
||||
|
||||
// Enable first, then UseASCII should disable colors
|
||||
SetColorEnabled(true)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import "testing"
|
||||
|
||||
func TestCheckBuilder(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseASCII() // Deterministic output
|
||||
|
||||
// Pass
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import "testing"
|
||||
|
||||
func TestGlyph(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseUnicode()
|
||||
if Glyph(":check:") != "✓" {
|
||||
t.Errorf("Expected ✓, got %s", Glyph(":check:"))
|
||||
|
|
@ -15,6 +16,7 @@ func TestGlyph(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCompileGlyphs(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseUnicode()
|
||||
got := compileGlyphs("Status: :check:")
|
||||
if got != "Status: ✓" {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ func captureOutput(f func()) string {
|
|||
}
|
||||
|
||||
func TestSemanticOutput(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseASCII()
|
||||
|
||||
// Test Success
|
||||
|
|
@ -102,6 +103,7 @@ func TestSemanticOutput(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSemanticOutput_GlyphShortcodes(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseASCII()
|
||||
|
||||
out := captureOutput(func() {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ import (
|
|||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Spinner frames (braille pattern).
|
||||
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
||||
// Spinner frames for the live tracker.
|
||||
var spinnerFramesUnicode = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
||||
var spinnerFramesASCII = []string{"-", "\\", "|", "/"}
|
||||
|
||||
// taskState tracks the lifecycle of a tracked task.
|
||||
type taskState int
|
||||
|
|
@ -227,7 +228,7 @@ func (tr *TaskTracker) renderLine(idx, frame int) {
|
|||
case taskPending:
|
||||
icon = DimStyle.Render(Glyph(":pending:"))
|
||||
case taskRunning:
|
||||
icon = InfoStyle.Render(spinnerFrames[frame%len(spinnerFrames)])
|
||||
icon = InfoStyle.Render(trackerSpinnerFrame(frame))
|
||||
case taskDone:
|
||||
icon = SuccessStyle.Render(Glyph(":check:"))
|
||||
case taskFailed:
|
||||
|
|
@ -304,16 +305,24 @@ func (tr *TaskTracker) String() string {
|
|||
var sb strings.Builder
|
||||
for _, t := range tasks {
|
||||
name, status, state := t.snapshot()
|
||||
icon := "…"
|
||||
icon := Glyph(":pending:")
|
||||
switch state {
|
||||
case taskDone:
|
||||
icon = "✓"
|
||||
icon = Glyph(":check:")
|
||||
case taskFailed:
|
||||
icon = "✗"
|
||||
icon = Glyph(":cross:")
|
||||
case taskRunning:
|
||||
icon = "⠋"
|
||||
icon = Glyph(":spinner:")
|
||||
}
|
||||
fmt.Fprintf(&sb, "%s %s %s\n", icon, Pad(name, nameW), status)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func trackerSpinnerFrame(frame int) string {
|
||||
frames := spinnerFramesUnicode
|
||||
if currentTheme == ThemeASCII {
|
||||
frames = spinnerFramesASCII
|
||||
}
|
||||
return frames[frame%len(frames)]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,17 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func restoreThemeAndColors(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
prevTheme := currentTheme
|
||||
prevColor := ColorEnabled()
|
||||
t.Cleanup(func() {
|
||||
currentTheme = prevTheme
|
||||
SetColorEnabled(prevColor)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTaskTracker_Good(t *testing.T) {
|
||||
t.Run("add and complete tasks", func(t *testing.T) {
|
||||
tr := NewTaskTracker()
|
||||
|
|
@ -159,6 +170,25 @@ func TestTaskTracker_Good(t *testing.T) {
|
|||
assert.Contains(t, out, "✗")
|
||||
assert.Contains(t, out, "⠋")
|
||||
})
|
||||
|
||||
t.Run("ASCII theme uses ASCII symbols", func(t *testing.T) {
|
||||
restoreThemeAndColors(t)
|
||||
UseASCII()
|
||||
|
||||
tr := NewTaskTracker()
|
||||
tr.out = &bytes.Buffer{}
|
||||
|
||||
tr.Add("repo-a").Done("clean")
|
||||
tr.Add("repo-b").Fail("dirty")
|
||||
tr.Add("repo-c").Update("pulling")
|
||||
|
||||
out := tr.String()
|
||||
assert.Contains(t, out, "[OK]")
|
||||
assert.Contains(t, out, "[FAIL]")
|
||||
assert.Contains(t, out, "-")
|
||||
assert.NotContains(t, out, "✓")
|
||||
assert.NotContains(t, out, "✗")
|
||||
})
|
||||
}
|
||||
|
||||
func TestTaskTracker_Bad(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -90,13 +90,17 @@ func (n *TreeNode) renderLabel() string {
|
|||
}
|
||||
|
||||
func (n *TreeNode) writeChildren(sb *strings.Builder, prefix string) {
|
||||
tee := Glyph(":tee:") + Glyph(":dash:") + Glyph(":dash:") + " "
|
||||
corner := Glyph(":corner:") + Glyph(":dash:") + Glyph(":dash:") + " "
|
||||
pipe := Glyph(":pipe:") + " "
|
||||
|
||||
for i, child := range n.children {
|
||||
last := i == len(n.children)-1
|
||||
|
||||
connector := "├── "
|
||||
next := "│ "
|
||||
connector := tee
|
||||
next := pipe
|
||||
if last {
|
||||
connector = "└── "
|
||||
connector = corner
|
||||
next = " "
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,28 @@ func TestTree_Good(t *testing.T) {
|
|||
"└── child\n"
|
||||
assert.Equal(t, expected, tree.String())
|
||||
})
|
||||
|
||||
t.Run("ASCII theme uses ASCII connectors", func(t *testing.T) {
|
||||
prevTheme := currentTheme
|
||||
prevColor := ColorEnabled()
|
||||
UseASCII()
|
||||
t.Cleanup(func() {
|
||||
currentTheme = prevTheme
|
||||
SetColorEnabled(prevColor)
|
||||
})
|
||||
|
||||
tree := NewTree("core-php")
|
||||
tree.Add("core-tenant").Add("core-bio")
|
||||
tree.Add("core-admin")
|
||||
tree.Add("core-api")
|
||||
|
||||
expected := "core-php\n" +
|
||||
"+-- core-tenant\n" +
|
||||
"| `-- core-bio\n" +
|
||||
"+-- core-admin\n" +
|
||||
"`-- core-api\n"
|
||||
assert.Equal(t, expected, tree.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestTree_Bad(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue