replicas-cli 0.2.640 → 0.2.642

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/README.md CHANGED
@@ -41,6 +41,9 @@ Choose the organization used by organization-scoped commands. The selection pers
41
41
  ### `replicas connect <workspace-name>`
42
42
  Connect to a workspace via SSH.
43
43
 
44
+ ### `replicas code [workspace-name...]`
45
+ Open a workspace in VS Code via Remote SSH. Omit the argument to pick a workspace with the arrow keys and Enter, or pass a name or ID. Names with spaces work with or without quotes.
46
+
44
47
  ### Coding agents
45
48
  Use `replicas claude-auth` or `replicas codex-auth` to connect Claude Code or Codex credentials. Cursor uses a Cursor API key configured in Coding Agents settings.
46
49
 
package/dist/index.cjs CHANGED
@@ -24617,6 +24617,11 @@ var SENTRY_ABILITY = {
24617
24617
  referenceFile: { name: "SENTRY.md", content: REFERENCE11 }
24618
24618
  };
24619
24619
 
24620
+ // ../shared/src/slack-response-style.ts
24621
+ var SLACK_CONCISE_REPLY_SHAPE = "one to three plain sentences on what you did and what it means, with no headers, bullet lists, command logs, or step-by-step narration";
24622
+ var SLACK_BLOCK_KIT_GUIDANCE = "Use Block Kit whenever structure or a link would make the message faster to scan or use. You have broad leeway to combine supported display elements: `section` and `fields` for compact updates; `context` for secondary metadata; `header` and `divider` for genuinely distinct sections; images for useful previews; and `actions` with URL buttons for pull requests, dashboards, downloads, or next steps. Consult the [Slack Block Kit reference](https://docs.slack.dev/reference/block-kit/blocks) for exact schemas before using an unfamiliar block. Do not add selects, date pickers, approval buttons, or other controls that require an interaction callback. Keep simple replies as plain text, do not add decorative UI or repeat content across blocks, and always keep `text` as a concise, complete fallback for notifications and accessibility.";
24623
+ var SLACK_PR_BUTTON_GUIDANCE = 'The first time you share a given pull request or merge request link in a thread \u2014 the message announcing you opened it \u2014 attach a button for it: send `blocks` alongside `text` \u2014 a `section` block holding your message as `mrkdwn`, then an `actions` block with one element per newly shared link, `{"type": "button", "style": "primary", "text": {"type": "plain_text", "text": "View PR"}, "url": "<pr url>", "action_id": "view_pr"}`. Later messages about a pull request you already linked in this thread keep the link in the sentence and get no button, and never re-post a button for a link someone already has. Label it "View MR" for GitLab, and if there are several give each button a unique `action_id` and name its repository in the label ("View PR \xB7 web"). Always keep `text` populated with the same message \u2014 Slack uses it for notifications and fallback \u2014 and keep the link in the sentence too.';
24624
+
24620
24625
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
24621
24626
  var SECTION12 = `### Slack
24622
24627
  Send messages, read threads, search conversations, and transfer files through the workspace-scoped TypeScript SDK.
@@ -24664,6 +24669,10 @@ await Bun.write('/tmp/recording.mov', response);
24664
24669
 
24665
24670
  ## Send and attach a message
24666
24671
 
24672
+ Write like a teammate, not a report. Lead with the outcome, remove repetition and filler, and preserve the facts needed to understand the result, blocker, or next action. A routine update is ${SLACK_CONCISE_REPLY_SHAPE}.
24673
+
24674
+ ${SLACK_BLOCK_KIT_GUIDANCE}
24675
+
24667
24676
  \`\`\`ts
24668
24677
  const message = await replicas.slack.call<{ channel: string; ts: string }>('chat.postMessage', {
24669
24678
  channel: 'C0123ABC',
@@ -24672,26 +24681,7 @@ const message = await replicas.slack.call<{ channel: string; ts: string }>('chat
24672
24681
  await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
24673
24682
  \`\`\`
24674
24683
 
24675
- A link in the message text stays plain text, so the message that first shares a pull request or merge request link passes \`blocks\` alongside \`text\` to render a button for it. Keep \`text\` set to the same message \u2014 Slack uses it for notifications and fallback.
24676
-
24677
- \`\`\`ts
24678
- await replicas.slack.call('chat.postMessage', {
24679
- channel: 'C0123ABC',
24680
- thread_ts: '1234567890.123456',
24681
- text: summary,
24682
- blocks: [
24683
- { type: 'section', text: { type: 'mrkdwn', text: summary } },
24684
- {
24685
- type: 'actions',
24686
- elements: [
24687
- { type: 'button', style: 'primary', text: { type: 'plain_text', text: 'View PR' }, url: prUrl, action_id: 'view_pr' },
24688
- ],
24689
- },
24690
- ],
24691
- });
24692
- \`\`\`
24693
-
24694
- Only the announcing message gets the button \u2014 follow-ups about a pull request already linked in the thread keep the link in the text and drop the \`blocks\`. Use "View MR" for GitLab. With several links, give each button a unique \`action_id\` and name its repository in the label ("View PR \xB7 web").
24684
+ ${SLACK_PR_BUTTON_GUIDANCE}
24695
24685
 
24696
24686
  For screenshots, recordings, diagrams, or other media, read \`references/MEDIA.md\` before posting. It is the single source of truth for uploading the Replicas copy and the raw file to Slack; a dashboard link in \`text\` is not an inline attachment.
24697
24687
 
@@ -27252,7 +27242,7 @@ var headlessFilesystemAgentResultSchema = external_exports.object({
27252
27242
  });
27253
27243
 
27254
27244
  // ../shared/src/cli-version.ts
27255
- var CLI_VERSION = "0.2.640";
27245
+ var CLI_VERSION = "0.2.642";
27256
27246
 
27257
27247
  // ../shared/src/version.ts
27258
27248
  function compareVersions(v1, v2) {
@@ -28512,27 +28502,37 @@ async function resolveWorkspaceRecord(workspaceName) {
28512
28502
  if (!canCallOrgApi()) {
28513
28503
  throw new Error('No organization selected. Please run "replicas org switch" first.');
28514
28504
  }
28515
- const exactWorkspace = await fetchWorkspaceById(workspaceName);
28516
- if (exactWorkspace) return exactWorkspace;
28517
- console.log(import_chalk5.default.blue(`
28518
- Searching for workspace: ${workspaceName}...`));
28519
- const response = await orgAuthenticatedFetch(
28520
- `/v1/workspaces?name=${encodeURIComponent(workspaceName)}`
28521
- );
28522
- if (response.workspaces.length === 0) {
28523
- throw new Error(`No workspaces found with name matching "${workspaceName}".`);
28505
+ if (workspaceName) {
28506
+ const exactWorkspace = await fetchWorkspaceById(workspaceName);
28507
+ if (exactWorkspace) return exactWorkspace;
28508
+ }
28509
+ console.log(import_chalk5.default.blue(workspaceName ? `
28510
+ Searching for workspace: ${workspaceName}...` : "\nLoading workspaces..."));
28511
+ const params = new URLSearchParams({ limit: "100" });
28512
+ if (workspaceName) params.set("name", workspaceName);
28513
+ const workspaces = [];
28514
+ for (let page = 1; ; page++) {
28515
+ params.set("page", String(page));
28516
+ const response = await orgAuthenticatedFetch(`/v1/workspaces?${params}`);
28517
+ workspaces.push(...response.workspaces);
28518
+ if (page >= response.totalPages || response.workspaces.length === 0) break;
28519
+ }
28520
+ if (workspaces.length === 0) {
28521
+ throw new Error(workspaceName ? `No workspaces found with name matching "${workspaceName}".` : "No workspaces found.");
28524
28522
  }
28525
28523
  let selectedWorkspace;
28526
- if (response.workspaces.length === 1) {
28527
- selectedWorkspace = response.workspaces[0];
28524
+ if (workspaceName && workspaces.length === 1) {
28525
+ selectedWorkspace = workspaces[0];
28528
28526
  } else {
28529
- console.log(import_chalk5.default.yellow(`
28530
- Found ${response.workspaces.length} workspaces matching "${workspaceName}":`));
28527
+ if (workspaceName) {
28528
+ console.log(import_chalk5.default.yellow(`
28529
+ Found ${workspaces.length} workspaces matching "${workspaceName}":`));
28530
+ }
28531
28531
  const selectResponse = await (0, import_prompts5.default)({
28532
28532
  type: "select",
28533
28533
  name: "workspaceId",
28534
28534
  message: "Select a workspace:",
28535
- choices: response.workspaces.map((ws) => ({
28535
+ choices: workspaces.map((ws) => ({
28536
28536
  title: `${ws.name} (${ws.status || "unknown"})`,
28537
28537
  value: ws.id,
28538
28538
  description: `Status: ${ws.status || "unknown"}`
@@ -28541,7 +28541,7 @@ Found ${response.workspaces.length} workspaces matching "${workspaceName}":`));
28541
28541
  if (!selectResponse.workspaceId) {
28542
28542
  throw new Error("Workspace selection cancelled.");
28543
28543
  }
28544
- const selected = response.workspaces.find((ws) => ws.id === selectResponse.workspaceId);
28544
+ const selected = workspaces.find((ws) => ws.id === selectResponse.workspaceId);
28545
28545
  if (!selected) {
28546
28546
  throw new Error("Selected workspace was not found.");
28547
28547
  }
@@ -28701,6 +28701,10 @@ async function codeCommand(workspaceName) {
28701
28701
  console.log(import_chalk7.default.red('Not logged in. Please run "replicas login" first.'));
28702
28702
  process.exit(1);
28703
28703
  }
28704
+ if (!workspaceName && !process.stdin.isTTY) {
28705
+ console.error(import_chalk7.default.red('Pass a workspace name or ID, or run "replicas code" in an interactive terminal.'));
28706
+ process.exit(1);
28707
+ }
28704
28708
  try {
28705
28709
  const { workspace, sshToken, sshHost, sshProxyCommand, repoName } = await prepareWorkspaceConnection(workspaceName);
28706
28710
  const hostAlias = getWorkspaceHostAlias(workspace.name);
@@ -32154,7 +32158,7 @@ Error: ${error51 instanceof Error ? error51.message : "Unknown error"}`));
32154
32158
  process.exit(1);
32155
32159
  }
32156
32160
  });
32157
- program.command("code <workspace-name...>").description("Open a workspace in VSCode/Cursor via Remote SSH").action(async (workspaceName) => {
32161
+ program.command("code [workspace-name...]").description("Open a workspace in VSCode/Cursor via Remote SSH (pick a workspace if omitted)").action(async (workspaceName = []) => {
32158
32162
  try {
32159
32163
  await codeCommand(workspaceName.join(" "));
32160
32164
  } catch (error51) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.640",
3
+ "version": "0.2.642",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {