@keystrokehq/cli 0.0.131 → 0.0.133

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.
package/dist/index.mjs CHANGED
@@ -1852,6 +1852,8 @@ const OrganizationSlugSchema = string().trim().toLowerCase().min(2, "Slug must b
1852
1852
  * fail to load.
1853
1853
  */
1854
1854
  const ClaimableOrganizationSlugSchema = OrganizationSlugSchema.refine((slug) => !RESERVED_ORGANIZATION_SLUGS.has(slug), "This slug is reserved");
1855
+ /** Same format as organization slugs — org-scoped, no global reserved list. */
1856
+ const ProjectSlugSchema = OrganizationSlugSchema;
1855
1857
  /** Browser→platform request budget (ping + small platform overhead). */
1856
1858
  const PROJECT_REACHABILITY_REQUEST_TIMEOUT_MS = 17e3;
1857
1859
  /** Normalize a public origin URL (no trailing slash). */
@@ -2053,6 +2055,17 @@ object({
2053
2055
  slug: string().trim().min(1),
2054
2056
  excludeOrganizationId: string().min(1).optional()
2055
2057
  });
2058
+ const ProjectSlugAvailabilityReasonSchema = _enum(["taken", "invalid"]);
2059
+ const ProjectSlugAvailabilityResponseSchema = object({
2060
+ slug: string(),
2061
+ available: boolean(),
2062
+ reason: ProjectSlugAvailabilityReasonSchema.optional(),
2063
+ suggestion: ProjectSlugSchema.optional()
2064
+ });
2065
+ object({
2066
+ slug: string().trim().min(1),
2067
+ excludeProjectId: string().min(1).optional()
2068
+ });
2056
2069
  const ProjectStatusSchema = _enum([
2057
2070
  "inactive",
2058
2071
  "starting",
@@ -2076,6 +2089,7 @@ const ProjectSchema = object({
2076
2089
  id: string(),
2077
2090
  organizationId: string(),
2078
2091
  name: string(),
2092
+ slug: ProjectSlugSchema,
2079
2093
  description: string().nullable(),
2080
2094
  status: ProjectStatusSchema,
2081
2095
  baseUrl: string().nullable(),
@@ -2116,8 +2130,9 @@ const CreateProjectRequestSchema = object({
2116
2130
  });
2117
2131
  const UpdateProjectRequestSchema = object({
2118
2132
  name: string().trim().min(1).optional(),
2119
- description: string().trim().optional()
2120
- }).refine((data) => data.name !== void 0 || data.description !== void 0, { message: "At least one field is required" });
2133
+ description: string().trim().optional(),
2134
+ slug: ProjectSlugSchema.optional()
2135
+ }).refine((data) => data.name !== void 0 || data.description !== void 0 || data.slug !== void 0, { message: "At least one field is required" });
2121
2136
  const CreateProjectResponseSchema = object({ project: ProjectSchema });
2122
2137
  const ProjectResponseSchema = object({ project: ProjectSchema });
2123
2138
  const ProjectReachabilityResponseSchema = object({ reachable: boolean() });
@@ -2670,6 +2685,16 @@ const CompleteProjectArtifactResponseSchema = object({
2670
2685
  artifact: ProjectArtifactSchema,
2671
2686
  project: ProjectSchema
2672
2687
  });
2688
+ const ListProjectDeploymentsResponseSchema = object({ deployments: array(object({
2689
+ id: string(),
2690
+ projectId: string(),
2691
+ version: number$1().int().positive(),
2692
+ deployedBy: string().nullable(),
2693
+ deployedByEmail: string().optional(),
2694
+ deployedByAvatarUrl: string().optional(),
2695
+ createdAt: isoDateTime$1,
2696
+ active: boolean()
2697
+ })) });
2673
2698
  const optionalCount = number$1().int().nonnegative().nullable().optional();
2674
2699
  const optionalTimestamp = string().nullable().optional();
2675
2700
  const AgentSummarySchema = object({
@@ -4979,6 +5004,16 @@ function createArtifactsResource(http) {
4979
5004
  }
4980
5005
  };
4981
5006
  }
5007
+ function createDeploymentsResource(http) {
5008
+ return { async listForProject(projectId) {
5009
+ try {
5010
+ const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/deployments`).json();
5011
+ return ListProjectDeploymentsResponseSchema.parse(data).deployments;
5012
+ } catch (error) {
5013
+ throw await toPlatformError(error);
5014
+ }
5015
+ } };
5016
+ }
4982
5017
  function createGatewayAttachmentsResource(http) {
4983
5018
  const projectPrefix = (projectId) => `/api/projects/${encodeURIComponent(projectId)}/gateways/slack/attachments`;
4984
5019
  return {
@@ -5154,6 +5189,16 @@ function createProjectsResource(http) {
5154
5189
  } catch (error) {
5155
5190
  throw await toPlatformError(error);
5156
5191
  }
5192
+ },
5193
+ async checkSlug(slug, options = {}) {
5194
+ const searchParams = new URLSearchParams({ slug });
5195
+ if (options.excludeProjectId) searchParams.set("excludeProjectId", options.excludeProjectId);
5196
+ try {
5197
+ const data = await http.get(`/api/projects/slug-available?${searchParams}`).json();
5198
+ return ProjectSlugAvailabilityResponseSchema.parse(data);
5199
+ } catch (error) {
5200
+ throw await toPlatformError(error);
5201
+ }
5157
5202
  }
5158
5203
  };
5159
5204
  }
@@ -6774,27 +6819,6 @@ const workflowSeed = [{
6774
6819
  stepCount: 4,
6775
6820
  lastRunAt: "2026-05-31T16:40:00Z"
6776
6821
  }];
6777
- const deploymentSeed = [{
6778
- id: "deployment-v002",
6779
- projectId: "project_personal_repo",
6780
- version: "v0.0.2",
6781
- commit: "0842e21",
6782
- commitSha: "0842e21",
6783
- deployedBy: "Dallin Bentley",
6784
- deployedByEmail: "dallin@keystroke.ai",
6785
- createdAt: "2026-04-20T15:44:00Z",
6786
- active: true
6787
- }, {
6788
- id: "deployment-v001",
6789
- projectId: "project_personal_repo",
6790
- version: "v0.0.1",
6791
- commit: "5aeb762",
6792
- commitSha: "5aeb762",
6793
- deployedBy: "Dallin Bentley",
6794
- deployedByEmail: "dallin@keystroke.ai",
6795
- createdAt: "2026-04-18T15:44:00Z",
6796
- active: false
6797
- }];
6798
6822
  /** Review personas for mocked project members and API key authors (not org membership). */
6799
6823
  const mockPersonaSeed = [
6800
6824
  {
@@ -6983,18 +7007,6 @@ function maskSecretPreview(value) {
6983
7007
  if (trimmed.length <= 4) return "•".repeat(Math.max(trimmed.length, 4));
6984
7008
  return `${"•".repeat(Math.min(trimmed.length - 4, 20))}${trimmed.slice(-4)}`;
6985
7009
  }
6986
- function createDeploymentsResource() {
6987
- return { listForProject: mock({
6988
- domain: "deployments",
6989
- method: "listForProject",
6990
- plannedEndpoint: "GET /api/projects/:id/deployments"
6991
- }, async (projectId) => {
6992
- return deploymentSeed.map((deployment) => ({
6993
- ...deployment,
6994
- projectId
6995
- }));
6996
- }) };
6997
- }
6998
7010
  const projectMembersStore = /* @__PURE__ */ new Map();
6999
7011
  function getProjectMembers(projectId) {
7000
7012
  let members = projectMembersStore.get(projectId);
@@ -8560,7 +8572,7 @@ function createPlatformClient(options) {
8560
8572
  apps: createAppsResource(),
8561
8573
  credentials: createCredentialsResource(),
8562
8574
  history: createHistoryResource(),
8563
- deployments: createDeploymentsResource(),
8575
+ deployments: createDeploymentsResource(http),
8564
8576
  gatewayAttachments: createGatewayAttachmentsResource(http),
8565
8577
  projectMetrics: createProjectMetricsResource(http),
8566
8578
  projectFiles: createProjectFilesResource(http),
@@ -8600,6 +8612,33 @@ function createCliPlatformClient(config) {
8600
8612
  });
8601
8613
  }
8602
8614
  //#endregion
8615
+ //#region src/resolve-resource-ref.ts
8616
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8617
+ function looksLikeUuid(value) {
8618
+ return UUID_PATTERN.test(value);
8619
+ }
8620
+ function formatProjectLabel(project) {
8621
+ return project.slug;
8622
+ }
8623
+ async function resolveProjectRef(platform, ref) {
8624
+ if (looksLikeUuid(ref)) try {
8625
+ return await platform.projects.get(ref);
8626
+ } catch (error) {
8627
+ if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${ref} not found in the active organization. Run \`keystroke project list\`, then \`keystroke config use project <slug>\`.`);
8628
+ throw error;
8629
+ }
8630
+ const match = (await platform.projects.list()).find((project) => project.slug === ref);
8631
+ if (!match) throw new Error(`Project "${ref}" not found in the active organization. Run \`keystroke project list\`.`);
8632
+ return match;
8633
+ }
8634
+ function resolveOrganizationRef(organizations, ref) {
8635
+ const byId = organizations.find((entry) => entry.organization.id === ref);
8636
+ if (byId) return byId;
8637
+ const bySlug = organizations.find((entry) => entry.organization.slug === ref);
8638
+ if (bySlug) return bySlug;
8639
+ throw new Error(`Organization "${ref}" was not found or you do not have access`);
8640
+ }
8641
+ //#endregion
8603
8642
  //#region src/organization/active-organization.ts
8604
8643
  function formatOrganization(membership) {
8605
8644
  return `${membership.organization.name} (${membership.role})`;
@@ -8613,15 +8652,11 @@ async function activateOrganization(config, organizationId) {
8613
8652
  platform.setActiveOrganizationId(membership.organization.id);
8614
8653
  return membership;
8615
8654
  }
8616
- async function pickOrganization(config, organizations, organizationId) {
8617
- if (organizationId) {
8618
- const membership = organizations.find((entry) => entry.organization.id === organizationId);
8619
- if (!membership) throw new Error(`Organization ${organizationId} was not found or you do not have access`);
8620
- return activateOrganization(config, membership.organization.id);
8621
- }
8655
+ async function pickOrganization(config, organizations, organizationRef) {
8656
+ if (organizationRef) return activateOrganization(config, resolveOrganizationRef(organizations, organizationRef).organization.id);
8622
8657
  if (!process.stdin.isTTY) {
8623
- const names = organizations.map((entry) => entry.organization.id).join(", ");
8624
- throw new Error(`Multiple organizations found. Run \`keystroke config use org <id>\`. Options: ${names}`);
8658
+ const names = organizations.map((entry) => entry.organization.slug).join(", ");
8659
+ throw new Error(`Multiple organizations found. Run \`keystroke config use org <slug>\`. Options: ${names}`);
8625
8660
  }
8626
8661
  return activateOrganization(config, await select({
8627
8662
  message: "Select an organization",
@@ -8635,7 +8670,7 @@ async function selectActiveOrganization(config, options = {}) {
8635
8670
  const organizations = await listOrganizations(config);
8636
8671
  if (organizations.length === 0) throw new Error(`No organization found — create one at ${getWebUrl(config)}`);
8637
8672
  if (organizations.length === 1) return activateOrganization(config, organizations[0].organization.id);
8638
- return pickOrganization(config, organizations, options.organizationId);
8673
+ return pickOrganization(config, organizations, options.organizationRef ?? options.organizationId);
8639
8674
  }
8640
8675
  async function syncActiveOrganizationAfterLogin(config, options = {}) {
8641
8676
  const organizations = await listOrganizations(config);
@@ -8651,8 +8686,8 @@ async function syncActiveOrganizationAfterLogin(config, options = {}) {
8651
8686
  process.stdout.write(`Using organization ${membership.organization.name}\n`);
8652
8687
  return membership;
8653
8688
  }
8654
- if (options.organizationId) {
8655
- const membership = await pickOrganization(config, organizations, options.organizationId);
8689
+ if (options.organizationRef ?? options.organizationId) {
8690
+ const membership = await pickOrganization(config, organizations, options.organizationRef ?? options.organizationId);
8656
8691
  process.stdout.write(`Using organization ${membership.organization.name}\n`);
8657
8692
  return membership;
8658
8693
  }
@@ -8725,19 +8760,19 @@ function resolveOrgPlatformTarget(config) {
8725
8760
  mode: "platform"
8726
8761
  };
8727
8762
  }
8728
- async function resolvePlatformTarget(config, projectId) {
8763
+ async function resolvePlatformTarget(config, projectRef) {
8729
8764
  const platform = createCliPlatformClient(config);
8730
8765
  let project;
8731
8766
  try {
8732
- project = await platform.projects.get(projectId);
8767
+ project = await resolveProjectRef(platform, projectRef);
8733
8768
  } catch (error) {
8734
- if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${projectId} not found in the active organization. Run \`keystroke config use project <id>\` or pass \`--project\` with a valid project id.`);
8769
+ if (error instanceof Error) throw error;
8735
8770
  throw error;
8736
8771
  }
8737
- if (project.status !== "active") throw new Error(`Project ${projectId} is not active (${project.status})`);
8772
+ if (project.status !== "active") throw new Error(`Project ${formatProjectLabel(project)} is not active (${project.status})`);
8738
8773
  return {
8739
- baseUrl: `${getPlatformUrl(config).replace(/\/+$/, "")}/api/projects/${encodeURIComponent(projectId)}`,
8740
- projectId,
8774
+ baseUrl: `${getPlatformUrl(config).replace(/\/+$/, "")}/api/projects/${encodeURIComponent(project.id)}`,
8775
+ projectId: project.id,
8741
8776
  mode: "platform"
8742
8777
  };
8743
8778
  }
@@ -8755,9 +8790,9 @@ async function resolveApiTarget(config, options = {}) {
8755
8790
  if (options.projectId) return resolvePlatformTarget(config, options.projectId);
8756
8791
  if (getEffectiveApiTarget(config) === "local") return resolveLocalTarget(config);
8757
8792
  if (!options.projectScoped) return resolveOrgPlatformTarget(config);
8758
- const projectId = options.projectId ?? config.get("activeProjectId");
8759
- if (!projectId) throw new Error("No project selected. Pass `--project <id>` or run `keystroke config use project <id>`.");
8760
- return resolvePlatformTarget(config, projectId);
8793
+ const projectRef = options.projectId ?? config.get("activeProjectId");
8794
+ if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke config use project <slug>`.");
8795
+ return resolvePlatformTarget(config, projectRef);
8761
8796
  }
8762
8797
  //#endregion
8763
8798
  //#region src/target-options.ts
@@ -8768,9 +8803,6 @@ function setCliTargetOptions(options) {
8768
8803
  function getCliTargetOptions() {
8769
8804
  return targetOptions;
8770
8805
  }
8771
- function resolveActiveProjectId(config) {
8772
- return getCliTargetOptions().projectId ?? config.get("activeProjectId");
8773
- }
8774
8806
  //#endregion
8775
8807
  //#region src/errors/example-input.ts
8776
8808
  function exampleValueFromValidationMessage(message) {
@@ -9245,7 +9277,7 @@ function resolveAuthLoginTargets(options, config) {
9245
9277
  //#endregion
9246
9278
  //#region src/commands/auth/login.ts
9247
9279
  function registerAuthLoginCommand(auth) {
9248
- auth.command("login").description("Authenticate via browser device flow").option("--web-url <url>", "Web dashboard origin (omit to use production defaults; local dev: http://localhost:3000)").option("--platform-url <url>", "Platform API origin (derived from web URL when omitted)").option("--org <id>", "Active organization id (skips prompt when you belong to several)").action(async (options) => {
9280
+ auth.command("login").description("Authenticate via browser device flow").option("--web-url <url>", "Web dashboard origin (omit to use production defaults; local dev: http://localhost:3000)").option("--platform-url <url>", "Platform API origin (derived from web URL when omitted)").option("--org <slug>", "Active organization slug (skips prompt when you belong to several)").action(async (options) => {
9249
9281
  const config = createCliConfig();
9250
9282
  const { webUrl, platformUrl } = resolveAuthLoginTargets(options, config);
9251
9283
  config.set("webUrl", webUrl);
@@ -9260,7 +9292,7 @@ function registerAuthLoginCommand(auth) {
9260
9292
  const user = await getSessionWithBearer(platformUrl, result.accessToken);
9261
9293
  if (user) process.stdout.write(`Logged in as ${user.name} (${user.email})\n`);
9262
9294
  else process.stdout.write(`Logged in to ${webUrl}\n`);
9263
- await syncActiveOrganizationAfterLogin(config, { organizationId: options.org });
9295
+ await syncActiveOrganizationAfterLogin(config, { organizationRef: options.org });
9264
9296
  } catch (error) {
9265
9297
  process.stderr.write(`${formatCliError(error, "Login failed", {
9266
9298
  webUrl,
@@ -9480,6 +9512,18 @@ function collectProjectSource(root) {
9480
9512
  };
9481
9513
  }
9482
9514
  //#endregion
9515
+ //#region src/resolve-project-target.ts
9516
+ async function resolveActiveProject(config, platform) {
9517
+ const ref = getCliTargetOptions().projectId ?? config.get("activeProjectId");
9518
+ if (!ref) return;
9519
+ const client = platform ?? createCliPlatformClient(config);
9520
+ if (!getCliTargetOptions().projectId && looksLikeUuid(ref)) return resolveProjectRef(client, ref);
9521
+ return resolveProjectRef(client, ref);
9522
+ }
9523
+ async function resolveActiveProjectId(config) {
9524
+ return (await resolveActiveProject(config))?.id;
9525
+ }
9526
+ //#endregion
9483
9527
  //#region src/commands/deploy.ts
9484
9528
  const POLL_INTERVAL_MS = 2e3;
9485
9529
  const DEPLOY_TIMEOUT_MS = 12e4;
@@ -9570,8 +9614,8 @@ async function runDeploy(options) {
9570
9614
  function registerDeployCommand(program) {
9571
9615
  program.command("deploy").description("Build, upload, and deploy the project to the platform").option("--dir <path>", "Project directory", process.cwd()).option("--skip-build", "Upload the existing dist/ without rebuilding").action(async (options) => {
9572
9616
  try {
9573
- const projectId = resolveActiveProjectId(createCliConfig());
9574
- if (!projectId) throw new Error("Usage: keystroke deploy --project <id>");
9617
+ const projectId = await resolveActiveProjectId(createCliConfig());
9618
+ if (!projectId) throw new Error("Usage: keystroke deploy --project <slug>");
9575
9619
  await runDeploy({
9576
9620
  dir: options.dir,
9577
9621
  projectId,
@@ -10131,7 +10175,7 @@ function writeInactiveDeployHints(projects) {
10131
10175
  const bin = cliBinaryName();
10132
10176
  for (const project of projects) {
10133
10177
  if (project.status !== "inactive") continue;
10134
- process.stderr.write(`Project ${project.id} is inactive. Deploy with: ${bin} deploy --project ${project.id}\n`);
10178
+ process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} deploy --project ${formatProjectLabel(project)}\n`);
10135
10179
  }
10136
10180
  }
10137
10181
  async function runProjectList(config) {
@@ -10149,10 +10193,22 @@ async function runProjectList(config) {
10149
10193
  async function runProjectMetrics(config, options) {
10150
10194
  await withActivePlatformClient(config, async (platform) => {
10151
10195
  const metrics = await platform.projectMetrics.list();
10152
- const output = options.projectId === void 0 ? metrics : Object.fromEntries(Object.entries(metrics).filter(([projectId]) => projectId === options.projectId));
10196
+ if (options.projectRef === void 0) {
10197
+ process.stdout.write(`${JSON.stringify(metrics, null, 2)}\n`);
10198
+ return;
10199
+ }
10200
+ const project = await resolveProjectRef(platform, options.projectRef);
10201
+ const output = Object.fromEntries(Object.entries(metrics).filter(([projectId]) => projectId === project.id));
10153
10202
  process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
10154
10203
  });
10155
10204
  }
10205
+ async function runProjectDeploymentsList(config, options) {
10206
+ await withActivePlatformClient(config, async (platform) => {
10207
+ const project = await resolveProjectRef(platform, options.projectRef);
10208
+ const deployments = await platform.deployments.listForProject(project.id);
10209
+ process.stdout.write(`${JSON.stringify({ deployments }, null, 2)}\n`);
10210
+ });
10211
+ }
10156
10212
  async function runProjectCreate(config, input) {
10157
10213
  await withActivePlatformClient(config, async (platform) => {
10158
10214
  const project = await platform.projects.create(input);
@@ -10213,10 +10269,10 @@ function registerProjectCommand(program) {
10213
10269
  process.exitCode = 1;
10214
10270
  }
10215
10271
  });
10216
- project.command("metrics").description("List rollup metrics for projects in the active organization").option("--project <id>", "Return metrics for a single project").action(async (options) => {
10272
+ project.command("metrics").description("List rollup metrics for projects in the active organization").option("--project <slug>", "Return metrics for a single project").action(async (options) => {
10217
10273
  const config = createCliConfig();
10218
10274
  try {
10219
- await runProjectMetrics(config, { ...options.project ? { projectId: options.project } : {} });
10275
+ await runProjectMetrics(config, { ...options.project ? { projectRef: options.project } : {} });
10220
10276
  } catch (error) {
10221
10277
  process.stderr.write(`${formatCliError(error, "List project metrics failed", {
10222
10278
  serverUrl: getPlatformUrl(config),
@@ -10225,6 +10281,20 @@ function registerProjectCommand(program) {
10225
10281
  process.exitCode = 1;
10226
10282
  }
10227
10283
  });
10284
+ project.command("deployments").description("View deployment history for platform projects").command("list").description("List deployment history for a project").option("--project <slug>", "Project slug").action(async (options) => {
10285
+ const config = createCliConfig();
10286
+ try {
10287
+ const projectRef = options.project ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
10288
+ if (!projectRef) throw new Error("Usage: keystroke project deployments list --project <slug>");
10289
+ await runProjectDeploymentsList(config, { projectRef });
10290
+ } catch (error) {
10291
+ process.stderr.write(`${formatCliError(error, "List deployments failed", {
10292
+ serverUrl: getPlatformUrl(config),
10293
+ webUrl: getWebUrl(config)
10294
+ })}\n`);
10295
+ process.exitCode = 1;
10296
+ }
10297
+ });
10228
10298
  project.command("create").description("Create a platform project in the active organization").requiredOption("--name <name>", "Project name").option("--description <description>", "Project description").action(async (options) => {
10229
10299
  const config = createCliConfig();
10230
10300
  try {
@@ -10243,8 +10313,8 @@ function registerProjectCommand(program) {
10243
10313
  project.command("update").description("Update a platform project's name and/or description").option("--name <name>", "Project name").option("--description <description>", "Project description").action(async (options) => {
10244
10314
  const config = createCliConfig();
10245
10315
  try {
10246
- const projectId = resolveActiveProjectId(config);
10247
- if (!projectId) throw new Error("Usage: keystroke --project <id> project update --name <name>");
10316
+ const projectId = await resolveActiveProjectId(config);
10317
+ if (!projectId) throw new Error("Usage: keystroke --project <slug> project update --name <name>");
10248
10318
  if (!options.name && options.description === void 0) throw new Error("At least one of --name or --description is required");
10249
10319
  await runProjectUpdate(config, {
10250
10320
  projectId,
@@ -10262,8 +10332,8 @@ function registerProjectCommand(program) {
10262
10332
  project.command("delete").description("Delete a platform project").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
10263
10333
  const config = createCliConfig();
10264
10334
  try {
10265
- const projectId = resolveActiveProjectId(config);
10266
- if (!projectId) throw new Error("Usage: keystroke --project <id> project delete");
10335
+ const projectId = await resolveActiveProjectId(config);
10336
+ if (!projectId) throw new Error("Usage: keystroke --project <slug> project delete");
10267
10337
  if (!options.yes) throw new Error("Pass --yes to confirm project deletion");
10268
10338
  await runProjectDelete(config, projectId);
10269
10339
  } catch (error) {
@@ -10424,27 +10494,23 @@ function registerWorkflowCommand(program) {
10424
10494
  }
10425
10495
  //#endregion
10426
10496
  //#region src/commands/config/use-project.ts
10427
- async function fetchProjectInOrg(config, projectId) {
10428
- const platform = createCliPlatformClient(config);
10429
- try {
10430
- return await platform.projects.get(projectId);
10431
- } catch (error) {
10432
- if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${projectId} not found in the active organization. Run \`keystroke project list\`, then \`keystroke config use project <id>\`.`);
10433
- throw error;
10434
- }
10497
+ async function fetchProjectInOrg(config, projectRef) {
10498
+ return resolveProjectRef(createCliPlatformClient(config), projectRef);
10435
10499
  }
10436
10500
  function assertProjectReadyForConfig(project) {
10437
- if (project.status === "failed") throw new Error(project.lastError ? `Project ${project.id} deploy failed: ${project.lastError}` : `Project ${project.id} deploy failed`);
10438
- if (project.status === "starting") throw new Error(`Project ${project.id} is still starting. Wait for deploy to finish, then run \`keystroke config use project ${project.id}\`.`);
10501
+ const label = formatProjectLabel(project);
10502
+ if (project.status === "failed") throw new Error(project.lastError ? `Project ${label} deploy failed: ${project.lastError}` : `Project ${label} deploy failed`);
10503
+ if (project.status === "starting") throw new Error(`Project ${label} is still starting. Wait for deploy to finish, then run \`keystroke config use project ${label}\`.`);
10439
10504
  }
10440
- async function runConfigUseProject(config, projectId) {
10441
- const project = await fetchProjectInOrg(config, projectId);
10505
+ async function runConfigUseProject(config, projectRef) {
10506
+ const project = await fetchProjectInOrg(config, projectRef);
10442
10507
  assertProjectReadyForConfig(project);
10443
- config.set("activeProjectId", projectId);
10508
+ config.set("activeProjectId", project.id);
10444
10509
  config.set("apiTarget", "platform");
10510
+ const label = formatProjectLabel(project);
10445
10511
  return {
10446
- stdout: project.status === "active" ? `Using platform project ${projectId}\n` : `Using platform project ${projectId} (${project.status})\n`,
10447
- stderr: project.status === "inactive" ? `Project is not active yet. Deploy with: ${cliBinaryName()} deploy --project ${projectId}\n` : void 0
10512
+ stdout: project.status === "active" ? `Using platform project ${label}\n` : `Using platform project ${label} (${project.status})\n`,
10513
+ stderr: project.status === "inactive" ? `Project is not active yet. Deploy with: ${cliBinaryName()} deploy --project ${label}\n` : void 0
10448
10514
  };
10449
10515
  }
10450
10516
  //#endregion
@@ -10473,7 +10539,7 @@ function registerConfigCommand(program) {
10473
10539
  return;
10474
10540
  }
10475
10541
  if (mode === "org") {
10476
- const membership = await selectActiveOrganization(cliConfig, { organizationId: id });
10542
+ const membership = await selectActiveOrganization(cliConfig, { organizationRef: id });
10477
10543
  process.stdout.write(`Using organization ${membership.organization.name}\n`);
10478
10544
  return;
10479
10545
  }
@@ -10483,9 +10549,9 @@ function registerConfigCommand(program) {
10483
10549
  process.stdout.write(activeProjectId ? `Using platform API (default project ${activeProjectId} for project-scoped commands)\n` : "Using platform API\n");
10484
10550
  return;
10485
10551
  }
10486
- if (mode !== "project") throw new Error("Usage: keystroke config use [local|cloud|project [projectId]|org [organizationId]]");
10552
+ if (mode !== "project") throw new Error("Usage: keystroke config use [local|cloud|project [slug]|org [slug]]");
10487
10553
  const targetProjectId = id ?? cliConfig.get("activeProjectId");
10488
- if (!targetProjectId) throw new Error("Usage: keystroke config use project <projectId>");
10554
+ if (!targetProjectId) throw new Error("Usage: keystroke config use project <slug>");
10489
10555
  const result = await runConfigUseProject(cliConfig, targetProjectId);
10490
10556
  process.stdout.write(result.stdout);
10491
10557
  if (result.stderr) process.stderr.write(result.stderr);
@@ -10559,7 +10625,7 @@ async function syncSkills(command) {
10559
10625
  //#endregion
10560
10626
  //#region src/program.ts
10561
10627
  function createProgram() {
10562
- const program = new Command().name("keystroke").description("Build, run, and manage keystroke projects").version(readCliVersion()).option("--project <id>", "Platform project id (deploy target or one-off API target)").option("--local", "Force local keystroke server URL").hook("preAction", async (thisCommand, actionCommand) => {
10628
+ const program = new Command().name("keystroke").description("Build, run, and manage keystroke projects").version(readCliVersion()).option("--project <slug>", "Platform project slug (deploy target or one-off API target)").option("--local", "Force local keystroke server URL").hook("preAction", async (thisCommand, actionCommand) => {
10563
10629
  const opts = thisCommand.opts();
10564
10630
  setCliTargetOptions({
10565
10631
  projectId: opts.project,