Skip to content

feat: add target flags to sync prompts and read sync flags only from the command line - #938

Open
ctawiah wants to merge 1 commit into
ctawiah/compact-sync-output-1e8afrom
ctawiah/sync-target-flags-1e8a
Open

ctawiah wants to merge 1 commit into
ctawiah/compact-sync-output-1e8afrom
ctawiah/sync-target-flags-1e8a

Conversation

@ctawiah

@ctawiah ctawiah commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Context

ldcli sync prompts keeps local prompt files in step with the variations of LaunchDarkly AI Configs. Its commands have two problems:

  • A user can name variations only with positional project-key/config-key/variation-key selectors. The project, AI Config, and variation questions have no flag, so a script cannot skip them.
  • Every sync flag also reads an LD_<FLAG> environment variable or the ldcli configuration file. For example, LD_CONFLICT=bogus ldcli sync prompts --dry-run fails with invalid conflict resolution "bogus". New flags read these sources too. Then LD_YES, or LD_PROJECT, which ldcli config --set project writes, can change what a repository sync plans or applies.

What changes

  • New target flags: --project, --config-key, and --key. --key is repeatable.
  • On sync prompts and watch, the flags limit the plan. Local files and sync.lock rows outside the filter drop out before any read from LaunchDarkly. sync.lock stores the state that a working copy last agreed on with LaunchDarkly.
  • On add and detach, the flags answer the questions. Without a terminal, the error names the missing flags, for example --config-key and --key are required with --no-input.
  • On attach and link, only --project exists. It skips the project question, and it fails if it disagrees with --to.
  • Positional selectors, --resolve, and --to still work. Selectors together with target flags fail.
  • Sync flags now come only from the command line. Global flags, such as the access token, still read the environment and the configuration file.
  • Each targeting error stops the command before any request. A --config-key with no synced variations gets its own error.
  • If the repository has no .launchdarkly/ directory, sync prompts --project demo starts the first-run add in that project.
  • On link, a 404 from the first read in the project becomes project "demo" was not found in LaunchDarkly, or the access token cannot read it.

The help examples now use the flags:

# Add variations by key without prompting
ldcli sync prompts add --project production --config-key support --key default --key concise --no-input

# Sync one config, and prefer local changes when conflicts occur
ldcli sync prompts --project production --config-key support --yes --conflict=local

A key that is not synced fails before any request:

variation production/support/serch is not synced in this repository. To start syncing it, run ldcli sync prompts add --project production --config-key support --key serch.

Design notes

  • validators.Validate() copies environment and configuration values into every flag and marks the flag as set. validatedArgs now records which sync flags the command line set before that call. After the call, it resets every other sync flag to its default.
  • I chose the reset over reading each flag through a separate record, because the reset keeps every existing flag read unchanged.
  • Global flags are persistent flags of the root command, so the reset does not touch them.
  • BaselineStore.Load reads the remote manifest only for the projects that the caller passes. Before, it read every project in sync.lock, and a failed read there made a filtered sync fail. The remote manifest is a shared copy of the sync state in LaunchDarkly, one for each project.
  • Save sends only the entries that changed, so sync never writes a project that Load did not read.
  • The filter lives on SyncAction, AddAction, and DetachAction. AttachAction and LinkAction carry only a project key, so the sync after attach or link stays a full sync.
  • The --key help on add and detach reads Repeat for more variations, because Repeat to add more variations did not fit detach.

Review focus

  • Does the reset in validatedArgs cover every sync flag and leave every global flag alone?
  • Does a filtered sync read and write nothing for projects outside the filter?
  • Do all targeting errors come before the first request, and are their messages clear?
  • Is it right that --project on attach and link does not limit the sync that runs after them?

Verification

  • go test ./...
  • golangci-lint run (v2.9.0)
  • TestValidatedArgsKeepsSyncFlagsFromTheCommandLineOnly fails without the reset in validatedArgs.
  • TestPromptIgnoresSyncFlagsFromTheEnvironment sets LD_YES, LD_CONFLICT, LD_PROJECT, LD_KEY, and LD_NO_INPUT, and none of them has an effect. TestPromptReadsTheAccessTokenFromTheEnvironment shows that LD_ACCESS_TOKEN still works.
  • TestPromptFilterSyncsOnlyTheNamedVariation sends no request for another project and keeps its sync.lock row.
  • TestPromptTargetFlagErrorsComeBeforeAnyRequest makes sure that each targeting error sends no request.
  • I built ldcli and ran it against a local stand-in for the LaunchDarkly API. The runs covered the filters, the errors, add and detach with each combination of flags, the --to mismatch, and LD_* variables. The request log showed no request for an error case or for a project outside the filter.

Related changes

  1. feat: show a compact sync plan and add a --diff flag #937: show a compact sync plan and add a --diff flag
  2. This PR: add target flags, and read sync flags only from the command line
  3. feat: plan standalone tool and skill rows by sync scope #939: plan tool and skill rows by sync scope
  4. feat: create skills during sync, and read again after a 409 on create #940: create skills during sync, and read again after a 409 on create
  5. feat: apply tool and skill rows in a sync #941: apply tool and skill rows in a sync

Note

Overview
Adds --project, --config-key, and repeatable --key to ldcli sync prompts so scripts can scope work and skip interactive project/config/variation prompts. On sync and watch, those flags narrow the plan (and API reads) to matching resources; sync.lock rows for other projects stay untouched because BaselineStore.Load now fetches remote manifests only for the projects in scope.

add and detach accept the same flags as alternatives to positional project/config/variation selectors (not both). attach / link add --project only (must match --to). Validation errors for unknown or unsynced targets fail before any LaunchDarkly request.

Sync-specific flags (--yes, --conflict, --key, etc.) are CLI-only: validatedArgs resets values that validators.Validate() pulled from LD_* or config, while global flags like --access-token still use env/config.

Reviewed by Cursor Bugbot for commit 3881023. Bugbot is set up for automated code reviews on this repo. Configure here.

…the command line

sync prompts and watch take --project, --config-key, and --key, and
plan only the variations that they name. The checks run before any
LaunchDarkly request, and a project that the filter leaves out is not
read. add and detach take the same flags instead of asking. attach and
link take --project. Sync flags no longer take values from LD_*
environment variables or the ldcli configuration file. Global flags
such as --access-token keep that behavior.
@cursor
cursor Bot force-pushed the ctawiah/sync-target-flags-1e8a branch from 36e896d to 3881023 Compare October 22, 2026 15:15
@ctawiah
ctawiah marked this pull request as ready for review October 22, 2026 15:31
@ctawiah
ctawiah requested a review from a team as a code owner October 22, 2026 15:31
@ctawiah
ctawiah requested review from nkester-ld and tiffanylphan and removed request for a team October 22, 2026 15:31

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3881023. Configure here.


// Read both sides again, so that no action uses state from before the review.
current, err := workspace.loadState(client)
current, err := workspace.loadState(client, options.syncAction().Filter)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Filter treats other projects as dropped

High Severity

A filtered sync plans only matching variations, but cleanupOrphanedAttachments and RemoveUnusedAttachments still look at every compiled file and ignore sync.lock. Missing files in another project—still tracked, and normally restored—look unused, so --yes can delete that project's tool and skill files and drop their lock rows.

Fix in Cursor Fix in Web

Triggered by learned rule: Orphaned attachments: keep without --yes; recheck before delete

Reviewed by Cursor Bugbot for commit 3881023. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant