@prisma/cli 3.0.0-alpha.3 → 3.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,10 +20,21 @@ function resolveEnvScope(flags, options) {
20
20
  }
21
21
  return null;
22
22
  }
23
- function parseKeyValuePositional(raw, command) {
23
+ function parseKeyValuePositional(raw, command, env = process.env) {
24
24
  if (!raw) throw usageError(`prisma-cli project env ${command} requires KEY=VALUE`, "No KEY=VALUE positional argument was supplied.", "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma-cli project env ${command} STRIPE_KEY=sk_test_xxx --role production`], "app");
25
25
  const separatorIndex = raw.indexOf("=");
26
- if (separatorIndex === -1) throw usageError(`KEY=VALUE argument is missing the = separator`, `"${raw}" does not contain an = character.`, "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma-cli project env ${command} STRIPE_KEY=sk_test_xxx --role production`], "app");
26
+ if (separatorIndex === -1) {
27
+ if (KEY_SHAPE.test(raw)) {
28
+ validateKey(raw, command);
29
+ const value = env[raw];
30
+ if (typeof value === "string" && value.length > 0) return {
31
+ key: raw,
32
+ value
33
+ };
34
+ throw usageError(`Value for "${raw}" was not provided`, `No KEY=VALUE assignment was supplied, and ${raw} is not set in the current environment.`, "Pass KEY=VALUE or export the variable before running the command.", [`prisma-cli project env ${command} ${raw}=value --role production`, `${raw}=value prisma-cli project env ${command} ${raw} --role production`], "app");
35
+ }
36
+ throw usageError(`KEY=VALUE argument is missing the = separator`, `"${raw}" does not contain an = character.`, "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma-cli project env ${command} STRIPE_KEY=sk_test_xxx --role production`], "app");
37
+ }
27
38
  const key = raw.slice(0, separatorIndex);
28
39
  const value = raw.slice(separatorIndex + 1);
29
40
  validateKey(key, command);
@@ -27,8 +27,7 @@ async function readAuthState(env) {
27
27
  authenticated: false,
28
28
  provider: null,
29
29
  user: null,
30
- workspace: null,
31
- linkedProjectId: null
30
+ workspace: null
32
31
  };
33
32
  const email = emailFromClaims(decodeJwtPayload(tokens.accessToken));
34
33
  const client = await requireComputeAuth(env);
@@ -46,8 +45,7 @@ async function readAuthState(env) {
46
45
  workspace: {
47
46
  id: workspaceId,
48
47
  name: workspaceName
49
- },
50
- linkedProjectId: null
48
+ }
51
49
  };
52
50
  }
53
51
  async function performLogout(env) {
@@ -1,5 +1,5 @@
1
- import path from "node:path";
2
1
  import os from "node:os";
2
+ import path from "node:path";
3
3
  //#region src/lib/auth/client.ts
4
4
  const CLIENT_ID = "cmm3lndn701oo0uefvxzo0ivw";
5
5
  const SERVICE_TOKEN_ENV_VAR = "PRISMA_API_TOKEN";
@@ -0,0 +1,148 @@
1
+ import { CliError } from "../../shell/errors.js";
2
+ import { canPrompt } from "../../shell/runtime.js";
3
+ import path from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+ //#region src/lib/project/resolution.ts
6
+ async function resolveProjectTarget(options) {
7
+ const projects = await options.listProjects();
8
+ if (options.explicitProject) return rememberIfRequested(options, resolveExplicitProject(options.explicitProject, projects, options.workspace), "explicit");
9
+ const platformMapping = await resolveDurablePlatformMapping();
10
+ if (platformMapping) return rememberIfRequested(options, platformMapping, "platform-mapping");
11
+ const remembered = await options.context.stateStore.readRememberedProject(options.workspace.id);
12
+ let staleRemembered = false;
13
+ if (remembered) {
14
+ const matched = projects.find((project) => project.id === remembered.id);
15
+ if (matched) return rememberIfRequested(options, matched, "remembered-local");
16
+ staleRemembered = true;
17
+ }
18
+ const packageName = await readPackageName(options.context.runtime.cwd);
19
+ if (packageName) {
20
+ const matches = projects.filter((project) => projectMatchesPackageName(project, packageName));
21
+ if (matches.length === 1) return rememberIfRequested(options, matches[0], "package-name");
22
+ if (matches.length > 1) return resolveAmbiguousProject(options, matches, "package-name");
23
+ }
24
+ if (options.allowCreate && options.createProject) {
25
+ const inferredName = packageName ?? path.basename(options.context.runtime.cwd);
26
+ if (inferredName) {
27
+ const existing = projects.filter((project) => projectMatchesPackageName(project, inferredName));
28
+ if (existing.length === 1) return rememberIfRequested(options, existing[0], "package-name");
29
+ if (existing.length > 1) return resolveAmbiguousProject(options, existing, "package-name");
30
+ return rememberIfRequested(options, await options.createProject(inferredName), "created");
31
+ }
32
+ }
33
+ if (options.prompt && canPrompt(options.context) && projects.length > 0) return rememberIfRequested(options, await options.prompt.select({
34
+ message: "Select a project",
35
+ choices: sortProjects(projects).map((project) => ({
36
+ label: `${project.name} (${project.id})`,
37
+ value: project
38
+ }))
39
+ }), "prompt");
40
+ if (staleRemembered && projects.length > 1) throw localStateStaleError();
41
+ throw projectUnresolvedError();
42
+ }
43
+ function projectNotFoundError(projectRef, workspace) {
44
+ return new CliError({
45
+ code: "PROJECT_NOT_FOUND",
46
+ domain: "project",
47
+ summary: "Project not found",
48
+ why: `The project "${projectRef}" does not exist in workspace "${workspace.name}" or is not accessible.`,
49
+ fix: "Pass a project id or name from prisma-cli project list.",
50
+ exitCode: 1,
51
+ nextSteps: ["prisma-cli project list"]
52
+ });
53
+ }
54
+ function projectAmbiguousError(projectRef, matches) {
55
+ return new CliError({
56
+ code: "PROJECT_AMBIGUOUS",
57
+ domain: "project",
58
+ summary: "Project resolution is ambiguous",
59
+ why: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
60
+ fix: "Pass --project <id-or-name> to choose the project explicitly.",
61
+ meta: { matches: matches.map((project) => ({
62
+ id: project.id,
63
+ name: project.name
64
+ })) },
65
+ exitCode: 1,
66
+ nextSteps: ["prisma-cli project list"]
67
+ });
68
+ }
69
+ function projectUnresolvedError() {
70
+ return new CliError({
71
+ code: "PROJECT_UNRESOLVED",
72
+ domain: "project",
73
+ summary: "No project is resolved for this directory",
74
+ why: "No project could be resolved from explicit input, platform mappings, remembered local context, or package metadata.",
75
+ fix: "Pass --project <id-or-name> on the command that needs a project, or add a package.json name that matches an accessible project.",
76
+ exitCode: 1,
77
+ nextSteps: ["prisma-cli project list", "prisma-cli project show --project <id-or-name>"]
78
+ });
79
+ }
80
+ function localStateStaleError() {
81
+ return new CliError({
82
+ code: "LOCAL_STATE_STALE",
83
+ domain: "project",
84
+ summary: "Remembered project context is stale",
85
+ why: "The remembered project is no longer available in the selected workspace, and automatic resolution would be ambiguous.",
86
+ fix: "Pass --project <id-or-name> to choose the project explicitly.",
87
+ exitCode: 1,
88
+ nextSteps: ["prisma-cli project list"]
89
+ });
90
+ }
91
+ async function readPackageName(cwd) {
92
+ try {
93
+ const raw = await readFile(path.join(cwd, "package.json"), "utf8");
94
+ const parsed = JSON.parse(raw);
95
+ if (!parsed || typeof parsed !== "object") return null;
96
+ const packageName = "name" in parsed ? parsed.name : null;
97
+ return typeof packageName === "string" && packageName.trim().length > 0 ? packageName.trim() : null;
98
+ } catch (error) {
99
+ if (error.code === "ENOENT") return null;
100
+ if (error instanceof SyntaxError) return null;
101
+ throw error;
102
+ }
103
+ }
104
+ function sortProjects(projects) {
105
+ return projects.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
106
+ }
107
+ function resolveExplicitProject(projectRef, projects, workspace) {
108
+ const matches = projects.filter((project) => project.id === projectRef || project.name === projectRef);
109
+ if (matches.length === 1) return matches[0];
110
+ if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
111
+ throw projectNotFoundError(projectRef, workspace);
112
+ }
113
+ function resolveAmbiguousProject(options, matches, projectRef) {
114
+ if (options.prompt && canPrompt(options.context)) return options.prompt.select({
115
+ message: "Select a project",
116
+ choices: sortProjects(matches).map((project) => ({
117
+ label: `${project.name} (${project.id})`,
118
+ value: project
119
+ }))
120
+ }).then((selected) => rememberIfRequested(options, selected, "prompt"));
121
+ throw projectAmbiguousError(projectRef, matches);
122
+ }
123
+ function projectMatchesPackageName(project, packageName) {
124
+ return project.id === packageName || project.name === packageName || project.slug === packageName;
125
+ }
126
+ async function resolveDurablePlatformMapping() {
127
+ return null;
128
+ }
129
+ async function rememberIfRequested(options, project, projectSource) {
130
+ if (options.remember) await options.context.stateStore.setRememberedProject({
131
+ id: project.id,
132
+ name: project.name,
133
+ workspaceId: options.workspace.id
134
+ });
135
+ return {
136
+ workspace: options.workspace,
137
+ project: toProjectSummary(project),
138
+ resolution: { projectSource }
139
+ };
140
+ }
141
+ function toProjectSummary(project) {
142
+ return {
143
+ id: project.id,
144
+ name: project.name
145
+ };
146
+ }
147
+ //#endregion
148
+ export { resolveProjectTarget, sortProjects };
@@ -74,7 +74,6 @@ function formatListItemValue(ui, item) {
74
74
  }
75
75
  function renderAnnotation(ui, status) {
76
76
  if (status === "active") return ui.success("(active)");
77
- if (status === "linked") return ui.accent("(linked)");
78
77
  if (status === "default") return ui.dim("(default)");
79
78
  return "";
80
79
  }
@@ -29,9 +29,17 @@ function renderAppDeploy(context, descriptor, result) {
29
29
  title: "Deploying the selected app.",
30
30
  descriptor,
31
31
  fields: [
32
+ {
33
+ key: "workspace",
34
+ value: result.workspace.name
35
+ },
32
36
  {
33
37
  key: "project",
34
- value: result.projectId
38
+ value: result.project.name
39
+ },
40
+ {
41
+ key: "branch",
42
+ value: result.branch.name
35
43
  },
36
44
  {
37
45
  key: "app",
@@ -2,11 +2,11 @@ import { renderList, renderMutate, renderShow, serializeList } from "../output/p
2
2
  //#region src/presenters/branch.ts
3
3
  function renderBranchList(context, descriptor, result) {
4
4
  return renderList({
5
- title: "Listing branches for the linked project.",
5
+ title: "Listing branches for the resolved project.",
6
6
  descriptor,
7
7
  parentContext: {
8
8
  key: "project",
9
- value: result.projectName ?? "not linked"
9
+ value: result.projectName ?? "not resolved"
10
10
  },
11
11
  items: result.branches.map((branch) => ({
12
12
  noun: "branch",
@@ -19,7 +19,7 @@ function renderBranchList(context, descriptor, result) {
19
19
  }
20
20
  function serializeBranchList(result) {
21
21
  return serializeList({
22
- context: { project: result.projectName ?? "not linked" },
22
+ context: { project: result.projectName ?? "not resolved" },
23
23
  items: result.branches.map((branch) => ({
24
24
  noun: "branch",
25
25
  label: branch.name,
@@ -30,7 +30,7 @@ function serializeBranchList(result) {
30
30
  }
31
31
  function serializeBranchShow(result) {
32
32
  return {
33
- linkedProjectId: result.linkedProjectId,
33
+ projectId: result.projectId,
34
34
  projectName: result.projectName,
35
35
  branch: {
36
36
  name: result.branch.name,
@@ -45,7 +45,7 @@ function renderBranchShow(context, descriptor, result) {
45
45
  const fields = [
46
46
  {
47
47
  key: "project",
48
- value: result.projectName ?? "not linked",
48
+ value: result.projectName ?? "not resolved",
49
49
  tone: result.projectName ? "default" : "dim"
50
50
  },
51
51
  {
@@ -92,7 +92,7 @@ function renderBranchUse(context, descriptor, result) {
92
92
  descriptor,
93
93
  context: [{
94
94
  key: "project",
95
- value: result.projectName ?? "not linked",
95
+ value: result.projectName ?? "not resolved",
96
96
  tone: result.projectName ? "default" : "dim"
97
97
  }, {
98
98
  key: "branch",
@@ -12,7 +12,7 @@ function renderProjectList(context, descriptor, result) {
12
12
  noun: "project",
13
13
  label: project.name,
14
14
  id: project.id,
15
- status: result.linkedProjectId === project.id ? "linked" : null
15
+ status: null
16
16
  })),
17
17
  emptyMessage: "No projects found."
18
18
  }, context.ui);
@@ -24,61 +24,101 @@ function serializeProjectList(result) {
24
24
  noun: "project",
25
25
  label: project.name,
26
26
  id: project.id,
27
- status: result.linkedProjectId === project.id ? "linked" : null
27
+ status: null
28
28
  }))
29
29
  });
30
30
  }
31
31
  function renderProjectShow(context, descriptor, result) {
32
- if (!result.linkedProjectId) return renderShow({
33
- title: "Showing the linked project for the current repo.",
34
- descriptor,
35
- fields: [{
36
- key: "project",
37
- value: "not linked",
38
- tone: "dim"
39
- }]
40
- }, context.ui);
41
- if (!result.project || !result.workspace) return renderShow({
42
- title: "Showing the linked project for the current repo.",
32
+ return renderShow({
33
+ title: "Showing the project Prisma resolves for this directory.",
43
34
  descriptor,
44
- fields: [{
45
- key: "project",
46
- value: "linked",
47
- tone: "success"
48
- }, {
49
- key: "remote details",
50
- value: "unavailable until you sign in",
51
- tone: "dim"
52
- }]
35
+ fields: [
36
+ {
37
+ key: "workspace",
38
+ value: result.workspace.name
39
+ },
40
+ {
41
+ key: "project",
42
+ value: result.project.name
43
+ },
44
+ {
45
+ key: "resolution",
46
+ value: formatProjectSource(result.resolution.projectSource)
47
+ }
48
+ ]
53
49
  }, context.ui);
54
- return renderShow({
55
- title: "Showing the linked project for the current repo.",
50
+ }
51
+ function serializeProjectShow(result) {
52
+ return result;
53
+ }
54
+ function renderGitConnect(context, descriptor, result) {
55
+ const connection = result.repositoryConnection;
56
+ return renderMutate({
57
+ title: "Connecting Git to the resolved project.",
56
58
  descriptor,
57
- fields: [{
58
- key: "project",
59
- value: result.project.name
60
- }, {
61
- key: "workspace",
62
- value: result.workspace.name
63
- }]
59
+ context: [
60
+ {
61
+ key: "project",
62
+ value: result.project.name
63
+ },
64
+ {
65
+ key: "workspace",
66
+ value: result.workspace.name
67
+ },
68
+ {
69
+ key: "repository",
70
+ value: connection.repository.fullName
71
+ },
72
+ {
73
+ key: "status",
74
+ value: connection.status
75
+ }
76
+ ],
77
+ operationDescription: "Applying repository connection",
78
+ operationCount: 1,
79
+ details: [formatGitConnectionDetail(connection.status)]
64
80
  }, context.ui);
65
81
  }
66
- function renderProjectLink(context, descriptor, result) {
67
- if (!result.project || !result.workspace) throw new Error("Linked project result must be enriched for human output.");
82
+ function renderGitDisconnect(context, descriptor, result) {
68
83
  return renderMutate({
69
- title: "Linking the current repo to an existing project.",
84
+ title: "Disconnecting Git from the resolved project.",
70
85
  descriptor,
71
- context: [{
72
- key: "project",
73
- value: result.project.name
74
- }, {
75
- key: "workspace",
76
- value: result.workspace.name
77
- }],
78
- operationDescription: "Applying local project link",
86
+ context: [
87
+ {
88
+ key: "project",
89
+ value: result.project.name
90
+ },
91
+ {
92
+ key: "workspace",
93
+ value: result.workspace.name
94
+ },
95
+ {
96
+ key: "repository",
97
+ value: result.repositoryConnection.repository.fullName
98
+ }
99
+ ],
100
+ operationDescription: "Applying repository disconnection",
79
101
  operationCount: 1,
80
- details: ["Project link written to local repo config."]
102
+ details: ["GitHub branch automation is no longer active for this project."]
81
103
  }, context.ui);
82
104
  }
105
+ function formatProjectSource(source) {
106
+ switch (source) {
107
+ case "explicit": return "explicit";
108
+ case "platform-mapping": return "platform mapping";
109
+ case "remembered-local": return "remembered local context";
110
+ case "package-name": return "package name";
111
+ case "created": return "created";
112
+ case "prompt": return "prompt";
113
+ }
114
+ }
115
+ function formatGitConnectionDetail(status) {
116
+ switch (status) {
117
+ case "active": return "GitHub branch automation is active for this project.";
118
+ case "pending": return "GitHub branch automation is pending GitHub App installation.";
119
+ case "archived": return "GitHub branch automation has been archived for this project.";
120
+ default: return "GitHub repository is connected, but branch automation is not active.";
121
+ }
122
+ }
83
123
  //#endregion
84
- export { renderProjectLink, renderProjectList, renderProjectShow, serializeProjectList };
124
+ export { renderGitConnect, renderGitDisconnect, renderProjectList, renderProjectShow, serializeProjectList, serializeProjectShow };
@@ -47,7 +47,7 @@ const DESCRIPTORS = [
47
47
  {
48
48
  id: "project",
49
49
  path: ["prisma", "project"],
50
- description: "Manage the link between this directory and a Prisma project",
50
+ description: "Manage and inspect your Prisma projects",
51
51
  examples: ["prisma-cli project list", "prisma-cli project show"]
52
52
  },
53
53
  {
@@ -62,6 +62,12 @@ const DESCRIPTORS = [
62
62
  description: "View your active Platform branches",
63
63
  examples: ["prisma-cli branch list", "prisma-cli branch show"]
64
64
  },
65
+ {
66
+ id: "git",
67
+ path: ["prisma", "git"],
68
+ description: "Manage Git repository connections for a project",
69
+ examples: ["prisma-cli git connect", "prisma-cli git disconnect"]
70
+ },
65
71
  {
66
72
  id: "project.list",
67
73
  path: [
@@ -79,18 +85,32 @@ const DESCRIPTORS = [
79
85
  "project",
80
86
  "show"
81
87
  ],
82
- description: "Show the Prisma project linked to this directory",
83
- examples: ["prisma-cli project show", "prisma-cli project show --json"]
88
+ description: "Show which project is active for this directory",
89
+ examples: ["prisma-cli project show", "prisma-cli project show --project proj_123 --json"]
84
90
  },
85
91
  {
86
- id: "project.link",
92
+ id: "git.connect",
87
93
  path: [
88
94
  "prisma",
89
- "project",
90
- "link"
95
+ "git",
96
+ "connect"
91
97
  ],
92
- description: "Link this directory to a Prisma project",
93
- examples: ["prisma-cli project link", "prisma-cli project link proj_123"]
98
+ description: "Connect the resolved project to a GitHub repository",
99
+ examples: [
100
+ "prisma-cli git connect",
101
+ "prisma-cli git connect git@github.com:prisma/prisma-cli.git",
102
+ "prisma-cli git connect --project proj_123"
103
+ ]
104
+ },
105
+ {
106
+ id: "git.disconnect",
107
+ path: [
108
+ "prisma",
109
+ "git",
110
+ "disconnect"
111
+ ],
112
+ description: "Disconnect the GitHub repository from the resolved project",
113
+ examples: ["prisma-cli git disconnect", "prisma-cli git disconnect --project proj_123"]
94
114
  },
95
115
  {
96
116
  id: "branch.list",
@@ -99,7 +119,7 @@ const DESCRIPTORS = [
99
119
  "branch",
100
120
  "list"
101
121
  ],
102
- description: "List active Platform branches linked to this project",
122
+ description: "List active Platform branches for the resolved project",
103
123
  examples: ["prisma-cli branch list", "prisma-cli branch list --json"]
104
124
  },
105
125
  {
@@ -268,7 +288,7 @@ const DESCRIPTORS = [
268
288
  "project",
269
289
  "env"
270
290
  ],
271
- description: "Manage environment variables for the linked project.",
291
+ description: "Manage environment variables for the active project",
272
292
  examples: [
273
293
  "prisma-cli project env list --role production",
274
294
  "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
@@ -284,7 +304,11 @@ const DESCRIPTORS = [
284
304
  "add"
285
305
  ],
286
306
  description: "Create a new environment variable.",
287
- examples: ["prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production", "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role preview"]
307
+ examples: [
308
+ "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
309
+ "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role preview",
310
+ "API_URL=https://api.example prisma-cli project env add API_URL --project proj_123 --role preview"
311
+ ]
288
312
  },
289
313
  {
290
314
  id: "project.env.update",
@@ -51,6 +51,9 @@ function authRequiredError(nextSteps = ["prisma-cli auth login"]) {
51
51
  nextSteps
52
52
  });
53
53
  }
54
+ function workspaceRequiredError() {
55
+ return usageError("Workspace required", "This command needs an active workspace, but the authenticated session does not have one.", "Run prisma-cli auth login and choose a workspace.", ["prisma-cli auth login"], "auth");
56
+ }
54
57
  function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cli") {
55
58
  return new CliError({
56
59
  code: "FEATURE_UNAVAILABLE",
@@ -63,4 +66,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
63
66
  });
64
67
  }
65
68
  //#endregion
66
- export { CliError, authRequiredError, featureUnavailableError, usageError };
69
+ export { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError };
@@ -40,13 +40,12 @@ function createAuthUseCases(dependencies) {
40
40
  };
41
41
  }
42
42
  async function resolveCurrentAuthState(dependencies) {
43
- const [session, linkedProjectId] = await Promise.all([dependencies.sessionGateway.readAuthSession(), dependencies.projectConfigGateway.readLinkedProjectId()]);
43
+ const session = await dependencies.sessionGateway.readAuthSession();
44
44
  if (!session) return {
45
45
  authenticated: false,
46
46
  provider: null,
47
47
  user: null,
48
- workspace: null,
49
- linkedProjectId
48
+ workspace: null
50
49
  };
51
50
  const provider = dependencies.identityGateway.getProvider(session.provider);
52
51
  const user = dependencies.identityGateway.getUser(session.userId);
@@ -55,15 +54,13 @@ async function resolveCurrentAuthState(dependencies) {
55
54
  authenticated: false,
56
55
  provider: null,
57
56
  user: null,
58
- workspace: null,
59
- linkedProjectId
57
+ workspace: null
60
58
  };
61
59
  return {
62
60
  authenticated: true,
63
61
  provider: provider.id,
64
62
  user: { email: user.email },
65
- workspace,
66
- linkedProjectId
63
+ workspace
67
64
  };
68
65
  }
69
66
  //#endregion
@@ -2,41 +2,41 @@
2
2
  function createBranchUseCases(dependencies) {
3
3
  return {
4
4
  list: async () => {
5
- const [linkedProjectId, activeBranch] = await Promise.all([dependencies.projectConfigGateway.readLinkedProjectId(), dependencies.branchStateGateway.readActiveBranch()]);
6
- const remoteBranches = await listRemoteBranches(dependencies.branchGateway, linkedProjectId);
5
+ const [projectId, activeBranch] = await Promise.all([dependencies.projectStateGateway.readRememberedProjectId(), dependencies.branchStateGateway.readActiveBranch()]);
6
+ const remoteBranches = await listRemoteBranches(dependencies.branchGateway, projectId);
7
7
  return {
8
- linkedProjectId,
9
- projectName: resolveProjectName(dependencies.projectGateway, linkedProjectId),
8
+ projectId,
9
+ projectName: resolveProjectName(dependencies.projectGateway, projectId),
10
10
  activeBranch,
11
11
  branches: buildBranchSummaries(activeBranch, remoteBranches)
12
12
  };
13
13
  },
14
14
  show: async () => {
15
- const [linkedProjectId, activeBranch] = await Promise.all([dependencies.projectConfigGateway.readLinkedProjectId(), dependencies.branchStateGateway.readActiveBranch()]);
15
+ const [projectId, activeBranch] = await Promise.all([dependencies.projectStateGateway.readRememberedProjectId(), dependencies.branchStateGateway.readActiveBranch()]);
16
16
  return {
17
- linkedProjectId,
18
- projectName: resolveProjectName(dependencies.projectGateway, linkedProjectId),
19
- branch: buildBranchDetail(dependencies.branchGateway, linkedProjectId, activeBranch)
17
+ projectId,
18
+ projectName: resolveProjectName(dependencies.projectGateway, projectId),
19
+ branch: buildBranchDetail(dependencies.branchGateway, projectId, activeBranch)
20
20
  };
21
21
  },
22
22
  use: async (branchName) => {
23
23
  await dependencies.branchStateGateway.writeActiveBranch(branchName);
24
- const linkedProjectId = await dependencies.projectConfigGateway.readLinkedProjectId();
24
+ const projectId = await dependencies.projectStateGateway.readRememberedProjectId();
25
25
  return {
26
- linkedProjectId,
27
- projectName: resolveProjectName(dependencies.projectGateway, linkedProjectId),
28
- branch: buildBranchDetail(dependencies.branchGateway, linkedProjectId, branchName)
26
+ projectId,
27
+ projectName: resolveProjectName(dependencies.projectGateway, projectId),
28
+ branch: buildBranchDetail(dependencies.branchGateway, projectId, branchName)
29
29
  };
30
30
  }
31
31
  };
32
32
  }
33
- function resolveProjectName(projectGateway, linkedProjectId) {
34
- if (!linkedProjectId) return null;
35
- return projectGateway.getProject(linkedProjectId)?.name ?? null;
33
+ function resolveProjectName(projectGateway, projectId) {
34
+ if (!projectId) return null;
35
+ return projectGateway.getProject(projectId)?.name ?? null;
36
36
  }
37
- async function listRemoteBranches(branchGateway, linkedProjectId) {
38
- if (!linkedProjectId) return [];
39
- return branchGateway.listBranchesForProject(linkedProjectId);
37
+ async function listRemoteBranches(branchGateway, projectId) {
38
+ if (!projectId) return [];
39
+ return branchGateway.listBranchesForProject(projectId);
40
40
  }
41
41
  function buildBranchSummaries(activeBranch, remoteBranches) {
42
42
  const byName = /* @__PURE__ */ new Map();
@@ -56,9 +56,9 @@ function buildBranchSummaries(activeBranch, remoteBranches) {
56
56
  });
57
57
  return sortBranches([...byName.values()]);
58
58
  }
59
- function buildBranchDetail(branchGateway, linkedProjectId, branchName) {
59
+ function buildBranchDetail(branchGateway, projectId, branchName) {
60
60
  const kind = toBranchKind(branchName);
61
- const remoteBranch = linkedProjectId ? branchGateway.getBranchForProject(linkedProjectId, branchName) : void 0;
61
+ const remoteBranch = projectId ? branchGateway.getBranchForProject(projectId, branchName) : void 0;
62
62
  return {
63
63
  name: branchName,
64
64
  kind,