@looma/prisma-cli 0.1.1

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.
Files changed (44) hide show
  1. package/README.md +39 -0
  2. package/dist/adapters/config.js +74 -0
  3. package/dist/adapters/local-state.js +98 -0
  4. package/dist/adapters/mock-api.js +57 -0
  5. package/dist/adapters/token-storage.js +43 -0
  6. package/dist/cli.js +9 -0
  7. package/dist/cli2.js +59 -0
  8. package/dist/commands/app/index.js +178 -0
  9. package/dist/commands/auth/index.js +42 -0
  10. package/dist/commands/env/index.js +51 -0
  11. package/dist/commands/project/index.js +45 -0
  12. package/dist/controllers/app.js +658 -0
  13. package/dist/controllers/auth.js +107 -0
  14. package/dist/controllers/env.js +73 -0
  15. package/dist/controllers/project.js +214 -0
  16. package/dist/controllers/select-prompt-port.js +12 -0
  17. package/dist/lib/app/local-dev.js +178 -0
  18. package/dist/lib/app/prototype-build.js +109 -0
  19. package/dist/lib/app/prototype-interaction.js +38 -0
  20. package/dist/lib/app/prototype-progress.js +115 -0
  21. package/dist/lib/app/prototype-provider.js +163 -0
  22. package/dist/lib/auth/auth-ops.js +57 -0
  23. package/dist/lib/auth/client.js +22 -0
  24. package/dist/lib/auth/guard.js +34 -0
  25. package/dist/lib/auth/login.js +117 -0
  26. package/dist/output/patterns.js +93 -0
  27. package/dist/presenters/app.js +333 -0
  28. package/dist/presenters/auth.js +73 -0
  29. package/dist/presenters/env.js +111 -0
  30. package/dist/presenters/project.js +84 -0
  31. package/dist/shell/command-meta.js +294 -0
  32. package/dist/shell/command-runner.js +33 -0
  33. package/dist/shell/errors.js +64 -0
  34. package/dist/shell/global-flags.js +25 -0
  35. package/dist/shell/help.js +78 -0
  36. package/dist/shell/output.js +48 -0
  37. package/dist/shell/prompt.js +31 -0
  38. package/dist/shell/runtime.js +51 -0
  39. package/dist/shell/ui.js +59 -0
  40. package/dist/use-cases/auth.js +70 -0
  41. package/dist/use-cases/create-cli-gateways.js +93 -0
  42. package/dist/use-cases/env.js +104 -0
  43. package/dist/use-cases/project.js +75 -0
  44. package/package.json +30 -0
@@ -0,0 +1,93 @@
1
+ import { formatDescriptorLabel } from "../shell/command-meta.js";
2
+ import { maskValue, padDisplay, renderSummaryLine } from "../shell/ui.js";
3
+ import stringWidth from "string-width";
4
+ //#region src/output/patterns.ts
5
+ function renderList(input, ui) {
6
+ const keyWidth = Math.max(stringWidth(`${input.parentContext.key}:`), ...input.items.map((item) => stringWidth(`⚬ ${item.noun}:`)), stringWidth("Read more"));
7
+ const lines = renderCardTitle(input.descriptor, input.title, ui);
8
+ lines.push(renderCardRow(ui, keyWidth, input.parentContext.key, input.parentContext.value));
9
+ if (input.items.length === 0) lines.push(renderPlainCardLine(ui, ui.dim(input.emptyMessage)));
10
+ else for (const item of input.items) lines.push(renderCardRow(ui, keyWidth, `⚬ ${item.noun}`, formatListItemValue(ui, item)));
11
+ lines.push(renderCardDivider(ui));
12
+ lines.push(renderReadMore(ui, keyWidth, input.descriptor));
13
+ return lines;
14
+ }
15
+ function serializeList(input) {
16
+ return {
17
+ context: input.context,
18
+ items: input.items.map((item) => ({
19
+ name: item.label,
20
+ id: item.id,
21
+ status: item.status
22
+ })),
23
+ count: input.items.length
24
+ };
25
+ }
26
+ function renderShow(input, ui) {
27
+ const keyWidth = Math.max(...input.fields.map((field) => stringWidth(`${field.key}:`)), stringWidth("Read more"));
28
+ const lines = renderCardTitle(input.descriptor, input.title, ui);
29
+ for (const field of input.fields) lines.push(renderCardRow(ui, keyWidth, field.key, formatValue(ui, field.value, field.tone, field.sensitive)));
30
+ lines.push(renderCardDivider(ui));
31
+ lines.push(renderReadMore(ui, keyWidth, input.descriptor));
32
+ return lines;
33
+ }
34
+ function renderMutate(input, ui) {
35
+ const rows = [...input.context, {
36
+ key: "mode",
37
+ value: "apply",
38
+ tone: "dim"
39
+ }];
40
+ const keyWidth = Math.max(...rows.map((row) => stringWidth(`${row.key}:`)), stringWidth("Read more"));
41
+ const lines = renderCardTitle(input.descriptor, input.title, ui);
42
+ for (const row of rows) lines.push(renderCardRow(ui, keyWidth, row.key, formatValue(ui, row.value, row.tone, row.sensitive)));
43
+ lines.push(renderCardDivider(ui));
44
+ lines.push(renderReadMore(ui, keyWidth, input.descriptor));
45
+ lines.push("");
46
+ lines.push(`${ui.warning("◇")} ${input.operationDescription}...`);
47
+ lines.push(renderSummaryLine(ui, "success", `Applied ${input.operationCount} operation(s)`));
48
+ for (const detail of input.details) lines.push(` ${detail}`);
49
+ for (const alert of input.alerts ?? []) {
50
+ lines.push("");
51
+ lines.push(renderSummaryLine(ui, alert.tone, alert.text));
52
+ }
53
+ return lines;
54
+ }
55
+ function renderCardTitle(descriptor, title, ui) {
56
+ return [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim(title)}`, ""];
57
+ }
58
+ function renderCardRow(ui, keyWidth, key, value) {
59
+ return `${renderCardRail(ui)} ${ui.accent(padDisplay(`${key}:`, keyWidth))} ${value}`;
60
+ }
61
+ function renderPlainCardLine(ui, text) {
62
+ return `${renderCardRail(ui)} ${text}`;
63
+ }
64
+ function renderCardDivider(ui) {
65
+ return renderCardRail(ui);
66
+ }
67
+ function renderReadMore(ui, keyWidth, descriptor) {
68
+ return `${renderCardRail(ui)} ${ui.accent(padDisplay("Read more", keyWidth))} ${ui.link(descriptor.docsPath ?? "")}`;
69
+ }
70
+ function renderCardRail(ui) {
71
+ return ui.dim("│");
72
+ }
73
+ function formatListItemValue(ui, item) {
74
+ const annotation = renderAnnotation(ui, item.status);
75
+ return annotation ? `${item.label} ${annotation}` : item.label;
76
+ }
77
+ function renderAnnotation(ui, status) {
78
+ if (status === "active") return ui.success("(active)");
79
+ if (status === "linked") return ui.accent("(linked)");
80
+ if (status === "default") return ui.dim("(default)");
81
+ return "";
82
+ }
83
+ function formatValue(ui, value, tone = "default", sensitive = false) {
84
+ const resolvedValue = sensitive ? maskValue(value) : value;
85
+ if (tone === "success") return ui.success(resolvedValue);
86
+ if (tone === "warning") return ui.warning(resolvedValue);
87
+ if (tone === "error") return ui.error(resolvedValue);
88
+ if (tone === "link") return ui.link(resolvedValue);
89
+ if (tone === "dim") return ui.dim(resolvedValue);
90
+ return resolvedValue;
91
+ }
92
+ //#endregion
93
+ export { renderList, renderMutate, renderShow, serializeList };
@@ -0,0 +1,333 @@
1
+ import { renderList, renderShow, serializeList } from "../output/patterns.js";
2
+ //#region src/presenters/app.ts
3
+ function renderAppBuild(context, descriptor, result) {
4
+ return renderShow({
5
+ title: "Building the local app artifact.",
6
+ descriptor,
7
+ fields: [
8
+ {
9
+ key: "build type",
10
+ value: result.buildType
11
+ },
12
+ {
13
+ key: "entrypoint",
14
+ value: result.entrypoint ?? "none",
15
+ tone: result.entrypoint ? "default" : "dim"
16
+ },
17
+ {
18
+ key: "directory",
19
+ value: result.directory
20
+ }
21
+ ]
22
+ }, context.ui);
23
+ }
24
+ function serializeAppBuild(result) {
25
+ return result;
26
+ }
27
+ function renderAppDeploy(context, descriptor, result) {
28
+ return renderShow({
29
+ title: "Deploying the selected app.",
30
+ descriptor,
31
+ fields: [
32
+ {
33
+ key: "project",
34
+ value: result.projectId
35
+ },
36
+ {
37
+ key: "app",
38
+ value: result.app.name
39
+ },
40
+ {
41
+ key: "deployment",
42
+ value: result.deployment.id
43
+ },
44
+ {
45
+ key: "status",
46
+ value: result.deployment.status,
47
+ tone: toneForStatus(result.deployment.status)
48
+ },
49
+ ...result.deployment.url ? [{
50
+ key: "url",
51
+ value: result.deployment.url,
52
+ tone: "link"
53
+ }] : []
54
+ ]
55
+ }, context.ui);
56
+ }
57
+ function serializeAppDeploy(result) {
58
+ return result;
59
+ }
60
+ function renderAppListDeploys(context, descriptor, result) {
61
+ return renderList({
62
+ title: "Listing deployments for the selected app.",
63
+ descriptor,
64
+ parentContext: {
65
+ key: "app",
66
+ value: result.app?.name ?? "not selected"
67
+ },
68
+ items: result.deployments.map((deployment) => ({
69
+ noun: "deployment",
70
+ label: deployment.id,
71
+ id: deployment.id,
72
+ status: deployment.live ? "active" : null
73
+ })),
74
+ emptyMessage: result.app ? "No deployments found." : "No apps found."
75
+ }, context.ui);
76
+ }
77
+ function serializeAppListDeploys(result) {
78
+ if (!result.app) return {
79
+ projectId: result.projectId,
80
+ app: null,
81
+ items: [],
82
+ count: 0
83
+ };
84
+ return {
85
+ projectId: result.projectId,
86
+ app: result.app,
87
+ ...serializeList({
88
+ context: { app: result.app.name },
89
+ items: result.deployments.map((deployment) => ({
90
+ noun: "deployment",
91
+ label: deployment.id,
92
+ id: deployment.id,
93
+ status: deployment.live ? "active" : null
94
+ }))
95
+ })
96
+ };
97
+ }
98
+ function renderAppShow(context, descriptor, result) {
99
+ return renderShow({
100
+ title: "Showing the selected app state.",
101
+ descriptor,
102
+ fields: [
103
+ {
104
+ key: "project",
105
+ value: result.projectId
106
+ },
107
+ {
108
+ key: "app",
109
+ value: result.app?.name ?? "not selected",
110
+ tone: result.app ? "default" : "dim"
111
+ },
112
+ {
113
+ key: "live deployment",
114
+ value: result.liveDeployment?.id ?? "none",
115
+ tone: result.liveDeployment ? toneForStatus(result.liveDeployment.status) : "dim"
116
+ },
117
+ {
118
+ key: "live url",
119
+ value: result.liveUrl ?? "unavailable",
120
+ tone: result.liveUrl ? "link" : "dim"
121
+ },
122
+ {
123
+ key: "recent deployments",
124
+ value: formatRecentDeployments(result.recentDeployments),
125
+ tone: result.recentDeployments.length > 0 ? "default" : "dim"
126
+ }
127
+ ]
128
+ }, context.ui);
129
+ }
130
+ function serializeAppShow(result) {
131
+ return result;
132
+ }
133
+ function renderAppShowDeploy(context, descriptor, result) {
134
+ return renderShow({
135
+ title: "Showing deployment details.",
136
+ descriptor,
137
+ fields: [
138
+ ...result.app ? [{
139
+ key: "app",
140
+ value: result.app.name
141
+ }] : [],
142
+ {
143
+ key: "deployment",
144
+ value: result.deployment.id
145
+ },
146
+ {
147
+ key: "status",
148
+ value: result.deployment.status,
149
+ tone: toneForStatus(result.deployment.status)
150
+ },
151
+ ...result.deployment.url ? [{
152
+ key: "url",
153
+ value: result.deployment.url,
154
+ tone: "link"
155
+ }] : [],
156
+ ...result.deployment.live === null ? [] : [{
157
+ key: "live",
158
+ value: result.deployment.live ? "yes" : "no",
159
+ tone: result.deployment.live ? "success" : "dim"
160
+ }],
161
+ {
162
+ key: "created",
163
+ value: result.deployment.createdAt,
164
+ tone: "dim"
165
+ }
166
+ ]
167
+ }, context.ui);
168
+ }
169
+ function serializeAppShowDeploy(result) {
170
+ return result;
171
+ }
172
+ function renderAppOpen(context, descriptor, result) {
173
+ return renderShow({
174
+ title: result.opened ? "Opening the live URL for the selected app." : "Resolving the live URL for the selected app.",
175
+ descriptor,
176
+ fields: [
177
+ {
178
+ key: "project",
179
+ value: result.projectId
180
+ },
181
+ {
182
+ key: "app",
183
+ value: result.app.name
184
+ },
185
+ {
186
+ key: "url",
187
+ value: result.url,
188
+ tone: "link"
189
+ },
190
+ {
191
+ key: "opened",
192
+ value: result.opened ? "yes" : "no",
193
+ tone: result.opened ? "success" : "dim"
194
+ }
195
+ ]
196
+ }, context.ui);
197
+ }
198
+ function serializeAppOpen(result) {
199
+ return result;
200
+ }
201
+ function renderAppPromote(context, descriptor, result) {
202
+ return renderShow({
203
+ title: "Switching the live deployment for the selected app.",
204
+ descriptor,
205
+ fields: [
206
+ {
207
+ key: "project",
208
+ value: result.projectId
209
+ },
210
+ {
211
+ key: "app",
212
+ value: result.app.name
213
+ },
214
+ {
215
+ key: "deployment",
216
+ value: result.deployment.id
217
+ },
218
+ {
219
+ key: "status",
220
+ value: result.deployment.status,
221
+ tone: toneForStatus(result.deployment.status)
222
+ },
223
+ ...result.deployment.url ? [{
224
+ key: "url",
225
+ value: result.deployment.url,
226
+ tone: "link"
227
+ }] : [],
228
+ {
229
+ key: "live",
230
+ value: result.deployment.live ? "yes" : "no",
231
+ tone: result.deployment.live ? "success" : "dim"
232
+ },
233
+ {
234
+ key: "created",
235
+ value: result.deployment.createdAt,
236
+ tone: "dim"
237
+ }
238
+ ]
239
+ }, context.ui);
240
+ }
241
+ function serializeAppPromote(result) {
242
+ return result;
243
+ }
244
+ function renderAppRollback(context, descriptor, result) {
245
+ return renderShow({
246
+ title: "Restoring the selected app to an earlier deployment.",
247
+ descriptor,
248
+ fields: [
249
+ {
250
+ key: "project",
251
+ value: result.projectId
252
+ },
253
+ {
254
+ key: "app",
255
+ value: result.app.name
256
+ },
257
+ {
258
+ key: "deployment",
259
+ value: result.deployment.id
260
+ },
261
+ {
262
+ key: "status",
263
+ value: result.deployment.status,
264
+ tone: toneForStatus(result.deployment.status)
265
+ },
266
+ ...result.deployment.url ? [{
267
+ key: "url",
268
+ value: result.deployment.url,
269
+ tone: "link"
270
+ }] : [],
271
+ {
272
+ key: "live",
273
+ value: result.deployment.live ? "yes" : "no",
274
+ tone: result.deployment.live ? "success" : "dim"
275
+ },
276
+ {
277
+ key: "created",
278
+ value: result.deployment.createdAt,
279
+ tone: "dim"
280
+ },
281
+ ...result.previousLiveDeploymentId ? [{
282
+ key: "replaced",
283
+ value: result.previousLiveDeploymentId,
284
+ tone: "dim"
285
+ }] : []
286
+ ]
287
+ }, context.ui);
288
+ }
289
+ function serializeAppRollback(result) {
290
+ return result;
291
+ }
292
+ function renderAppRun(_context, _descriptor, _result) {
293
+ return [];
294
+ }
295
+ function serializeAppRun(result) {
296
+ return result;
297
+ }
298
+ function renderAppRemove(context, descriptor, result) {
299
+ return renderShow({
300
+ title: "Removing the selected app.",
301
+ descriptor,
302
+ fields: [
303
+ {
304
+ key: "project",
305
+ value: result.projectId
306
+ },
307
+ {
308
+ key: "app",
309
+ value: result.app.name
310
+ },
311
+ {
312
+ key: "removed",
313
+ value: result.removed ? "yes" : "no",
314
+ tone: result.removed ? "success" : "dim"
315
+ }
316
+ ]
317
+ }, context.ui);
318
+ }
319
+ function serializeAppRemove(result) {
320
+ return result;
321
+ }
322
+ function toneForStatus(status) {
323
+ if (status === "running" || status === "ready" || status === "healthy") return "success";
324
+ if (status === "provisioning" || status === "building" || status === "starting") return "warning";
325
+ if (status === "failed" || status === "error") return "error";
326
+ return "default";
327
+ }
328
+ function formatRecentDeployments(deployments) {
329
+ if (deployments.length === 0) return "none";
330
+ return deployments.map((deployment) => `${deployment.id}${deployment.live ? " (live)" : ""}`).join(", ");
331
+ }
332
+ //#endregion
333
+ export { renderAppBuild, renderAppDeploy, renderAppListDeploys, renderAppOpen, renderAppPromote, renderAppRemove, renderAppRollback, renderAppRun, renderAppShow, renderAppShowDeploy, serializeAppBuild, serializeAppDeploy, serializeAppListDeploys, serializeAppOpen, serializeAppPromote, serializeAppRemove, serializeAppRollback, serializeAppRun, serializeAppShow, serializeAppShowDeploy };
@@ -0,0 +1,73 @@
1
+ import { renderMutate, renderShow } from "../output/patterns.js";
2
+ //#region src/presenters/auth.ts
3
+ function renderAuthSuccess(context, descriptor, command, result) {
4
+ if (command === "auth.login") {
5
+ const rows = [];
6
+ if (result.provider) rows.push({
7
+ key: "provider",
8
+ value: providerLabel(result.provider)
9
+ });
10
+ if (result.user) rows.push({
11
+ key: "user",
12
+ value: `${result.user.name} <${result.user.email}>`
13
+ });
14
+ if (result.workspace?.name) rows.push({
15
+ key: "workspace",
16
+ value: result.workspace.name
17
+ });
18
+ return renderMutate({
19
+ title: "Starting an authenticated CLI session.",
20
+ descriptor,
21
+ context: rows,
22
+ operationDescription: "Applying authentication session changes",
23
+ operationCount: 1,
24
+ details: ["Session stored in local CLI state."]
25
+ }, context.ui);
26
+ }
27
+ if (command === "auth.logout") return renderMutate({
28
+ title: "Clearing the current CLI session.",
29
+ descriptor,
30
+ context: [{
31
+ key: "session",
32
+ value: "local CLI state",
33
+ tone: "dim"
34
+ }],
35
+ operationDescription: "Applying authentication session changes",
36
+ operationCount: 1,
37
+ details: ["Session removed from local CLI state."]
38
+ }, context.ui);
39
+ return renderShow({
40
+ title: "Showing the current authenticated identity.",
41
+ descriptor,
42
+ fields: result.authenticated ? [
43
+ {
44
+ key: "status",
45
+ value: "signed in",
46
+ tone: "success"
47
+ },
48
+ ...result.user ? [{
49
+ key: "user",
50
+ value: `${result.user.name} <${result.user.email}>`
51
+ }] : [],
52
+ ...result.provider ? [{
53
+ key: "provider",
54
+ value: providerLabel(result.provider)
55
+ }] : [],
56
+ ...result.workspace?.name ? [{
57
+ key: "workspace",
58
+ value: result.workspace.name
59
+ }] : []
60
+ ] : [{
61
+ key: "status",
62
+ value: "signed out",
63
+ tone: "dim"
64
+ }]
65
+ }, context.ui);
66
+ }
67
+ function providerLabel(provider) {
68
+ if (provider === "github") return "GitHub";
69
+ if (provider === "google") return "Google";
70
+ return "";
71
+ }
72
+ //#endregion
73
+ export { renderAuthSuccess };
@@ -0,0 +1,111 @@
1
+ import { renderList, renderMutate, renderShow, serializeList } from "../output/patterns.js";
2
+ //#region src/presenters/env.ts
3
+ function renderEnvList(context, descriptor, result) {
4
+ return renderList({
5
+ title: "Listing environments for the linked project.",
6
+ descriptor,
7
+ parentContext: {
8
+ key: "project",
9
+ value: result.projectName ?? "not linked"
10
+ },
11
+ items: result.environments.map((environment) => ({
12
+ noun: "environment",
13
+ label: environment.name,
14
+ id: environment.id,
15
+ status: environment.active ? "active" : null
16
+ })),
17
+ emptyMessage: "No environments found."
18
+ }, context.ui);
19
+ }
20
+ function serializeEnvList(result) {
21
+ return serializeList({
22
+ context: { project: result.projectName ?? "not linked" },
23
+ items: result.environments.map((environment) => ({
24
+ noun: "environment",
25
+ label: environment.name,
26
+ id: environment.id,
27
+ status: environment.active ? "active" : null
28
+ }))
29
+ });
30
+ }
31
+ function serializeEnvShow(result) {
32
+ return {
33
+ linkedProjectId: result.linkedProjectId,
34
+ projectName: result.projectName,
35
+ environment: {
36
+ name: result.environment.name,
37
+ kind: result.environment.kind,
38
+ active: result.environment.active,
39
+ remoteState: result.environment.remoteState,
40
+ liveDeployment: result.environment.liveDeployment
41
+ }
42
+ };
43
+ }
44
+ function renderEnvShow(context, descriptor, result) {
45
+ const fields = [
46
+ {
47
+ key: "project",
48
+ value: result.projectName ?? "not linked",
49
+ tone: result.projectName ? "default" : "dim"
50
+ },
51
+ {
52
+ key: "environment",
53
+ value: result.environment.name,
54
+ tone: result.environment.active ? "success" : "default"
55
+ },
56
+ {
57
+ key: "kind",
58
+ value: result.environment.kind
59
+ }
60
+ ];
61
+ if (result.environment.liveDeployment) {
62
+ fields.push({
63
+ key: "status",
64
+ value: result.environment.liveDeployment.status,
65
+ tone: toneForDeploymentStatus(result.environment.liveDeployment.status)
66
+ });
67
+ if (result.environment.liveDeployment.url) fields.push({
68
+ key: "url",
69
+ value: result.environment.liveDeployment.url,
70
+ tone: "link"
71
+ });
72
+ } else if (result.environment.kind !== "local" && !result.environment.remoteState) fields.push({
73
+ key: "remote state",
74
+ value: "not created yet",
75
+ tone: "dim"
76
+ });
77
+ return renderShow({
78
+ title: "Showing the current active environment context.",
79
+ descriptor,
80
+ fields
81
+ }, context.ui);
82
+ }
83
+ function toneForDeploymentStatus(status) {
84
+ if (status === "ready" || status === "active" || status === "healthy") return "success";
85
+ if (status === "pending" || status === "building" || status === "starting") return "warning";
86
+ if (status === "failed" || status === "error") return "error";
87
+ return "default";
88
+ }
89
+ function renderEnvUse(context, descriptor, result) {
90
+ return renderMutate({
91
+ title: "Changing the local default environment context.",
92
+ descriptor,
93
+ context: [{
94
+ key: "project",
95
+ value: result.projectName ?? "not linked",
96
+ tone: result.projectName ? "default" : "dim"
97
+ }, {
98
+ key: "environment",
99
+ value: result.environment.name
100
+ }],
101
+ operationDescription: "Applying active environment change",
102
+ operationCount: 1,
103
+ details: ["Active environment updated in local CLI state for this repo."],
104
+ alerts: result.environment.kind === "production" ? [{
105
+ tone: "warning",
106
+ text: "Production is now the default target"
107
+ }] : void 0
108
+ }, context.ui);
109
+ }
110
+ //#endregion
111
+ export { renderEnvList, renderEnvShow, renderEnvUse, serializeEnvList, serializeEnvShow };
@@ -0,0 +1,84 @@
1
+ import { renderList, renderMutate, renderShow, serializeList } from "../output/patterns.js";
2
+ //#region src/presenters/project.ts
3
+ function renderProjectList(context, descriptor, result) {
4
+ return renderList({
5
+ title: "Listing projects for the authenticated workspace.",
6
+ descriptor,
7
+ parentContext: {
8
+ key: "workspace",
9
+ value: result.workspace.name
10
+ },
11
+ items: result.projects.map((project) => ({
12
+ noun: "project",
13
+ label: project.name,
14
+ id: project.id,
15
+ status: result.linkedProjectId === project.id ? "linked" : null
16
+ })),
17
+ emptyMessage: "No projects found."
18
+ }, context.ui);
19
+ }
20
+ function serializeProjectList(result) {
21
+ return serializeList({
22
+ context: { workspace: result.workspace.name },
23
+ items: result.projects.map((project) => ({
24
+ noun: "project",
25
+ label: project.name,
26
+ id: project.id,
27
+ status: result.linkedProjectId === project.id ? "linked" : null
28
+ }))
29
+ });
30
+ }
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.",
43
+ 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
+ }]
53
+ }, context.ui);
54
+ return renderShow({
55
+ title: "Showing the linked project for the current repo.",
56
+ descriptor,
57
+ fields: [{
58
+ key: "project",
59
+ value: result.project.name
60
+ }, {
61
+ key: "workspace",
62
+ value: result.workspace.name
63
+ }]
64
+ }, context.ui);
65
+ }
66
+ function renderProjectLink(context, descriptor, result) {
67
+ if (!result.project || !result.workspace) throw new Error("Linked project result must be enriched for human output.");
68
+ return renderMutate({
69
+ title: "Linking the current repo to an existing project.",
70
+ 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",
79
+ operationCount: 1,
80
+ details: ["Project link written to local repo config."]
81
+ }, context.ui);
82
+ }
83
+ //#endregion
84
+ export { renderProjectLink, renderProjectList, renderProjectShow, serializeProjectList };