replicas-cli 0.2.113 → 0.2.116

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.
@@ -8213,69 +8213,51 @@ function generatePlaceholderName() {
8213
8213
  }
8214
8214
 
8215
8215
  // ../shared/src/workspace-groups.ts
8216
- function findRepositorySetForWorkspace(workspaceRepoIds, repositorySets) {
8217
- if (workspaceRepoIds.length <= 1) return null;
8218
- for (const set of repositorySets) {
8219
- const setRepoIds = new Set(set.repositories.map((r) => r.id));
8220
- if (workspaceRepoIds.every((id) => setRepoIds.has(id))) {
8221
- return set;
8222
- }
8223
- }
8224
- return null;
8225
- }
8226
- function buildGroups(workspaces, workspacesData, repositories, repositorySets, mockGroupAssignments) {
8216
+ function buildGroups(workspaces, _workspacesData, environments, _repositorySets, mockGroupAssignments) {
8227
8217
  const groupMap = /* @__PURE__ */ new Map();
8228
- for (const repo of repositories) {
8229
- groupMap.set(repo.id, { type: "repo", id: repo.id, name: repo.name, url: repo.url, workspaces: [] });
8230
- }
8231
- for (const repoSet of repositorySets) {
8232
- const setKey = `set:${repoSet.id}`;
8233
- groupMap.set(setKey, { type: "set", id: setKey, name: repoSet.name, workspaces: [] });
8218
+ for (const env of environments) {
8219
+ if (env.is_global) continue;
8220
+ if (!env.repository_id && !env.repository_set_id) continue;
8221
+ groupMap.set(env.id, {
8222
+ type: "env",
8223
+ id: env.id,
8224
+ name: env.name,
8225
+ environmentId: env.id,
8226
+ repoIdForCreate: env.repository_id,
8227
+ repoSetIdForCreate: env.repository_set_id,
8228
+ workspaces: []
8229
+ });
8234
8230
  }
8235
- const allWsRepos = workspacesData?.workspace_repositories ?? [];
8231
+ const placeUngrouped = (workspace) => {
8232
+ const key = "__ungrouped__";
8233
+ if (!groupMap.has(key)) {
8234
+ groupMap.set(key, {
8235
+ type: "env",
8236
+ id: key,
8237
+ name: "Other",
8238
+ environmentId: key,
8239
+ repoIdForCreate: null,
8240
+ repoSetIdForCreate: null,
8241
+ workspaces: []
8242
+ });
8243
+ }
8244
+ groupMap.get(key).workspaces.push(workspace);
8245
+ };
8236
8246
  for (const workspace of workspaces) {
8237
- const repos = allWsRepos.filter((r) => r.workspace_id === workspace.id);
8238
- if (repos.length > 0) {
8239
- const matchingSet = findRepositorySetForWorkspace(repos.map((r) => r.id), repositorySets);
8240
- let placedInSet = false;
8241
- if (matchingSet) {
8242
- const setKey = `set:${matchingSet.id}`;
8243
- if (groupMap.has(setKey)) {
8244
- groupMap.get(setKey).workspaces.push(workspace);
8245
- placedInSet = true;
8246
- }
8247
- }
8248
- if (!placedInSet) {
8249
- const primaryRepo = repos[0];
8250
- const group = groupMap.get(primaryRepo.id);
8251
- if (group) {
8252
- group.workspaces.push(workspace);
8253
- } else {
8254
- groupMap.set(primaryRepo.id, {
8255
- type: "repo",
8256
- id: primaryRepo.id,
8257
- name: primaryRepo.name,
8258
- url: primaryRepo.url,
8259
- workspaces: [workspace]
8260
- });
8261
- }
8262
- }
8263
- } else {
8264
- const assignedGroup = mockGroupAssignments.get(workspace.id);
8265
- if (assignedGroup && groupMap.has(assignedGroup)) {
8266
- groupMap.get(assignedGroup).workspaces.push(workspace);
8267
- } else {
8268
- const key = "__ungrouped__";
8269
- if (!groupMap.has(key)) {
8270
- groupMap.set(key, { type: "repo", id: key, name: "Other", workspaces: [] });
8271
- }
8272
- groupMap.get(key).workspaces.push(workspace);
8273
- }
8247
+ if (workspace.environment_id && groupMap.has(workspace.environment_id)) {
8248
+ groupMap.get(workspace.environment_id).workspaces.push(workspace);
8249
+ continue;
8250
+ }
8251
+ const assignedGroup = mockGroupAssignments.get(workspace.id);
8252
+ if (assignedGroup && groupMap.has(assignedGroup)) {
8253
+ groupMap.get(assignedGroup).workspaces.push(workspace);
8254
+ continue;
8274
8255
  }
8256
+ placeUngrouped(workspace);
8275
8257
  }
8276
8258
  return Array.from(groupMap.values()).sort((a, b) => a.name.localeCompare(b.name));
8277
8259
  }
8278
- function createMockWorkspaceRecord(organizationId, name) {
8260
+ function createMockWorkspaceRecord(organizationId, name, environmentId) {
8279
8261
  const id = "mock-" + Array.from(
8280
8262
  { length: 4 },
8281
8263
  () => Math.random().toString(36).slice(2, 6)
@@ -8296,7 +8278,8 @@ function createMockWorkspaceRecord(organizationId, name) {
8296
8278
  creator_email: null,
8297
8279
  soft_delete: false,
8298
8280
  lifecycle_policy: "default",
8299
- auto_stop_minutes: null
8281
+ auto_stop_minutes: null,
8282
+ environment_id: environmentId ?? null
8300
8283
  };
8301
8284
  }
8302
8285
 
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  setIdeCommand,
17
17
  setOrganizationId,
18
18
  writeConfig
19
- } from "./chunk-LQKKNYFF.mjs";
19
+ } from "./chunk-HOO346QO.mjs";
20
20
 
21
21
  // src/index.ts
22
22
  import "dotenv/config";
@@ -1463,15 +1463,17 @@ async function replicaCreateCommand(name, options) {
1463
1463
  process.exit(1);
1464
1464
  }
1465
1465
  try {
1466
- const repoResponse = await orgAuthenticatedFetch("/v1/repositories");
1467
- const repositories = repoResponse.repositories;
1468
- if (repositories.length === 0) {
1469
- console.log(chalk14.red("No repositories found. Please add a repository first."));
1466
+ const envResponse = await orgAuthenticatedFetch("/v1/environments");
1467
+ const environments = envResponse.environments.filter(
1468
+ (env) => !env.is_global && (env.repository_id || env.repository_set_id)
1469
+ );
1470
+ if (environments.length === 0) {
1471
+ console.log(chalk14.red("No repository-bound environments found. Please add a repository or bind an environment first."));
1470
1472
  process.exit(1);
1471
1473
  }
1472
1474
  let replicaName = name;
1473
1475
  let message = options.message;
1474
- let selectedRepositoryIds = options.repositories?.split(",").map((repository) => repository.trim()).filter((repository) => repository.length > 0);
1476
+ let selectedEnvironmentId = options.environment?.trim();
1475
1477
  let codingAgent = options.agent;
1476
1478
  if (replicaName && /\s/.test(replicaName)) {
1477
1479
  console.log(chalk14.red("Replica name cannot contain spaces."));
@@ -1494,23 +1496,22 @@ async function replicaCreateCommand(name, options) {
1494
1496
  }
1495
1497
  replicaName = response2.name;
1496
1498
  }
1497
- if (!selectedRepositoryIds || selectedRepositoryIds.length === 0) {
1499
+ if (!selectedEnvironmentId) {
1498
1500
  const response2 = await prompts3({
1499
- type: "multiselect",
1500
- name: "repositories",
1501
- message: "Select repositories:",
1502
- choices: repositories.map((repo) => ({
1503
- title: repo.name,
1504
- value: repo.id,
1505
- description: repo.url
1506
- })),
1507
- min: 1
1501
+ type: "select",
1502
+ name: "environment",
1503
+ message: "Select environment:",
1504
+ choices: environments.map((env) => ({
1505
+ title: env.name,
1506
+ value: env.id,
1507
+ description: env.repository_set_id ? "repository set" : "repository"
1508
+ }))
1508
1509
  });
1509
- if (!response2.repositories || response2.repositories.length === 0) {
1510
+ if (!response2.environment) {
1510
1511
  console.log(chalk14.yellow("\nCancelled."));
1511
1512
  return;
1512
1513
  }
1513
- selectedRepositoryIds = response2.repositories;
1514
+ selectedEnvironmentId = response2.environment;
1514
1515
  }
1515
1516
  if (!message) {
1516
1517
  const response2 = await prompts3({
@@ -1549,7 +1550,7 @@ async function replicaCreateCommand(name, options) {
1549
1550
  const body = {
1550
1551
  name: replicaName,
1551
1552
  message,
1552
- repository_ids: selectedRepositoryIds,
1553
+ environment_id: selectedEnvironmentId,
1553
1554
  coding_agent: codingAgent
1554
1555
  };
1555
1556
  console.log(chalk14.gray("\nCreating replica..."));
@@ -1757,6 +1758,16 @@ function truncate2(text, maxLength) {
1757
1758
  if (text.length <= maxLength) return text;
1758
1759
  return text.substring(0, maxLength) + "...";
1759
1760
  }
1761
+ function resolveSelectableEnvironmentId(envInput, selectableEnvs) {
1762
+ const env = selectableEnvs.find((e) => e.name === envInput || e.id === envInput);
1763
+ if (!env) {
1764
+ console.log(chalk16.red(`Environment not found: ${envInput}`));
1765
+ const available = selectableEnvs.map((e) => e.name).join(", ");
1766
+ console.log(chalk16.gray(`Available: ${available || "(none)"}`));
1767
+ process.exit(1);
1768
+ }
1769
+ return env.id;
1770
+ }
1760
1771
  function printAutomation(automation2) {
1761
1772
  console.log(chalk16.white(` ${automation2.name}`));
1762
1773
  console.log(chalk16.gray(` ID: ${automation2.id}`));
@@ -1830,12 +1841,7 @@ Automation: ${automation2.name}
1830
1841
  }
1831
1842
  }
1832
1843
  console.log(chalk16.gray(` Prompt: ${automation2.prompt}`));
1833
- if (automation2.repository_ids.length > 0) {
1834
- console.log(chalk16.gray(` Repository IDs: ${automation2.repository_ids.join(", ")}`));
1835
- }
1836
- if (automation2.repository_set_id) {
1837
- console.log(chalk16.gray(` Repository Set: ${automation2.repository_set_id}`));
1838
- }
1844
+ console.log(chalk16.gray(` Environment: ${automation2.environment_id}`));
1839
1845
  if (automation2.cron_expression) {
1840
1846
  console.log(chalk16.gray(` Cron: ${automation2.cron_expression}`));
1841
1847
  }
@@ -1934,10 +1940,6 @@ async function promptForTriggers(repositories) {
1934
1940
  }
1935
1941
  async function automationCreateCommand(name, options) {
1936
1942
  ensureAuthenticated();
1937
- if (options.repositories && options.repositorySet) {
1938
- console.log(chalk16.red("Cannot use both --repositories (-r) and --repository-set (-s). Choose one."));
1939
- process.exit(1);
1940
- }
1941
1943
  const validLifecycles = ["default", "delete_when_done", "delete_after_inactivity"];
1942
1944
  if (options.lifecycle && !validLifecycles.includes(options.lifecycle)) {
1943
1945
  console.log(chalk16.red(`Invalid lifecycle policy: ${options.lifecycle}`));
@@ -1956,12 +1958,15 @@ async function automationCreateCommand(name, options) {
1956
1958
  }
1957
1959
  }
1958
1960
  try {
1959
- const repoResponse = await orgAuthenticatedFetch("/v1/repositories");
1960
- const repositories = repoResponse.repositories;
1961
- if (repositories.length === 0) {
1962
- console.log(chalk16.red("No repositories found. Please add a repository first."));
1961
+ const envResponse = await orgAuthenticatedFetch("/v1/environments");
1962
+ const allEnvs = envResponse.environments;
1963
+ const selectableEnvs = allEnvs.filter((env) => !env.is_global);
1964
+ if (selectableEnvs.length === 0) {
1965
+ console.log(chalk16.red("No environments found. Please create an environment first."));
1963
1966
  process.exit(1);
1964
1967
  }
1968
+ const repoResponse = await orgAuthenticatedFetch("/v1/repositories");
1969
+ const repositories = repoResponse.repositories;
1965
1970
  let automationName = name;
1966
1971
  if (!automationName) {
1967
1972
  const response2 = await prompts4({
@@ -1990,83 +1995,25 @@ async function automationCreateCommand(name, options) {
1990
1995
  }
1991
1996
  automationPrompt = response2.prompt;
1992
1997
  }
1993
- let selectedRepoIds = [];
1994
- let selectedRepoSetId;
1995
- if (options.repositorySet) {
1996
- const setsResponse = await orgAuthenticatedFetch("/v1/repository-sets");
1997
- const repoSet = setsResponse.repository_sets.find((s) => s.name === options.repositorySet);
1998
- if (!repoSet) {
1999
- console.log(chalk16.red(`Repository set not found: ${options.repositorySet}`));
2000
- const available = setsResponse.repository_sets.map((s) => s.name).join(", ");
2001
- console.log(chalk16.gray(`Available: ${available || "(none)"}`));
2002
- process.exit(1);
2003
- }
2004
- selectedRepoSetId = repoSet.id;
2005
- } else if (options.repositories) {
2006
- const repoNames = options.repositories.split(",").map((r) => r.trim()).filter(Boolean);
2007
- for (const repoName of repoNames) {
2008
- const repo = repositories.find((r) => r.name === repoName);
2009
- if (!repo) {
2010
- console.log(chalk16.red(`Repository not found: ${repoName}`));
2011
- console.log(chalk16.gray(`Available: ${repositories.map((r) => r.name).join(", ")}`));
2012
- process.exit(1);
2013
- }
2014
- selectedRepoIds.push(repo.id);
2015
- }
1998
+ let selectedEnvironmentId;
1999
+ if (options.environment) {
2000
+ selectedEnvironmentId = resolveSelectableEnvironmentId(options.environment, selectableEnvs);
2016
2001
  } else {
2017
- const setsResponse = await orgAuthenticatedFetch("/v1/repository-sets");
2018
- const repoSets = setsResponse.repository_sets;
2019
- let useRepoSet = false;
2020
- if (repoSets.length > 0) {
2021
- const modeResponse = await prompts4({
2022
- type: "select",
2023
- name: "mode",
2024
- message: "Select repositories by:",
2025
- choices: [
2026
- { title: "Individual repositories", value: "repos" },
2027
- { title: "Repository set", value: "set" }
2028
- ]
2029
- });
2030
- if (!modeResponse.mode) {
2031
- console.log(chalk16.yellow("\nCancelled."));
2032
- return;
2033
- }
2034
- useRepoSet = modeResponse.mode === "set";
2035
- }
2036
- if (useRepoSet) {
2037
- const setResponse = await prompts4({
2038
- type: "select",
2039
- name: "set",
2040
- message: "Select repository set:",
2041
- choices: repoSets.map((s) => ({
2042
- title: s.name,
2043
- value: s.id,
2044
- description: s.description || `${s.repositories.length} repos`
2045
- }))
2046
- });
2047
- if (!setResponse.set) {
2048
- console.log(chalk16.yellow("\nCancelled."));
2049
- return;
2050
- }
2051
- selectedRepoSetId = setResponse.set;
2052
- } else {
2053
- const response2 = await prompts4({
2054
- type: "multiselect",
2055
- name: "repositories",
2056
- message: "Select repositories:",
2057
- choices: repositories.map((repo) => ({
2058
- title: repo.name,
2059
- value: repo.id,
2060
- description: repo.url
2061
- })),
2062
- min: 1
2063
- });
2064
- if (!response2.repositories || response2.repositories.length === 0) {
2065
- console.log(chalk16.yellow("\nCancelled."));
2066
- return;
2067
- }
2068
- selectedRepoIds = response2.repositories;
2002
+ const envResponse2 = await prompts4({
2003
+ type: "select",
2004
+ name: "env",
2005
+ message: "Select environment:",
2006
+ choices: selectableEnvs.map((env) => ({
2007
+ title: env.name,
2008
+ value: env.id,
2009
+ description: env.description || (env.repository_id ? "repo-bound" : env.repository_set_id ? "set-bound" : "unbound")
2010
+ }))
2011
+ });
2012
+ if (!envResponse2.env) {
2013
+ console.log(chalk16.yellow("\nCancelled."));
2014
+ return;
2069
2015
  }
2016
+ selectedEnvironmentId = envResponse2.env;
2070
2017
  }
2071
2018
  let triggers = [];
2072
2019
  if (options.triggerCron) {
@@ -2108,18 +2055,10 @@ async function automationCreateCommand(name, options) {
2108
2055
  process.exit(1);
2109
2056
  }
2110
2057
  }
2111
- const body = selectedRepoSetId ? {
2112
- name: automationName,
2113
- prompt: automationPrompt,
2114
- repository_set_id: selectedRepoSetId,
2115
- triggers,
2116
- enabled: options.enabled !== false,
2117
- ...options.lifecycle ? { workspace_lifecycle_policy: options.lifecycle } : {},
2118
- ...options.autoStopMinutes ? { workspace_auto_stop_minutes: parseInt(options.autoStopMinutes, 10) } : {}
2119
- } : {
2058
+ const body = {
2120
2059
  name: automationName,
2121
2060
  prompt: automationPrompt,
2122
- repository_ids: selectedRepoIds,
2061
+ environment_id: selectedEnvironmentId,
2123
2062
  triggers,
2124
2063
  enabled: options.enabled !== false,
2125
2064
  ...options.lifecycle ? { workspace_lifecycle_policy: options.lifecycle } : {},
@@ -2149,10 +2088,6 @@ Created automation: ${automation2.name}`));
2149
2088
  }
2150
2089
  async function automationEditCommand(id, options) {
2151
2090
  ensureAuthenticated();
2152
- if (options.repositories && options.repositorySet) {
2153
- console.log(chalk16.red("Cannot use both --repositories (-r) and --repository-set (-s). Choose one."));
2154
- process.exit(1);
2155
- }
2156
2091
  const validLifecycles = ["default", "delete_when_done", "delete_after_inactivity"];
2157
2092
  if (options.lifecycle && !validLifecycles.includes(options.lifecycle)) {
2158
2093
  console.log(chalk16.red(`Invalid lifecycle policy: ${options.lifecycle}`));
@@ -2173,7 +2108,7 @@ async function automationEditCommand(id, options) {
2173
2108
  try {
2174
2109
  const existing = await orgAuthenticatedFetch(`/v1/automations/${id}`);
2175
2110
  const body = {};
2176
- const hasOptions = options.name || options.prompt || options.enabled !== void 0 || options.triggerCron || options.triggerGithub || options.repositories || options.repositorySet || options.lifecycle || options.autoStopMinutes;
2111
+ const hasOptions = options.name || options.prompt || options.enabled !== void 0 || options.triggerCron || options.triggerGithub || options.environment || options.lifecycle || options.autoStopMinutes;
2177
2112
  if (!hasOptions) {
2178
2113
  const nameResponse = await prompts4({
2179
2114
  type: "text",
@@ -2257,30 +2192,10 @@ async function automationEditCommand(id, options) {
2257
2192
  }
2258
2193
  body.triggers = triggers;
2259
2194
  }
2260
- if (options.repositorySet) {
2261
- const setsResponse = await orgAuthenticatedFetch("/v1/repository-sets");
2262
- const repoSet = setsResponse.repository_sets.find((s) => s.name === options.repositorySet);
2263
- if (!repoSet) {
2264
- console.log(chalk16.red(`Repository set not found: ${options.repositorySet}`));
2265
- const available = setsResponse.repository_sets.map((s) => s.name).join(", ");
2266
- console.log(chalk16.gray(`Available: ${available || "(none)"}`));
2267
- process.exit(1);
2268
- }
2269
- body.repository_set_id = repoSet.id;
2270
- } else if (options.repositories) {
2271
- const repoResponse = await orgAuthenticatedFetch("/v1/repositories");
2272
- const repoNames = options.repositories.split(",").map((r) => r.trim()).filter(Boolean);
2273
- const repoIds = [];
2274
- for (const repoName of repoNames) {
2275
- const repo = repoResponse.repositories.find((r) => r.name === repoName);
2276
- if (!repo) {
2277
- console.log(chalk16.red(`Repository not found: ${repoName}`));
2278
- console.log(chalk16.gray(`Available: ${repoResponse.repositories.map((r) => r.name).join(", ")}`));
2279
- process.exit(1);
2280
- }
2281
- repoIds.push(repo.id);
2282
- }
2283
- body.repository_ids = repoIds;
2195
+ if (options.environment) {
2196
+ const envResp = await orgAuthenticatedFetch("/v1/environments");
2197
+ const selectableEnvs = envResp.environments.filter((e) => !e.is_global);
2198
+ body.environment_id = resolveSelectableEnvironmentId(options.environment, selectableEnvs);
2284
2199
  }
2285
2200
  if (options.lifecycle) {
2286
2201
  body.workspace_lifecycle_policy = options.lifecycle;
@@ -2505,12 +2420,12 @@ async function interactiveCommand() {
2505
2420
  );
2506
2421
  }
2507
2422
  console.log(chalk18.gray("Starting interactive mode..."));
2508
- const { launchInteractive } = await import("./interactive-R7XRKHTY.mjs");
2423
+ const { launchInteractive } = await import("./interactive-V22J5FZI.mjs");
2509
2424
  await launchInteractive();
2510
2425
  }
2511
2426
 
2512
2427
  // src/index.ts
2513
- var CLI_VERSION = "0.2.113";
2428
+ var CLI_VERSION = "0.2.116";
2514
2429
  var program = new Command();
2515
2430
  program.name("replicas").description("CLI for managing Replicas workspaces").version(CLI_VERSION);
2516
2431
  program.command("login").description("Authenticate with your Replicas account").action(async () => {
@@ -2695,7 +2610,7 @@ program.command("get <id>").description("Get replica details by ID").action(asyn
2695
2610
  process.exit(1);
2696
2611
  }
2697
2612
  });
2698
- program.command("create [name]").description("Create a new replica").option("-m, --message <message>", "Initial message for the replica").option("-r, --repositories <repositories>", "Comma-separated repository names").option("-a, --agent <agent>", "Coding agent (claude, codex)").action(async (name, options) => {
2613
+ program.command("create [name]").description("Create a new replica").option("-m, --message <message>", "Initial message for the replica").option("-e, --environment <environment>", "Environment ID").option("-a, --agent <agent>", "Coding agent (claude, codex)").action(async (name, options) => {
2699
2614
  try {
2700
2615
  await replicaCreateCommand(name, options);
2701
2616
  } catch (error) {
@@ -2768,7 +2683,7 @@ automation.command("get <id>").description("Get automation details by ID").actio
2768
2683
  process.exit(1);
2769
2684
  }
2770
2685
  });
2771
- automation.command("create [name]").description("Create a new automation").option("-p, --prompt <prompt>", "Prompt for the automation").option("-r, --repositories <repositories>", "Comma-separated repository names").option("-s, --repository-set <name>", "Use a repository set (mutually exclusive with -r)").option("--trigger-cron <schedule>", 'Cron schedule expression (e.g. "0 9 * * 1-5")').option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger (default: UTC)").option("--trigger-github <event>", 'GitHub event (e.g. "pull_request.opened")').option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--lifecycle <policy>", "Workspace lifecycle: delete_when_done, delete_after_inactivity, default").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle delete_after_inactivity)").option("--disabled", "Create in disabled state").action(async (name, options) => {
2686
+ automation.command("create [name]").description("Create a new automation").option("-p, --prompt <prompt>", "Prompt for the automation").option("-e, --environment <environment>", "Environment name or ID").option("--trigger-cron <schedule>", 'Cron schedule expression (e.g. "0 9 * * 1-5")').option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger (default: UTC)").option("--trigger-github <event>", 'GitHub event (e.g. "pull_request.opened")').option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--lifecycle <policy>", "Workspace lifecycle: delete_when_done, delete_after_inactivity, default").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle delete_after_inactivity)").option("--disabled", "Create in disabled state").action(async (name, options) => {
2772
2687
  try {
2773
2688
  await automationCreateCommand(name, {
2774
2689
  ...options,
@@ -2783,7 +2698,7 @@ automation.command("create [name]").description("Create a new automation").optio
2783
2698
  process.exit(1);
2784
2699
  }
2785
2700
  });
2786
- automation.command("edit <id>").description("Edit an existing automation").option("-n, --name <name>", "New name").option("-p, --prompt <prompt>", "New prompt").option("-e, --enabled <enabled>", "Enable or disable (true/false)").option("--trigger-cron <schedule>", "Set cron schedule (replaces existing triggers)").option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger").option("--trigger-github <event>", "Set GitHub event (replaces existing triggers)").option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("-r, --repositories <repositories>", "Comma-separated repository names").option("-s, --repository-set <name>", "Use a repository set (mutually exclusive with -r)").option("--lifecycle <policy>", "Workspace lifecycle: delete_when_done, delete_after_inactivity, default").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle delete_after_inactivity)").action(async (id, options) => {
2701
+ automation.command("edit <id>").description("Edit an existing automation").option("-n, --name <name>", "New name").option("-p, --prompt <prompt>", "New prompt").option("-e, --enabled <enabled>", "Enable or disable (true/false)").option("--trigger-cron <schedule>", "Set cron schedule (replaces existing triggers)").option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger").option("--trigger-github <event>", "Set GitHub event (replaces existing triggers)").option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--environment <environment>", "Environment name or ID").option("--lifecycle <policy>", "Workspace lifecycle: delete_when_done, delete_after_inactivity, default").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle delete_after_inactivity)").action(async (id, options) => {
2787
2702
  try {
2788
2703
  await automationEditCommand(id, options);
2789
2704
  } catch (error) {
@@ -12,7 +12,7 @@ import {
12
12
  isAgentBackendEvent,
13
13
  parseAgentEvents,
14
14
  parseUserMessage
15
- } from "./chunk-LQKKNYFF.mjs";
15
+ } from "./chunk-HOO346QO.mjs";
16
16
 
17
17
  // src/interactive/index.tsx
18
18
  import { createCliRenderer } from "@opentui/core";
@@ -445,17 +445,6 @@ function useWorkspaceEvents(workspaceId, enabled = true) {
445
445
 
446
446
  // ../shared/src/hooks/useRepositories.ts
447
447
  import { useQuery as useQuery3 } from "@tanstack/react-query";
448
- function useRepositories() {
449
- const auth = useReplicasAuth();
450
- const orgFetch = useOrgFetch();
451
- const orgId = auth.getOrganizationId();
452
- return useQuery3({
453
- queryKey: ["repositories", orgId],
454
- queryFn: () => orgFetch("/v1/repositories"),
455
- enabled: !!orgId,
456
- staleTime: 2 * 60 * 1e3
457
- }, auth.queryClient);
458
- }
459
448
  function useRepositorySets() {
460
449
  const auth = useReplicasAuth();
461
450
  const orgFetch = useOrgFetch();
@@ -469,33 +458,13 @@ function useRepositorySets() {
469
458
 
470
459
  // ../shared/src/hooks/useEnvironment.ts
471
460
  import { useQuery as useQuery4 } from "@tanstack/react-query";
472
- function useEnvironmentSkills() {
461
+ function useEnvironments() {
473
462
  const auth = useReplicasAuth();
474
463
  const orgFetch = useOrgFetch();
475
464
  const orgId = auth.getOrganizationId();
476
465
  return useQuery4({
477
- queryKey: ["env-skills", orgId],
478
- queryFn: () => orgFetch("/v1/environment/skills"),
479
- enabled: !!orgId
480
- }, auth.queryClient);
481
- }
482
- function useEnvironmentVariables() {
483
- const auth = useReplicasAuth();
484
- const orgFetch = useOrgFetch();
485
- const orgId = auth.getOrganizationId();
486
- return useQuery4({
487
- queryKey: ["env-variables", orgId],
488
- queryFn: () => orgFetch("/v1/environment/variables"),
489
- enabled: !!orgId
490
- }, auth.queryClient);
491
- }
492
- function useEnvironmentFiles() {
493
- const auth = useReplicasAuth();
494
- const orgFetch = useOrgFetch();
495
- const orgId = auth.getOrganizationId();
496
- return useQuery4({
497
- queryKey: ["env-files", orgId],
498
- queryFn: () => orgFetch("/v1/environment/files"),
466
+ queryKey: ["environments", orgId],
467
+ queryFn: () => orgFetch("/v1/environments"),
499
468
  enabled: !!orgId
500
469
  }, auth.queryClient);
501
470
  }
@@ -559,7 +528,13 @@ function flattenGroups(groups, collapsedGroups) {
559
528
  for (const group of groups) {
560
529
  if (group.id === "__ungrouped__" && group.workspaces.length === 0) continue;
561
530
  const expanded = !collapsedGroups.has(group.id);
562
- items.push({ type: "group", id: group.id, name: group.name, groupType: group.type, expanded });
531
+ items.push({
532
+ type: "group",
533
+ id: group.id,
534
+ name: group.name,
535
+ isSetBound: !!group.repoSetIdForCreate,
536
+ expanded
537
+ });
563
538
  if (expanded) {
564
539
  for (const ws of group.workspaces) {
565
540
  items.push({
@@ -571,7 +546,7 @@ function flattenGroups(groups, collapsedGroups) {
571
546
  });
572
547
  }
573
548
  if (group.id !== "__ungrouped__") {
574
- items.push({ type: "add", groupId: group.id, groupType: group.type });
549
+ items.push({ type: "add", group });
575
550
  }
576
551
  }
577
552
  }
@@ -640,7 +615,7 @@ function WorkspaceSidebar({
640
615
  } else if (item.type === "workspace") {
641
616
  onSelectWorkspace(item.workspaceId);
642
617
  } else if (item.type === "add") {
643
- onCreateWorkspace(item.groupId, item.groupType);
618
+ onCreateWorkspace(item.group);
644
619
  }
645
620
  },
646
621
  [onSelectWorkspace, onCreateWorkspace]
@@ -712,7 +687,7 @@ function WorkspaceSidebar({
712
687
  if (key.name === "a") {
713
688
  const item = items[cursorIndex];
714
689
  if (item?.type === "add") {
715
- onCreateWorkspace(item.groupId, item.groupType);
690
+ onCreateWorkspace(item.group);
716
691
  }
717
692
  return;
718
693
  }
@@ -780,7 +755,7 @@ function WorkspaceSidebar({
780
755
  if (item.type === "group") {
781
756
  const chevron = item.expanded ? "\u25BE" : "\u25B8";
782
757
  const label = truncate(item.name, 20);
783
- const suffix = item.groupType === "set" ? " Set" : "";
758
+ const suffix = item.isSetBound ? " Set" : "";
784
759
  return /* @__PURE__ */ jsx2(
785
760
  "box",
786
761
  {
@@ -856,7 +831,7 @@ function WorkspaceSidebar({
856
831
  /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#888888" : "#333333", children: "New workspace" })
857
832
  ] })
858
833
  },
859
- `a-${item.groupId}`
834
+ `a-${item.group.id}`
860
835
  );
861
836
  }),
862
837
  focused && /* @__PURE__ */ jsx2("box", { height: 1, paddingX: 1, backgroundColor: "#111111", children: /* @__PURE__ */ jsxs2("text", { children: [
@@ -1562,6 +1537,11 @@ function StructuredUserMessage({ parsed }) {
1562
1537
  return /* @__PURE__ */ jsx5(GitHubPRGeneralMessage, { data: parsed });
1563
1538
  case "slack_task":
1564
1539
  return /* @__PURE__ */ jsx5(SlackTaskMessage, { data: parsed });
1540
+ case "automation_triggered":
1541
+ case "plan_quote":
1542
+ return /* @__PURE__ */ jsx5("text", { fg: "#cccccc", selectable: true, children: parsed.source === "plan_quote" ? parsed.blocks.map((b) => `> ${b.quotedText}
1543
+ ${b.replyText}`).join("\n\n") : `Automation: ${parsed.automationName} (${parsed.triggerType})
1544
+ ${parsed.userPrompt}` });
1565
1545
  default: {
1566
1546
  const _exhaustive = parsed;
1567
1547
  throw new Error(`Unhandled prompt source: ${_exhaustive.source}`);
@@ -1573,7 +1553,8 @@ function StructuredUserMessage({ parsed }) {
1573
1553
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
1574
1554
  var AGENT_LABELS = {
1575
1555
  claude: "Claude",
1576
- codex: "Codex"
1556
+ codex: "Codex",
1557
+ relay: "Relay"
1577
1558
  };
1578
1559
  function truncate2(text, maxLen) {
1579
1560
  return text.length > maxLen ? text.slice(0, maxLen - 1) + "\u2026" : text;
@@ -2190,7 +2171,7 @@ function ViewModeRow({
2190
2171
  }
2191
2172
  );
2192
2173
  }
2193
- function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, envConfig, agentAvailability, previews, onWakeWorkspace, onViewDiff, wakingWorkspaceId, viewMode, viewingDiffRepoName, onSelectChatMode, onSelectDiffMode, onCreatePr, isPlanMode }) {
2174
+ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, agentAvailability, previews, onWakeWorkspace, onViewDiff, wakingWorkspaceId, viewMode, viewingDiffRepoName, onSelectChatMode, onSelectDiffMode, onCreatePr, isPlanMode }) {
2194
2175
  const borderColor = focused ? "#66bb6a" : "#333333";
2195
2176
  const [cursorIndex, setCursorIndex] = useState6(0);
2196
2177
  const interactiveItems = useMemo5(() => {
@@ -2345,9 +2326,6 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
2345
2326
  claudeAuthMethod: agentAvailability.claude.available ? rawEnv.claudeAuthMethod : "none",
2346
2327
  codexAuthMethod: agentAvailability.codex.available ? rawEnv.codexAuthMethod : "none"
2347
2328
  } : rawEnv;
2348
- const expectedSkills = (envConfig.skills?.environment_skills ?? []).map((s) => s.source);
2349
- const expectedGlobalVars = (envConfig.variables?.environment_variables ?? []).filter((v) => v.scope_type === "global").map((v) => v.key);
2350
- const expectedGlobalFiles = (envConfig.files?.environment_files ?? []).filter((f) => f.scope_type === "global").map((f) => f.path);
2351
2329
  const dashboardItem = findItem("dashboard");
2352
2330
  const wakeItem = findItem("wake");
2353
2331
  const createPrItem = findItem("createPr");
@@ -2571,9 +2549,9 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
2571
2549
  ] })
2572
2550
  ] }, i))
2573
2551
  ] }),
2574
- env && (expectedSkills.length > 0 || env.skillsInstalled.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Skills", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedSkills, actual: env.skillsInstalled }) }),
2575
- env && (expectedGlobalVars.length > 0 || env.envVarsSet.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Env Vars", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedGlobalVars, actual: env.envVarsSet }) }),
2576
- env && (expectedGlobalFiles.length > 0 || env.filesUploaded.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Files", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedGlobalFiles, actual: env.filesUploaded }) })
2552
+ env && env.skillsInstalled.length > 0 && /* @__PURE__ */ jsx8(Section, { title: "Skills", children: /* @__PURE__ */ jsx8(DetailList, { expected: [], actual: env.skillsInstalled }) }),
2553
+ env && env.envVarsSet.length > 0 && /* @__PURE__ */ jsx8(Section, { title: "Env Vars", children: /* @__PURE__ */ jsx8(DetailList, { expected: [], actual: env.envVarsSet }) }),
2554
+ env && env.filesUploaded.length > 0 && /* @__PURE__ */ jsx8(Section, { title: "Files", children: /* @__PURE__ */ jsx8(DetailList, { expected: [], actual: env.filesUploaded }) })
2577
2555
  ] })
2578
2556
  }
2579
2557
  );
@@ -2646,8 +2624,8 @@ function Toast() {
2646
2624
  import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
2647
2625
  var FOCUS_ORDER = ["sidebar", "chat-tabs", "chat-history", "chat-input", "info"];
2648
2626
  var MOCK_CHATS = [
2649
- { id: "mock-claude", provider: "claude", title: "Claude Code", createdAt: "", updatedAt: "", processing: false },
2650
- { id: "mock-codex", provider: "codex", title: "Codex", createdAt: "", updatedAt: "", processing: false }
2627
+ { id: "mock-claude", provider: "claude", title: "Claude Code", createdAt: "", updatedAt: "", processing: false, parentChatId: null },
2628
+ { id: "mock-codex", provider: "codex", title: "Codex", createdAt: "", updatedAt: "", processing: false, parentChatId: null }
2651
2629
  ];
2652
2630
  var MONOLITH_URL = process.env.REPLICAS_MONOLITH_URL || "https://api.replicas.dev";
2653
2631
  var workspacePollingStarted = false;
@@ -2722,11 +2700,8 @@ function AppInner() {
2722
2700
  const mockIds = useMemo6(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
2723
2701
  const isMockSelected = selectedWorkspaceId ? mockIds.has(selectedWorkspaceId) : false;
2724
2702
  const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(1, 100, "organization");
2725
- const { data: reposData } = useRepositories();
2726
2703
  const { data: setsData } = useRepositorySets();
2727
- const { data: envSkills } = useEnvironmentSkills();
2728
- const { data: envVariables } = useEnvironmentVariables();
2729
- const { data: envFiles } = useEnvironmentFiles();
2704
+ const { data: envsData } = useEnvironments();
2730
2705
  const { data: statusData, isLoading: loadingStatus } = useWorkspaceStatus(
2731
2706
  isMockSelected ? null : selectedWorkspaceId,
2732
2707
  { includeDiffs: true }
@@ -2756,13 +2731,13 @@ function AppInner() {
2756
2731
  const sendMessageMutation = useSendChatMessage(selectedWorkspaceId, resolvedChatId);
2757
2732
  const interruptMutation = useInterruptChat(selectedWorkspaceId, resolvedChatId);
2758
2733
  const workspaces = workspacesData?.workspaces ?? [];
2759
- const repositories = reposData?.repositories ?? [];
2760
2734
  const repositorySets = setsData?.repository_sets ?? [];
2735
+ const environments = envsData?.environments ?? [];
2761
2736
  const previews = previewsData?.previews ?? [];
2762
2737
  const allWorkspaces = useMemo6(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
2763
2738
  const groups = useMemo6(
2764
- () => buildGroups(allWorkspaces, workspacesData, repositories, repositorySets, mockGroupRef.current),
2765
- [allWorkspaces, workspacesData, repositories, repositorySets]
2739
+ () => buildGroups(allWorkspaces, workspacesData, environments, repositorySets, mockGroupRef.current),
2740
+ [allWorkspaces, workspacesData, environments, repositorySets]
2766
2741
  );
2767
2742
  const selectedWorkspace = allWorkspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
2768
2743
  const selectedChat = chats.find((c) => c.id === resolvedChatId) ?? null;
@@ -2843,17 +2818,25 @@ function AppInner() {
2843
2818
  setFocusPanel(panel);
2844
2819
  }, []);
2845
2820
  const handleCreateWorkspace = useCallback7(
2846
- async (groupId, groupType) => {
2821
+ async (group) => {
2822
+ if (group.id === "__ungrouped__") {
2823
+ toast.error("Cannot create a workspace in the Other / Ungrouped folder");
2824
+ return;
2825
+ }
2847
2826
  const orgId = getOrganizationId() ?? "";
2848
2827
  const mock = createMockWorkspaceRecord(orgId);
2849
- mockGroupRef.current.set(mock.id, groupId);
2828
+ mockGroupRef.current.set(mock.id, group.id);
2850
2829
  setMockWorkspaces((prev) => [mock, ...prev]);
2851
2830
  setSelectedWorkspaceId(mock.id);
2852
2831
  setViewingDiff(null);
2853
2832
  setLastViewedDiff(null);
2854
2833
  setFocusPanel("chat-input");
2855
2834
  try {
2856
- const request = groupType === "set" ? { repository_set_id: groupId.replace(/^set:/, ""), name: mock.name, placeholder: true } : { repository_ids: [groupId], name: mock.name, placeholder: true };
2835
+ const request = {
2836
+ environment_id: group.environmentId,
2837
+ name: mock.name,
2838
+ placeholder: true
2839
+ };
2857
2840
  const result = await createWorkspaceMutation.mutateAsync(request);
2858
2841
  mockToRealRef.current.set(mock.id, result.workspace.id);
2859
2842
  } catch (err) {
@@ -3101,11 +3084,6 @@ function AppInner() {
3101
3084
  workspaceId: selectedWorkspaceId,
3102
3085
  focused: focusPanel === "info",
3103
3086
  loading: loadingStatus,
3104
- envConfig: {
3105
- skills: envSkills ?? null,
3106
- variables: envVariables ?? null,
3107
- files: envFiles ?? null
3108
- },
3109
3087
  previews,
3110
3088
  agentAvailability: agentAvailability ?? null,
3111
3089
  onWakeWorkspace: handleWakeWorkspace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.113",
3
+ "version": "0.2.116",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {