Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 2 deletions docs/product/command-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1299,12 +1299,13 @@ Behavior:

```text
? Which Project should this directory use?
❯ Acme Dashboard
❯ + Create a new Project
Acme Dashboard
Billing API
Create a new Project
Cancel
```

- "Create a new Project" is listed first, highlighted with a `+` glyph that does not rely on color, so it stays reachable without paging through a long project list; Cancel is last
- when "Create a new Project" is selected, prompts for a Project name with the package/directory name as a suggestion
- when no Project is resolved in `--json` / `--no-interactive` mode, fails with `PROJECT_SETUP_REQUIRED`
- `PROJECT_SETUP_REQUIRED` preserves readable recovery commands in `nextSteps` and includes structured `nextActions` for choosing, linking, creating, or retrying with an explicit Project
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/lib/project/interactive-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,18 @@ export async function promptForProjectSetupChoice(options: {
input: options.context.runtime.stdin,
output: options.context.runtime.stderr,
message: "Which Project should this directory use?",
// "Create a new Project" stays first so it is reachable without paging
// through a long project list; Cancel stays last by convention. The "+"
// glyph highlights it without color, since the prompt library owns the
// active-row styling and the style guide forbids color-only meaning.
choices: [
{ label: "+ Create a new Project", value: { kind: "create" as const } },
...sortedProjects.map((project) => ({
label: duplicateNames.has(project.name)
? `${project.name} (${project.id})`
: project.name,
value: { kind: "project" as const, project },
})),
{ label: "Create a new Project", value: { kind: "create" as const } },
{ label: "Cancel", value: { kind: "cancel" as const } },
],
});
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/tests/app-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3136,7 +3136,7 @@ describe("app controller", () => {
cwd,
stateDir,
isTTY: true,
stdinText: "\r",
stdinText: "\u001B[B\r",
env: {
...process.env,
PRISMA_CLI_TEST_REMEMBER_PROJECT_ID: "",
Expand Down Expand Up @@ -3221,7 +3221,7 @@ describe("app controller", () => {
cwd,
stateDir,
isTTY: true,
stdinText: "\r\r",
stdinText: "\u001B[B\r\r",
env: {
...process.env,
PRISMA_CLI_TEST_REMEMBER_PROJECT_ID: "",
Expand Down Expand Up @@ -3528,7 +3528,7 @@ describe("app controller", () => {
cwd,
stateDir,
isTTY: true,
stdinText: "\u001B[B\rinteractive-project\r",
stdinText: "\rinteractive-project\r",
env: {
...process.env,
PRISMA_CLI_TEST_REMEMBER_PROJECT_ID: "",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tests/project-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ describe("project controller", () => {
cwd,
stateDir,
isTTY: true,
stdinText: "\u001B[B\rInteractive Project\r",
stdinText: "\rInteractive Project\r",
env: {
...process.env,
PRISMA_CLI_TEST_REMEMBER_PROJECT_ID: "",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/tests/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ describe("project commands", () => {
stateDir,
fixturePath,
isTTY: true,
stdinText: "\r",
stdinText: "\u001B[B\r",
});
const stderr = stripAnsi(result.stderr);

Expand Down Expand Up @@ -264,7 +264,7 @@ describe("project commands", () => {
stateDir,
fixturePath: ambiguousFixturePath,
isTTY: true,
stdinText: "\r",
stdinText: "\u001B[B\r",
});
const stderr = stripAnsi(result.stderr);

Expand Down
Loading