Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ The runtime system provides a pluggable architecture for managing workspaces on

**Optional Interfaces:**
- **StorageAware**: Enables runtimes to persist data in a dedicated storage directory
- **AgentLister**: Enables runtimes to report which agents they support
- **Terminal**: Enables interactive terminal sessions with running instances

**For detailed runtime implementation guidance, use:** `/working-with-runtime-system`
Expand Down
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,63 @@ The system works without any configuration files and merges only the ones that e

## Commands

### `info` - Display Information About kortex-cli

Displays version, available agents, and supported runtimes.

#### Usage

```bash
kortex-cli info [flags]
```

#### Flags

- `--output, -o <format>` - Output format (supported: `json`)
- `--storage <path>` - Storage directory for kortex-cli data (default: `$HOME/.kortex-cli`)

#### Examples

**Show info (human-readable format):**
```bash
kortex-cli info
```
Output:
```text
Version: 0.3.0
Agents: claude
Runtimes: fake, podman
```

**Show info in JSON format:**
```bash
kortex-cli info --output json
```
Output:
```json
{
"version": "0.3.0",
"agents": [
"claude"
],
"runtimes": [
"fake",
"podman"
]
}
```

**Show info using short flag:**
```bash
kortex-cli info -o json
```

#### Notes

- Agents are discovered from runtimes that support agent configuration (e.g., the Podman runtime reports agents from its configuration files)
- Runtimes are listed based on availability in the current environment (e.g., the Podman runtime only appears if the `podman` CLI is installed)
- **JSON error handling**: When `--output json` is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

### `init` - Register a New Workspace

Registers a new workspace with kortex-cli, making it available for agent launch and configuration.
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ require (
)

require github.com/inconshreveable/mousetrap v1.1.0 // indirect

replace github.com/kortex-hub/kortex-cli-api/cli/go => /home/brian/kortex-cli-api/cli/go
11 changes: 1 addition & 10 deletions pkg/cmd/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,10 @@ func newOutputFlagCompletion(validFormats []string) func(cmd *cobra.Command, arg
}

// completeRuntimeFlag provides completion for the --runtime flag
// It lists all available runtimes, excluding the "fake" runtime (used only for testing)
func completeRuntimeFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Get all available runtimes without requiring a manager instance
// This avoids creating storage directories during tab-completion
runtimes := runtimesetup.ListAvailable()

// Filter out "fake" runtime (used only for testing)
var filteredRuntimes []string
for _, rt := range runtimes {
if rt != "fake" {
filteredRuntimes = append(filteredRuntimes, rt)
}
}

return filteredRuntimes, cobra.ShellCompDirectiveNoFileComp
return runtimes, cobra.ShellCompDirectiveNoFileComp
}
6 changes: 4 additions & 2 deletions pkg/cmd/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"fmt"
"io"

api "github.com/kortex-hub/kortex-cli-api/cli/go"

Check failure on line 26 in pkg/cmd/conversion.go

View workflow job for this annotation

GitHub Actions / CI Checks (ubuntu-24.04)

github.com/kortex-hub/kortex-cli-api/cli/go@v0.0.0-20260326121446-aa3f0c37fc1e: replacement directory /home/brian/kortex-cli-api/cli/go does not exist
"github.com/kortex-hub/kortex-cli/pkg/instances"
)

Expand All @@ -36,11 +36,13 @@

// instanceToWorkspace converts an Instance to an api.Workspace
func instanceToWorkspace(instance instances.Instance) api.Workspace {
project := instance.GetProject()
agent := instance.GetAgent()
return api.Workspace{
Id: instance.GetID(),
Name: instance.GetName(),
Project: instance.GetProject(),
Agent: instance.GetAgent(),
Project: &project,
Agent: &agent,
Paths: api.WorkspacePaths{
Configuration: instance.GetConfigDir(),
Source: instance.GetSourceDir(),
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ func TestInstanceToWorkspace(t *testing.T) {
t.Errorf("Expected name 'test-workspace', got '%s'", result.Name)
}

if result.Project != "test-project" {
t.Errorf("Expected project 'test-project', got '%s'", result.Project)
if result.Project == nil || *result.Project != "test-project" {
t.Errorf("Expected project 'test-project', got '%v'", result.Project)
}

if result.Paths.Source != sourceDir {
Expand Down
128 changes: 128 additions & 0 deletions pkg/cmd/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**********************************************************************
* Copyright (C) 2026 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

package cmd

import (
"encoding/json"
"fmt"
"path/filepath"
"strings"

api "github.com/kortex-hub/kortex-cli-api/cli/go"
"github.com/kortex-hub/kortex-cli/pkg/runtimesetup"
"github.com/kortex-hub/kortex-cli/pkg/version"
"github.com/spf13/cobra"
)

// infoCmd contains the configuration for the info command
type infoCmd struct {
output string
}

// preRun validates the parameters and flags
func (i *infoCmd) preRun(cmd *cobra.Command, args []string) error {
// Validate output format if specified
if i.output != "" && i.output != "json" {
return fmt.Errorf("unsupported output format: %s (supported: json)", i.output)
}

return nil
}
Comment on lines +39 to +46
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 1, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Enforce --show-logs + --output json conflict in preRun.

This command accepts JSON output but does not reject the forbidden --show-logs combination. Add the conflict check in preRun before enabling JSON-mode silence.

🔧 Suggested patch
 func (i *infoCmd) preRun(cmd *cobra.Command, args []string) error {
 	// Validate output format if specified
 	if i.output != "" && i.output != "json" {
 		return fmt.Errorf("unsupported output format: %s (supported: json)", i.output)
 	}
 
+	// Reject incompatible flags in JSON mode
+	if i.output == "json" {
+		if f := cmd.Flags().Lookup("show-logs"); f != nil {
+			showLogs, err := cmd.Flags().GetBool("show-logs")
+			if err != nil {
+				return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to read --show-logs flag: %w", err))
+			}
+			if showLogs {
+				return fmt.Errorf("--show-logs cannot be used with --output json")
+			}
+		}
+	}
+
 	// Silence Cobra's error output when JSON mode is enabled
 	// This prevents "Error: ..." prefix from being printed
 	if i.output == "json" {
 		cmd.SilenceErrors = true
 	}

As per coding guidelines, pkg/cmd/**/*.go: “Commands must not combine --show-logs with --output json flags (enforce in preRun)”.
Based on learnings, for this specific invalid --output json flag-conflict case, return plain fmt.Errorf instead of outputErrorIfJSON().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cmd/info.go` around lines 46 - 59, In infoCmd.preRun, add a conflict
check that rejects using --show-logs together with --output json: if i.showLogs
(or the flag's field) is true and i.output == "json" return a plain fmt.Errorf
indicating the unsupported combination (do this before setting
cmd.SilenceErrors); ensure you reference the preRun method and the i.output and
i.showLogs fields so the logic sits near the existing output format validation
and silence toggle.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there is no show-logs flag on this command

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@feloy, you're right — the info command doesn't define a --show-logs flag, so the conflict check I suggested is not applicable here. The guideline only applies to commands that expose both --show-logs and --output. My apologies for the noise!


🧠 Learnings used
Learnt from: CR
Repo: kortex-hub/kortex-cli PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-31T09:23:38.234Z
Learning: Applies to pkg/cmd/**/*.go : Commands must not combine `--show-logs` with `--output json` flags (enforce in `preRun`)

Learnt from: feloy
Repo: kortex-hub/kortex-cli PR: 134
File: pkg/cmd/init.go:61-63
Timestamp: 2026-03-30T13:24:26.500Z
Learning: In Go command files under `pkg/cmd/**`, when a command supports JSON output, ensure that any errors produced by the `preRun` and `run` paths are routed through the existing `outputErrorIfJSON()` helper so they render correctly in JSON mode. Exceptions: (1) for the initial `--output` format validation check itself (before JSON mode is confirmed), return a plain `fmt.Errorf` instead of `outputErrorIfJSON()`; and (2) for flag-compatibility checks that explicitly reject an invalid `--output json` combination (e.g., `--show-logs` with `--output json`), also use plain `fmt.Errorf`, since those errors inherently invalidate JSON mode and should be presented as plain text.

Learnt from: feloy
Repo: kortex-hub/kortex-cli PR: 134
File: pkg/cmd/workspace_stop.go:50-52
Timestamp: 2026-03-30T13:24:34.540Z
Learning: In Go command files under pkg/cmd/**, if a command supports JSON output, send command errors through the existing outputErrorIfJSON() helper (covering errors returned from preRun and run). Do not use outputErrorIfJSON() for these two cases: (1) the initial --output format validation (e.g., unsupported output format), because JSON mode hasn’t been confirmed yet; (2) flag-conflict checks that reject --output json itself (e.g., --show-logs cannot be used with --output json), since JSON mode is invalidated and a JSON error response would be contradictory—return a plain fmt.Errorf instead.

Learnt from: CR
Repo: kortex-hub/kortex-cli PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-31T09:23:38.234Z
Learning: Applies to pkg/cmd/**/*.go : Use factory pattern for all Cobra commands: export a `New<Command>Cmd()` function that returns `*cobra.Command`, with no global variables or `init()` functions

Learnt from: CR
Repo: kortex-hub/kortex-cli PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-31T09:23:38.234Z
Learning: Applies to pkg/cmd/**/*.go : All Cobra commands must include an `Example` field with usage examples


// run executes the info command logic
func (i *infoCmd) run(cmd *cobra.Command, args []string) error {
runtimes := runtimesetup.ListAvailable()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In pkg/cmd/autocomplete.go, the fake runtime is filtered out. I think it would be helpful to move the filter inside the runtimesetup.ListAvailable method, so we don't display it here


// Discover agents from runtimes that implement AgentLister
storageDir, err := cmd.Flags().GetString("storage")
if err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to read --storage flag: %w", err))
}

absStorageDir, err := filepath.Abs(storageDir)
if err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to resolve storage directory path: %w", err))
}

runtimeStorageDir := filepath.Join(absStorageDir, "runtimes")
agents, err := runtimesetup.ListAgents(runtimeStorageDir)
if err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to list agents: %w", err))
}

if i.output == "json" {
return i.outputJSON(cmd, agents, runtimes)
}

// Text output
out := cmd.OutOrStdout()
fmt.Fprintf(out, "Version: %s\n", version.Version)
fmt.Fprintf(out, "Agents: %s\n", strings.Join(agents, ", "))
fmt.Fprintf(out, "Runtimes: %s\n", strings.Join(runtimes, ", "))

return nil
}

// outputJSON outputs the info response as JSON
func (i *infoCmd) outputJSON(cmd *cobra.Command, agents, runtimes []string) error {
if agents == nil {
agents = []string{}
}
if runtimes == nil {
runtimes = []string{}
}
response := api.Info{
Version: version.Version,
Agents: agents,
Runtimes: runtimes,
}

jsonData, err := json.MarshalIndent(response, "", " ")
if err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to marshal info to JSON: %w", err))
}

fmt.Fprintln(cmd.OutOrStdout(), string(jsonData))
return nil
}

func NewInfoCmd() *cobra.Command {
c := &infoCmd{}

cmd := &cobra.Command{
Use: "info",
Short: "Display information about kortex-cli",
Example: `# Show info
kortex-cli info

# Show info in JSON format
kortex-cli info --output json

# Show info using short flag
kortex-cli info -o json`,
Args: cobra.NoArgs,
PreRunE: c.preRun,
RunE: c.run,
}

cmd.Flags().StringVarP(&c.output, "output", "o", "", "Output format (supported: json)")
cmd.RegisterFlagCompletionFunc("output", newOutputFlagCompletion([]string{"json"}))

return cmd
}
Loading
Loading