Repository navigation
Conversation
c69f654 to
b34a204
Compare
3eff865 to
a02b3bc
Compare
a02b3bc to
f5c0cc0
Compare
c2dd645 to
919f852
Compare
919f852 to
c9b5ec1
Compare
| return fmt.Errorf("%s is already synced. Edit %s and run ldcli sync %ss.", id.Label(), path, id.Kind) | ||
| case slices.ContainsFunc(lock.Resources, func(resource syncmanifest.Resource) bool { return resource.ID() == id }): | ||
| return fmt.Errorf("%s has a sync.lock entry but no local file. Run ldcli sync %ss to restore it.", id.Label(), id.Kind) |
There was a problem hiding this comment.
do we want to mention sync all here?
There was a problem hiding this comment.
Yes. Both refusals here, and the refusal for a LaunchDarkly resource with other content, now end with or ldcli sync all. TestCreateRefusesAResourceThatIsAlreadySynced now also runs sync tools and sync all after the refusal and checks that each one restores the standalone file. Fixed in ba4f410.
One limit I found while checking: if a local variation uses the tool, every sync fails before planning, because compiling the variation needs that tool file. That behavior is older than this stack.
c9b5ec1 to
ba4f410
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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 ba4f410. Configure here.
An add of a tool or a skill can now carry the content of a new resource: the input schema and the description of a tool, or the Markdown, the name, and the description of a skill. The new package internal/sync/create checks that content before any request, asks in a terminal for the values that are missing, and builds the resource with upsert. If LaunchDarkly has the resource with the content that the user passed, add starts syncing it through the existing add. If the content differs, add stops and changes nothing. Otherwise add plans the create in memory, asks for confirmation, and only then writes the file. It then applies the plan of that one resource through the normal sync path. A changed state or a rejected create removes the file again. A write with no response keeps the file, so that the next sync finishes the create. A skill file with upsert now keeps its name, because the create sends it. The checks of an add for a file or a sync.lock entry move into CheckNotSynced, so that the create uses the same checks. requiredValue moves from link into create. The command flags come in the next change.
ba4f410 to
4cd1c89
Compare

Context
sync tools addandsync skills addstart syncing a tool or skill that exists in LaunchDarkly. To create a new one, a user must write the file by hand withupsert: true, and then run sync. This PR and the next letaddcreate a missing tool or skill from flags. This PR has the create flow, throughRunner.Run. The next PR adds the flags. The work is in two PRs to keep each one small.What changes
AddAction.Createholds the content of a create. For a tool, that is the input schema and the description. For a skill, it is the Markdown, the name, and the description. A nil value is the existing add.inputSchema,schema, orformatVersionkey, gets--json-schema is the input schema only. Pass the value of "inputSchema", and pass the description with --description."$(cat file)"has the fingerprint of the file.--nameor--descriptionwithout the content flag gets--name and --description need --content. add creates a skill only when --content is set.--key. The key, the name, and the description must pass the create rules of LaunchDarkly.tool creation requires --project and --key without interactive input.create_serverrow in memory, and asks for confirmation unless--yesis set. Only then does it write the file withupsert: true. It applies the plan of that one resource through the normal sync path, so other pending changes stay as they are.name:, because a create sends that name.With the flags of the next PR, against a stand-in for the LaunchDarkly API:
An adopt and a refusal:
Design notes
internal/sync/createholds the input and its rules, so thatpromptimportscreateand not the other way around.Targetasks for the project and the key,Input.Askasks for the name and the description of a skill, andInput.Attachmentbuilds the resource. The input fields are pointers, so--content ""is a blank value and not a missing flag.runCreatereuses the sync path and adds no second apply. It usesplanAttachmentfor the reviewed row, thenreviewAndConfirmPlan. After the write, it usesloadStatewith a filter for the one ID,checkUnchanged,applyPlan, andRollbackCreation.applyPlannow returns the outcomes. So the create removes the file only when its own row failed with a definite error, not after a failed save.watchapplies a plan without deletes by itself, so a file that exists during the question can let a running watch create the resource first.sync.lockrow move from the add intobootstrap.CheckNotSynced, so both use one copy. The create runs them again right before the write, because a file can appear while the question waits.link.requiredValuemoves intocreateasRequiredValue, and its test moves with it.Review focus
Verification
go test ./...andgolangci-lint run(v2.9.0) pass.prompt/create_test.goruns each case of the add table throughRunner.Run, without a terminal and with an input reader that fails on any read:--yes400, a write with no response that the next sync finishes, and a409that reads againTestCreateAsksBeforeItWritesTheFilerecords whether the file exists when the question reads its answer. It fails when the write moves before the question.TestCreateKeepsTheFileAfterAWriteWithNoResponsefails when the removal ignoresMutationMayHaveSucceeded.create/create_test.gocovers the content rules, the builders, the adopt comparison, and the error for missing flags.ldcliand ran the flag-only script of the sync commands against a stand-in for the LaunchDarkly API. Its output is the same as before this PR. The next PR has the manual checks of the create.Related changes
This stack builds on #937 to #941, which are merged.
ctawiah/fix-prompt-tool-diff-5b1c)ctawiah/plan-referenced-attachments-1e8a)ctawiah/keep-unused-attachments-1e8a)ctawiah/add-attachments-1e8a)ctawiah/route-scopes-1e8a)ctawiah/scope-commands-1e8a)ctawiah/create-attachments-1e8a)ctawiah/create-attachment-flags-1e8a)ctawiah/share-variation-selection-1e8a)ctawiah/create-variations-1e8a)ctawiah/create-variation-flags-1e8a)Note
Overview
sync tools add/sync skills addcan create a missing tool or skill from create input (wired throughAddAction.Create; CLI flags land in a follow-up PR). A newinternal/sync/createpackage validates flags before any API call, buildsupsertattachments, and supports interactive project/key/skill metadata.The add runner plans a single
create_serverrow, confirms before writing the local file, then syncs only that resource. If LaunchDarkly already has matching content (flag fields only), it adopts via the existing add path; mismatches are refused with no writes. Failures after review roll back the new file unless the mutation may have succeeded; concurrent creates are detected inattachmentResolver.publish.Shared
bootstrap.CheckNotSyncedguards add and create; skill files withupsertnow persistname:in front matter. “Already synced” hints mentionldcli sync all.Reviewed by Cursor Bugbot for commit 4cd1c89. Bugbot is set up for automated code reviews on this repo. Configure here.