feat: pin a project and environment to a directory with vf link (COR-14197) - #35
Open
Bradenream wants to merge 4 commits into
Open
Bradenream wants to merge 4 commits into
Bradenream wants to merge 4 commits into
Conversation
…14197) Every project command needs --project-id and --environment-alias, and nothing can set a default, so a coding agent starts every task with discovery (workspace list, then project list): extra turns, and a path into workspaces it has no business reading. vf link <project-id> checks that the project and environment exist, then writes .voiceflow/project.json. Every command run in that directory, or below it, gets the linked project, environment and workspace unless the flag is passed. The root PersistentPreRunE sets the flags before cobra's required-flag checks run, so all generated commands pick the link up without per-command changes. An explicit flag always wins, and a link never fills in what project/environment/workspace delete would destroy. - vf unlink removes the link that applies here, including a damaged one - vf whoami shows the link in effect - vf link prints a snippet for the agent's instructions file - a Creator URL is refused with an explanation: its id is a version id, and the public API cannot map a version to its project
…4197) In agent mode the output is TOON, whose encoder prints a json tag verbatim, so omitempty became part of the key: "file,omitempty", "projectID,omitempty". Found by the live end-to-end test. The tags are plain now, and a behaviour test checks both commands' TOON output.
5 of 6 tasks
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Usage and dry-run modes can mutate links, while linked defaults can override explicit request-body values.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Adds directory-scoped project and environment linking to reduce repeated CLI flags.
Changes:
- Adds link storage, discovery, and default flag application.
- Introduces
vf link,vf unlink, andwhoamiintegration. - Adds documentation and unit/end-to-end coverage.
| File | Description |
|---|---|
README.md |
Documents project linking. |
docs/vf.md |
Lists the new commands. |
docs/vf_link.md |
Documents vf link. |
docs/vf_unlink.md |
Documents vf unlink. |
internal/cli/link.go |
Implements linking commands and CLI integration. |
internal/cli/root.go |
Applies linked defaults globally. |
internal/cli/whoami.go |
Displays active link information. |
internal/link/link.go |
Implements link persistence and default resolution. |
internal/link/link_test.go |
Tests link storage and flag behavior. |
internal/output/format.go |
Exposes resolved output formatting. |
test/link.test.ts |
Adds end-to-end command coverage. |
Files not reviewed (2)
- internal/cli/root.go: Generated file
- internal/cli/whoami.go: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Review found that a linked environment silently replaced one the user wrote
in the request body. Link defaults were applied with Flags().Set, which marks
a flag as set, and BuildRequest lets set flags override --body and stdin. In
a folder linked to "dev", `transcript search --body
'{"environmentAlias":"production"}'` sent "dev". 21 commands carry the
environment alias in their body.
A linked value is now a fallback, never an override: explicit flag, then
--body or stdin, then the link. flagutil.SetLinkDefault puts the value in the
flag without marking it set, and tags it as linked:
- A required path or query parameter still gets it from the flag's value, as
before.
- A body field is only filled by BuildRequest's new last pass, and only when
the flags, --body and stdin left it empty.
- The interactive prompt treats a linked value as answered.
Tests pin down the order with a request shaped like transcript search. Its
body and stdin cases fail under the old behaviour. An end-to-end case runs
transcript search in a linked folder.
Review found that both hand-written commands ignored flags every command inherits: - `vf link --usage` called the API and could write the link. `vf link --usage` with no project id failed argument validation first. - `vf unlink --usage` removed the link. - `vf unlink --dry-run` removed the link despite promising no changes. - The generated usage table had no entry for either command, so EmitSchema errored for both. Both commands now start with the usual UsageRequested / EmitSchema guard, and vf link's argument check lets --usage through. The new usage.RegisterCommand adds a hand-written command's schema to the table and to the root schema, leaving the generated file untouched. vf unlink --dry-run prints the file it would remove and stops.
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.


Summary
Every project command needs
--project-idand--environment-alias, and nothing could set a default. A coding agent therefore starts every task with discovery (workspace list, thenproject list). That costs turns, and it opens a path into workspaces the agent has no business reading.vf link <project-id> [--environment-alias main]checks that the project and environment exist, then writes.voiceflow/project.json. The file holds ids, the project name and the alias, nothing secret.PersistentPreRunEsets the flags before cobra's required-flag checks run, so all generated commands pick up the link without per-command changes.project delete,environment deleteorworkspace deletewould destroy.vf unlinkremoves the link that applies here, including a damaged one.vf whoamishows the link in effect.vf linkprints a snippet for the agent's own instructions file (CLAUDE.md,AGENTS.md)./project/:id/is a version id, and the public API cannot map a version to its project. The error points to Settings → General → Metadata → Project ID.Test plan
gofmt,go vet ./...andgo test ./...pass;go.modis unchanged.internal/link/link_test.gocovers:test/link.test.tshas 14 hermetic cases (mock server, isolated HOME). They cover:--dry-runwhoami, damaged links, and plain TOON keys in agent modeagent patch-instructionsadded a rule with a code word.environment compilestill gave the template's answer.docs-command×2,flag-errors,flag-raw-text).Part of COR-14197.
vf contextfollows as a stacked PR.