Draft
Conversation
Implements CLI commands for the full plugin API surface under `cy beta plugin`: - `cy beta plugin list/get/install/uninstall/upgrade/logs` — manage plugin installs (upgrade has alias: update; --version-id required; --config key=val / --config-file) - `cy beta plugin manager list/get/create/accept/reject/delete` — manage plugin managers (PluginManager agents with invite flow: accept/reject pending invites) - `cy beta plugin registry list/get/add/delete` — manage plugin registries - `cy beta plugin registry plugin list/get/create/delete` — plugins within a registry - `cy beta plugin registry plugin version list/get/publish/delete/install/logs/retry` All commands support dual-form completion (name OR numeric ID). Resolvers handle ambiguity: error message lists conflicting IDs and asks for a numeric ID. Also adds: - 19 plugin model files (copied from backend gen/models) - middleware interface + implementation (plugins.go) - cyargs helpers: AddPluginConfigFlags, GetPluginConfig, Complete*/Resolve* for all entities - Makefile: prune generated client dir after swagger codegen, keep only models Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
xescugc
previously approved these changes
Apr 9, 2026
Three plugin commands defined --url inline without going through cyargs. Consolidate into AddURLFlag/GetURL in cyargs/plugins.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion publish, and pipeline log commands CLI-113: cy beta plugin install now accepts --version-id (optional) to pin a specific plugin version at install time. cy beta plugin registry plugin version publish now accepts --docker-image as a mutually exclusive alternative to --url, allowing Docker image references (scheme-less URIs) to be published without strfmt.URI validation issues. Refactored upgrade.go to use the shared cyargs.AddPluginVersionIDFlag helper. CLI-114: Added cy pipeline builds logs (dump or --watch a build by ID) and cy pipeline job logs (latest build logs, --watch tails future builds with --poll-interval). Both commands reuse the existing buildwatch SSE streaming and formatting infrastructure via a new StreamLogs function with ReadOnly=true and an idle-timeout reader for dump mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…WithOptions
Migrates 13 key resource command files (list + get) to use the new
cyout.PrintWithOptions helper with curated table column defaults:
- projects: Canonical, Name, Owner.Username (dot-notation)
- apikey: Canonical, Name, LastSeven, LastUsed
- roles: Canonical, Name, Description, Default
- catalogrepositories: Canonical, Name, URL, Branch, StackCount
- configrepositories: Canonical, Name, URL, Branch, Default
- builds: ID, Name, Status, JobName, StartTime
- credentials: Canonical, Name, Type, Path, Keys (Raw field excluded)
- environments: Canonical, Name
- members: Username, Email, GivenName, FamilyName
- pipelines: Name, Status, Paused, Project, Environment, Component (Transform)
- jobs: Name, Paused, PipelineName, CurrentBuildID (Transform)
- teams: Canonical, Name, MemberCount, Roles (Transform, join role names)
- components: Canonical, Name, Description, StackRef, UseCase, Version (Transform)
Also:
- Fixes table printer to handle []interface{} slices (interface unwrapping)
- Removes force-JSON hacks in multi-arg get commands (now typed slices)
- Adds cyout.RegisterModel to all list/get commands for --output completion
- Fixes pre-existing lint issues (misspell, staticcheck, unconvert)
- Updates tests to match new lipgloss table output format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ands - projects get: accept multiple canonicals; loop returns []*models.Project - roles get: accept multiple canonicals via positional args; --role flag keeps precedence - credentials get: accept multiple canonicals when no --canonical/--path flags set - environments get/delete: accept env canonicals as positional args; --env as fallback - components get/delete: accept component canonicals as positional args; --component as fallback - organizations get/delete/list: accept org canonicals as positional args; migrate to cyout - members get/delete: accept numeric member IDs as positional args; --id as fallback All delete commands migrated from factory.GetPrinter+SmartPrint to cyout.Print. Also includes goimports formatting fixes across unrelated files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all remaining factory.GetPrinter + printer.SmartPrint boilerplate with
cyout.PrintWithOptions / cyout.Print across:
- projects, credentials (create/update/delete/list-env)
- teams (all), roles (all)
- environments, components, stacks (non-list/get)
- organizations, configrepositories, catalogrepositories
- apikey, events, members (invite/update/list-invites)
- externalbackends, login, terracost, status, version
- beta/config, beta/plugins (all subcommands)
- pipelines (pause/unpause/diff/update/clear-task-cache/build-trigger/etc.)
Also removes all "if output == 'table' { output = 'json' }" workarounds
(now handled uniformly by the new table printer and cyout).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s non-struct slices - cyout.PrintWithOptions: use cmd.ErrOrStderr() for error output instead of cmd.OutOrStderr(). OutOrStderr falls back to cmd.outWriter which points to stdout in tests that call SetOut — causing error JSON to land on stdout. ErrOrStderr correctly uses the dedicated errWriter set by SetErr. - printer/table: guard headersFromStruct against non-struct values (strings, ints). Returns nil when v.Kind() != Struct; Print() exits early with no output. Fixes panic when deleteProject returns []string of deleted canonicals. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iption to project list - BorderHeader(false): with HiddenBorder, the header separator renders as a line of spaces causing a blank line. Disabling it removes the gap entirely. - projectTableOptions: add Description column (Canonical, Name, Description, Owner.Username). Component already had Description in its default columns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hidden-border with NormalBorder (all sides off except header separator) to render a dim ─ line between header and data rows. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 2-space right padding per cell so columns don't run together when column borders are disabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CLAUDE.md: update command pattern to cyout.PrintWithOptions, add cyout package description, fix Hard Rule 6 - docs/architecture.md: update request lifecycle, add --output system section (grammar, field extraction, shell completion, cyout helper) - docs/adding-a-command.md: update get.go example to use cyout pattern with RegisterModel and PrintWithOptions - changelog: extend positional-args entry to cover environments, components, organizations, members, roles, credentials; add new output-format entry for table=cols/jq=/field extraction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
--jq <expr> is sugar for --output jq=<expr>. GetOutput() checks the --jq flag first; if set, it returns "jq="+expr, bypassing --output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sistent default
- Default table uses kubectl-style `─` header separator with `#` row index column
- New `table:border` option enables nushell-style rounded-border grid (╭─┬─╮)
- New `table:noindex` option suppresses the `#` row index column
- On wide terminals, extra struct fields are shown beyond curated defaults;
as terminal narrows, extra columns are dropped first while curated columns survive
- Nested structs render as `{record N fields}` instead of the type name
- Numeric types (int, int32, float32, float64) render as formatted values
- New `cy output set/get/reset` commands persist the default output format
to ~/.config/cycloid-cli/config.yaml
- Priority chain: --jq > --output > CY_OUTPUT > cy output set > "table"
- Delete commands now use Columns: []string{"Canonical"} for clean table output
- Added e2e tests for delete table output and bulk delete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The # index column was purely cosmetic and not usable for filtering or referencing resources, which could mislead users into trying commands like `cy plugin get 0`. https://claude.ai/code/session_01NkqEDktWxTPxXBtjET5iZK
…nics
Audit of the entire output system (table, field, jq printers) uncovered
multiple crash paths when the table printer receives nil pointers, slices
containing nil elements, or unexpected types. These occur in production
when the API returns null objects or partial data.
Fixes:
- Print() now returns early for nil interface and typed nil pointers
- build() guards nil pointer before calling Elem()
- Slice iteration skips nil elements gracefully (empty row)
- expandColumns() nil-safe pointer/interface unwrap loops
- renderValue() handles invalid Value, nil slices, maps, interfaces
- headersFromStruct() guards invalid Value
- entryFromStruct() guards invalid Value
- Maps now render as "{N entries}" instead of "map"
Adds firstNonNilElem() and derefValue() helpers. 18 new test cases
covering every crash path found during audit.
https://claude.ai/code/session_01NkqEDktWxTPxXBtjET5iZK
Replace the broken path where API errors were rendered as table rows. Errors now bypass the output printer entirely and are formatted as a human-readable block on stderr regardless of --output flag. API errors show: - Status code, HTTP method, and request path - Sanitized request body (credentials and secrets redacted to [REDACTED]) - Structured error payload: [Code] message with sub-detail lines - Request-ID from the backend payload (for support/debugging copy-paste) - Fallback to raw response body when no structured payload is available Local errors (missing flags, bad args) show the command invocation hint (command path + set flags) before the Error: summary line. New sanitizeBody() in middleware recursively redacts sensitive JSON keys: ssh_key, password, secret_key, access_key, client_secret, json_key, token, ca_cert, raw, current — covering all credential and auth models. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a full
cy beta plugincommand tree covering the plugin API surface:cy beta plugin …) — list, get, install, uninstall, upgrade (alias: update), logscy beta plugin manager …) — list, get, create, accept, reject, deletecy beta plugin registry …) — list, get, add, deletecy beta plugin registry plugin …) — list, get, create, deletecy beta plugin registry plugin version …) — list, get, publish, delete, install, logs, retryAll commands live under the
betaflag and follow existing cobra conventions.Key design decisions
<name-or-id>argument accepts both the name and the numeric ID via tab-completion. Resolvers handle both; ambiguous names (multiple IDs) produce a clear error listing the conflicts and asking the user to use a numeric ID.--config key=val(repeatable) and--config-file path.jsonfor plugin install/upgrade configuration (map[string]string, distinct from StackForms).cy beta plugin upgradeis the canonical verb;updateis an alias.--version-idis required.http) → name.youdeploy-http-api/gen/models/(identical output toswagger generate client). The Makefileclient-generatetarget now prunes the generated HTTP client dir, keeping onlymodels/.version/helpers.gocentralises the two-step registry+plugin resolution used by all version subcommands.Not included (follow-ups)
cy beta plugin registry update(middleware method exists, no CLI command)cy beta plugin registry plugin update(middleware method exists, no CLI command)Test plan
go build ./...— clean (verified locally)cy beta plugin --helprenders expected subcommand tree<registry>proposes name, URL, and numeric ID formscy beta plugin install <name>resolves by name; numeric ID also accepted🤖 Generated with Claude Code