feat: CLI v2#43
Draft
khvn26 wants to merge 65 commits into
Draft
Conversation
Remove the TypeScript/oclif CLI entirely and start over in Go + cobra, per the installation & auth design (docs/design/001-installation-and-auth.md). The PoC implements interactive auth against the core OAuth 2.1 server (Flagsmith/flagsmith#8029): - `flagsmith login`: authorization-code + PKCE on a loopback listener (literal 127.0.0.1/[::1], RFC 8252), endpoints discovered via RFC 8414 metadata, explicit `management-api` scope, `--no-browser` for headless - credentials in the OS keychain, 0600-file fallback - `flagsmith auth status` / `auth token`: transparent refresh-token rotation (server grace period 120s) - `flagsmith logout`: server-side revocation + local removal Verified end-to-end against ghcr.io/flagsmith/flagsmith:pr-8029. beep boop
Tracks the scope rename in Flagsmith/flagsmith#8029. Verified e2e against the rebuilt pr-8029 image (metadata now advertises admin-api). beep boop
- oauth: full PKCE login against an in-process fake authorization server (asserts the exact authorization request and token-exchange contract, including verifier→challenge), state-mismatch rejection, access_denied, context cancellation, exchange failure, discovery errors, refresh rotation form, and revocation - store: keychain round-trip (go-keyring mock), forced file fallback with 0600 perms, corrupt-file and not-logged-in paths; make keychain removal in Delete best-effort so a broken keychain can't fail logout - cmd: drives login --no-browser → auth status → auth token → logout end-to-end in-process, with the test playing the browser beep boop
Implements the 02-authentication design (bar OIDC), red-green TDD: - FLAGSMITH_API_KEY resolved ahead of stored logins, classified by shape: dotted -> Master API key (Api-Key header), dotless -> bearer token; ser.* keys and legacy 40-hex authtokens rejected with targeted errors - flagsmith login --token / --token-stdin to store a Master API key (verified against /organisations/ before saving) - credential store keyed per instance API URL (keychain account = URL, file fallback becomes a URL-keyed map); logout --api targets one instance and only revokes OAuth sessions - auth status shows organisations and the active credential source; plaintext file fallback now warns visibly - auth token prints whichever Admin API credential is active Verified against staging (real browser PKCE login, refresh-token rotation, per-instance isolation) and against a local pr-8029 stack for the master-key paths with a real generated key. beep boop
`flagsmith auth login` / `flagsmith auth logout` now work identically to the top-level spellings (gh-style muscle memory). Commands are built by constructors and registered under both parents; the flow tests are parametrised over both shapes. beep boop
…y doc - `environment` now holds the client-side SDK key (env names have no server-side uniqueness constraint; keys are unique and public); `environmentKey` removed; schema anyOf(project, environment) so SDK-only configs are one field. Names come from a cosmetic name cache (os.UserCacheDir), never from the file. - Sources collapse to flag/env/default; schema fields map 1:1 to flags and FLAGSMITH_* env vars; config --json works credless/offline. - New 02-output-and-interactivity.md for cross-cutting conventions (TTY rules, --yes/--no-input/FLAGSMITH_NO_INPUT, --json, exit codes); auth and project-config docs renumbered to 03/04. beep boop
The plaintext file store is no longer a silent fallback. When the OS keychain is unavailable (headless Linux, containers, SSH), login fails closed before starting any flow — browser logins probe storage first so we never mint tokens we can't keep — and the error names both ways out: FLAGSMITH_API_KEY, or --insecure-storage to opt into the 0600 plaintext file. Refreshed sessions persist to whichever store they were loaded from (never migrating between stores), and auth status labels the opt-in store as "file (plaintext)". beep boop
Matches the flagsmith.json field name (apiUrl) and the --config-path convention from the project-config design. The alias is implemented via cobra's global flag-name normalization, so it works in any position and never appears in help. Also specs out-of-repo config discovery (cwd only) and keys-only -e/FLAGSMITH_ENVIRONMENT in 04. beep boop
…ONMENT (context) FLAGSMITH_ENVIRONMENT_KEY is the SDK credential — takes precedence and is the only home for server-side ser.* keys. FLAGSMITH_ENVIRONMENT is the default-environment context (client-side key, mirroring flagsmith.json). Also: 04 rewrite cleanups (env-key diff fix, typos, cache path, out-of-repo discovery, keys-only -e), matrix rows updated for the removed name-derivation path. beep boop
Nearest-file discovery walking up to the git toplevel (cwd only outside a repository), forward-compatible parsing (unknown fields warn, not fail), and rejection of server-side keys in the environment field with an error pointing at FLAGSMITH_ENVIRONMENT_KEY. A schema-drift guard test cross-checks the parser's field list against schema/flagsmith.json — and immediately caught $schema missing from the schema's properties, which with additionalProperties:false would make editors reject the $schema line that flagsmith init writes. Restored. beep boop
- Global context flags (-p/--project, --organisation, -e/--environment, --sdk-api-url, -c/--config-path) with cli > env > config > default precedence per value; sdkApiUrl follows a non-default apiUrl, else Edge; ser.* keys rejected in context with a pointer at FLAGSMITH_ENVIRONMENT_KEY - flagsmith config shows the resolved context with per-value sources; --json is the scripting interface; credless and offline, names are best-effort enrichment from the local cache - name cache at os.UserCacheDir()/flagsmith/cache.json keyed by instance; auth status seeds organisation names opportunistically - auth commands resolve their instance through the full chain, so a committed apiUrl finally works without --api-url beep boop
Implements the rest of 04-project-config: - flagsmith init: interactive flow (inline browser login when needed, org picker for multi-org users with the choice recorded, project picker with inline creation defaulting to the cwd name, environment picker writing the client-side key) and non-interactive flow (--project/--environment/--yes; missing input is a usage error). Values from an existing flagsmith.json act as prompt defaults, never as decisions; re-init shows a -/+ diff and confirms. The environments call doubles as the access check and seeds the name cache. $schema is pinned to the writing CLI's version tag. - Admin API client: Projects, CreateProject, Environments, with paginated-or-bare-array list decoding. - Exit codes per 02: usage errors (a prompt would have collected it) exit 2, everything else 1. --yes/--no-input aliases with FLAGSMITH_NO_INPUT; prompts require a real TTY. - Bare `flagsmith` nudges towards init when there is neither project context nor credentials. Verified live against staging (non-interactive init against a real project). Also fixes a test-harness bug where cobra's sticky --help local flag leaked across Execute calls. beep boop
- --json is a global flag with FLAGSMITH_JSON_OUTPUT, now covering auth status (identity, organisations, source, expiry) and auth token alongside config - browser rule enforced: without a TTY, login never opens a browser (implicit --no-browser); with --yes/--no-input it refuses outright, pointing at --token-stdin and FLAGSMITH_API_KEY instead of hanging CI for the login timeout - logout's revoke warning moves to stderr; stream discipline pinned by tests with split stdout/stderr capture - invalid promptable input (e.g. bad FLAGSMITH_PROJECT) is a usage error: exit 2, not 1 - fixes the FLASGMITH_JSON_OUTPUT typo in 02 beep boop
Replace the hand-rolled selector with huh: on a real terminal, pickers are full-terminal selects with arrow keys, j/k, and type-to-/-filter for long lists (a real win for the org picker); everywhere else huh's accessible mode gives numbered, line-based, screen-reader-friendly prompts that re-prompt on invalid input and terminate on EOF (never hang). Colors come from termenv and respect NO_COLOR. internal/prompt keeps its Select/Text/Confirm API and the stdinIsTTY/rawTerminal cmd seam, so the accessible path is exactly what the command tests drive. Accessible input is fed one byte at a time because huh spins up a fresh scanner per prompt and a buffered source would let one prompt swallow the next's input (regression-tested). Verified live against staging in tmux: arrow-key org selection, /-filtering the project list, and the ←/→ confirm on re-init. Docs transcripts in 04 updated to the real huh rendering. beep boop
Two bugs in how `flagsmith init` treated an existing file's organisation: - It was dropped entirely: organisationID started at 0 and was only set by the multi-org picker, so any invocation that skipped the picker (single-org user, or explicit --project) rewrote the file without its organisation, showing `- "organisation": N` in the diff. Seed it from the resolved context. - A config-file organisation suppressed the picker, so a multi-org user could never change it via re-init. Now only a flag/env organisation skips the picker (explicit decision); a config value is just the picker's pre-selected default, so re-init always re-offers the choice. Verified live against staging in tmux: re-init re-offers the org picker pre-selected to the current org. beep boop
A project with no environments (typically one just created inline) had nothing to pick from, so init wrote a config with no environment. Now, interactively, it prompts to create one (default name Development), creates it via the new api.CreateEnvironment, and records its key. Non-interactively an empty project is written without an environment rather than creating one silently. The test fake now distinguishes an accessible-but-empty project (200, empty list) from a no-access project (403), which the access check relies on. Verified live against staging: a freshly created project reports [] environments and init creates Development end-to-end. beep boop
New internal/output package owns the JSON-vs-human decision once: output.Render(w, data, opts, human) marshals the same data value that feeds the human view — so the two can't drift — and Table/Detail/Success give commands a consistent, NO_COLOR-aware, pipe-friendly vocabulary. - --jq <expr> (global, implies --json) filters JSON output through a jq expression via itchyny/gojq: raw for string results, compact JSON otherwise. Works on every command that outputs data. - Result model (02): stdout is the data result; ✓ confirmations, progress, warnings and prompts go to stderr. Migrated config, auth status (now a Detail view, single UsersMe call) and auth token onto Render; moved login/init/logout ✓ lines to stderr so mutations leave stdout empty (delete-style contract). - JSON mirrors the resource shape (bare object/array); config keeps its bespoke keyed shape. Verified live against staging: --jq filtering, migrated rendering, and the stdout/stderr split. beep boop
Answers the 02 gap where two init prompts (project name, environment name) had no flag equivalent and the overwrite-confirm exited 1 instead of 2. - Prompt primitives now take the flag that supplies the same value and self-guard: called without a TTY they return a usage error (exit 2) naming that flag. A prompt can no longer be written without linking a flag, and can no longer hang non-interactively — the contract is structural, not per-call-site discipline. confirmOrYes centralises the yes/no case (--yes answers it; no TTY without it is exit 2 naming --yes). - --create-project <name> / --create-environment <name> make creation flag-drivable, each mutually exclusive (exit 2) with its select counterpart; org resolution factored into resolveOrganisation so both the create and select paths share it. - Re-init now carries a config-file environment forward non-interactively (it was silently dropped, the same class of bug as the earlier org drop). Verified live against staging: non-interactive create of project + environment, mutual-exclusion and overwrite-without-yes both exit 2. beep boop
Move the result model out of 02 (it is CRUD-shaped, not general output) into a new 05-crud.md defining the shared shape every resource command follows: command layout, addressing, result model (human list shows a count; JSON is a bare array), mutations, errors, and pagination (list fetches all pages, --limit caps). Also fixes a typo in 02. beep boop
Scope flags to feature states (the SDK view); project-level feature definitions are a separate `features` resource, deferred. Resource definition plus `flags list` as the minimum viable command: reads via the SDK API with just an environment key (GET /api/v1/flags/), so it works right after `init` and makes the init nudge real. get and the enable/disable/set mutations (Admin API, v2-versioning branch) noted as later work. Also lands 05-crud.md (CRUD conventions) from the prior step. beep boop
Implements the minimum from 06-flags.md, making the `flagsmith init` next-step nudge real. `flags list` reads feature states from the SDK API (GET /api/v1/flags/, X-Environment-Key) using the environment key from context — FLAGSMITH_ENVIRONMENT_KEY (may be server-side) or the client-side key in flagsmith.json — so it works with no Admin API credentials at all. Human output is a NAME/ENABLED/VALUE table with a count; --json/--jq get the bare array as the API returns it (per 05's result model). Verified live against staging with only a committed environment key. beep boop
Colour the human tables after tabwriter has aligned them, not before: Table bolds the header line and Detail colours the label column once the plain text is laid out, so the ANSI bytes never count toward column width. Restores the bold header (and cyan detail labels) while keeping `flags list` and the detail views aligned; piped output stays plain. beep boop
So a flagsmith.json written by a branch build resolves its schema (main has no schema/flagsmith.json yet). Revert to main / a release tag before merge. beep boop
Point $schema at the feat/cli-v2 ref so it resolves like the files init now writes. Revert to main / a release tag before merge. beep boop
Adopt singular resource nouns for CRUD commands (gh/kubectl style): `flagsmith flag list`, not `flags`. Renames the command and its cobra identifiers, the init next-step nudge, tests, and the command examples in 05-crud.md / 06-flags.md. beep boop
Leftover from the rename to 04-project-config.md; accidentally re-added in the previous commit by `git add -A`. 03 is authentication; the project-config doc lives at 04. beep boop
Implements named references (04 §3) for the one live consumer: `flag list` now accepts an environment name (via -e, FLAGSMITH_ENVIRONMENT, or flagsmith.json) and resolves it to a client-side key. - New internal/cmd/resolve.go: a scope-chain resolver (environment -> project -> organisation) sharing one match-by-name + disambiguation helper, so projects/orgs slot in when they gain consumers. Detection keys off canonical type (int id vs opaque key), no shape sniffing. - Environment names resolve over the project-scoped Admin list, needing admin creds; a canonical key needs none, so the credless flag-list path holds (a name without creds is assumed a key, validated by the SDK). - Ambiguous names disambiguate per 05 §2 (TTY pick / exit 2). Names are matched only within the project's own environment list, never the flat instance name cache — env names are unique per project, not per instance, so a flat-cache match conflated projects (caught live: a name resolving to 6 environments across projects). The cache still backs display and confirms a known key. A project-scoped resolution cache can come later. Verified live against staging: a unique env name resolves and lists; a duplicated name exits 2. beep boop
Project and organisation references now accept a numeric ID or a name anywhere environment already did — the config file, the -p/--organisation flags, and FLAGSMITH_PROJECT/FLAGSMITH_ORGANISATION. Names resolve to their canonical ID lazily over the Admin API, matching the named-reference design (04 §3); the resolveProjectID/resolveOrganisationID name branches were previously unreachable. config.File.Project/Organisation become *config.Ref, parsing a JSON number, a numeric string, or a name, and marshalling back to the authored shape. The -p/--organisation flags and their env vars are now strings classified by parseRef (all-digit -> ID, else name). init resolves any name to its ID up front, keeping its ID-based flow and written output unchanged. The schema widens project/organisation to integer-or-string and fixes the environment/project descriptions. This drops the early "must be a positive integer" check: a non-numeric FLAGSMITH_PROJECT is now a project name that surfaces as "not found" only when a command needs it, keeping flagsmith config offline. beep boop
resolveCredential had no coordination around its load-refresh-save sequence. Concurrent callers with an expired OAuth session would each POST refresh_token independently — a thundering herd that also churned rotated refresh tokens, with the keychain entry decided by whichever Save landed last. It survived only on the server's 120s rotation grace, and only because the CLI is single-shot today; any Admin API fan-out would expose it. Memoise the resolved credential per instance behind a mutex: the first caller performs the refresh and single Save under the lock, later callers get the cached result. Only successful resolutions are cached, so a login performed mid-invocation is still picked up. The memo is cleared once per command run via a root PersistentPreRunE (keeps in-process reuse — tests, future embeddings — from leaking a stale credential). beep boop
Slice 1 of 06-flags: reads. flag list moves off the credless SDK
/api/v1/flags/ onto the Admin GET /api/v1/projects/{project}/features/
?environment={id}, gaining feature type and lifecycle stage columns; its
JSON now mirrors the features item shape (raw items preserved via a custom
Feature marshaller). Adds flag get <feature>, a detail view resolving the
exact feature name client-side (the server's search is a contains match).
Both commands are Admin-only now: they need a credential, a resolved
project, and an environment. resolveEnvironmentKey (credless, key-only) is
replaced by resolveEnvironment, which lists the project's environments and
returns the full record — flag commands need the numeric id for the
features query (and later the key for update-flag mutations). The SDK
api.Flags path is retired.
beep boop
Slice 2 of 06-flags: environment-default mutations. `flag update <feature>` toggles state (--enable/--disable) and sets the value (--value, with --type string|integer|boolean; otherwise inferred: true/false → boolean, all-digit → integer, else string). update-flag-v2 requires the whole environment default on every call, so the command reads the current state first and resends the unchanged half. Mutations confirm (or --yes; exit 2 without a TTY), emit ✓ lines per changed field, then reprint the flag. A workflow-gated environment (change requests enabled) surfaces as a clear message rather than a bare 403. `flag create` is a permanent exit-2 nudge toward `feature create`, since flags exist per environment. Still to come: segment overrides (--segment) and identity overrides (--identifier), plus flag delete. beep boop
flag create only intercepts the invocation to redirect to `feature create`; registering it visibly listed a command that does nothing but error. Mark it hidden — the nudge still fires, but `flagsmith flag --help` no longer advertises it. beep boop
Slice 3 of 06-flags: segment overrides. - `flag get <feature> --segment <id>` reads segment_feature_state from the features endpoint (environment + segment params) and shows the override, erroring when the feature has no override for that segment. - `flag update <feature> --segment <id>` sets the override via update-flag-v2: the environment default is always carried forward unchanged, and a fresh override with no --value inherits the environment default value. - `flag delete <feature> --segment <id>` removes an override via delete-segment-override; a bare `flag delete` exits 2 asking for --segment, and a missing override surfaces the endpoint's 404 as a clear message. Identity overrides (--identifier) are deferred. beep boop
Address flag list feedback (applied consistently to flag get and segment detail where the same fields appear): - feature type shown lower-case (standard/multivariate, not STANDARD) - the ENABLED true/false column is now STATE on/off - long values are truncated in the table (values can be huge); the detail views still show the full value - a null identity-override count (Edge/Dynamo projects) shows 0, not - beep boop
Values are often multi-line JSON blobs; truncating by rune count left the embedded newlines in place, so they spilled across rows and broke the table's column alignment. Collapse all whitespace to single spaces before truncating, so each value stays on its own row. beep boop
The raw features-endpoint item made `flag get --json` awkward: 20-odd fields with the state buried in a nested environment_feature_state, next to dashboard-only noise (owners, uuid, multivariate_options). Project it into a curated, scriptable shape instead — state hoisted to the top (enabled, value) with the metadata the human view shows (segment/identity overrides, code references, lifecycle stage) — used for both get and list, so `jq '.enabled'` just works and human/JSON output stay in lockstep. Segment overrides get their own curated shape. Retire the raw-passthrough machinery on api.Feature. 06 updated to match. beep boop
`flagsmith api` (the curl-like escape hatch) sequences before the flag resource: it is the more foundational capability. Add it as 06-api.md and renumber the flags design doc to 07-flags.md; update the README index. beep boop
Implements 06-api. `flagsmith api <path>` calls any endpoint with the resolved credential applied automatically: Admin by default (OAuth/master key), or the SDK API with X-Environment-Key under --sdk. The path is appended to the resolved base URL verbatim (nothing auto-prefixed), so every API version stays reachable and self-hosted works unchanged. Request: -X method (POST inferred when a body/field is present), -F typed and -f raw fields (JSON body, or query params on GET), --input for a raw body (file or -), -H headers. Response: body verbatim to stdout so the global --jq composes; -i to include status and headers; non-2xx goes to stderr with a non-zero exit. No confirmation on any method, DELETE included. Pagination is deferred — raw single requests only for a first version. beep boop
Slice 4 of 07-flags: identity overrides across get/update/delete via
--identifier. The CLI fetches the project's use_edge_identities and
branches on it — the authoritative signal the dashboard uses, so
self-hosted Edge and mid-migration projects are handled correctly (no
hardcoded host list).
- Core (Postgres): resolve identifier → identity id (exact ?q="..."),
read/create/update/delete the identity feature-state; a missing identity
is created on update.
- Edge (DynamoDB): set/delete via the identifier endpoint (create-or-update
in one PUT, note the double-`environments` no-trailing-slash URL); reads
resolve the identity uuid first since that endpoint has no GET.
Identity endpoints take a native-typed feature_state_value (not the
update-flag-v2 {type,value} form), so --value is converted to a native
scalar. Semantics mirror --segment: current state read first, a new
override inherits the environment default value, --segment/--identifier are
mutually exclusive. Gating is a non-issue — identity overrides bypass the
change-request workflow gate.
beep boop
cobra's default advertised two usage modes for a runnable command with subcommands — "flagsmith [flags]" and "flagsmith [command]" — which reads oddly for a root whose bare run just prints help. Rewrite the Usage block so each command shows one context-appropriate line: "<path> [command] [flags]" for groups, or the command's own use line for leaves. Done as a string replace on cobra's own default template so it survives version bumps (no-op if the block changes, guarded by a test). beep boop
08-segments.md: `flagsmith segment` CRUD. The rule tree is authored as a single SegmentRule object matching Flagsmith's evaluation-context schema (referenced directly — no vendored copy); the CLI maps it to the API's rules array, serialises IN arrays to a JSON-array string, enforces the two-level depth cap, and stamps a $schema pointer onto get output. 07-flags gains a `flag list --segment` section (list a segment's overrides). beep boop
Adds --segment to `flag list`: fetches features with the segment param, keeps only those with an override for it, and renders the override's state (NAME/TYPE/STATE/VALUE, no lifecycle) — human and JSON (the segment-override shape). Makes the `flag get`/segment-doc nudge real. beep boop
Implements 08-segments. `flagsmith segment` list/get/create/update/delete, project-scoped, referenced by name or id. The rule tree is authored as a single SegmentRule object (the evaluation-context shape); the CLI maps it to the API's rules array, maps IN values between an array (authoring/output) and the JSON-array string the API stores, enforces the two-level depth cap, and stamps the upstream $schema pointer onto emitted rules. get renders an indented ALL/ANY/NONE tree (human) and the curated segment (JSON); update reads-modifies-writes so a description-only change keeps the rules; --feature resolves a feature name to its id (no environment needed). The list drops the doc's OVERRIDES column — it is not in the list response and would need an N+1 associated-features call per segment; CONDITIONS (a recursive count) stands in. 08-segments and the README are updated to match. beep boop
`flagsmith feature` CRUD for the project-level definition (name, type, default value, variants). Standard fields are ordinary CRUD, but two things earn a dedicated doc: create/update asymmetry (name, default value, and default-enabled freeze after create) and multivariate variants. Variants are authored inline on create (--variants) but managed granularly afterwards via a `feature variant` subcommand — never a bulk replace, because a variant's id anchors per-environment/segment/identity weight overrides that a replace would cascade away. list gains --include-archived. beep boop
Slice A of 09-features: reads. `feature list` (project-scoped, name/id/type/ value/description; --include-archived, hidden by default via is_archived=false) and `feature get <feature>` (detail + variants table, curated JSON). Adds the project-level feature fields (initial_value, default_enabled, is_archived, multivariate_options) to api.Feature, plus ProjectFeatures/GetFeature and the MultivariateOption type with typed-value decode. The project-scoped context helper is shared with segments (renamed projectScopedContext). beep boop
Slice B of 09-features. `feature create <name>` (--value seed, --enabled, --description, inline --variants typed by JSON value); `feature update <feature>` (mutable fields only — --description, --archive/--unarchive, which are mutually exclusive; nothing-to-update errors); `feature delete <feature>` (confirmed). Adds FeatureWrite + CreateFeature/UpdateFeature (PATCH, partial)/DeleteFeature to the client. beep boop
Slice C of 09-features. `feature variant list|add|update|delete <feature>` manages multivariate options via the mv-options sub-resource, by id or key. add/update type the value from --value (inferred, --type override); update patches only the fields given, so a variant's id — and the per-environment weight overrides anchored to it — survive (never a bulk replace). default_percentage_allocation becomes a pointer so an update sends only the weight when that's all that changed. Adds CreateMVOption/UpdateMVOption/ DeleteMVOption. beep boop
Feature default values can be large (JSON blobs), same as flag values. Run the VALUE column through the shared truncateValue (flatten whitespace, cap at 40 with an ellipsis), matching flag list. beep boop
Rename the feature's value throughout to make it clearly the default seed: list column DEFAULT VALUE, get detail label "Default value", JSON key default_value (variant values keep `value`). `feature create --value` gains a hidden `--default-value` alias. 09-features updated. beep boop
Two write paths omitted a field the Admin API requires in the body,
not just the URL:
- segment create/update returned 400 {"project":["This field is
required."]} — the serializer requires `project` in the body.
- feature variant update returned 500 (KeyError: 'feature' at
multivariate/serializers.py:83) — validate() reads attrs["feature"]
even on a partial PATCH, so an update omitting it is unhandled.
Set the URL-derived id into the body in CreateSegment, UpdateSegment,
and UpdateMVOption. Tests assert each field reaches the wire.
beep boop
Regenerate every example in the implemented-command docs (feature, segment, flag) from real CLI runs against a seeded demo project, so the column widths, ID formats, JSON shapes, and error messages match what the tool actually emits. Leaves 10-projects-organisations.md as-is (those commands are not implemented yet). beep boop
`flagsmith project` and `flagsmith organisation` are flat CRUD (05), so a short doc suffices — with callouts for the wrinkles: gated organisation create (superuser setting / SAML), project create requiring an organisation that is then immutable, read-only/plan-gated project fields, and delete needing object admin. beep boop
Slice A of 10. `flagsmith organisation` (alias `org`) list/get/create/update/ delete, referenced by name or id. --json mirrors the API's full field set via a raw-preserving Organisation (rawItem). Adds GetOrganisation/CreateOrganisation /UpdateOrganisation/DeleteOrganisation; create/update take a flat field body (--force-2fa, --webhook-email, --name on update). beep boop
Slice B of 10. `flagsmith project` list/get/create/update/delete, referenced by name or id. list shows the organisation name (--organisation scopes it, else all accessible); --json mirrors the API's full field set via a raw-preserving Project. create uses the resolved organisation (--organisation context) and it's immutable thereafter; update patches name/settings. CreateProject moves to a flat-body signature (Projects gains an all-orgs mode when organisationID is 0); init and the api test updated to match. beep boop
Now that project/organisation CRUD is implemented, replace the design draft's illustrative output with format-accurate examples: real column spacing, "Name (id)" organisation labels in project list, the detail block create/update print after the confirmation, and the exact count lines. Organisation data stays curated (Acme/Beta) since the live list is the account's real orgs; mutation examples are illustrative as those operate on real billing entities. beep boop
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.
Contributes to #42.
To try it, make sure you've got Go 1.26+ installed and: