Merge pull request '[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/cli/RFC.md fully. Find ONE feature ...' (#19) from agent/update-the-code-against-the-ax-design-pr into dev
Some checks failed
Security Scan / security (push) Has been cancelled
Some checks failed
Security Scan / security (push) Has been cancelled
This commit is contained in:
commit
7f0044fa41
3 changed files with 196 additions and 11 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
package pkgcmd
|
package pkgcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -15,19 +16,40 @@ import (
|
||||||
|
|
||||||
// addPkgListCommand adds the 'pkg list' command.
|
// addPkgListCommand adds the 'pkg list' command.
|
||||||
func addPkgListCommand(parent *cobra.Command) {
|
func addPkgListCommand(parent *cobra.Command) {
|
||||||
|
var format string
|
||||||
listCmd := &cobra.Command{
|
listCmd := &cobra.Command{
|
||||||
Use: "list",
|
Use: "list",
|
||||||
Short: i18n.T("cmd.pkg.list.short"),
|
Short: i18n.T("cmd.pkg.list.short"),
|
||||||
Long: i18n.T("cmd.pkg.list.long"),
|
Long: i18n.T("cmd.pkg.list.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runPkgList()
|
format, err := cmd.Flags().GetString("format")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runPkgList(format)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listCmd.Flags().StringVar(&format, "format", "table", "Output format: table or json")
|
||||||
parent.AddCommand(listCmd)
|
parent.AddCommand(listCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPkgList() error {
|
type pkgListEntry struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Installed bool `json:"installed"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pkgListReport struct {
|
||||||
|
Format string `json:"format"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
Installed int `json:"installed"`
|
||||||
|
Missing int `json:"missing"`
|
||||||
|
Packages []pkgListEntry `json:"packages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPkgList(format string) error {
|
||||||
regPath, err := repos.FindRegistry(coreio.Local)
|
regPath, err := repos.FindRegistry(coreio.Local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml_workspace"))
|
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml_workspace"))
|
||||||
|
|
@ -52,8 +74,7 @@ func runPkgList() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n\n", repoNameStyle.Render(i18n.T("cmd.pkg.list.title")))
|
var entries []pkgListEntry
|
||||||
|
|
||||||
var installed, missing int
|
var installed, missing int
|
||||||
for _, r := range allRepos {
|
for _, r := range allRepos {
|
||||||
repoPath := filepath.Join(basePath, r.Name)
|
repoPath := filepath.Join(basePath, r.Name)
|
||||||
|
|
@ -64,20 +85,58 @@ func runPkgList() error {
|
||||||
missing++
|
missing++
|
||||||
}
|
}
|
||||||
|
|
||||||
status := successStyle.Render("✓")
|
|
||||||
if !exists {
|
|
||||||
status = dimStyle.Render("○")
|
|
||||||
}
|
|
||||||
|
|
||||||
desc := r.Description
|
desc := r.Description
|
||||||
if len(desc) > 40 {
|
if len(desc) > 40 {
|
||||||
desc = desc[:37] + "..."
|
desc = desc[:37] + "..."
|
||||||
}
|
}
|
||||||
if desc == "" {
|
if desc == "" {
|
||||||
desc = dimStyle.Render(i18n.T("cmd.pkg.no_description"))
|
desc = i18n.T("cmd.pkg.no_description")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(" %s %s\n", status, repoNameStyle.Render(r.Name))
|
entries = append(entries, pkgListEntry{
|
||||||
|
Name: r.Name,
|
||||||
|
Description: desc,
|
||||||
|
Installed: exists,
|
||||||
|
Path: repoPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if format == "json" {
|
||||||
|
report := pkgListReport{
|
||||||
|
Format: "json",
|
||||||
|
Total: len(entries),
|
||||||
|
Installed: installed,
|
||||||
|
Missing: missing,
|
||||||
|
Packages: entries,
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := json.MarshalIndent(report, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to format package list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(out))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "table" {
|
||||||
|
return fmt.Errorf("unsupported format %q: expected table or json", format)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n\n", repoNameStyle.Render(i18n.T("cmd.pkg.list.title")))
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
status := successStyle.Render("✓")
|
||||||
|
if !entry.Installed {
|
||||||
|
status = dimStyle.Render("○")
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := entry.Description
|
||||||
|
if !entry.Installed {
|
||||||
|
desc = dimStyle.Render(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" %s %s\n", status, repoNameStyle.Render(entry.Name))
|
||||||
fmt.Printf(" %s\n", desc)
|
fmt.Printf(" %s\n", desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
116
cmd/core/pkgcmd/cmd_manage_test.go
Normal file
116
cmd/core/pkgcmd/cmd_manage_test.go
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
package pkgcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func capturePkgOutput(t *testing.T, fn func()) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
}()
|
||||||
|
|
||||||
|
fn()
|
||||||
|
|
||||||
|
require.NoError(t, w.Close())
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func withWorkingDir(t *testing.T, dir string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
oldwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, os.Chdir(dir))
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, os.Chdir(oldwd))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTestRegistry(t *testing.T, dir string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
registry := strings.TrimSpace(`
|
||||||
|
org: host-uk
|
||||||
|
base_path: .
|
||||||
|
repos:
|
||||||
|
core-alpha:
|
||||||
|
type: foundation
|
||||||
|
description: Alpha package
|
||||||
|
core-beta:
|
||||||
|
type: module
|
||||||
|
description: Beta package
|
||||||
|
`) + "\n"
|
||||||
|
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "repos.yaml"), []byte(registry), 0644))
|
||||||
|
require.NoError(t, os.MkdirAll(filepath.Join(dir, "core-alpha", ".git"), 0755))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunPkgList_Good(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
writeTestRegistry(t, tmp)
|
||||||
|
withWorkingDir(t, tmp)
|
||||||
|
|
||||||
|
out := capturePkgOutput(t, func() {
|
||||||
|
err := runPkgList("table")
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Contains(t, out, "core-alpha")
|
||||||
|
assert.Contains(t, out, "core-beta")
|
||||||
|
assert.Contains(t, out, "core setup")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunPkgList_JSON(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
writeTestRegistry(t, tmp)
|
||||||
|
withWorkingDir(t, tmp)
|
||||||
|
|
||||||
|
out := capturePkgOutput(t, func() {
|
||||||
|
err := runPkgList("json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
var report pkgListReport
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(strings.TrimSpace(out)), &report))
|
||||||
|
assert.Equal(t, "json", report.Format)
|
||||||
|
assert.Equal(t, 2, report.Total)
|
||||||
|
assert.Equal(t, 1, report.Installed)
|
||||||
|
assert.Equal(t, 1, report.Missing)
|
||||||
|
require.Len(t, report.Packages, 2)
|
||||||
|
assert.Equal(t, "core-alpha", report.Packages[0].Name)
|
||||||
|
assert.True(t, report.Packages[0].Installed)
|
||||||
|
assert.Equal(t, filepath.Join(tmp, "core-alpha"), report.Packages[0].Path)
|
||||||
|
assert.Equal(t, "core-beta", report.Packages[1].Name)
|
||||||
|
assert.False(t, report.Packages[1].Installed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunPkgList_UnsupportedFormat(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
writeTestRegistry(t, tmp)
|
||||||
|
withWorkingDir(t, tmp)
|
||||||
|
|
||||||
|
err := runPkgList("yaml")
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unsupported format")
|
||||||
|
}
|
||||||
|
|
@ -98,6 +98,16 @@ core pkg list
|
||||||
|
|
||||||
Shows installed status (✓) and description for each package.
|
Shows installed status (✓) and description for each package.
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
|
||||||
|
| Flag | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `--format` | Output format (`table` or `json`) |
|
||||||
|
|
||||||
|
### JSON Output
|
||||||
|
|
||||||
|
When `--format json` is set, `core pkg list` emits a structured report with package entries, installed state, and summary counts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## pkg update
|
## pkg update
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue