Skip to content

Noun-first commands, multi-profile support, semantic WP identifiers, and new commands#15

Open
cbliard wants to merge 61 commits into
mainfrom
cbl-improvements
Open

Noun-first commands, multi-profile support, semantic WP identifiers, and new commands#15
cbliard wants to merge 61 commits into
mainfrom
cbl-improvements

Conversation

@cbliard

@cbliard cbliard commented Jun 4, 2026

Copy link
Copy Markdown
Member

Summary

This PR brings a series of improvements to the OpenProject CLI, built incrementally
over ~20 commits. The changes span command naming, new features, and identifier handling.

Sorry for not splitting it in multiple PRs, but I don't have much time for that, and each commits build on top of each other.

Command restructuring (breaking change)

Commands now follow op <noun> <verb> instead of op <verb> <noun>. The old
verb-first syntax has been removed entirely:

Before After
op list workpackages op work-package list
op create workpackage op work-package create
op update workpackage op work-package update
op list time-entries op time-entry list

Each noun (work-package, time-entry, project, budget, …) is now its own
subcommand group with discoverable verbs and consistent flag help.

Multi-profile support

op login now supports named profiles for working with multiple OpenProject instances:

op login --profile work
op login --profile staging
op work-package list --profile work

The OP_CLI_PROFILE env var selects a profile for the whole session.

New commands

  • op whoami — shows all configured profiles and the authenticated user for each
  • op time-entry create — creates a time entry with activity, hours, date, comment
  • op budget list / op budget inspect — lists and inspects project budgets
  • op work-package search — searches work packages by subject, type, status, project name, or identifier; multiple words are ANDed; returns up to 100 results

New flags

  • op work-package list --parent-id — filter by parent work package
  • op work-package create/update --assignee — assign a user
  • op work-package create/update --description — set description

Semantic work package identifiers

Work packages can now be referenced by their project-based identifier wherever a
numeric ID was previously required. Both slug-based identifiers (e.g. PROJ-42,
used before semantic identifiers were introduced) and semantic identifiers
(e.g. PROJ-123) are supported. The displayId field from the API drives display:
semantic form when project-based identifiers are enabled, #N otherwise.

JSON output

All list/inspect commands support --format json for machine-readable output.

Project identifiers

Project path arguments now accept either a numeric ID or a human-readable slug
(e.g. my-project), matching what the OpenProject API supports natively.

Developer docs

Added CLAUDE.md documenting the architecture, conventions, and data flow for
contributors.

Suggested test plan

  • go test ./... passes
  • op login --profile <name> creates and stores a named profile
  • op whoami lists all profiles with their URLs and users
  • op work-package list --format json returns valid JSON
  • op work-package list --parent-id <id> filters correctly
  • op time-entry create prompts for missing required fields and submits
  • Work packages can be referenced as PROJ-42 or PROJ-123 in inspect/update/open
  • op budget list and op budget inspect work on a project with budgets
  • op work-package search cascade returns matching work packages
  • op work-package search fix login ANDs both terms

Breaking change

The verb-first commands from 0.5.5 (op list workpackages, op update workpackage, …) are removed without aliases; the README now contains a migration table to the noun-first forms.

Deferred review items

Findings from external review that are intentionally not addressed in this PR: (1) whoami re-initializes the global HTTP client once per profile — works correctly, to be revisited when the client stops being a global singleton; (2) components/resources sometimes prints user hints directly via printer — consistent with existing layering, to be cleaned up in a dedicated pass; (3) --activity resolution uses GET /api/v3/time_entries/activities, which 404s on some instances — switching to the form/schema endpoint is tracked in TODO.md; (4) config writes are atomic (temp file + rename) but not file-locked against concurrent logins — deemed unnecessary for an interactive CLI.

🤖 Follow-up fixes (post-review)

Added by an agent on Alex's behalf. A code-review pass over the branch surfaced some smaller correctness and output-contract issues, fixed in commits on top of the original work above. These do not change any of the features or behaviours described in the sections above.

  • Guard the attachment paths against a work package with no _links (and against an addAttachment link dropped by a custom action) so op work-package update --attach returns a handled error instead of panicking.
  • Accept INI config files whose first line is a comment — previously such a file was mistaken for the legacy host token format and rejected as corrupt, locking the user out of every command.
  • op whoami --format json now always emits an array, including for a single or explicit profile, so scripts no longer branch on the invocation. Only the JSON shape changed; the per-profile global-client re-initialization noted in deferred item (1) above is still deferred.
  • On an unresolved --type, the "available types" hint now goes to stderr, keeping --format json stdout clean on failure — a partial step toward deferred item (2); resource-layer hints are otherwise unchanged.
  • ProjectUrl falls back to the numeric id when a project's identifier is empty, instead of linking to the projects index.
  • op time-entry create reports an unresolved --activity once instead of twice. The endpoint itself is unchanged, so deferred item (3) still applies.

cbliard added 23 commits April 20, 2026 14:26
New noun-first commands (old verb-first commands kept during transition):

  op workpackage list/create/update/inspect  (was: op list/create/update/inspect workpackage[s])
  op activities list [--wp id]               (was: op list activities [id])
  op project list/inspect                    (was: op list/inspect project[s])
  op user search                             (was: op search user)
  op timeentry list                          (was: op list timeentries)
  op type list                               (was: op list types)
  op status list                             (was: op list status)
  op notification list                       (was: op list notifications)

op activities list uses --wp flag instead of positional argument to
allow future scoping (--project, global) without breaking the interface.

op git start workpackage is unchanged.
Deleted: cmd/list/, cmd/create/, cmd/update/, cmd/inspect/, cmd/search/

Remaining commands:
  op workpackage list/create/update/inspect
  op activities list [--wp id]
  op project list/inspect
  op user search
  op timeentry list
  op type list
  op status list
  op notification list
  op git start workpackage (unchanged)
  op login (unchanged)
- --wp renamed to --work-package for consistency with other flag names
- MarkFlagRequired ensures Cobra validates the flag before execution
  rather than failing at runtime
Introduce a Renderer interface with TextRenderer (existing behavior) and
JsonRenderer (new) implementations. The active renderer is selected at startup
via the root command's PersistentPreRun based on the --format flag value.
The predicate (Id == target) is not monotone — sort.Search requires >=.
Also, len(users)-1 as the upper bound excluded the last user from the search.
Together, these bugs caused the wrong user name to be resolved when the
target user appeared before the last position in the slice.
Cobra supports hyphens in command names; aligning with common CLI conventions
(docker, kubectl, gh). Package names remain unhyphenated as Go requires.
Also updates README and CLAUDE.md to reflect noun-first syntax throughout.
go build -o op produces a local binary that should not be versioned.
Lists budgets for a given project via GET /api/v3/projects/{id}/budgets.
Fetches a single budget by ID via GET /api/v3/budgets/{id}.
These flags are irrelevant for shell completion generation.
Assignee was already supported for update and display; create was missing it.
Takes a user ID, consistent with the update command.
Supports multi-line markdown descriptions. Format is hardcoded to markdown
as textile is no longer supported in OpenProject.
Shows configured server URL and current user info.
Handles missing config (not logged in) and 401 (invalid token) gracefully.
Supports --work-package (required), --hours (required), --activity (optional,
looked up by name), --spent-on (default: today), --user, --comment flags.

Also fixes nil pointer dereferences in TimeEntryDto.Convert() for optional
linked resources (project, work package, user, activity, comment).
When GET /api/v3/time_entries/activities returns an error (endpoint not
available in all OpenProject versions), show a clear message instead of
the raw API error. When the endpoint works but the name doesn't match,
list available activities.
Previously the CLI stored a single host+token pair in a flat config file,
making it awkward to switch between instances (community, self-hosted, staging,
etc.). Users had to re-run `op login` each time, overwriting their credentials.

This commit introduces named profiles so multiple instances can be configured
and used side-by-side.

Config format
  The config file (~/.config/openproject/config, or $XDG_CONFIG_HOME) is now
  stored as INI with one section per profile:

    [default]
    host  = https://community.openproject.org
    token = abc123

    [work]
    host  = https://work.example.com
    token = xyz789

  Existing single-line files ("host token") are silently migrated to [default]
  on the first read.

Profile names
  Only letters, digits, - and _ are allowed, with no leading/trailing hyphens.
  The interactive prompt sanitizes invalid input and re-prompts with the
  corrected name as the new default.

op login
  - Prompts "Profile name? [default]" before asking for credentials.
  - --profile <name> skips the prompt and uses that name directly (validated
    upfront; errors immediately if the name is invalid).
  - OP_CLI_PROFILE env var behaves the same as --profile for login.
  - If the profile already exists the user is asked to confirm overwrite.

op logout (new command)
  - Removes a profile from the config file.
  - Defaults to "default"; use --profile to target another.
  - Idempotent: no error if the profile does not exist.
  - Always asks for confirmation before deleting.

op whoami
  - Without --profile: shows every configured profile (server + user), each
    block separated by a blank line.
  - With --profile <name>: shows only that profile.
  - Output now includes a "Profile:" header line per entry.

All other commands
  - Accept a global --profile flag (persistent, default "default").
  - OP_CLI_PROFILE env var sets the default profile; --profile overrides it.
  - OP_CLI_HOST / OP_CLI_TOKEN still override everything (highest priority).
  - If an explicitly named profile does not exist, an error is shown and
    nothing is done.
Hiding flags via InheritedFlags().MarkHidden() mutates the shared *Flag
structs, accidentally hiding --format and --verbose from all sub-commands.
Removing the loop lets them appear correctly under "Global Flags" everywhere.
Allows listing the direct children of a work package by passing its ID:

  op work-package list --parent-id 42

The flag translates to a parent filter on the OpenProject API v3
(operator "="), which is ANDed with any other active filters such as
--project-id, --status, or --type.

A pre-flight lookup verifies that the given work package exists before
issuing the filter query. If it does not, the command exits with a
clear error naming the flag:

  --parent-id: work package #42 not found.

Only direct children are returned (depth 1). Listing a full subtree
would require multiple API calls and is out of scope.
All commands that reference a project — op project inspect, op work-package
list, op work-package create, and op budget list — now accept both the
numeric project ID (e.g. 42) and the human-readable project identifier
found in the URL (e.g. "foobar" from /projects/foobar/work_packages).

Changes:
- --project-id flag on op work-package list renamed to --project; the old
  name is kept as a hidden alias for backward compatibility
- op project inspect updated to accept [id|identifier] as its argument;
  the previous hard rejection of non-numeric input is removed
- op budget list --project flag updated to accept string values
- op work-package create --project flag updated to accept string values
- Project identifier field added to models, DTOs, and JSON output so it is
  visible when listing or inspecting projects
- Project display format updated to "#14 MyProject (my-project)" so users
  can discover the identifier to reuse in flags
- routes.ProjectUrl now builds the human-readable URL (projects/my-project)
  instead of the numeric one (projects/14)
- Input validated client-side: only alphanumeric characters, -, _, and +
  are accepted; invalid input is rejected before any API call with a clear
  error message pointing to the offending flag
- A friendly 404 error is shown when the project is not found instead of
  the raw API error response
OpenProject community now supports Jira-style identifiers introduced in
https://community.openproject.org/work_packages/72427. All commands that
accept a work package ID (inspect, update, list --parent-id, activities
--work-package, git start workpackage) now accept both numeric IDs for
backward compatibility and project-based identifiers (PROJECTID-NUMBER).

Validation enforces the identifier rules: uppercase letters, digits, or
underscores; max 10 characters; must start with a letter; followed by a
hyphen and a numeric sequence number.
…ayId field

The OpenProject API returns a displayId field alongside the numeric id.
When project-based identifiers are enabled, this is the semantic key
(e.g. SJF-13); on instances without the feature it matches the numeric
id and the display falls back to the conventional #N format.

Wire displayId through DTO → model → printer (text and JSON renderers).
Column alignment in list output handles mixed numeric/#N and semantic IDs.
@myabc
myabc requested review from Copilot and myabc and removed request for Copilot June 4, 2026 14:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR is a broad refactor and feature expansion of the OpenProject CLI, primarily restructuring commands into a noun-first hierarchy while adding multi-profile configuration, semantic work package identifiers, JSON output, and several new resource commands (time entries, budgets, whoami).

Changes:

  • Reworked CLI command structure to op <noun> <verb> and introduced new command groups (work-package, time-entry, project, budget, status, notification, user, etc.).
  • Added multi-profile configuration support (INI-based), along with op whoami, op logout, and profile selection via --profile / OP_CLI_PROFILE.
  • Added semantic identifier handling for work packages/projects, JSON output rendering via a new renderer abstraction, and new resource capabilities (time entry create, budget list/inspect).

Reviewed changes

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

Show a summary per file
File Description
TODO.md Tracks follow-up items discovered during review.
README.md Updates documentation/examples to noun-first commands and multi-profile usage.
.gitignore Adds built binary and Claude local settings file.
CLAUDE.md Adds contributor/developer architecture and conventions documentation.
models/work_package.go Adds DisplayId to support semantic display identifiers.
models/project.go Adds Identifier to support slug/identifier-based project access.
models/time_entry_activity.go Adds time entry activity model.
models/budget.go Adds budget model.
dtos/work_package.go Maps API displayId and hardens link/description conversion against nils.
dtos/project.go Adds project identifier mapping.
dtos/time_entry.go Renames/exports links DTO and hardens conversion against nil links/comment.
dtos/time_entry_activity.go Adds DTOs and conversion for time entry activities collection.
dtos/budget.go Adds budget DTOs and collection conversion.
components/paths/paths.go Updates path helpers to accept string identifiers; adds budgets/time entry activities endpoints.
components/routes/routes.go Changes browser routes to use wp/<displayId> and projects/<identifier>.
components/resources/work_packages/validate.go Adds identifier validation for numeric vs semantic WP IDs.
components/resources/work_packages/validate_test.go Tests new WP identifier validation.
components/resources/work_packages/read.go Converts WP APIs to accept string IDs and adds parent filter handling.
components/resources/work_packages/filters.go Adds Parent filter option and filter builder.
components/resources/work_packages/create.go Allows project identifier string; adds assignee/description create options.
components/resources/work_packages/update.go Allows string WP id; adds description patching and improves assignee parsing errors.
components/resources/work_packages/activities.go Switches WP activities to accept string WP identifiers.
components/resources/time_entries/create.go Implements time entry creation plus activity lookup.
components/resources/projects/functions.go Updates project lookup to accept numeric ID or identifier and validates input.
components/resources/projects/versions.go Updates versions API to take string project identifiers/IDs.
components/resources/projects/validate.go Adds project identifier validation helper.
components/resources/projects/validate_test.go Tests new project identifier validation.
components/resources/budgets/functions.go Adds budgets lookup and listing for a project.
components/configuration/util.go Removes legacy single-profile read/write logic (replaced by profiles).
components/configuration/profiles.go Implements INI-based multi-profile config + migration from legacy format.
components/configuration/profiles_test.go Comprehensive tests for profile sanitization/validation/read/write/migration.
components/printer/renderer.go Introduces renderer abstraction and --format handling.
components/printer/renderer_test.go Tests renderer init behavior on unknown/known formats.
components/printer/text_renderer.go Centralizes existing text rendering logic behind renderer interface.
components/printer/json_renderer.go Adds JSON output rendering for list/inspect commands.
components/printer/work_packages.go Updates WP printing to use DisplayId via renderer and adds formatting helpers.
components/printer/work_packages_test.go Updates tests for DisplayId and adds semantic display ID alignment test.
components/printer/projects.go Routes project output via renderer.
components/printer/projects_test.go Updates expected project output to include identifier.
components/printer/users.go Routes user output via renderer.
components/printer/types.go Routes type output via renderer.
components/printer/status.go Routes status output via renderer.
components/printer/time_entries.go Routes time entry output via renderer.
components/printer/notifications.go Routes notifications output via renderer while keeping grouping helper.
components/printer/custom_actions.go Routes custom actions output via renderer.
components/printer/numbers.go Routes numeric output via renderer.
components/printer/budgets.go Adds budgets printer entry points routed through renderer.
components/printer/budgets_test.go Adds budgets output/alignment tests.
components/printer/whoami.go Adds whoami printer entry point routed through renderer.
components/printer/whoami_test.go Adds whoami output test.
components/printer/activities.go Routes activities output via renderer.
components/printer/activities_test.go Adds regression test for prior activities user-lookup bug.
cmd/root.go Registers new noun-first command groups; adds --format and --profile; initializes renderer/requests in pre-run.
cmd/login.go Adds profile selection/prompting and profile overwrite confirmation; stores credentials per profile.
cmd/logout.go Adds logout command to remove stored profiles with confirmation.
cmd/whoami.go Adds whoami command to display authenticated user for one/all profiles.
cmd/workpackage/workpackage.go Adds new work-package command group and wires flags/subcommands.
cmd/workpackage/list.go Implements work-package list with project/parent filters and improved errors.
cmd/workpackage/list_flags.go Defines flags for work-package list (project, parent-id, filters, total, etc.).
cmd/workpackage/create.go Implements work-package create supporting project identifier, assignee, description, open-in-browser.
cmd/workpackage/update.go Implements work-package update supporting semantic IDs and new --description.
cmd/workpackage/options_test.go Adds tests for description flag semantics in create/update.
cmd/workpackage/inspect.go Implements work-package inspect supporting semantic IDs and listing types.
cmd/workpackage/workpackage_test.go Adds test ensuring hyphenated command name usage.
cmd/timeentry/timeentry.go Adds time-entry command group and wires list/create.
cmd/timeentry/list.go Implements time-entry list as noun-first list subcommand.
cmd/timeentry/list_flags.go Wires filters for time-entry list.
cmd/timeentry/create.go Implements time-entry create including defaults and optional activity/user/comment.
cmd/timeentry/create_flags.go Defines flags and required inputs for time-entry create.
cmd/timeentry/timeentry_test.go Adds test ensuring hyphenated command name usage.
cmd/project/project.go Adds project command group and wires list/inspect with open flag.
cmd/project/list.go Implements project list as noun-first list subcommand.
cmd/project/inspect.go Implements project inspect accepting numeric ID or identifier.
cmd/budget/budget.go Adds budget command group and wires list/inspect.
cmd/budget/list.go Implements `budget list --project <id
cmd/budget/inspect.go Implements budget inspect <id>.
cmd/status/status.go Adds status command group.
cmd/status/list.go Implements status list.
cmd/notification/notification.go Adds notification command group.
cmd/notification/list.go Implements notification list with reason filter.
cmd/user/user.go Adds user command group.
cmd/user/search.go Implements user search (including me keyword behavior).
cmd/wptype/wptype.go Adds type command group for work package types.
cmd/wptype/list.go Implements type list.
cmd/activities/activities.go Adds activities command group.
cmd/activities/list.go Implements `activities list --work-package <id
cmd/git/start/work_package.go Updates git start command to accept semantic WP identifiers.
cmd/update/update.go Removes legacy verb-first update command group.
cmd/update/work_package.go Removes legacy verb-first update workpackage command.
cmd/list/list.go Removes legacy verb-first list command group.
cmd/list/activities.go Removes legacy verb-first activities list command.
cmd/list/status.go Removes legacy verb-first status list command.
cmd/inspect/inspect.go Removes legacy verb-first inspect command group.
cmd/create/create.go Removes legacy verb-first create command group.
cmd/create/work_package.go Removes legacy verb-first create workpackage command.
cmd/search/search.go Removes legacy verb-first search command group.
Comments suppressed due to low confidence (2)

cmd/workpackage/list.go:156

  • validatedVersionId() logs an error but continues, which can lead to a nil dereference (project may be nil, yet project.Identifier is accessed on the next lines). It should abort/return immediately when projects.Lookup fails.
    cmd/workpackage/list.go:161
  • validatedVersionId() logs an error but continues, which can lead to using an uninitialized versions slice (and inconsistent CLI behavior). Exit/return when AvailableVersions fails.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cmd/root.go Outdated
Comment thread components/printer/json_renderer.go
Comment thread cmd/login.go Outdated
Comment thread components/routes/routes.go
- Handle url.Parse error in PersistentPreRunE instead of silently ignoring it
- Route printJson output through activePrinter instead of fmt directly
- Use printer.Input for API token prompt in login instead of fmt.Printf
- Add comment explaining DisplayId is always non-empty (API guarantees identifier.presence || id)
myabc added 8 commits July 21, 2026 17:39
Drop omitempty from lockVersion so a zero value is still sent in
PATCH bodies; omitting it made every update of a fresh work package
fail. Propagate update errors and exit nonzero instead of masking.
The helper has no callers since version filtering moved to the
filters package. It also contained an unreachable nil dereference
after a printed-but-ignored lookup error, flagged in review.
Info, Done, Error, ErrorText, Debug, Input, and the spinner now
write to standard error, so --format json output stays parseable.
Version text moves to a new printer.Output which stays on stdout.
All leaf commands convert from Run to RunE and return a sentinel
ErrHandled after printing errors, so main exits 1 without printing
twice. Replaces scattered os.Exit calls, including exit code 255
from os.Exit(-1). Filter validation and --parent-id lookups now
report the real error instead of exiting or masking it as 404.
Rewrite the config via a same-directory temp file and rename, so
interrupted writes cannot truncate it and migration resets a
permissive legacy mode to 0600. Migration is now best effort for
read-only configs. Deleting removes duplicate sections and errors
on missing profiles; hosts are validated as absolute URLs on read.
With OP_CLI_HOST/OP_CLI_TOKEN set, whoami shows the environment
identity instead of querying the same server once per stored
profile. Multi-profile output renders through a new WhoamiList so
JSON mode emits one array instead of several top-level objects.
Filter values are JSON-marshaled so quotes cannot corrupt query
filters. Activity matching rejects empty and ambiguous input, hours
reject NaN/Inf and sub-second values, and activity actors are
sorted before the binary search in the renderers. Work package
output and browser URLs fall back to the numeric id when the
server omits displayId. Completion and --version work offline.
Remove the nonexistent --status update flag from the README,
document the full profile-name rules and the breaking verb-first
to noun-first migration, correct the activity and inspect --types
descriptions, and gofmt files the formatter flagged.

Copilot AI left a comment

Copy link
Copy Markdown

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 120 out of 124 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

components/resources/work_packages/activities.go:16

  • Activities() logs the request error but continues parsing the (likely nil/empty) response, which can lead to misleading parse errors or panics. It should return immediately on request failure and propagate the error to the caller (which already handles it). Also fix the misspelled named return variable (activites).

myabc added 8 commits July 21, 2026 20:00
Unknown formats previously printed a warning and continued with
text output. Return a handled error before command execution so
scripts never receive output in an unexpected format.
Abort before POST when type resolution fails. Once creation succeeds,
report browser-launch problems as warnings so callers do not retry and
create duplicates.
Resolve actions, patch values, and attachment paths before the first
mutation. This prevents local validation errors from leaving earlier
changes applied while preserving lock-version refreshes.

Attachment validation also requires the addAttachment link and rejects
fog storages before any mutation, so a missing upload capability
surfaces as an error instead of a panic after the action and patch have
already run.
Return request failures from activity and user lookups instead of
printing and continuing. The command now emits one diagnostic and stops
before rendering incomplete activity data.
Read complete environment credentials without touching the config
directory. Reject non-HTTP hosts from environment variables or stored
profiles before request setup.
Always invoke the collection renderer, even when a search has no
matches. JSON callers now receive [] while text users still get an
empty-result diagnostic on standard error.
Treat browser launch as part of read-only inspect commands. Return a
handled error so scripts do not mistake an unopened page for success.
Complete the noun-first migration table and remove resolved TODOs. Also
describe cross-request partial failures for multi-option updates.
@myabc
myabc force-pushed the cbl-improvements branch from 2d0a41c to 60a279f Compare July 21, 2026 18:02
myabc added 6 commits July 21, 2026 20:52
validateAttachment and upload dereferenced WorkPackageDto.Links without
a nil check, so a server payload lacking _links, or a work package
re-fetched after a custom action that dropped the addAttachment link,
panicked the CLI. Convert() was already hardened for nil Links; make
both attachment paths agree by returning a handled error instead.
readOrMigrate routed anything not starting with '[' to the legacy
"host token" path, so a valid INI file whose first line was a comment
failed the host check and was rejected as corrupt, locking the user
out of every command. Detect INI by parsing, since parseIni already
skips comment and blank lines, and fall back to the legacy format only
when no sections are found.
whoami --format json returned a bare object for an explicit profile or
environment credentials but an array for the all-profiles listing, so
scripts had to branch on the invocation. Route every path through
WhoamiList so the JSON output is always an array, consistent with the
list and search commands.
On a failed create or update --type, the available-types list was
rendered through the active renderer to stdout, so --format json
emitted a types array instead of nothing. Add printer.AvailableTypes,
which lists names on stderr, and use it on both not-found paths so
stdout carries only the command result.
ProjectUrl built projects/<identifier> with no fallback, so a project
whose identifier is empty produced projects/ (the index) instead of
the project. Mirror WorkPackageUrl and use the numeric id, which is
also a valid /projects/ address, when the identifier is missing.
activityCreate printed guidance and then returned a plain error that
the command layer reported a second time, so failures showed twice.
Return ErrHandled from the paths that already printed, and skip
re-reporting it in the caller, matching how work_packages signals an
already-reported error.

Copilot AI left a comment

Copy link
Copy Markdown

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 136 out of 140 changed files in this pull request and generated 2 comments.

Comment thread cmd/root.go
Comment on lines +78 to +90
if host == "" && explicit {
printer.Error(openerrors.Custom(fmt.Sprintf(
"Profile %q not found. Run 'op login --profile %s' to create it.",
profile, profile,
)))
return openerrors.ErrHandled
}

parse, err := url.Parse(host)
if err != nil {
printer.ErrorText(fmt.Sprintf("invalid host URL %q: %s", host, err))
return openerrors.ErrHandled
}
Comment on lines +182 to +185
func printProject(p *models.Project) {
id := fmt.Sprintf("#%d", p.Id)
activePrinter.Printf("%s %s (%s)\n", Red(id), Cyan(p.Name), p.Identifier)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request go Pull requests that update go code

Development

Successfully merging this pull request may close these issues.

3 participants