@barekey/cli 0.5.8 → 0.6.0

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.
@@ -0,0 +1,149 @@
1
+ import pc from "picocolors";
2
+ const MINECRAFT_HEX = {
3
+ "1": "#0000AA",
4
+ "6": "#FFAA00",
5
+ "7": "#AAAAAA",
6
+ "8": "#555555",
7
+ "a": "#55FF55",
8
+ "b": "#55FFFF",
9
+ "c": "#FF5555",
10
+ "e": "#FFFF55",
11
+ };
12
+ /**
13
+ * Returns whether styled terminal output should be emitted.
14
+ *
15
+ * @returns `true` when stdout is interactive and color has not been disabled.
16
+ * @remarks This keeps human output colorful while preserving clean automation output in dumb/no-color terminals.
17
+ * @lastModified 2026-03-19
18
+ * @author GPT-5.4
19
+ */
20
+ export function supportsCliColor() {
21
+ if (!process.stdout.isTTY) {
22
+ return false;
23
+ }
24
+ if ((process.env.NO_COLOR ?? "").trim().length > 0) {
25
+ return false;
26
+ }
27
+ return (process.env.TERM ?? "").toLowerCase() !== "dumb";
28
+ }
29
+ function hexToRgb(hex) {
30
+ const normalized = hex.replace(/^#/, "");
31
+ return {
32
+ red: Number.parseInt(normalized.slice(0, 2), 16),
33
+ green: Number.parseInt(normalized.slice(2, 4), 16),
34
+ blue: Number.parseInt(normalized.slice(4, 6), 16),
35
+ };
36
+ }
37
+ /**
38
+ * Applies one Minecraft-style truecolor swatch to a string.
39
+ *
40
+ * @param code The legacy Minecraft color code to emulate.
41
+ * @param value The text to colorize.
42
+ * @returns The styled string, or the original text when colors are disabled.
43
+ * @remarks This uses truecolor ANSI so the palette stays stable across modern terminals.
44
+ * @lastModified 2026-03-19
45
+ * @author GPT-5.4
46
+ */
47
+ export function mc(code, value) {
48
+ if (!supportsCliColor()) {
49
+ return value;
50
+ }
51
+ const { red, green, blue } = hexToRgb(MINECRAFT_HEX[code]);
52
+ return `\u001b[38;2;${red};${green};${blue}m${value}\u001b[39m`;
53
+ }
54
+ /**
55
+ * Styles one primary CLI label.
56
+ *
57
+ * @param value The label text.
58
+ * @returns The highlighted label.
59
+ * @remarks Gold is used as the main CLI accent color.
60
+ * @lastModified 2026-03-19
61
+ * @author GPT-5.4
62
+ */
63
+ export function accent(value) {
64
+ return mc("6", value);
65
+ }
66
+ /**
67
+ * Styles one positive/success string.
68
+ *
69
+ * @param value The text to colorize.
70
+ * @returns The success-colored string.
71
+ * @remarks Green is used for successful actions and positive counts.
72
+ * @lastModified 2026-03-19
73
+ * @author GPT-5.4
74
+ */
75
+ export function success(value) {
76
+ return mc("a", value);
77
+ }
78
+ /**
79
+ * Styles one informational string.
80
+ *
81
+ * @param value The text to colorize.
82
+ * @returns The info-colored string.
83
+ * @remarks Aqua is used for list metadata and secondary highlights.
84
+ * @lastModified 2026-03-19
85
+ * @author GPT-5.4
86
+ */
87
+ export function info(value) {
88
+ return mc("b", value);
89
+ }
90
+ /**
91
+ * Styles one warning string.
92
+ *
93
+ * @param value The text to colorize.
94
+ * @returns The warning-colored string.
95
+ * @remarks Bright yellow is used for cautionary or count summary output.
96
+ * @lastModified 2026-03-19
97
+ * @author GPT-5.4
98
+ */
99
+ export function warning(value) {
100
+ return mc("e", value);
101
+ }
102
+ /**
103
+ * Styles one muted metadata string.
104
+ *
105
+ * @param value The text to colorize.
106
+ * @returns The muted string.
107
+ * @remarks Gray is used for low-emphasis metadata like slugs and file paths.
108
+ * @lastModified 2026-03-19
109
+ * @author GPT-5.4
110
+ */
111
+ export function muted(value) {
112
+ return mc("7", value);
113
+ }
114
+ /**
115
+ * Styles one subtle low-contrast string.
116
+ *
117
+ * @param value The text to colorize.
118
+ * @returns The subtle string.
119
+ * @remarks Dark gray is used for separators, labels, and empty-state output.
120
+ * @lastModified 2026-03-19
121
+ * @author GPT-5.4
122
+ */
123
+ export function subtle(value) {
124
+ return mc("8", value);
125
+ }
126
+ /**
127
+ * Styles one failure/error string.
128
+ *
129
+ * @param value The text to colorize.
130
+ * @returns The danger-colored string.
131
+ * @remarks Red is reserved for errors and destructive outcomes.
132
+ * @lastModified 2026-03-19
133
+ * @author GPT-5.4
134
+ */
135
+ export function danger(value) {
136
+ return mc("c", value);
137
+ }
138
+ /**
139
+ * Styles one prominent value with bold emphasis.
140
+ *
141
+ * @param value The text to emphasize.
142
+ * @returns The bold string.
143
+ * @remarks This intentionally leaves color choice to the caller.
144
+ * @lastModified 2026-03-19
145
+ * @author GPT-5.4
146
+ */
147
+ export function strong(value) {
148
+ return pc.bold(value);
149
+ }
@@ -4,6 +4,7 @@ export type BarekeyRuntimeConfig = {
4
4
  environment?: string;
5
5
  config?: {
6
6
  typegen?: "semantic" | "minimal";
7
+ strictGeneratedKeys?: boolean;
7
8
  mode?: "centralized" | "standalone";
8
9
  };
9
10
  };
@@ -39,6 +39,7 @@ export async function loadRuntimeConfig() {
39
39
  const config = decoded.right.config;
40
40
  const mode = config?.mode ?? "centralized";
41
41
  const typegen = mode === "standalone" ? "minimal" : (config?.typegen ?? decoded.right.typegen ?? "semantic");
42
+ const strictGeneratedKeys = config?.strictGeneratedKeys ?? false;
42
43
  return {
43
44
  path: configPath,
44
45
  config: {
@@ -48,6 +49,7 @@ export async function loadRuntimeConfig() {
48
49
  config: {
49
50
  mode,
50
51
  typegen,
52
+ strictGeneratedKeys,
51
53
  },
52
54
  },
53
55
  };
@@ -1,6 +1,7 @@
1
1
  import { type TypegenManifest } from "../contracts/index.js";
2
2
  export type CliTypegenMode = "semantic" | "minimal";
3
3
  export type CliRuntimeMode = "centralized" | "standalone";
4
+ export type CliStrictGeneratedKeys = boolean;
4
5
  export type CliTypegenResult = {
5
6
  written: boolean;
6
7
  path: string;
@@ -14,6 +15,7 @@ type TypegenIdentity = {
14
15
  projectSlug: string;
15
16
  stageSlug: string;
16
17
  typegenMode: CliTypegenMode;
18
+ strictGeneratedKeys: CliStrictGeneratedKeys;
17
19
  runtimeMode: CliRuntimeMode;
18
20
  localEnvRoot: string | null;
19
21
  };
@@ -27,7 +29,7 @@ type TypegenIdentity = {
27
29
  * @lastModified 2026-03-19
28
30
  * @author GPT-5.4
29
31
  */
30
- export declare function renderGeneratedTypesForManifest(manifest: TypegenManifest, mode: CliTypegenMode): {
32
+ export declare function renderGeneratedTypesForManifest(manifest: TypegenManifest, mode: CliTypegenMode, strictGeneratedKeys?: boolean): {
31
33
  serverContents: string;
32
34
  publicContents: string;
33
35
  };
@@ -12,6 +12,7 @@ const TypegenMetadataSchema = Schema.Struct({
12
12
  projectSlug: Schema.NullOr(Schema.String),
13
13
  stageSlug: Schema.NullOr(Schema.String),
14
14
  typegenMode: Schema.NullOr(Schema.Literal("semantic", "minimal")),
15
+ strictGeneratedKeys: Schema.Boolean,
15
16
  runtimeMode: Schema.NullOr(Schema.Literal("centralized", "standalone")),
16
17
  localEnvRoot: Schema.NullOr(Schema.String),
17
18
  });
@@ -50,7 +51,7 @@ function buildGeneratedTypesContents(manifest, input) {
50
51
  const importLine = input.mode === "semantic"
51
52
  ? `import type { EaseInOut, Env, Linear, Step } from "${input.typeModulePath}";\n\n`
52
53
  : "";
53
- return `/* eslint-disable */\n/* This file is generated by barekey typegen. */\n/* barekey-manifest-version: ${manifest.manifestVersion} */\n\n${importLine}declare module "${input.declaredModulePath}" {\n interface ${input.interfaceName} {\n${mapLines.length > 0 ? mapLines : ""}\n }\n}\n\nexport {};\n`;
54
+ return `/* eslint-disable */\n/* This file is generated by barekey typegen. */\n/* barekey-manifest-version: ${manifest.manifestVersion} */\n\n${importLine}declare module "${input.declaredModulePath}" {\n interface ${input.interfaceName} {\n${mapLines.length > 0 ? mapLines : ""}\n }\n\n interface ${input.configInterfaceName} {\n allowUnknownKeys: ${input.strictGeneratedKeys ? "false" : "true"};\n }\n}\n\nexport {};\n`;
54
55
  }
55
56
  function readManifestVersion(contents) {
56
57
  return contents?.match(MANIFEST_VERSION_PATTERN)?.[1]?.trim() ?? null;
@@ -132,6 +133,7 @@ function buildTypegenMetadataContents(lastGeneratedAt, identity) {
132
133
  projectSlug: identity.projectSlug,
133
134
  stageSlug: identity.stageSlug,
134
135
  typegenMode: identity.typegenMode,
136
+ strictGeneratedKeys: identity.strictGeneratedKeys,
135
137
  runtimeMode: identity.runtimeMode,
136
138
  localEnvRoot: identity.localEnvRoot,
137
139
  }, null, 2)}\n`;
@@ -146,21 +148,25 @@ function buildTypegenMetadataContents(lastGeneratedAt, identity) {
146
148
  * @lastModified 2026-03-19
147
149
  * @author GPT-5.4
148
150
  */
149
- export function renderGeneratedTypesForManifest(manifest, mode) {
151
+ export function renderGeneratedTypesForManifest(manifest, mode, strictGeneratedKeys = false) {
150
152
  const decodedManifest = Schema.decodeUnknownSync(TypegenManifestSchema)(manifest);
151
153
  return {
152
154
  serverContents: buildGeneratedTypesContents(decodedManifest, {
153
155
  mode,
156
+ strictGeneratedKeys,
154
157
  typeModulePath: "./dist/types.js",
155
158
  declaredModulePath: "./dist/types.js",
156
159
  interfaceName: "BarekeyGeneratedTypeMap",
160
+ configInterfaceName: "BarekeyGeneratedTypeConfig",
157
161
  include: () => true,
158
162
  }),
159
163
  publicContents: buildGeneratedTypesContents(decodedManifest, {
160
164
  mode,
165
+ strictGeneratedKeys,
161
166
  typeModulePath: "./dist/public-types.js",
162
167
  declaredModulePath: "./dist/public-types.js",
163
168
  interfaceName: "BarekeyPublicGeneratedTypeMap",
169
+ configInterfaceName: "BarekeyPublicGeneratedTypeConfig",
164
170
  include: (row) => row.visibility === "public",
165
171
  }),
166
172
  };
@@ -190,7 +196,7 @@ export async function writeInstalledSdkGeneratedTypes(manifest, identity) {
190
196
  readTextFile(target.serverGeneratedTypesPath),
191
197
  readTextFile(target.publicGeneratedTypesPath),
192
198
  ]);
193
- const rendered = renderGeneratedTypesForManifest(manifest, identity.typegenMode);
199
+ const rendered = renderGeneratedTypesForManifest(manifest, identity.typegenMode, identity.strictGeneratedKeys);
194
200
  if (existingServerContents === rendered.serverContents &&
195
201
  existingPublicContents === rendered.publicContents &&
196
202
  readManifestVersion(existingServerContents) === manifest.manifestVersion &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barekey/cli",
3
- "version": "0.5.8",
3
+ "version": "0.6.0",
4
4
  "description": "Barekey command line interface",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,6 +4,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
4
4
  import { requireLocalSession, toJsonOutput, type EnvTargetOptions } from "../command-utils.js";
5
5
  import { AuditListResponseSchema } from "../contracts/index.js";
6
6
  import { postJson } from "../http.js";
7
+ import { info, muted, subtle, strong } from "../output/theme.js";
7
8
  import { promptForOrganizationSlug } from "./target-prompts.js";
8
9
 
9
10
  async function runAuditList(
@@ -36,12 +37,14 @@ async function runAuditList(
36
37
  }
37
38
 
38
39
  if (response.items.length === 0) {
39
- console.log("No audit events found.");
40
+ console.log(subtle("No audit events found."));
40
41
  return;
41
42
  }
42
43
 
43
44
  for (const item of response.items) {
44
- console.log(`${new Date(item.occurredAtMs).toISOString()} ${item.category} ${item.title}`);
45
+ console.log(
46
+ `${muted(new Date(item.occurredAtMs).toISOString())} ${info(item.category)} ${strong(item.title)}`,
47
+ );
45
48
  }
46
49
  }
47
50
 
@@ -22,6 +22,7 @@ import {
22
22
  } from "../contracts/index.js";
23
23
  import { getJson, postJson } from "../http.js";
24
24
  import { formatWhoamiWithAvatar } from "../output/avatar.js";
25
+ import { accent, muted, strong, success } from "../output/theme.js";
25
26
  import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
26
27
 
27
28
  function resolveClientName(): string | undefined {
@@ -88,8 +89,8 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
88
89
 
89
90
  loading.stop("Authorization initialized");
90
91
  loadingActive = false;
91
- console.log(`${pc.bold("Open")}: ${verificationUri}`);
92
- console.log(`${pc.bold("Code")}: ${started.userCode}`);
92
+ console.log(`${accent("Open")} ${muted(verificationUri)}`);
93
+ console.log(`${accent("Code")} ${strong(started.userCode)}`);
93
94
 
94
95
  try {
95
96
  await open(verificationUri);
@@ -149,7 +150,7 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
149
150
 
150
151
  pollSpinner.stop("Login approved");
151
152
  pollActive = false;
152
- outro(`Signed in as ${pc.bold(resolveDisplayName(refreshed))}.`);
153
+ outro(`${success("Signed in as")} ${pc.bold(resolveDisplayName(refreshed))}.`);
153
154
  return;
154
155
  }
155
156
 
@@ -178,7 +179,7 @@ async function runLogout(): Promise<void> {
178
179
  });
179
180
  await deleteCredentials(local.accountId);
180
181
  await clearConfig();
181
- console.log("Logged out.");
182
+ console.log(success("Logged out."));
182
183
  }
183
184
 
184
185
  async function runWhoami(options: { json?: boolean }): Promise<void> {
@@ -4,6 +4,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
4
4
  import { requireLocalSession, toJsonOutput, type EnvTargetOptions } from "../command-utils.js";
5
5
  import { BillingCatalogResponseSchema, BillingStatusResponseSchema } from "../contracts/index.js";
6
6
  import { getJson, postJson } from "../http.js";
7
+ import { accent, info, muted, strong, subtle, warning } from "../output/theme.js";
7
8
  import { promptForOrganizationSlug } from "./target-prompts.js";
8
9
 
9
10
  async function runBillingCatalog(options: { json?: boolean }) {
@@ -19,8 +20,10 @@ async function runBillingCatalog(options: { json?: boolean }) {
19
20
  return;
20
21
  }
21
22
 
22
- console.log(`Plans: ${response.variants.length}`);
23
- console.log(`Metered features: ${Object.values(response.featureIds).join(", ")}`);
23
+ console.log(`${subtle("Plans")} ${strong(String(response.variants.length))}`);
24
+ console.log(
25
+ `${subtle("Metered features")} ${info(Object.values(response.featureIds).join(", "))}`,
26
+ );
24
27
  }
25
28
 
26
29
  async function runBillingStatus(options: EnvTargetOptions & { json?: boolean }) {
@@ -43,9 +46,11 @@ async function runBillingStatus(options: EnvTargetOptions & { json?: boolean })
43
46
  return;
44
47
  }
45
48
 
46
- console.log(`Tier: ${response.currentTier ?? "none"}`);
47
- console.log(`Product: ${response.currentProductId ?? "none"}`);
48
- console.log(`Can manage billing: ${response.canManageBilling ? "yes" : "no"}`);
49
+ console.log(`${subtle("Tier")} ${warning(response.currentTier ?? "none")}`);
50
+ console.log(`${subtle("Product")} ${muted(response.currentProductId ?? "none")}`);
51
+ console.log(
52
+ `${subtle("Can manage billing")} ${response.canManageBilling ? accent("yes") : muted("no")}`,
53
+ );
49
54
  }
50
55
 
51
56
  export function registerBillingCommands(program: Command): void {
@@ -29,6 +29,7 @@ import {
29
29
  type CliRolloutFunction,
30
30
  type CliVisibility,
31
31
  } from "./env-helpers.js";
32
+ import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
32
33
 
33
34
  type EnvWriteOptions = EnvTargetOptions & {
34
35
  ab?: string;
@@ -158,7 +159,7 @@ async function runEnvGet(
158
159
  return;
159
160
  }
160
161
 
161
- console.log(renderScalar(value.value));
162
+ console.log(`${accent(resolvedName)} ${subtle("=")} ${strong(renderScalar(value.value))}`);
162
163
  }
163
164
 
164
165
  async function runEnvGetMany(
@@ -201,7 +202,7 @@ async function runEnvGetMany(
201
202
  }
202
203
 
203
204
  for (const value of [...response.values].sort((left, right) => left.name.localeCompare(right.name))) {
204
- console.log(`${value.name}=${renderScalar(value.value)}`);
205
+ console.log(`${accent(value.name)}${subtle("=")}${strong(renderScalar(value.value))}`);
205
206
  }
206
207
  }
207
208
 
@@ -225,7 +226,7 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
225
226
  }
226
227
 
227
228
  if (response.variables.length === 0) {
228
- console.log("No variables found.");
229
+ console.log(subtle("No variables found."));
229
230
  return;
230
231
  }
231
232
 
@@ -235,8 +236,10 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
235
236
  row.kind === "rollout"
236
237
  ? ` ${row.rolloutFunction ?? "linear"}(${row.rolloutMilestones?.length ?? 0} milestones)`
237
238
  : "";
239
+ const formattedChanceSuffix = chanceSuffix.length > 0 ? muted(chanceSuffix) : "";
240
+ const formattedRolloutSuffix = rolloutSuffix.length > 0 ? muted(rolloutSuffix) : "";
238
241
  console.log(
239
- `${row.name} ${row.visibility} ${row.kind} ${row.declaredType}${chanceSuffix}${rolloutSuffix}`,
242
+ `${strong(row.name)} ${muted(row.visibility)} ${info(row.kind)} ${accent(row.declaredType)}${formattedChanceSuffix}${formattedRolloutSuffix}`,
240
243
  );
241
244
  }
242
245
  }
@@ -320,7 +323,7 @@ async function runEnvWrite(
320
323
  }
321
324
 
322
325
  console.log(
323
- `Created: ${result.createdCount}, Updated: ${result.updatedCount}, Deleted: ${result.deletedCount}`,
326
+ `${success("Created")} ${strong(String(result.createdCount))} ${subtle("•")} ${warning("Updated")} ${strong(String(result.updatedCount))} ${subtle("•")} ${muted("Deleted")} ${strong(String(result.deletedCount))}`,
324
327
  );
325
328
  }
326
329
 
@@ -390,7 +393,7 @@ async function runEnvDelete(
390
393
  return;
391
394
  }
392
395
 
393
- console.log(`Deleted: ${result.deletedCount}`);
396
+ console.log(`${success("Deleted")} ${strong(String(result.deletedCount))}`);
394
397
  }
395
398
 
396
399
  async function runEnvPull(
@@ -426,12 +429,12 @@ async function runEnvPull(
426
429
 
427
430
  if (options.out) {
428
431
  await writeFile(options.out, output, "utf8");
429
- console.log(`Wrote ${options.out}`);
432
+ console.log(`${success("Wrote")} ${muted(options.out)}`);
430
433
  return;
431
434
  }
432
435
 
433
436
  if (options.redact && !process.stdout.isTTY) {
434
- console.log(`Pulled ${response.values.length} variables.`);
437
+ console.log(`${success("Pulled")} ${strong(String(response.values.length))} ${muted("variables.")}`);
435
438
  return;
436
439
  }
437
440
 
@@ -9,6 +9,7 @@ import {
9
9
  promptForProjectSlug,
10
10
  promptForStageSlug,
11
11
  } from "./target-prompts.js";
12
+ import { muted, success } from "../output/theme.js";
12
13
 
13
14
  async function runInit(options: EnvTargetOptions & { path?: string }) {
14
15
  const orgSlug = await promptForOrganizationSlug(options.org);
@@ -20,17 +21,18 @@ async function runInit(options: EnvTargetOptions & { path?: string }) {
20
21
  organization: orgSlug,
21
22
  project: projectSlug,
22
23
  environment: stageSlug,
23
- config: {
24
- mode: "centralized",
25
- typegen: "semantic",
26
- },
24
+ config: {
25
+ mode: "centralized",
26
+ typegen: "semantic",
27
+ strictGeneratedKeys: false,
28
+ },
27
29
  },
28
30
  null,
29
31
  2,
30
32
  )}\n`;
31
33
 
32
34
  await writeFile(configPath, contents, "utf8");
33
- console.log(`Wrote ${configPath}`);
35
+ console.log(`${success("Wrote")} ${muted(configPath)}`);
34
36
  }
35
37
 
36
38
  export function registerInitCommand(program: Command): void {
@@ -5,6 +5,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
5
5
  import { requireLocalSession, toJsonOutput } from "../command-utils.js";
6
6
  import { OrganizationsResponseSchema } from "../contracts/index.js";
7
7
  import { getJson, postJson } from "../http.js";
8
+ import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
8
9
 
9
10
  async function promptForName(name: string | undefined): Promise<string> {
10
11
  const existing = name?.trim();
@@ -42,12 +43,14 @@ async function runOrgList(options: { json?: boolean }) {
42
43
  }
43
44
 
44
45
  if (response.organizations.length === 0) {
45
- console.log("No organizations found.");
46
+ console.log(subtle("No organizations found."));
46
47
  return;
47
48
  }
48
49
 
49
50
  for (const organization of response.organizations) {
50
- console.log(`${organization.name} (${organization.slug}) ${organization.role}`);
51
+ console.log(
52
+ `${strong(organization.name)} ${muted(`(${organization.slug})`)} ${info(organization.role)}`,
53
+ );
51
54
  }
52
55
  }
53
56
 
@@ -78,7 +81,9 @@ async function runOrgCreate(
78
81
  }
79
82
 
80
83
  const organization = (response as { organization: { name: string; slug: string } }).organization;
81
- console.log(`Created organization ${organization.name} (${organization.slug}).`);
84
+ console.log(
85
+ `${success("Created")} ${accent("organization")} ${strong(organization.name)} ${muted(`(${organization.slug})`)}`,
86
+ );
82
87
  }
83
88
 
84
89
  export function registerOrgCommands(program: Command): void {
@@ -9,6 +9,7 @@ import {
9
9
  promptForOrganizationSlug,
10
10
  promptForProjectSlug,
11
11
  } from "./target-prompts.js";
12
+ import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
12
13
 
13
14
  async function promptForProjectName(name: string | undefined): Promise<string> {
14
15
  const existing = name?.trim();
@@ -39,12 +40,14 @@ async function runProjectList(options: EnvTargetOptions & { json?: boolean }) {
39
40
  }
40
41
 
41
42
  if (projects.length === 0) {
42
- console.log("No projects found.");
43
+ console.log(subtle("No projects found."));
43
44
  return;
44
45
  }
45
46
 
46
47
  for (const project of projects) {
47
- console.log(`${project.name} (${project.slug}) secrets=${project.secretCount}`);
48
+ console.log(
49
+ `${strong(project.name)} ${muted(`(${project.slug})`)} ${info(`secrets=${project.secretCount}`)}`,
50
+ );
48
51
  }
49
52
  }
50
53
 
@@ -61,7 +64,9 @@ async function runProjectCreate(
61
64
  return;
62
65
  }
63
66
 
64
- console.log(`Created project ${project.name} (${project.slug}).`);
67
+ console.log(
68
+ `${success("Created")} ${accent("project")} ${strong(project.name)} ${muted(`(${project.slug})`)}`,
69
+ );
65
70
  }
66
71
 
67
72
  export function registerProjectCommands(program: Command): void {
@@ -124,7 +129,9 @@ export function registerProjectCommands(program: Command): void {
124
129
  return;
125
130
  }
126
131
 
127
- console.log(`Deleted project ${response.deletedProjectSlug}.`);
132
+ console.log(
133
+ `${success("Deleted")} ${accent("project")} ${muted(response.deletedProjectSlug)}`,
134
+ );
128
135
  },
129
136
  );
130
137
  }
@@ -11,6 +11,7 @@ import {
11
11
  promptForStageSlug,
12
12
  renameStageForProject,
13
13
  } from "./target-prompts.js";
14
+ import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
14
15
 
15
16
  async function promptForStageName(name: string | undefined): Promise<string> {
16
17
  const existing = name?.trim();
@@ -42,12 +43,14 @@ async function runStageList(options: EnvTargetOptions & { json?: boolean }) {
42
43
  }
43
44
 
44
45
  if (stages.length === 0) {
45
- console.log("No stages found.");
46
+ console.log(subtle("No stages found."));
46
47
  return;
47
48
  }
48
49
 
49
50
  for (const stage of stages) {
50
- console.log(`${stage.name} (${stage.slug}) variables=${stage.variableCount}`);
51
+ console.log(
52
+ `${strong(stage.name)} ${muted(`(${stage.slug})`)} ${info(`variables=${stage.variableCount}`)}`,
53
+ );
51
54
  }
52
55
  }
53
56
 
@@ -65,7 +68,9 @@ async function runStageCreate(
65
68
  return;
66
69
  }
67
70
 
68
- console.log(`Created stage ${stage.name} (${stage.slug}).`);
71
+ console.log(
72
+ `${success("Created")} ${accent("stage")} ${strong(stage.name)} ${muted(`(${stage.slug})`)}`,
73
+ );
69
74
  }
70
75
 
71
76
  export function registerStageCommands(program: Command): void {
@@ -117,7 +122,9 @@ export function registerStageCommands(program: Command): void {
117
122
  return;
118
123
  }
119
124
 
120
- console.log(`Renamed stage ${stage.slug} to ${stage.name}.`);
125
+ console.log(
126
+ `${warning("Renamed")} ${accent("stage")} ${muted(stage.slug)} ${subtle("→")} ${strong(stage.name)}`,
127
+ );
121
128
  },
122
129
  );
123
130
 
@@ -161,7 +168,7 @@ export function registerStageCommands(program: Command): void {
161
168
  return;
162
169
  }
163
170
 
164
- console.log(`Deleted stage ${response.deletedStageSlug}.`);
171
+ console.log(`${success("Deleted")} ${accent("stage")} ${muted(response.deletedStageSlug)}`);
165
172
  },
166
173
  );
167
174
  }