Released v1

This commit is contained in:
Steve Cliff
2026-02-12 10:37:19 +00:00
commit b07572fed5
77 changed files with 19518 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
## ADDED Requirements
### Requirement: Base HTTP client
The system SHALL provide a base HTTP client that sends requests to the Planka API. The client SHALL construct URLs by joining the configured base URL with the API path. The client SHALL attach an `x-api-key: <key>` header to every request. The client SHALL send and receive JSON (`Content-Type: application/json`). The client SHALL accept a `*slog.Logger` and log every request at DEBUG level with method, path, status code, and duration. The client SHALL log errors at WARN level.
#### Scenario: Successful API request
- **WHEN** the client sends a request to a valid endpoint
- **THEN** the response body SHALL be returned as parsed JSON
- **AND** the request SHALL include the `x-api-key` header
- **AND** a DEBUG log entry SHALL be emitted with method, path, status, and duration
#### Scenario: API returns error status
- **WHEN** the API responds with a 4xx or 5xx status code
- **THEN** the client SHALL return an `APIError` containing the HTTP status code and response message
- **AND** a WARN log entry SHALL be emitted
#### Scenario: Network failure
- **WHEN** the HTTP request fails due to a network error (connection refused, timeout, DNS failure)
- **THEN** the client SHALL return a Go error wrapping the underlying network error
### Requirement: Authentication from environment
The system SHALL read `PLANKA_URL` from the environment to determine the API base URL. The system SHALL read `PLANKA_API_KEY` from the environment to determine the API key. Global flags `--url` and `--api-key` SHALL override the corresponding environment variables. Flag values SHALL take precedence over environment variables.
#### Scenario: Auth from environment variables
- **WHEN** `PLANKA_URL` and `PLANKA_API_KEY` are set in the environment
- **AND** no `--url` or `--api-key` flags are provided
- **THEN** the client SHALL use the environment variable values
#### Scenario: Flag overrides environment
- **WHEN** `--url` or `--api-key` flags are provided
- **THEN** the flag values SHALL take precedence over environment variables
#### Scenario: Missing configuration
- **WHEN** neither the environment variable nor the flag is set for URL or API key
- **THEN** the system SHALL print an error message and exit with code 1
### Requirement: Cursor-based pagination
The system SHALL implement cursor-based pagination for all list endpoints that support it. Paginated endpoints are: `GET /boards/{boardId}/actions` (`beforeId`), `GET /cards/{cardId}/actions` (`beforeId`), `GET /lists/{listId}/cards` (`before`), `GET /cards/{cardId}/comments` (`beforeId`). List methods SHALL accept a `limit` parameter. When `limit` is 0, the client SHALL fetch all pages. When `limit` is greater than 0, the client SHALL stop fetching after accumulating at least `limit` items and truncate the result to exactly `limit` items. Each page fetch SHALL be logged at DEBUG level.
#### Scenario: Fetch all pages
- **WHEN** a list method is called with limit 0
- **THEN** the client SHALL fetch pages until an empty page is returned
- **AND** all items from all pages SHALL be returned
#### Scenario: Fetch with limit
- **WHEN** a list method is called with limit N (N > 0)
- **THEN** the client SHALL stop fetching after accumulating N or more items
- **AND** the returned slice SHALL contain exactly N items
#### Scenario: Single page result
- **WHEN** the API returns fewer items than a page size and no more pages exist
- **THEN** the client SHALL return those items without making additional requests
### Requirement: Project operations
The client SHALL provide methods to list all accessible projects (`GET /projects`) and get a single project by ID (`GET /projects/{id}`).
#### Scenario: List projects
- **WHEN** `ListProjects` is called
- **THEN** the client SHALL send `GET /projects` and return a slice of Project models
#### Scenario: Get project
- **WHEN** `GetProject` is called with a project ID
- **THEN** the client SHALL send `GET /projects/{id}` and return a Project model
### Requirement: Board operations
The client SHALL provide a method to get a single board by ID (`GET /boards/{id}`) and list board actions (`GET /boards/{boardId}/actions`) with pagination support.
#### Scenario: Get board
- **WHEN** `GetBoard` is called with a board ID
- **THEN** the client SHALL send `GET /boards/{id}` and return a Board model including its included lists
#### Scenario: List board actions
- **WHEN** `ListBoardActions` is called with a board ID and limit
- **THEN** the client SHALL send paginated `GET /boards/{boardId}/actions` requests and return a slice of Action models
### Requirement: Card CRUD operations
The client SHALL provide methods for: get card (`GET /cards/{id}`), create card (`POST /lists/{listId}/cards`), update card (`PATCH /cards/{id}`), delete card (`DELETE /cards/{id}`), and duplicate card (`POST /cards/{id}/duplicate`). The client SHALL provide a method to list cards in a list (`GET /lists/{listId}/cards`) with pagination support. The client SHALL provide a method to list card actions (`GET /cards/{cardId}/actions`) with pagination support.
#### Scenario: Get card
- **WHEN** `GetCard` is called with a card ID
- **THEN** the client SHALL send `GET /cards/{id}` and return a Card model
#### Scenario: Create card
- **WHEN** `CreateCard` is called with a list ID and card fields (name, description, type, position, dueDate, isDueCompleted)
- **THEN** the client SHALL send `POST /lists/{listId}/cards` with the provided fields and return the created Card
#### Scenario: Update card
- **WHEN** `UpdateCard` is called with a card ID and update fields
- **THEN** the client SHALL send `PATCH /cards/{id}` with only the provided fields and return the updated Card
#### Scenario: Delete card
- **WHEN** `DeleteCard` is called with a card ID
- **THEN** the client SHALL send `DELETE /cards/{id}`
#### Scenario: Duplicate card
- **WHEN** `DuplicateCard` is called with a card ID, name, and position
- **THEN** the client SHALL send `POST /cards/{id}/duplicate` and return the new Card
#### Scenario: List cards in list
- **WHEN** `ListCards` is called with a list ID and limit
- **THEN** the client SHALL send paginated `GET /lists/{listId}/cards` requests and return a slice of Card models
#### Scenario: List card actions
- **WHEN** `ListCardActions` is called with a card ID and limit
- **THEN** the client SHALL send paginated `GET /cards/{cardId}/actions` requests and return a slice of Action models
### Requirement: Comment operations
The client SHALL provide methods for: list comments (`GET /cards/{cardId}/comments`) with pagination, create comment (`POST /cards/{cardId}/comments`), update comment (`PATCH /comments/{id}`), and delete comment (`DELETE /comments/{id}`).
#### Scenario: List comments
- **WHEN** `ListComments` is called with a card ID and limit
- **THEN** the client SHALL send paginated `GET /cards/{cardId}/comments` requests and return a slice of Comment models
#### Scenario: Create comment
- **WHEN** `CreateComment` is called with a card ID and text
- **THEN** the client SHALL send `POST /cards/{cardId}/comments` with the text and return the created Comment
#### Scenario: Update comment
- **WHEN** `UpdateComment` is called with a comment ID and text
- **THEN** the client SHALL send `PATCH /comments/{id}` and return the updated Comment
#### Scenario: Delete comment
- **WHEN** `DeleteComment` is called with a comment ID
- **THEN** the client SHALL send `DELETE /comments/{id}`
### Requirement: Task list operations
The client SHALL provide methods for: create task list (`POST /cards/{cardId}/task-lists`), get task list (`GET /task-lists/{id}`), update task list (`PATCH /task-lists/{id}`), and delete task list (`DELETE /task-lists/{id}`).
#### Scenario: Create task list
- **WHEN** `CreateTaskList` is called with a card ID and fields (name, position, showOnFrontOfCard, hideCompletedTasks)
- **THEN** the client SHALL send `POST /cards/{cardId}/task-lists` and return the created TaskList
#### Scenario: Get task list
- **WHEN** `GetTaskList` is called with a task list ID
- **THEN** the client SHALL send `GET /task-lists/{id}` and return a TaskList model
#### Scenario: Update task list
- **WHEN** `UpdateTaskList` is called with a task list ID and update fields
- **THEN** the client SHALL send `PATCH /task-lists/{id}` and return the updated TaskList
#### Scenario: Delete task list
- **WHEN** `DeleteTaskList` is called with a task list ID
- **THEN** the client SHALL send `DELETE /task-lists/{id}`
### Requirement: Task operations
The client SHALL provide methods for: create task (`POST /task-lists/{taskListId}/tasks`), update task (`PATCH /tasks/{id}`), and delete task (`DELETE /tasks/{id}`).
#### Scenario: Create task
- **WHEN** `CreateTask` is called with a task list ID and fields (name, position, isCompleted, assigneeUserId, linkedCardId)
- **THEN** the client SHALL send `POST /task-lists/{taskListId}/tasks` and return the created Task
#### Scenario: Update task
- **WHEN** `UpdateTask` is called with a task ID and update fields (name, position, isCompleted, assigneeUserId, taskListId)
- **THEN** the client SHALL send `PATCH /tasks/{id}` and return the updated Task
#### Scenario: Delete task
- **WHEN** `DeleteTask` is called with a task ID
- **THEN** the client SHALL send `DELETE /tasks/{id}`
### Requirement: Label operations
The client SHALL provide methods for: create label (`POST /boards/{boardId}/labels`), update label (`PATCH /labels/{id}`), and delete label (`DELETE /labels/{id}`).
#### Scenario: Create label
- **WHEN** `CreateLabel` is called with a board ID and fields (name, color, position)
- **THEN** the client SHALL send `POST /boards/{boardId}/labels` and return the created Label
#### Scenario: Update label
- **WHEN** `UpdateLabel` is called with a label ID and update fields (name, color, position)
- **THEN** the client SHALL send `PATCH /labels/{id}` and return the updated Label
#### Scenario: Delete label
- **WHEN** `DeleteLabel` is called with a label ID
- **THEN** the client SHALL send `DELETE /labels/{id}`
### Requirement: Card label operations
The client SHALL provide methods for: add label to card (`POST /cards/{cardId}/card-labels`) and remove label from card (`DELETE /cards/{cardId}/card-labels/labelId:{labelId}`).
#### Scenario: Add label to card
- **WHEN** `AddCardLabel` is called with a card ID and label ID
- **THEN** the client SHALL send `POST /cards/{cardId}/card-labels` with the label ID
#### Scenario: Remove label from card
- **WHEN** `RemoveCardLabel` is called with a card ID and label ID
- **THEN** the client SHALL send `DELETE /cards/{cardId}/card-labels/labelId:{labelId}`
### Requirement: Card membership operations
The client SHALL provide methods for: assign user to card (`POST /cards/{cardId}/card-memberships`) and unassign user from card (`DELETE /cards/{cardId}/card-memberships/userId:{userId}`).
#### Scenario: Assign user to card
- **WHEN** `AddCardMember` is called with a card ID and user ID
- **THEN** the client SHALL send `POST /cards/{cardId}/card-memberships` with the user ID
#### Scenario: Unassign user from card
- **WHEN** `RemoveCardMember` is called with a card ID and user ID
- **THEN** the client SHALL send `DELETE /cards/{cardId}/card-memberships/userId:{userId}`
+88
View File
@@ -0,0 +1,88 @@
## ADDED Requirements
### Requirement: Move card between lists
The system SHALL provide a `card move` operation that updates a card's `listId` to move it to a different list. The operation SHALL accept an optional `position` to place the card at a specific position within the target list. The move operation SHALL be implemented as a `PATCH /cards/{id}` call with `listId` and optionally `position` fields. The CLI command SHALL be `pcli card move <id> --list <listId> [--position N]`.
#### Scenario: Move card to another list
- **WHEN** `pcli card move <id> --list <targetListId>` is executed
- **THEN** the system SHALL send `PATCH /cards/{id}` with `{"listId": "<targetListId>"}`
- **AND** the system SHALL output the updated card with its new listId
#### Scenario: Move card to specific position
- **WHEN** `pcli card move <id> --list <targetListId> --position 0` is executed
- **THEN** the system SHALL send `PATCH /cards/{id}` with `{"listId": "<targetListId>", "position": 0}`
- **AND** the card SHALL appear at the specified position in the target list
#### Scenario: Move card missing list flag
- **WHEN** `pcli card move <id>` is executed without `--list`
- **THEN** the system SHALL print an error indicating `--list` is required and exit with code 1
### Requirement: Duplicate card
The system SHALL provide a `card duplicate` operation that creates a copy of an existing card. The operation SHALL call `POST /cards/{id}/duplicate`. The CLI command SHALL be `pcli card duplicate <id> [--name <name>] [--position N]`. If `--name` is not provided, the API determines the name of the duplicate.
#### Scenario: Duplicate card with defaults
- **WHEN** `pcli card duplicate <id>` is executed
- **THEN** the system SHALL send `POST /cards/{id}/duplicate`
- **AND** the system SHALL output the newly created duplicate card
#### Scenario: Duplicate card with custom name
- **WHEN** `pcli card duplicate <id> --name "Copy of task"` is executed
- **THEN** the system SHALL send `POST /cards/{id}/duplicate` with `{"name": "Copy of task"}`
- **AND** the duplicate card SHALL have the specified name
### Requirement: Assign and unassign card members
The system SHALL provide `card assign` and `card unassign` operations to manage card memberships. `card assign` SHALL call `POST /cards/{cardId}/card-memberships` with the user ID. `card unassign` SHALL call `DELETE /cards/{cardId}/card-memberships/userId:{userId}`.
#### Scenario: Assign user to card
- **WHEN** `pcli card assign <cardId> --user <userId>` is executed
- **THEN** the system SHALL send `POST /cards/{cardId}/card-memberships` with `{"userId": "<userId>"}`
- **AND** the system SHALL output a success confirmation
#### Scenario: Unassign user from card
- **WHEN** `pcli card unassign <cardId> --user <userId>` is executed
- **THEN** the system SHALL send `DELETE /cards/{cardId}/card-memberships/userId:{userId}`
- **AND** the system SHALL output a success confirmation
#### Scenario: Assign missing user flag
- **WHEN** `pcli card assign <cardId>` is executed without `--user`
- **THEN** the system SHALL print an error indicating `--user` is required and exit with code 1
### Requirement: Add and remove card labels
The system SHALL provide `card add-label` and `card remove-label` operations to manage labels on cards. `card add-label` SHALL call `POST /cards/{cardId}/card-labels` with the label ID. `card remove-label` SHALL call `DELETE /cards/{cardId}/card-labels/labelId:{labelId}`.
#### Scenario: Add label to card
- **WHEN** `pcli card add-label <cardId> --label <labelId>` is executed
- **THEN** the system SHALL send `POST /cards/{cardId}/card-labels` with `{"labelId": "<labelId>"}`
- **AND** the system SHALL output a success confirmation
#### Scenario: Remove label from card
- **WHEN** `pcli card remove-label <cardId> --label <labelId>` is executed
- **THEN** the system SHALL send `DELETE /cards/{cardId}/card-labels/labelId:{labelId}`
- **AND** the system SHALL output a success confirmation
#### Scenario: Add label missing label flag
- **WHEN** `pcli card add-label <cardId>` is executed without `--label`
- **THEN** the system SHALL print an error indicating `--label` is required and exit with code 1
### Requirement: Enriched board-level card listing
The system SHALL provide a `card list --board <id>` operation that returns all cards across all lists in a board, with each card enriched with the `listName` field. The operation SHALL: (1) call `GET /boards/{id}` to retrieve the board and its included lists, (2) call `GET /lists/{listId}/cards` for each list to retrieve cards (with pagination support), (3) inject `listName` into each card based on the list it belongs to. The `--limit` flag SHALL apply to the total number of cards returned across all lists.
#### Scenario: List all cards on a board
- **WHEN** `pcli card list --board <id>` is executed
- **THEN** the system SHALL return all cards from all lists in the board
- **AND** each card SHALL include a `listName` field with the name of its containing list
- **AND** each card SHALL include a `listId` field
#### Scenario: Board card listing with limit
- **WHEN** `pcli card list --board <id> --limit 10` is executed
- **THEN** the system SHALL return at most 10 cards total across all lists
- **AND** each card SHALL include the `listName` field
#### Scenario: Board with no cards
- **WHEN** `pcli card list --board <id>` is executed on a board with empty lists
- **THEN** the system SHALL return an empty array
#### Scenario: Board with multiple lists
- **WHEN** a board has lists "To Do", "In Progress", and "Done" each containing cards
- **THEN** the returned cards SHALL have `listName` set to the respective list name
- **AND** cards from all lists SHALL be included in the result
+208
View File
@@ -0,0 +1,208 @@
## ADDED Requirements
### Requirement: Root command and global flags
The system SHALL provide a root command `pcli` that serves as the entry point. The root command SHALL register global flags: `--format` (string, default `json`, values `json` or `table`), `--url` (string, overrides `PLANKA_URL`), `--api-key` (string, overrides `PLANKA_API_KEY`), and `--log-level` (string, default `warn`, values `debug`, `info`, `warn`, `error`). The root command SHALL initialize the logger based on `--log-level` and configure it to write structured JSON to stderr. The root command SHALL validate that URL and API key are available (from flags or environment) before executing any subcommand.
#### Scenario: Display help
- **WHEN** `pcli` is run with no arguments or `--help`
- **THEN** the system SHALL display usage information listing all resource subcommands and global flags
#### Scenario: Invalid format flag
- **WHEN** `--format` is set to an unsupported value
- **THEN** the system SHALL print an error and exit with code 1
#### Scenario: Log level controls output
- **WHEN** `--log-level=debug` is set
- **THEN** DEBUG-level log entries SHALL appear on stderr
- **AND** stdout SHALL contain only the command's data output
#### Scenario: Missing API key
- **WHEN** neither `PLANKA_API_KEY` environment variable nor `--api-key` flag is provided
- **THEN** the system SHALL print an error indicating `PLANKA_API_KEY` must be set via `--api-key` flag or `PLANKA_API_KEY` environment variable
- **AND** the system SHALL exit with code 1
### Requirement: Project commands
The system SHALL provide a `project` command group with subcommands `list` and `get`. `pcli project list` SHALL call the client's ListProjects method and output the result. `pcli project get <id>` SHALL accept a project ID as a positional argument, call GetProject, and output the result.
#### Scenario: List projects
- **WHEN** `pcli project list` is executed
- **THEN** the system SHALL output all accessible projects
#### Scenario: Get project by ID
- **WHEN** `pcli project get <id>` is executed with a valid project ID
- **THEN** the system SHALL output the project details
#### Scenario: Get project missing ID
- **WHEN** `pcli project get` is executed without an ID argument
- **THEN** the system SHALL print an error indicating the ID is required and exit with code 1
### Requirement: Board commands
The system SHALL provide a `board` command group with subcommands `get` and `actions`. `pcli board get <id>` SHALL accept a board ID as a positional argument and output the board details. `pcli board actions <id>` SHALL accept a board ID and an optional `--limit` flag (int, default 0) and output the board's action history.
#### Scenario: Get board
- **WHEN** `pcli board get <id>` is executed
- **THEN** the system SHALL output the board details including its lists
#### Scenario: List board actions
- **WHEN** `pcli board actions <id>` is executed
- **THEN** the system SHALL output the board's action history
#### Scenario: List board actions with limit
- **WHEN** `pcli board actions <id> --limit 10` is executed
- **THEN** the system SHALL output at most 10 action entries
### Requirement: Card commands
The system SHALL provide a `card` command group with subcommands: `list`, `get`, `create`, `update`, `delete`, `duplicate`, `move`, `assign`, `unassign`, `add-label`, `remove-label`, `actions`.
`pcli card list` SHALL require either `--board <id>` or `--list <id>` flag and accept an optional `--limit` flag. `pcli card get <id>` SHALL accept a card ID as a positional argument. `pcli card create` SHALL require `--list <id>` and `--name <name>` flags and accept optional flags: `--description`, `--type`, `--position`, `--due-date`, `--due-completed`. `pcli card update <id>` SHALL accept a card ID and optional flags for each updatable field: `--name`, `--description`, `--type`, `--position`, `--due-date`, `--due-completed`. `pcli card delete <id>` SHALL accept a card ID. `pcli card duplicate <id>` SHALL accept a card ID and optional `--name` and `--position` flags. `pcli card move <id>` SHALL require `--list <id>` and accept optional `--position` flag. `pcli card assign <id>` SHALL require `--user <userId>`. `pcli card unassign <id>` SHALL require `--user <userId>`. `pcli card add-label <id>` SHALL require `--label <labelId>`. `pcli card remove-label <id>` SHALL require `--label <labelId>`. `pcli card actions <id>` SHALL accept an optional `--limit` flag.
#### Scenario: List cards by board
- **WHEN** `pcli card list --board <id>` is executed
- **THEN** the system SHALL output all cards across all lists in the board, each enriched with listName
#### Scenario: List cards by list
- **WHEN** `pcli card list --list <id>` is executed
- **THEN** the system SHALL output cards in the specified list
#### Scenario: List cards with limit
- **WHEN** `pcli card list --list <id> --limit 5` is executed
- **THEN** the system SHALL output at most 5 cards
#### Scenario: Card list missing board or list flag
- **WHEN** `pcli card list` is executed without `--board` or `--list`
- **THEN** the system SHALL print an error indicating one is required and exit with code 1
#### Scenario: Get card
- **WHEN** `pcli card get <id>` is executed
- **THEN** the system SHALL output the card details
#### Scenario: Create card
- **WHEN** `pcli card create --list <id> --name "Task name"` is executed
- **THEN** the system SHALL create the card and output the created card
#### Scenario: Create card missing required flags
- **WHEN** `pcli card create` is executed without `--list` or `--name`
- **THEN** the system SHALL print an error and exit with code 1
#### Scenario: Update card
- **WHEN** `pcli card update <id> --name "New name"` is executed
- **THEN** the system SHALL update the card and output the updated card
#### Scenario: Delete card
- **WHEN** `pcli card delete <id>` is executed
- **THEN** the system SHALL delete the card and output a success confirmation
#### Scenario: Duplicate card
- **WHEN** `pcli card duplicate <id>` is executed
- **THEN** the system SHALL duplicate the card and output the new card
#### Scenario: Move card
- **WHEN** `pcli card move <id> --list <listId>` is executed
- **THEN** the system SHALL update the card's listId (and optionally position) and output the updated card
#### Scenario: Assign user to card
- **WHEN** `pcli card assign <id> --user <userId>` is executed
- **THEN** the system SHALL add the user as a card member
#### Scenario: Unassign user from card
- **WHEN** `pcli card unassign <id> --user <userId>` is executed
- **THEN** the system SHALL remove the user from the card's members
#### Scenario: Add label to card
- **WHEN** `pcli card add-label <id> --label <labelId>` is executed
- **THEN** the system SHALL add the label to the card
#### Scenario: Remove label from card
- **WHEN** `pcli card remove-label <id> --label <labelId>` is executed
- **THEN** the system SHALL remove the label from the card
#### Scenario: List card actions
- **WHEN** `pcli card actions <id>` is executed
- **THEN** the system SHALL output the card's action history
### Requirement: Comment commands
The system SHALL provide a `comment` command group with subcommands: `list`, `create`, `update`, `delete`. `pcli comment list` SHALL require `--card <id>` and accept optional `--limit`. `pcli comment create` SHALL require `--card <id>` and `--text <text>`. `pcli comment update <id>` SHALL require `--text <text>`. `pcli comment delete <id>` SHALL accept a comment ID.
#### Scenario: List comments
- **WHEN** `pcli comment list --card <id>` is executed
- **THEN** the system SHALL output comments for the card
#### Scenario: Create comment
- **WHEN** `pcli comment create --card <id> --text "comment text"` is executed
- **THEN** the system SHALL create the comment and output the created comment
#### Scenario: Update comment
- **WHEN** `pcli comment update <id> --text "updated text"` is executed
- **THEN** the system SHALL update the comment and output the updated comment
#### Scenario: Delete comment
- **WHEN** `pcli comment delete <id>` is executed
- **THEN** the system SHALL delete the comment and output a success confirmation
### Requirement: Task list commands
The system SHALL provide a `task-list` command group with subcommands: `create`, `get`, `update`, `delete`. `pcli task-list create` SHALL require `--card <id>` and `--name <name>` and accept optional flags: `--position`, `--show-on-front`, `--hide-completed`. `pcli task-list get <id>` SHALL accept a task list ID. `pcli task-list update <id>` SHALL accept optional flags for each updatable field. `pcli task-list delete <id>` SHALL accept a task list ID.
#### Scenario: Create task list
- **WHEN** `pcli task-list create --card <id> --name "Checklist"` is executed
- **THEN** the system SHALL create the task list and output the created task list
#### Scenario: Get task list
- **WHEN** `pcli task-list get <id>` is executed
- **THEN** the system SHALL output the task list details including its tasks
#### Scenario: Update task list
- **WHEN** `pcli task-list update <id> --name "Renamed"` is executed
- **THEN** the system SHALL update the task list and output the updated task list
#### Scenario: Delete task list
- **WHEN** `pcli task-list delete <id>` is executed
- **THEN** the system SHALL delete the task list and output a success confirmation
### Requirement: Task commands
The system SHALL provide a `task` command group with subcommands: `create`, `update`, `delete`. `pcli task create` SHALL require `--task-list <id>` and `--name <name>` and accept optional flags: `--position`, `--completed`, `--assignee`, `--linked-card`. `pcli task update <id>` SHALL accept optional flags for each updatable field: `--name`, `--position`, `--completed`, `--assignee`, `--task-list`. `pcli task delete <id>` SHALL accept a task ID.
#### Scenario: Create task
- **WHEN** `pcli task create --task-list <id> --name "Do something"` is executed
- **THEN** the system SHALL create the task and output the created task
#### Scenario: Update task
- **WHEN** `pcli task update <id> --completed` is executed
- **THEN** the system SHALL mark the task as completed and output the updated task
#### Scenario: Move task to different list
- **WHEN** `pcli task update <id> --task-list <newListId>` is executed
- **THEN** the system SHALL move the task to the specified task list
#### Scenario: Delete task
- **WHEN** `pcli task delete <id>` is executed
- **THEN** the system SHALL delete the task and output a success confirmation
### Requirement: Status command
The system SHALL provide a top-level `status` command registered directly on the root command (not as a subcommand of any resource group). `pcli status` SHALL take no positional arguments. The command SHALL fetch all boards via `ListBoards`, then fetch each board's details via `GetBoard` sequentially, aggregate card counts per list, and output the result via the standard `output.Print` mechanism respecting the global `--format` flag.
#### Scenario: Run status command
- **WHEN** `pcli status` is executed
- **THEN** the system SHALL output a summary of all boards with their lists and card counts
#### Scenario: Status command respects format flag
- **WHEN** `pcli status --format table` is executed
- **THEN** the output SHALL be in table format
#### Scenario: Status command default format
- **WHEN** `pcli status` is executed without a `--format` flag
- **THEN** the output SHALL be in JSON envelope format
### Requirement: Label commands
The system SHALL provide a `label` command group with subcommands: `create`, `update`, `delete`. `pcli label create` SHALL require `--board <id>` and `--name <name>` and accept optional flags: `--color`, `--position`. `pcli label update <id>` SHALL accept optional flags: `--name`, `--color`, `--position`. `pcli label delete <id>` SHALL accept a label ID.
#### Scenario: Create label
- **WHEN** `pcli label create --board <id> --name "Bug" --color red` is executed
- **THEN** the system SHALL create the label and output the created label
#### Scenario: Update label
- **WHEN** `pcli label update <id> --name "Feature" --color green` is executed
- **THEN** the system SHALL update the label and output the updated label
#### Scenario: Delete label
- **WHEN** `pcli label delete <id>` is executed
- **THEN** the system SHALL delete the label and output a success confirmation
+66
View File
@@ -0,0 +1,66 @@
## ADDED Requirements
### Requirement: JSON output envelope
The system SHALL wrap all successful command output in a JSON envelope with the structure `{"data": <result>, "error": null}`. The system SHALL wrap all error output in a JSON envelope with the structure `{"data": null, "error": "<message>"}`. The envelope SHALL be written to stdout. The `data` field SHALL contain the direct result of the command (object or array). The `error` field SHALL be null on success and a string message on failure.
#### Scenario: Successful command output
- **WHEN** a command completes successfully in JSON format
- **THEN** stdout SHALL contain `{"data": <result>, "error": null}`
- **AND** the process SHALL exit with code 0
#### Scenario: Error command output
- **WHEN** a command fails in JSON format
- **THEN** stdout SHALL contain `{"data": null, "error": "<message>"}`
- **AND** the process SHALL exit with code 1
#### Scenario: Envelope structure is consistent
- **WHEN** any command is executed in JSON format
- **THEN** the output SHALL always contain exactly the keys `data` and `error` at the top level
### Requirement: Table format output
The system SHALL support a `--format=table` flag that outputs results as human-readable tabular text. Table output SHALL be written to stdout. Table output SHALL NOT use the JSON envelope. When table format is active and an error occurs, the error message SHALL be written to stderr (not stdout). Each resource type SHALL define its own column set for table rendering.
#### Scenario: Table output for a list of items
- **WHEN** a list command is executed with `--format=table`
- **THEN** stdout SHALL contain a header row followed by one row per item
- **AND** columns SHALL be aligned and separated by whitespace
#### Scenario: Table output for a single item
- **WHEN** a get command is executed with `--format=table`
- **THEN** stdout SHALL contain a key-value representation of the item
#### Scenario: Error in table format
- **WHEN** a command fails with `--format=table`
- **THEN** the error message SHALL be written to stderr
- **AND** the process SHALL exit with code 1
### Requirement: Format flag default
The system SHALL default to JSON format when no `--format` flag is provided. The `--format` flag SHALL accept values `json` and `table`. Any other value SHALL cause an error and exit with code 1.
#### Scenario: Default format is JSON
- **WHEN** a command is executed without `--format`
- **THEN** the output SHALL be in JSON envelope format
#### Scenario: Explicit JSON format
- **WHEN** a command is executed with `--format=json`
- **THEN** the output SHALL be in JSON envelope format
#### Scenario: Explicit table format
- **WHEN** a command is executed with `--format=table`
- **THEN** the output SHALL be in table format
#### Scenario: Invalid format value
- **WHEN** a command is executed with `--format=xml`
- **THEN** the system SHALL print an error and exit with code 1
### Requirement: Log output separation
All log output SHALL be written to stderr using structured JSON via `log/slog`. Log output SHALL never appear on stdout. This ensures that stdout contains only the command's data output (JSON envelope or table) and is safe to pipe or parse programmatically.
#### Scenario: Logs do not pollute stdout
- **WHEN** a command is executed with `--log-level=debug`
- **THEN** all log entries SHALL appear on stderr
- **AND** stdout SHALL contain only the command's data output
#### Scenario: Logs are structured JSON
- **WHEN** a log entry is emitted
- **THEN** it SHALL be a valid JSON object with at minimum `time`, `level`, and `msg` fields
+53
View File
@@ -0,0 +1,53 @@
## ADDED Requirements
### Requirement: Status command summary output
The system SHALL provide a top-level `pcli status` command that outputs a summary of all boards, their lists, and card counts. The summary SHALL include the total number of boards. For each board, the summary SHALL include the board name and a breakdown of each list within that board showing the list name, the number of open cards (where `isClosed` is false), and the number of closed cards (where `isClosed` is true). Empty lists SHALL be included in the output with 0 open and 0 closed cards.
#### Scenario: Status with multiple boards and lists
- **WHEN** `pcli status` is executed and there are boards with lists containing cards
- **THEN** the output SHALL include the total board count
- **AND** each board SHALL list all its lists with open and closed card counts
#### Scenario: Status with empty lists
- **WHEN** a board contains a list with no cards
- **THEN** that list SHALL appear in the output with 0 open cards and 0 closed cards
#### Scenario: Status with no boards
- **WHEN** `pcli status` is executed and there are no boards
- **THEN** the output SHALL indicate 0 boards
#### Scenario: Status with closed cards
- **WHEN** a list contains both open and closed cards
- **THEN** the open card count SHALL exclude closed cards
- **AND** the closed card count SHALL be shown separately
### Requirement: Status command JSON output
The system SHALL output the status summary in the standard JSON envelope format (`{"data": ..., "error": null}`) when `--format json` is used or no format flag is provided. The `data` field SHALL contain an object with `totalBoards` (integer) and `boards` (array). Each board object SHALL contain `id` (string), `name` (string), and `lists` (array). Each list object SHALL contain `id` (string), `name` (string), `openCards` (integer), and `closedCards` (integer).
#### Scenario: JSON output structure
- **WHEN** `pcli status` is executed with `--format json` or no format flag
- **THEN** the output SHALL be a JSON envelope with the status summary as the `data` field
#### Scenario: JSON output field types
- **WHEN** the JSON output is parsed
- **THEN** `totalBoards` SHALL be an integer
- **AND** each list's `openCards` and `closedCards` SHALL be integers
### Requirement: Status command table output
The system SHALL output the status summary in a human-readable table format when `--format table` is specified. The table output SHALL begin with a line showing the total number of boards (e.g., `3 boards`). For each board, the output SHALL display a board header line (e.g., `Board: Sprint Planning`) followed by a table with columns `LIST` and `CARDS`. The `CARDS` column SHALL display the open card count, and if there are closed cards, append ` (<n> closed)` (e.g., `12 (2 closed)`). If there are no closed cards, only the open count SHALL be displayed (e.g., `12`).
#### Scenario: Table output with closed cards
- **WHEN** `pcli status --format table` is executed and a list has 12 open and 2 closed cards
- **THEN** the CARDS column for that list SHALL display `12 (2 closed)`
#### Scenario: Table output with no closed cards
- **WHEN** `pcli status --format table` is executed and a list has 5 open and 0 closed cards
- **THEN** the CARDS column for that list SHALL display `5`
#### Scenario: Table output with empty list
- **WHEN** `pcli status --format table` is executed and a list has 0 open and 0 closed cards
- **THEN** the CARDS column for that list SHALL display `0`
#### Scenario: Table output board count line
- **WHEN** `pcli status --format table` is executed
- **THEN** the first line of output SHALL show the total board count (e.g., `3 boards`)