Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3329f60
Fizzy CLI foundation
jeremy Jan 13, 2026
7339f65
Config command for settings management
jeremy Jan 13, 2026
0810b78
Query commands for boards, cards, and users
jeremy Jan 13, 2026
b68e37d
Card action commands
jeremy Jan 13, 2026
56bf10b
Name resolution for fuzzy matching
jeremy Jan 13, 2026
36d0932
Fix OAuth callback SIGPIPE and public client token exchange
jeremy Jan 13, 2026
6acdc40
--in alias for --board flag
jeremy Jan 13, 2026
5015069
README and tab completion
jeremy Jan 13, 2026
35d9e8c
Multi-origin credential storage and install script
jeremy Jan 13, 2026
78d1751
CSP validation for OAuth redirect_uri
jeremy Jan 13, 2026
9336d76
Fix API response handling for RESTful patterns
jeremy Jan 13, 2026
7770e22
Pagination support for cards command
jeremy Jan 13, 2026
8dbd104
Fix installer symlink path
jeremy Jan 13, 2026
f52d363
Fix name resolution: tag .title field, pagination, regex safety
jeremy Jan 13, 2026
1eed7b4
Fix card create to chain follow-up actions
jeremy Jan 13, 2026
9f350dc
Store account_slug per-origin in credentials
jeremy Jan 13, 2026
76e67ac
Add --page flag to query commands
jeremy Jan 13, 2026
41609ff
Add pagination fallback + remove Python dependency
jeremy Jan 13, 2026
ee48859
Fix completions and add postponing_soon status
jeremy Jan 13, 2026
d0fd940
Update README for global flags and local dev default
jeremy Jan 13, 2026
704463d
Add --all flag for auto-pagination to list commands
jeremy Jan 13, 2026
e878b11
Add card update command
jeremy Jan 13, 2026
c1c4ba0
Add comment edit/delete as subcommands
jeremy Jan 13, 2026
2cc67ce
Document env var auth path in help and status
jeremy Jan 13, 2026
4f39351
Add card delete command
jeremy Jan 13, 2026
4f0063b
Add card image delete command
jeremy Jan 13, 2026
0a5655c
Fix API request/response handling
jeremy Jan 13, 2026
d18762f
Add steps and reactions parity
jeremy Jan 13, 2026
b1f4a3d
Add identity, users, and image upload support
jeremy Jan 14, 2026
6b33aac
Add JSON responders for webhooks controller
jeremy Jan 14, 2026
1facba5
Add JSON responders for pins, involvements, publications
jeremy Jan 14, 2026
17e894c
Add JSON responders for account, board, column, and role endpoints
jeremy Jan 14, 2026
33eb7fd
Update API docs for fidelity with controllers
jeremy Jan 14, 2026
da8ba81
Add CLI parity matrix
jeremy Jan 14, 2026
72a1dce
Add webhook CLI commands
jeremy Jan 14, 2026
972b87d
Add account CLI commands
jeremy Jan 14, 2026
31966fb
Add self-update command and version tracking
jeremy Jan 14, 2026
c2efbe9
Add board publish, unpublish, and entropy commands
jeremy Jan 14, 2026
81e426a
Add user role command
jeremy Jan 14, 2026
1f1d6ce
Add card publish and move commands
jeremy Jan 14, 2026
6316a94
Update help and API client for new commands
jeremy Jan 14, 2026
fcb117f
Update CLI plan with complete command reference
jeremy Jan 14, 2026
0595776
Fix self-update: EXIT_API_ERROR → EXIT_API, add safety guards
jeremy Jan 14, 2026
7f34e6d
Switch to commit-hash versioning and add uninstall command
jeremy Jan 14, 2026
2dff42e
Harden install and self-update against edge cases
jeremy Jan 14, 2026
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
5 changes: 4 additions & 1 deletion app/controllers/account/entropies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ class Account::EntropiesController < ApplicationController

def update
Current.account.entropy.update!(entropy_params)
redirect_to account_settings_path, notice: "Account updated"
respond_to do |format|
format.html { redirect_to account_settings_path, notice: "Account updated" }
format.json { head :no_content }
end
end

private
Expand Down
18 changes: 16 additions & 2 deletions app/controllers/account/exports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,25 @@ class Account::ExportsController < ApplicationController
CURRENT_EXPORT_LIMIT = 10

def show
respond_to do |format|
format.html
format.json do
if @export
render json: @export.as_json(only: %i[id status created_at])
else
head :not_found
end
end
end
end

def create
Current.account.exports.create!(user: Current.user).build_later
redirect_to account_settings_path, notice: "Export started. You'll receive an email when it's ready."
export = Current.account.exports.create!(user: Current.user)
export.build_later
respond_to do |format|
format.html { redirect_to account_settings_path, notice: "Export started. You'll receive an email when it's ready." }
format.json { render json: export.as_json(only: %i[id status created_at]), status: :accepted }
end
end

private
Expand Down
19 changes: 16 additions & 3 deletions app/controllers/account/join_codes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,35 @@ class Account::JoinCodesController < ApplicationController
before_action :ensure_admin, only: %i[ update destroy ]

def show
respond_to do |format|
format.html
format.json { render json: @join_code.as_json(only: %i[code usage_limit usage_count]) }
end
end

def edit
end

def update
if @join_code.update(join_code_params)
redirect_to account_join_code_path
respond_to do |format|
format.html { redirect_to account_join_code_path }
format.json { head :no_content }
end
else
render :edit, status: :unprocessable_entity
respond_to do |format|
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: { errors: @join_code.errors }, status: :unprocessable_entity }
end
end
end

def destroy
@join_code.reset
redirect_to account_join_code_path
respond_to do |format|
format.html { redirect_to account_join_code_path }
format.json { render json: @join_code.as_json(only: %i[code usage_limit usage_count]) }
end
end

private
Expand Down
9 changes: 8 additions & 1 deletion app/controllers/account/settings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ class Account::SettingsController < ApplicationController

def show
@users = @account.users.active.alphabetically.includes(:identity)
respond_to do |format|
format.html
format.json { render json: @account.as_json(only: %i[id name]) }
end
end

def update
@account.update!(account_params)
redirect_to account_settings_path
respond_to do |format|
format.html { redirect_to account_settings_path }
format.json { head :no_content }
end
end

private
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/boards/entropies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ class Boards::EntropiesController < ApplicationController

def update
@board.update!(entropy_params)
respond_to do |format|
format.html
format.turbo_stream
format.json { head :no_content }
end
end

private
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/boards/involvements_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ class Boards::InvolvementsController < ApplicationController

def update
@board.access_for(Current.user).update!(involvement: params[:involvement])
respond_to do |format|
format.html
format.turbo_stream
format.json { head :no_content }
end
end
end
10 changes: 10 additions & 0 deletions app/controllers/boards/publications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ class Boards::PublicationsController < ApplicationController

def create
@board.publish
respond_to do |format|
format.html
format.turbo_stream
format.json { render json: { key: @board.publication.key, url: published_board_url(@board) } }
end
end

def destroy
@board.unpublish
@board.reload
respond_to do |format|
format.html
format.turbo_stream
format.json { head :no_content }
end
end
end
18 changes: 14 additions & 4 deletions app/controllers/cards/pins_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@ def show
def create
@pin = @card.pin_by Current.user

broadcast_add_pin_to_tray
render_pin_button_replacement
respond_to do |format|
format.html do
broadcast_add_pin_to_tray
render_pin_button_replacement
end
format.json { head :no_content }
end
end

def destroy
@pin = @card.unpin_by Current.user

broadcast_remove_pin_from_tray
render_pin_button_replacement
respond_to do |format|
format.html do
broadcast_remove_pin_from_tray
render_pin_button_replacement
end
format.json { head :no_content }
end
end

private
Expand Down
15 changes: 10 additions & 5 deletions app/controllers/cards/publishes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ class Cards::PublishesController < ApplicationController
def create
@card.publish

if add_another_param?
card = @board.cards.create!(status: :drafted)
redirect_to card_draft_path(card), notice: "Card added"
else
redirect_to @card.board
respond_to do |format|
format.html do
if add_another_param?
card = @board.cards.create!(status: :drafted)
redirect_to card_draft_path(card), notice: "Card added"
else
redirect_to @card.board
end
end
format.json { head :no_content }
end
end

Expand Down
5 changes: 5 additions & 0 deletions app/controllers/columns/left_positions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@ class Columns::LeftPositionsController < ApplicationController
def create
@left_column = @column.left_column
@column.move_left
respond_to do |format|
format.html
format.turbo_stream
format.json { head :no_content }
end
end
end
5 changes: 5 additions & 0 deletions app/controllers/columns/right_positions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@ class Columns::RightPositionsController < ApplicationController
def create
@right_column = @column.right_column
@column.move_right
respond_to do |format|
format.html
format.turbo_stream
format.json { head :no_content }
end
end
end
17 changes: 17 additions & 0 deletions app/controllers/oauth/authorizations_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
class Oauth::AuthorizationsController < Oauth::BaseController
# Allow form submission to the client's redirect_uri for OAuth callbacks
# Only widen CSP if the client allows this redirect_uri (validated in before_action)
content_security_policy only: :new do |policy|
if (redirect_uri = params[:redirect_uri]).present? && (client_id = params[:client_id]).present?
client = Oauth::Client.find_by(client_id: client_id)
if client&.allows_redirect?(redirect_uri)
begin
uri = URI.parse(redirect_uri)
origin = "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port}"
policy.form_action :self, origin
rescue URI::InvalidURIError
# Invalid URI - don't widen CSP
end
end
end
end

before_action :save_oauth_return_url
before_action :require_authentication

Expand Down
5 changes: 4 additions & 1 deletion app/controllers/users/roles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ class Users::RolesController < ApplicationController

def update
@user.update!(role_params)
redirect_to account_settings_path
respond_to do |format|
format.html { redirect_to account_settings_path }
format.json { head :no_content }
end
end

private
Expand Down
17 changes: 13 additions & 4 deletions app/controllers/webhooks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ def new
end

def create
webhook = @board.webhooks.create!(webhook_params)
redirect_to webhook
@webhook = @board.webhooks.create!(webhook_params)
respond_to do |format|
format.html { redirect_to @webhook }
format.json
end
end

def edit
end

def update
@webhook.update!(webhook_params.except(:url))
redirect_to @webhook
respond_to do |format|
format.html { redirect_to @webhook }
format.json { head :no_content }
end
end

def destroy
@webhook.destroy!
redirect_to board_webhooks_path
respond_to do |format|
format.html { redirect_to board_webhooks_path }
format.json { head :no_content }
end
end

private
Expand Down
2 changes: 2 additions & 0 deletions app/views/webhooks/create.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
json.(@webhook, :id, :name, :url, :subscribed_actions)
json.created_at @webhook.created_at.utc
4 changes: 4 additions & 0 deletions app/views/webhooks/index.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
json.array! @webhooks do |webhook|
json.(webhook, :id, :name, :url, :subscribed_actions)
json.created_at webhook.created_at.utc
end
3 changes: 3 additions & 0 deletions app/views/webhooks/show.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
json.(@webhook, :id, :name, :url, :subscribed_actions)
json.created_at @webhook.created_at.utc
json.updated_at @webhook.updated_at.utc
43 changes: 43 additions & 0 deletions cli/PARITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# API ↔ CLI Parity Matrix

Status: ✅ full | ⚠️ partial | — not covered

| Area | API Docs | CLI Commands | Notes |
|------|----------|--------------|-------|
| Auth | ✅ PAT + magic link | ✅ PAT (`FIZZY_TOKEN`) | OAuth undocumented |
| Identity | ✅ `GET /my/identity` | ✅ `fizzy identity` | |
| Boards | ✅ list/show/create/update/delete | ✅ `boards`, `board show/create/update/delete` | |
| Board publish | ✅ publish/unpublish | ✅ `board publish/unpublish` | Uses publish response URL |
| Board entropy | ✅ | ✅ `board entropy` | |
| Columns | ✅ list/show/create/update/delete | ✅ `columns`, `column show/create/update/delete` | |
| Column reorder | ✅ left/right | ✅ `column left/right` | Shallow routes (no board in path) |
| Cards list/show | ✅ filters + show | ✅ `cards`, `show` | Search via `terms[]` filter |
| Card create/update/delete | ✅ | ✅ `card`, `card update`, `card delete` | |
| Card image | ✅ delete | ✅ `card image delete` | |
| Card lifecycle | ✅ close/reopen/postpone | ✅ `close`, `reopen`, `postpone` | |
| Card triage | ✅ triage/untriage | ✅ `triage`, `untriage` | |
| Card tagging | ✅ | ✅ `tag`, `untag` | |
| Card assignment | ✅ | ✅ `assign`, `unassign` | |
| Card watch | ✅ | ✅ `watch`, `unwatch` | |
| Card gold | ✅ | ✅ `gild`, `ungild` | |
| Card publish | ✅ | ✅ `card publish` | |
| Card move | ✅ | ✅ `card move --to` | Top-level `board_id` |
| Comments | ✅ list/show/create/update/delete | ✅ `comments`, `comment`, `comment edit/delete` | Update returns 204 |
| Reactions | ✅ list/create/delete | ✅ `reactions`, `react`, `react delete` | |
| Steps | ✅ show/create/update/delete | ✅ `step show/create/update/delete` | |
| Tags | ✅ list | ✅ `tags` | |
| Users | ✅ list/show/update/delete/role | ✅ `people`, `user show/update/delete/role` | |
| Notifications | ✅ list/read/unread/bulk | ✅ `notifications`, `read/unread/read-all` | |
| Account | ✅ show/update/entropy/join-code/export | ✅ `account show/update/entropy/join-code/export` | Export accepts 202 |
| Webhooks | ✅ list/show/create/update/delete | ✅ `webhooks`, `webhook create/show/update/delete` | Actions match `PERMITTED_ACTIONS` |

## Intentional Omissions

These API endpoints exist but are intentionally not documented or exposed in the CLI:

| Endpoint | Reason |
|----------|--------|
| `POST/DELETE /cards/:card_id/pin` | Internal UI feature |
| `PUT /boards/:board_id/involvement` | Internal UI feature |

These are Tier 3 endpoints—functional but not part of the public API contract.
Loading