Skip to content

Add --json output flag to status and configure connection list#66

Merged
ewega merged 3 commits intomainfrom
copilot/add-json-output-flag
Feb 26, 2026
Merged

Add --json output flag to status and configure connection list#66
ewega merged 3 commits intomainfrom
copilot/add-json-output-flag

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

  • Add outputJSON bool var + persistent --json flag to cmd/root.go
  • Update Execute() in cmd/root.go to print JSON error on failure in JSON mode
  • Add printJSON(v any) error helper to cmd/helpers.go
  • Add JSON output path to cmd/status.go (runStatus + runStatusJSON)
  • Fix runStatusJSON: return discovery error instead of swallowing it (exit non-zero when DevLake unreachable)
  • Add JSON output path to cmd/configure_connection_list.go (silent discovery in JSON mode)
  • Fix configure_connection_list.go: redirect ⚠️ warnings to os.Stderr in JSON mode to keep stdout clean
  • Fix test TestRunStatusJSON_NoStateTestRunStatusJSON_NoState_DiscoveryError: assert error is returned
  • Fix test TestRunStatusJSON_WithState: use httptest.NewServer instead of http://localhost:8080
  • Update README.md to document --json flag with global flags table and examples
  • go build ./..., go test ./..., go vet ./... all pass
Original prompt

This section details on the original issue you should resolve

<issue_title>Add --json output flag to read commands</issue_title>
<issue_description>## Problem

All CLI output is human-formatted (emoji, Unicode box-drawing, banners). There is no machine-readable output mode. This blocks two scenarios:

  1. Agent consumption — coding agents (Copilot CLI agent, Claude, etc.) cannot reliably parse CLI output to take follow-up actions
  2. Scripting — users who want to pipe gh devlake output into jq, dashboards, or automation scripts must screen-scrape

The Playwright CLI demonstrates the pattern: CLI commands return structured snapshots to stdout that agents consume directly. For gh-devlake, the equivalent is structured JSON from read commands.

Proposed Solution

Add a --json flag (or --output json) to all read-only commands. When set:

  • Suppress emoji, banners, box-drawing, and interactive prompts
  • Print a single JSON object to stdout
  • Exit with code 0 on success, non-zero on error (with JSON error body)

Commands to update

Command JSON output shape
status { deployment: {…}, endpoints: [{name, url, healthy}], connections: [{plugin, id, name, healthy}], projects: [{name, blueprintId}] }
configure connection list [{id, plugin, name, endpoint, organization, enterprise}]
configure scope list (from #55) [{scopeId, name, fullName, plugin, connectionId}]
configure project list (from #56) [{name, description, blueprintId}]

Implementation approach

  1. Add a package-level outputJSON bool flag on rootCmd.PersistentFlags() so it's available to all commands
  2. Create a helper func printJSON(v any) error in cmd/helpers.go that marshals and writes to stdout
  3. Each command's RunE checks outputJSON early and branches to a JSON code path that builds a struct and calls printJSON
  4. Human output remains the default — no behavioral change for interactive users

Example

# Human (default)
$ gh devlake status
╭──────────────────────────────────────────────╮
│  DevLake Status                              │
╰──────────────────────────────────────────────╯
  ✅ Backend API: http://localhost:8080
  ...

# Machine
$ gh devlake status --json
{"deployment":{"method":"local","stateFile":".devlake-local.json"},"endpoints":[{"name":"backend","url":"http://localhost:8080","healthy":true}],"connections":[{"plugin":"github","id":1,"name":"GitHub - my-org","healthy":true}]}

Acceptance Criteria

Target Version

v0.4.x — Foundation for agent-friendly CLI and AI features.

References

<agent_instructions>Context: This is a Go CLI built with Cobra. The repo's conventions are in .github/copilot-instructions.md and AGENTS.md. Terminal output rules are in .github/instructions/terminal-output.instructions.md. Read these files FIRST before writing any code.

Task: Add a --json flag to read-only commands (status, configure connection list) that outputs structured JSON instead of human-formatted text.

Key constraints:

  1. Add a persistent --json bool flag on rootCmd.PersistentFlags() so it's available to all commands. Store in a package-level var outputJSON.
  2. Create a helper func printJSON(v any) error in cmd/helpers.go that marshals to JSON (compact, single-line) and writes to os.Stdout.
  3. status command (cmd/status.go): In runStatus, when outputJSON is true, build a struct with fields deployment, endpoints (array of {name, url, healthy}), connections (array of {plugin, id, name, organization, enterprise}), and project — then call printJSON. Skip all emoji, banners, and printBanner() calls.
  4. **configure connection list*...

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
Copilot AI changed the title [WIP] Add --json output flag to read commands Add --json output flag to status and configure connection list Feb 26, 2026
@ewega ewega requested a review from Copilot February 26, 2026 16:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a global JSON output mode to make selected read-only CLI commands (status, configure connection list) machine-parseable, while keeping existing human-formatted output as the default.

Changes:

  • Adds persistent --json flag and root-level JSON error emission.
  • Implements JSON output branches for status and configure connection list.
  • Adds tests and README documentation for JSON output behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cmd/root.go Adds global --json flag and switches error printing to JSON on stdout in JSON mode.
cmd/helpers.go Introduces printJSON(any) helper for compact single-line JSON to stdout.
cmd/status.go Adds JSON output structs and runStatusJSON, plus boolean health-check helper.
cmd/configure_connection_list.go Adds JSON list item struct and JSON output path for connection listing.
cmd/json_output_test.go Adds tests for printJSON, status JSON output, and connection list item serialization.
README.md Documents global --json flag and provides output shapes/examples.

cmd/status.go Outdated
Comment on lines +280 to +302
if err == nil {
client := devlake.NewClient(disc.URL)
_, healthy := client.Health()
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "backend",
URL: disc.URL,
Healthy: healthy == nil,
})
if disc.GrafanaURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "grafana",
URL: disc.GrafanaURL,
Healthy: checkEndpointHealth(disc.GrafanaURL, "grafana"),
})
}
if disc.ConfigUIURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "config-ui",
URL: disc.ConfigUIURL,
Healthy: checkEndpointHealth(disc.ConfigUIURL, "config-ui"),
})
}
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In JSON mode with no state file, runStatusJSON swallows discovery errors (disc, err := devlake.Discover(cfgURL) and only proceeds when err == nil). This causes gh devlake status --json to exit 0 and emit an empty status object when DevLake is unreachable, instead of returning a non-zero error that Execute() can convert into {"error":...} JSON as described in the PR. Consider returning the discovery error (wrapped with context) so the command fails predictably in JSON mode.

Suggested change
if err == nil {
client := devlake.NewClient(disc.URL)
_, healthy := client.Health()
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "backend",
URL: disc.URL,
Healthy: healthy == nil,
})
if disc.GrafanaURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "grafana",
URL: disc.GrafanaURL,
Healthy: checkEndpointHealth(disc.GrafanaURL, "grafana"),
})
}
if disc.ConfigUIURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "config-ui",
URL: disc.ConfigUIURL,
Healthy: checkEndpointHealth(disc.ConfigUIURL, "config-ui"),
})
}
}
if err != nil {
return fmt.Errorf("discovering DevLake: %w", err)
}
client := devlake.NewClient(disc.URL)
_, healthy := client.Health()
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "backend",
URL: disc.URL,
Healthy: healthy == nil,
})
if disc.GrafanaURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "grafana",
URL: disc.GrafanaURL,
Healthy: checkEndpointHealth(disc.GrafanaURL, "grafana"),
})
}
if disc.ConfigUIURL != "" {
out.Endpoints = append(out.Endpoints, statusEndpoint{
Name: "config-ui",
URL: disc.ConfigUIURL,
Healthy: checkEndpointHealth(disc.ConfigUIURL, "config-ui"),
})
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0a8dd59. runStatusJSON now returns fmt.Errorf("discovering DevLake: %w", err) when discovery fails, so Execute() emits {"error":"discovering DevLake: ..."} with a non-zero exit code.

Comment on lines +102 to +116
// ── JSON output path ──
if outputJSON {
items := make([]connectionListItem, len(rows))
for i, r := range rows {
items[i] = connectionListItem{
ID: r.ID,
Plugin: r.plugin,
Name: r.name,
Endpoint: r.endpoint,
Organization: r.organization,
Enterprise: r.enterprise,
}
}
return printJSON(items)
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON path returns printJSON(items), but earlier in this function ListConnections failures emit ⚠️ warnings via fmt.Printf(...) to stdout. If any plugin list fails, that warning line will precede the JSON array and make the output unparseable. In --json mode, suppress those warnings / redirect them to stderr, or make list failures fatal so stdout remains valid JSON.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0a8dd59. In JSON mode, ListConnections failures now write to os.Stderr instead of fmt.Printf to stdout, keeping the JSON array on stdout clean and parseable.

Comment on lines +78 to +96
// Should not error even if discovery fails — just returns empty output
if err := runStatusJSON(nil, ""); err != nil {
t.Fatalf("runStatusJSON returned error: %v", err)
}
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
out := strings.TrimSpace(buf.String())

var got statusOutput
if err := json.Unmarshal([]byte(out), &got); err != nil {
t.Fatalf("output is not valid JSON: %v — got: %q", err, out)
}
if got.Deployment != nil {
t.Errorf("expected nil deployment when no state, got %+v", got.Deployment)
}
if got.Connections == nil {
t.Errorf("expected non-nil connections slice")
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestRunStatusJSON_NoState currently expects runStatusJSON(nil, "") to succeed even when discovery fails (cfgURL is set to an unreachable address). If JSON mode is supposed to produce a {"error":...} response with non-zero exit when DevLake can't be discovered, this test should instead expect an error (and validate the JSON error contract via Execute() / the root error handler).

Suggested change
// Should not error even if discovery fails — just returns empty output
if err := runStatusJSON(nil, ""); err != nil {
t.Fatalf("runStatusJSON returned error: %v", err)
}
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
out := strings.TrimSpace(buf.String())
var got statusOutput
if err := json.Unmarshal([]byte(out), &got); err != nil {
t.Fatalf("output is not valid JSON: %v — got: %q", err, out)
}
if got.Deployment != nil {
t.Errorf("expected nil deployment when no state, got %+v", got.Deployment)
}
if got.Connections == nil {
t.Errorf("expected non-nil connections slice")
}
// In JSON mode, discovery failure should surface as an error.
if err := runStatusJSON(nil, ""); err == nil {
t.Fatalf("expected error when discovery fails, got nil")
}
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
_ = strings.TrimSpace(buf.String())

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0a8dd59. The test is now TestRunStatusJSON_NoState_DiscoveryError and asserts err != nil with the message containing "discovering DevLake".

Comment on lines +108 to +114
state := &devlake.State{
Method: "local",
DeployedAt: "2024-01-01T00:00:00Z",
Endpoints: devlake.StateEndpoints{
Backend: "http://localhost:8080",
},
Connections: []devlake.StateConnection{
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestRunStatusJSON_WithState uses Backend: "http://localhost:8080", which makes the test perform real network calls via checkEndpointHealth and can become flaky/slow depending on the environment (or hang up to the 8s timeout). Prefer using an httptest.Server and setting state.Endpoints.Backend to the server URL so health checks are deterministic and the test never depends on local ports.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0a8dd59. TestRunStatusJSON_WithState now uses httptest.NewServer that responds 200 to all requests, so the health check is deterministic and doesn't touch real network ports. Also added an assertion that verifies the backend endpoint is reported as healthy.

@ewega
Copy link
Contributor

ewega commented Feb 26, 2026

@copilot Please fix the following issues found by Code Review and QA:

1. BUG — JSON mode swallows discovery errors (Code Review: status.go:302)
In runStatusJSON, when no state file exists and devlake.Discover(cfgURL) fails, the function silently returns an empty status object with exit 0. Instead, return the discovery error so Execute() can convert it to {"error":"..."} JSON:

if err != nil {
    return fmt.Errorf("discovering DevLake: %w", err)
}

2. BUG — Warning output corrupts JSON (Code Review: configure_connection_list.go:116)
In runListConnections, the ListConnections failure path prints ⚠️ warnings via fmt.Printf to stdout. In --json mode, this text precedes the JSON array and makes output unparseable. When outputJSON is true, either suppress these warnings entirely, redirect them to os.Stderr, or collect failed plugins and include them in the JSON output.

3. Fix test: discovery failure should return error (Code Review: json_output_test.go:96)
TestRunStatusJSON_NoState currently expects runStatusJSON(nil, "") to succeed. After fixing issue #1, this test should instead expect an error when discovery fails. Update the test to assert err != nil.

4. Fix test: use httptest.Server instead of real network calls (Code Review: json_output_test.go:114)
TestRunStatusJSON_WithState uses Backend: "http://localhost:8080" which makes real HTTP calls. This is flaky and can hang for 8 seconds. Use httptest.NewServer to create a mock backend that responds to /ping, and set the state's Backend URL to the test server's URL.

Ensure go build ./..., go test ./..., and go vet ./... pass.

…use httptest in tests

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot Please fix the following issues found by Code Review and QA:

1. BUG — JSON mode swallows discovery errors (Code Review: status.go:302)...

All four fixes applied in 0a8dd59:

  1. runStatusJSON now returns the discovery error instead of swallowing it
  2. ⚠️ warnings in JSON mode go to os.Stderr, keeping stdout as valid JSON
  3. TestRunStatusJSON_NoState_DiscoveryError now asserts err != nil
  4. TestRunStatusJSON_WithState uses httptest.NewServer — no real network calls

@ewega ewega marked this pull request as ready for review February 26, 2026 17:36
Copilot AI review requested due to automatic review settings February 26, 2026 17:36
@ewega ewega merged commit e4f70ab into main Feb 26, 2026
2 checks passed
@ewega ewega deleted the copilot/add-json-output-flag branch February 26, 2026 17:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

cmd/json_output_test.go:118

  • Same pipe-handling issue here: the read end (r) isn’t closed after reading, which can leak file descriptors across the test suite. Consider closing both ends via t.Cleanup and handling ReadFrom errors.
	orig := os.Stdout
	r, w, err := os.Pipe()
	if err != nil {
		t.Fatalf("os.Pipe: %v", err)
	}
	os.Stdout = w
	t.Cleanup(func() { os.Stdout = orig })

	state := &devlake.State{
		Method:     "local",
		DeployedAt: "2024-01-01T00:00:00Z",
		Endpoints: devlake.StateEndpoints{
			Backend: srv.URL,
		},
		Connections: []devlake.StateConnection{
			{Plugin: "github", ConnectionID: 1, Name: "GitHub - my-org", Organization: "my-org"},
		},
		Project: &devlake.StateProject{
			Name:        "my-project",
			BlueprintID: 7,
		},
	}

	if err := runStatusJSON(state, ".devlake-local.json"); err != nil {
		t.Fatalf("runStatusJSON returned error: %v", err)
	}
	w.Close()
	var buf bytes.Buffer
	buf.ReadFrom(r)
	out := strings.TrimSpace(buf.String())

Comment on lines +330 to +345
// checkEndpointHealth returns true if the endpoint responds successfully.
func checkEndpointHealth(url string, kind string) bool {
checkURL := strings.TrimRight(url, "/")
if kind == "backend" {
checkURL += "/ping"
}
if kind == "grafana" {
checkURL += "/api/health"
}
client := &http.Client{Timeout: 8 * time.Second}
resp, err := client.Get(checkURL)
if err != nil {
return false
}
defer resp.Body.Close()
return resp.StatusCode >= 200 && resp.StatusCode < 400
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkEndpointHealth duplicates the URL-building + HTTP GET logic from pingEndpoint (same timeout, same status code range, same kind handling). Keeping these in sync will be error-prone; consider refactoring to a single helper that returns both the boolean health and (optionally) the human-facing status/icon.

Copilot uses AI. Check for mistakes.
if err != nil {
fmt.Printf("\n⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
if outputJSON {
fmt.Fprintf(os.Stderr, "⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In JSON mode, this warning message drops the leading newline before the emoji (compare discoverClient and other warnings which start with \n⚠️ ...). For consistent terminal UX (even on stderr), keep the newline prefix: write \n⚠️ ... to stderr.

Suggested change
fmt.Fprintf(os.Stderr, "⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
fmt.Fprintf(os.Stderr, "\n⚠️ Could not list %s connections: %v\n", def.DisplayName, err)

Copilot uses AI. Check for mistakes.
Comment on lines 50 to +121
@@ -53,8 +74,9 @@ func runListConnections(cmd *cobra.Command, args []string) error {
// ── Collect connections from all relevant plugins ──
type row struct {
plugin string
id int
ID int
name string
endpoint string
organization string
enterprise string
}
@@ -63,21 +85,43 @@ func runListConnections(cmd *cobra.Command, args []string) error {
for _, def := range defs {
conns, err := client.ListConnections(def.Plugin)
if err != nil {
fmt.Printf("\n⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
if outputJSON {
fmt.Fprintf(os.Stderr, "⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
} else {
fmt.Printf("\n⚠️ Could not list %s connections: %v\n", def.DisplayName, err)
}
continue
}
for _, c := range conns {
rows = append(rows, row{
plugin: def.Plugin,
id: c.ID,
ID: c.ID,
name: c.Name,
endpoint: c.Endpoint,
organization: c.Organization,
enterprise: c.Enterprise,
})
}
}

// ── JSON output path ──
if outputJSON {
items := make([]connectionListItem, len(rows))
for i, r := range rows {
items[i] = connectionListItem{
ID: r.ID,
Plugin: r.plugin,
Name: r.name,
Endpoint: r.endpoint,
Organization: r.organization,
Enterprise: r.enterprise,
}
}
return printJSON(items)
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new --json path and stderr-warning behavior aren’t covered by tests. Existing tests only validate unknown plugin handling and registry wiring, plus JSON struct field tags in json_output_test.go. Consider adding a test that runs runListConnections with outputJSON=true against a mocked/httptest DevLake API and asserts stdout is valid JSON with no banner/warning text and warnings (if any) go to stderr.

Copilot uses AI. Check for mistakes.
return fmt.Errorf("marshaling JSON: %w", err)
}
_, err = fmt.Fprintf(os.Stdout, "%s\n", data)
return err
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printJSON returns the raw write error from fmt.Fprintf without context. Wrapping it (e.g., "writing JSON to stdout") would make failures easier to diagnose, especially since JSON mode relies on stdout being clean and available.

Suggested change
return err
if err != nil {
return fmt.Errorf("writing JSON to stdout: %w", err)
}
return nil

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +31
t.Cleanup(func() { os.Stdout = orig })

if err := printJSON(v); err != nil {
t.Fatalf("printJSON: %v", err)
}
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests replace os.Stdout with a pipe but never close the read end (r). Over many tests this can leak file descriptors and cause flaky CI failures on some platforms. Consider closing r via defer/t.Cleanup, and also checking/handling buf.ReadFrom errors.

This issue also appears on line 89 of the same file.

Suggested change
t.Cleanup(func() { os.Stdout = orig })
if err := printJSON(v); err != nil {
t.Fatalf("printJSON: %v", err)
}
w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
t.Cleanup(func() {
os.Stdout = orig
_ = r.Close()
})
if err := printJSON(v); err != nil {
t.Fatalf("printJSON: %v", err)
}
w.Close()
var buf bytes.Buffer
if _, err := buf.ReadFrom(r); err != nil {
t.Fatalf("reading from pipe: %v", err)
}

Copilot uses AI. Check for mistakes.
Comment on lines 44 to 52
func Execute() {
rootCmd.SilenceErrors = true
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
if outputJSON {
_ = printJSON(map[string]string{"error": err.Error()})
} else {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(1)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In --json mode, Cobra may still print usage/help text on errors (e.g., flag/arg validation) because SilenceUsage is not set. That can break the documented contract of emitting only JSON for failures. Consider setting rootCmd.SilenceUsage = true (at least when outputJSON is true) and/or customizing SetFlagErrorFunc so JSON mode errors don’t print usage banners.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +35
Deployment *statusDeployment `json:"deployment"`
Endpoints []statusEndpoint `json:"endpoints"`
Connections []statusConnection `json:"connections"`
Project *statusProject `json:"project"`
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statusOutput uses pointer fields without omitempty, so successful JSON output can contain "deployment":null and/or "project":null (e.g., when no state file is present but discovery succeeds). This makes the JSON shape less consistent for consumers than the README implies. Consider either always populating these objects with defaults or adding omitempty to omit absent sections.

Suggested change
Deployment *statusDeployment `json:"deployment"`
Endpoints []statusEndpoint `json:"endpoints"`
Connections []statusConnection `json:"connections"`
Project *statusProject `json:"project"`
Deployment *statusDeployment `json:"deployment,omitempty"`
Endpoints []statusEndpoint `json:"endpoints"`
Connections []statusConnection `json:"connections"`
Project *statusProject `json:"project,omitempty"`

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add --json output flag to read commands

3 participants