rush-ai 0.5.0 → 0.7.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.
package/AGENTS.md ADDED
@@ -0,0 +1,37 @@
1
+ # AGENTS.md — for AI agents using rush-ai
2
+
3
+ If you are an AI agent (Cursor, Claude Code, or another IDE-resident assistant) and you landed in this package, read this file first.
4
+
5
+ ## What rush-ai is for
6
+
7
+ `rush-ai` is how a local agent hands off work to the Rush platform — without asking the user to re-state anything. Two situations you'll use it in:
8
+
9
+ 1. **Hand-off** — the conversation you're in produces work that belongs on Rush (a live website, a task for a specialist). Relay the context you already have. → read [`skills/hand-off.md`](./skills/hand-off.md).
10
+ 2. **Sub-agent in your workflow** — you're composing a multi-step plan and a step needs a specialist from Rush's agent shelf. → read [`skills/agent-shelf.md`](./skills/agent-shelf.md).
11
+
12
+ ## Getting usage on demand
13
+
14
+ From inside any shell (the one you already have a Bash tool for), these three commands give you everything you need:
15
+
16
+ ```bash
17
+ npx rush-ai skill # prints the index — start here
18
+ npx rush-ai skill hand-off # full hand-off playbook
19
+ npx rush-ai skill agent-shelf # full agent-shelf playbook
20
+ npx rush-ai agent list # see the live list of available agents
21
+ ```
22
+
23
+ Every command also supports `--help` with a "When to use" section and example flows.
24
+
25
+ ## Defaults that save you a round-trip
26
+
27
+ - Recommended invocation: **`npx rush-ai`** (always latest, zero install).
28
+ - Default agent: **`rush`** — `-a` defaults to `rush` when omitted. Pass `-a web-builder` for site-building tasks.
29
+ - Always pass **`--json`** when the result will be parsed.
30
+ - Iterate with **`task send <id>`**, not a new `task create`.
31
+
32
+ ## What NOT to do
33
+
34
+ - Don't ask the user to summarize requirements they already told you — relay them.
35
+ - Don't invent agent names; always confirm via `agent list`.
36
+ - Don't run `npx rush-ai auth login` for the user (it's interactive / opens a browser). Ask them to.
37
+ - Don't silently retry a failed task. Surface the error and ask what to do.
package/README.md CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  # rush-ai
4
4
 
5
- **Command-line interface for the Rush AI platform**
6
-
7
- *Create tasks, manage agents, stream results, and integrate IDE plugins — all from the terminal*
5
+ **Call Rush agents from your terminal — relay the context, not the prompt.**
8
6
 
9
7
  [![npm version](https://img.shields.io/npm/v/rush-ai.svg)](https://www.npmjs.com/package/rush-ai)
10
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -13,23 +11,60 @@
13
11
 
14
12
  ---
15
13
 
16
- ## Quick Start
14
+ ## Why rush-ai
15
+
16
+ ### Hand off your context, not your prompt
17
+
18
+ You're already deep in a conversation with your local agent — Cursor, Claude Code, or your own. It knows the codebase, the constraints, what you've already tried. When part of that work belongs on the Rush platform (building a site, asking a specialist agent), you don't want to summarize everything again.
19
+
20
+ `rush-ai` is the hand-off. Your local agent calls `rush-ai task create` with the context it already has. Rush picks it up, you switch to the browser to watch the preview, and you keep iterating there.
17
21
 
18
22
  ```bash
19
- npm install -g rush-ai
23
+ # From inside Cursor / Claude Code, the local agent runs:
24
+ npx rush-ai task create -a web-builder -p "做一个公司官网,风格大气专业"
25
+ ```
20
26
 
21
- rush-ai auth login
22
- rush-ai agent list
23
- rush-ai task create -a <agent> -p "Build a landing page"
24
- rush-ai task watch <task-id>
27
+ ### Rush agents as your sub-agents
28
+
29
+ Rush hosts a growing shelf of specialist agents. Any of them can become a sub-agent inside your own workflow — you don't rewrite the prompt, you call the agent.
30
+
31
+ ```bash
32
+ # Browse the shelf
33
+ npx rush-ai agent list
34
+
35
+ # Pick one and put it to work — e.g. the HR analytics specialist
36
+ npx rush-ai task create -a 人力资源分析专家 -p "分析 Q1 部门人效趋势"
37
+ ```
38
+
39
+ ### Featured: `web-builder`
40
+
41
+ `web-builder` is Rush's most intensive agent. One prompt gives you back a live preview URL and a git repository — share it, clone it, or keep iterating via `task send`.
42
+
43
+ ```bash
44
+ npx rush-ai task create -a web-builder -p "Build a product landing page"
45
+ # → Task lodig8oknq0r running
46
+
47
+ npx rush-ai task status lodig8oknq0r
48
+ # Preview: https://lodig8oknq0r-preview.rush.zhenguanyu.com/
49
+ # Git: https://gitlab-ee.zhenguanyu.com/rush/online/lodig8oknq0r
25
50
  ```
26
51
 
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ npx rush-ai auth login
56
+ npx rush-ai agent list
57
+ npx rush-ai task create -a rush -p "Add a contact form to the homepage"
58
+ npx rush-ai task watch <task-id>
59
+ ```
60
+
61
+ `rush` is the default general-purpose agent. Swap `-a` for any agent from `agent list` to call a specialist instead.
62
+
27
63
  ## Features
28
64
 
29
- - **Task lifecycle** — Create, watch (SSE streaming), cancel, and download artifacts
30
- - **Agent management** — Browse agents, inspect skills and MCP servers
31
- - **MCP integration** — Discover MCP servers, list tools, run as MCP stdio server
32
- - **IDE plugins** — Install, update, diagnose plugins for Claude Code and Cursor
65
+ - **Task lifecycle** — Create, watch (SSE streaming), send follow-ups, cancel, and download artifacts
66
+ - **Agent shelf** — Browse agents, inspect skills and MCP servers, plug any agent into your own workflow as a sub-agent
67
+ - **MCP integration** — Discover MCP servers, list tools, run as an MCP stdio server
33
68
  - **Multi-profile** — Switch between environments (test, production, custom)
34
69
  - **Shell completion** — Bash, Zsh, and Fish auto-completion
35
70
  - **CI-friendly** — `--json` output, `--ci` mode, non-zero exit codes on failure
@@ -39,8 +74,11 @@ rush-ai task watch <task-id>
39
74
  **Requirements:** Node.js >= 18.0.0
40
75
 
41
76
  ```bash
42
- npm install -g rush-ai # Global install
43
- npx rush-ai <command> # Or use npx directly
77
+ # Recommended always runs the latest version, zero setup
78
+ npx rush-ai <command>
79
+
80
+ # Optional — pin a global binary
81
+ npm install -g rush-ai
44
82
  ```
45
83
 
46
84
  ## Commands
@@ -64,12 +102,6 @@ npx rush-ai <command> # Or use npx directly
64
102
  | `mcp serve` | - | Start MCP stdio server |
65
103
  | `mcp list` | `mcp ls` | List available MCP servers |
66
104
  | `mcp list-tools <id>` | - | List tools provided by an MCP server |
67
- | `plugin install <name>` | - | Install an IDE plugin |
68
- | `plugin update <name>` | - | Update an installed plugin |
69
- | `plugin uninstall <name>` | - | Remove a plugin |
70
- | `plugin list` | `plugin ls` | List available and installed plugins |
71
- | `plugin status <name>` | - | Show plugin installation status |
72
- | `plugin doctor [name]` | - | Diagnose plugin issues |
73
105
  | `config show` | - | Show current configuration |
74
106
  | `config set <key> <value>` | - | Set a config value (`api`, `collectMetrics`, `currentTeam`) |
75
107
  | `config use <profile>` | - | Switch active profile |
@@ -148,19 +180,6 @@ rush-ai mcp list --search "database"
148
180
  rush-ai mcp list-tools <server-id>
149
181
  ```
150
182
 
151
- ### IDE Plugin Management
152
-
153
- ```bash
154
- # Install plugin for Claude Code
155
- rush-ai plugin install claude-code
156
-
157
- # Check plugin health
158
- rush-ai plugin doctor claude-code
159
-
160
- # Update plugin
161
- rush-ai plugin update claude-code --force
162
- ```
163
-
164
183
  ### Multi-Profile Configuration
165
184
 
166
185
  ```bash
@@ -203,9 +222,7 @@ Config files are stored in `~/.rush/`:
203
222
  ```
204
223
  ~/.rush/
205
224
  ├── auth.json # Credentials (token, refresh token, expiry)
206
- ├── config.json # Global settings (profiles, metrics, team)
207
- └── plugins/
208
- └── installed.json # Plugin manifest
225
+ └── config.json # Global settings (profiles, metrics, team)
209
226
  ```
210
227
 
211
228
  ## Development
package/dist/index.js CHANGED
@@ -161,37 +161,122 @@ function requireAuth() {
161
161
  }
162
162
 
163
163
  // src/commands/agent/index.ts
164
+ var AGENT_HELP_AFTER = `
165
+ When to use:
166
+ Rush hosts a shelf of specialist agents \u2014 call them as sub-agents from
167
+ your own workflows instead of writing the prompts yourself. Always
168
+ discover first, then invoke.
169
+
170
+ Typical flows:
171
+ $ rush-ai agent list --default --json # just the built-in defaults
172
+ $ rush-ai agent list --search "\u4EBA\u529B" --json # fuzzy-filter by name / desc
173
+ $ rush-ai agent list --all --json # everything \u2014 may be long
174
+ $ rush-ai agent info <name> --json # inspect one agent's skills / MCP
175
+
176
+ $ rush-ai task create -a <name> -p "..." # call the agent as a sub-agent
177
+ $ rush-ai task status <id> --json # grab its output
178
+
179
+ Built-in default agents (see --default):
180
+ rush general-purpose agent
181
+ web-builder site / landing-page builder (returns previewUrl + gitRepoUrl)
182
+ skill-publisher publishes reusable skills back onto the platform
183
+
184
+ For agents: run \`rush-ai skill agent-shelf\` for the full playbook.
185
+ `;
164
186
  function registerAgentCommand(program) {
165
- const agent = program.command("agent").description("Manage and inspect agents");
166
- agent.command("list").alias("ls").description("List available agents").action(async () => {
167
- requireAuth();
168
- const format = resolveFormat(program.opts());
169
- const client = createClient();
170
- const { data } = await client.get("/api/agents");
171
- if (format === "json") {
172
- output.log(JSON.stringify(data, null, 2));
173
- return;
174
- }
175
- if (data.agents.length === 0) {
176
- output.info("No agents found.");
177
- return;
187
+ const agent = program.command("agent").description("Manage and inspect agents").addHelpText("after", AGENT_HELP_AFTER);
188
+ agent.command("list").alias("ls").description("List available agents").option(
189
+ "-l, --limit <n>",
190
+ "Max agents per page (1\u2013500, default 50)",
191
+ (v) => Number.parseInt(v, 10),
192
+ 50
193
+ ).option(
194
+ "-p, --page <n>",
195
+ "Page number (1-indexed, default 1)",
196
+ (v) => Number.parseInt(v, 10),
197
+ 1
198
+ ).option("-q, --search <text>", "Filter agents by name / description").option(
199
+ "--default",
200
+ "Only show built-in default agents (web-builder, rush, skill-publisher, ...)"
201
+ ).option(
202
+ "--all",
203
+ "Fetch every page and return the full list (may issue several requests)"
204
+ ).action(
205
+ async (opts) => {
206
+ requireAuth();
207
+ const format = resolveFormat(program.opts());
208
+ const client = createClient();
209
+ const baseParams = new URLSearchParams();
210
+ const rawLimit = Number.isFinite(opts.limit) ? opts.limit : 50;
211
+ const apiLimit = Math.min(Math.max(rawLimit, 1), 500);
212
+ const rawPage = Number.isFinite(opts.page) ? opts.page : 1;
213
+ const requestedPage = Math.max(rawPage, 1);
214
+ if (opts.search) baseParams.set("q", opts.search);
215
+ if (opts.default) baseParams.set("is_builtin", "true");
216
+ async function fetchPage(page2, limit) {
217
+ const params = new URLSearchParams(baseParams);
218
+ params.set("page", String(page2));
219
+ params.set("limit", String(limit));
220
+ const { data: data2 } = await client.get(
221
+ `/api/agents?${params.toString()}`
222
+ );
223
+ return data2;
224
+ }
225
+ let data;
226
+ if (opts.all) {
227
+ const first = await fetchPage(1, 500);
228
+ const totalPages2 = first.pagination?.totalPages ?? 1;
229
+ const all = [...first.agents];
230
+ for (let p = 2; p <= totalPages2; p++) {
231
+ const next = await fetchPage(p, 500);
232
+ all.push(...next.agents);
233
+ }
234
+ data = {
235
+ agents: all,
236
+ pagination: first.pagination ? { ...first.pagination, page: 1, limit: all.length } : {
237
+ total: all.length,
238
+ page: 1,
239
+ limit: all.length,
240
+ totalPages: 1
241
+ }
242
+ };
243
+ } else {
244
+ data = await fetchPage(requestedPage, apiLimit);
245
+ }
246
+ if (format === "json") {
247
+ output.log(JSON.stringify(data, null, 2));
248
+ return;
249
+ }
250
+ if (data.agents.length === 0) {
251
+ output.info("No agents found.");
252
+ return;
253
+ }
254
+ const total = data.pagination?.total ?? data.agents.length;
255
+ const shown = data.agents.length;
256
+ const page = data.pagination?.page ?? 1;
257
+ const totalPages = data.pagination?.totalPages ?? 1;
258
+ output.log(output.bold(`Agents (showing ${shown} of ${total}):`));
259
+ output.newline();
260
+ const rows = data.agents.map((a) => ({
261
+ Name: a.name,
262
+ Description: truncate2(a.description ?? "", 50),
263
+ Status: a.status,
264
+ Skills: String(a.skills?.length ?? 0),
265
+ MCP: String(a.mcp_servers?.length ?? 0)
266
+ }));
267
+ output.log(
268
+ formatOutput(rows, format, {
269
+ columns: { Description: { maxWidth: 50 } }
270
+ })
271
+ );
272
+ if (!opts.all && shown < total) {
273
+ output.newline();
274
+ output.dim(
275
+ `Page ${page}/${totalPages}. Use --page <n>, --limit <n>, --search <text>, or --all to see more.`
276
+ );
277
+ }
178
278
  }
179
- const total = data.pagination?.total ?? data.agents.length;
180
- output.log(output.bold(`Agents (${total} total):`));
181
- output.newline();
182
- const rows = data.agents.map((a) => ({
183
- Name: a.name,
184
- Description: truncate2(a.description ?? "", 50),
185
- Status: a.status,
186
- Skills: String(a.skills?.length ?? 0),
187
- MCP: String(a.mcp_servers?.length ?? 0)
188
- }));
189
- output.log(
190
- formatOutput(rows, format, {
191
- columns: { Description: { maxWidth: 50 } }
192
- })
193
- );
194
- });
279
+ );
195
280
  agent.command("info").description("Show agent details").argument("<name>", "Agent name or ID").action(async (nameOrId) => {
196
281
  requireAuth();
197
282
  const format = resolveFormat(program.opts());
@@ -286,7 +371,7 @@ function getApiBaseUrl() {
286
371
  async function loginViaBrowser(jsonMode) {
287
372
  const baseUrl = getApiBaseUrl();
288
373
  const state = randomBytes(16).toString("hex");
289
- return new Promise((resolve9, reject) => {
374
+ return new Promise((resolve10, reject) => {
290
375
  const server = createServer(async (req, res) => {
291
376
  try {
292
377
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -382,7 +467,7 @@ async function loginViaBrowser(jsonMode) {
382
467
  );
383
468
  }
384
469
  }
385
- resolve9();
470
+ resolve10();
386
471
  } catch (err) {
387
472
  server.close();
388
473
  reject(
@@ -3244,6 +3329,66 @@ function printVerifyResults(results) {
3244
3329
  }
3245
3330
  }
3246
3331
 
3332
+ // src/commands/skill/index.ts
3333
+ import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync10 } from "fs";
3334
+ import { basename as basename2, resolve as resolve8 } from "path";
3335
+ var SKILL_DESCRIPTION = "Print agent usage skills (hand-off, agent-shelf, ...) as raw markdown.";
3336
+ var SKILL_HELP_AFTER = `
3337
+ Examples:
3338
+ $ npx rush-ai skill Print the skills index
3339
+ $ npx rush-ai skill hand-off Print the hand-off playbook
3340
+ $ npx rush-ai skill agent-shelf Print the agent-shelf playbook
3341
+ $ npx rush-ai skill --list List available skills
3342
+
3343
+ Designed to be consumed by AI agents inside IDEs (Cursor, Claude Code,
3344
+ etc). Output is raw markdown on stdout \u2014 pipe it, grep it, feed it to
3345
+ your own context.
3346
+ `;
3347
+ function resolveSkillsDir() {
3348
+ const baseDir = import.meta.dirname ?? __dirname;
3349
+ if (!baseDir) return null;
3350
+ const candidates = [
3351
+ resolve8(baseDir, "skills"),
3352
+ resolve8(baseDir, "..", "skills"),
3353
+ resolve8(baseDir, "..", "..", "skills"),
3354
+ resolve8(baseDir, "..", "..", "..", "skills"),
3355
+ resolve8(baseDir, "..", "..", "..", "..", "skills")
3356
+ ];
3357
+ for (const p of candidates) {
3358
+ if (existsSync17(p)) return p;
3359
+ }
3360
+ return null;
3361
+ }
3362
+ function listSkills(dir) {
3363
+ return readdirSync2(dir).filter((f) => f.endsWith(".md")).map((f) => basename2(f, ".md")).sort();
3364
+ }
3365
+ function registerSkillCommand(program) {
3366
+ program.command("skill [name]").description(SKILL_DESCRIPTION).addHelpText("after", SKILL_HELP_AFTER).option("--list", "List available skill names and exit").action((name, opts) => {
3367
+ const dir = resolveSkillsDir();
3368
+ if (!dir) {
3369
+ output.error(
3370
+ "Could not locate the skills/ directory. This is a packaging bug \u2014 please file an issue."
3371
+ );
3372
+ process.exit(2);
3373
+ return;
3374
+ }
3375
+ const available = listSkills(dir);
3376
+ if (opts.list) {
3377
+ for (const s of available) output.log(s);
3378
+ return;
3379
+ }
3380
+ const target = name ?? "README";
3381
+ const file = resolve8(dir, `${target}.md`);
3382
+ if (!existsSync17(file)) {
3383
+ output.error(`Unknown skill "${target}".`);
3384
+ output.dim(`Available: ${available.join(", ") || "(none)"}`);
3385
+ process.exit(1);
3386
+ return;
3387
+ }
3388
+ process.stdout.write(readFileSync10(file, "utf-8"));
3389
+ });
3390
+ }
3391
+
3247
3392
  // src/commands/task/index.ts
3248
3393
  import { createWriteStream } from "fs";
3249
3394
  import { mkdir as mkdir2 } from "fs/promises";
@@ -3285,28 +3430,28 @@ async function readStdinIfPiped() {
3285
3430
  if (process.stdin.isTTY) {
3286
3431
  return null;
3287
3432
  }
3288
- return new Promise((resolve9, reject) => {
3433
+ return new Promise((resolve10, reject) => {
3289
3434
  const chunks = [];
3290
3435
  process.stdin.on("data", (chunk) => {
3291
3436
  chunks.push(chunk);
3292
3437
  });
3293
3438
  process.stdin.on("end", () => {
3294
- resolve9(Buffer.concat(chunks).toString("utf-8").trim());
3439
+ resolve10(Buffer.concat(chunks).toString("utf-8").trim());
3295
3440
  });
3296
3441
  process.stdin.on("error", reject);
3297
3442
  });
3298
3443
  }
3299
3444
 
3300
3445
  // src/commands/task/deploy.ts
3301
- import { resolve as resolve8 } from "path";
3446
+ import { resolve as resolve9 } from "path";
3302
3447
  import chalk7 from "chalk";
3303
3448
 
3304
3449
  // src/util/git.ts
3305
3450
  import { execFileSync as execFileSync3 } from "child_process";
3306
- import { existsSync as existsSync17 } from "fs";
3451
+ import { existsSync as existsSync18 } from "fs";
3307
3452
  import { join as join8 } from "path";
3308
3453
  function isGitRepo(projectPath) {
3309
- return existsSync17(join8(projectPath, ".git"));
3454
+ return existsSync18(join8(projectPath, ".git"));
3310
3455
  }
3311
3456
  function gitInit(projectPath) {
3312
3457
  execFileSync3("git", ["init"], { cwd: projectPath, stdio: "pipe" });
@@ -3342,7 +3487,15 @@ function gitPushUrl(projectPath, url) {
3342
3487
  cwd: projectPath,
3343
3488
  stdio: "pipe",
3344
3489
  timeout: 3e5,
3345
- maxBuffer: 10 * 1024 * 1024
3490
+ maxBuffer: 10 * 1024 * 1024,
3491
+ env: {
3492
+ ...process.env,
3493
+ // 失败时不要弹密码 / SSH key 提示,否则在非交互环境(CI / 测试)
3494
+ // 会一直 hang 到外层 timeout(300s)才返回。
3495
+ GIT_TERMINAL_PROMPT: "0",
3496
+ GIT_ASKPASS: "true",
3497
+ SSH_ASKPASS: "true"
3498
+ }
3346
3499
  });
3347
3500
  return { success: true, stderr: "" };
3348
3501
  } catch (err) {
@@ -3366,7 +3519,7 @@ function registerDeploySubcommand(task, program) {
3366
3519
  requireAuth();
3367
3520
  const format = resolveFormat(program.opts());
3368
3521
  const client = createClient();
3369
- const projectPath = resolve8(opts.path);
3522
+ const projectPath = resolve9(opts.path);
3370
3523
  if (format !== "json") {
3371
3524
  output.info("Checking project readiness...");
3372
3525
  }
@@ -3610,10 +3763,31 @@ function buildCategorySummary(files) {
3610
3763
  }
3611
3764
  return Object.entries(counts).map(([cat, count]) => `${count} ${cat}`).join(", ");
3612
3765
  }
3766
+ var TASK_HELP_AFTER = `
3767
+ When to use:
3768
+ Call \`task create\` whenever the next unit of work belongs on the Rush
3769
+ platform instead of your local machine \u2014 a specialist Rush agent runs
3770
+ it, and you get a task id to track / iterate on.
3771
+
3772
+ Typical flows:
3773
+ # Hand-off from an IDE conversation \u2192 Rush agent builds, you iterate
3774
+ $ rush-ai task create -a web-builder -p "<prompt synthesized from context>" --json
3775
+ $ rush-ai task status <id> --json # includes previewUrl / gitRepoUrl
3776
+ $ rush-ai task send <id> -p "<follow-up>" # same task, keep iterating
3777
+
3778
+ # Quick one-shot with the default agent (\`rush\`)
3779
+ $ rush-ai task create -a rush -p "Summarize the latest release notes"
3780
+
3781
+ For agents: run \`rush-ai skill hand-off\` for the full playbook.
3782
+ `;
3613
3783
  function registerTaskCommand(program) {
3614
- const task = program.command("task").description("Create and manage tasks");
3784
+ const task = program.command("task").description("Create and manage tasks").addHelpText("after", TASK_HELP_AFTER);
3615
3785
  registerDeploySubcommand(task, program);
3616
- task.command("create").description("Create a task asynchronously").requiredOption("-a, --agent <name>", "Agent name to execute the task").option("-p, --prompt <text>", "Task prompt").option("--skills <skills>", "Comma-separated skills to add", commaSplit).option("--mcp <servers>", "Comma-separated MCP servers to add", commaSplit).action(
3786
+ task.command("create").description("Create a task asynchronously").option(
3787
+ "-a, --agent <name>",
3788
+ "Agent name to execute the task (defaults to `rush`)",
3789
+ "rush"
3790
+ ).option("-p, --prompt <text>", "Task prompt").option("--skills <skills>", "Comma-separated skills to add", commaSplit).option("--mcp <servers>", "Comma-separated MCP servers to add", commaSplit).action(
3617
3791
  async (options) => {
3618
3792
  requireAuth();
3619
3793
  const format = resolveFormat(program.opts());
@@ -3945,7 +4119,7 @@ function registerTaskCommand(program) {
3945
4119
  }
3946
4120
  }
3947
4121
  );
3948
- task.command("list").alias("ls").description("List tasks").option("-l, --limit <limit>", "Maximum number of tasks", "20").option("-s, --status <status>", "Filter by status").action(async (options) => {
4122
+ task.command("list").alias("ls").description("List tasks").option("-l, --limit <limit>", "Maximum number of tasks", "50").option("-s, --status <status>", "Filter by status").action(async (options) => {
3949
4123
  requireAuth();
3950
4124
  const format = resolveFormat(program.opts());
3951
4125
  const client = createClient();
@@ -4070,6 +4244,7 @@ function registerCommands(program) {
4070
4244
  registerAuthCommand(program);
4071
4245
  registerAgentCommand(program);
4072
4246
  registerTaskCommand(program);
4247
+ registerSkillCommand(program);
4073
4248
  registerCheckCommand(program);
4074
4249
  registerMcpCommand(program);
4075
4250
  registerPluginCommand(program);
@@ -4141,17 +4316,29 @@ async function checkForUpdate(currentVersion) {
4141
4316
  // src/index.ts
4142
4317
  var BANNER = `
4143
4318
  ${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}
4144
- ${chalk8.dim("The command-line interface for the Rush AI platform")}
4319
+ ${chalk8.dim("Call Rush agents from your terminal \u2014 relay the context, not the prompt.")}
4320
+
4321
+ ${chalk8.dim("Use cases:")}
4322
+ ${chalk8.dim("\xB7")} From Cursor / Claude Code, hand off a task to a Rush agent without restating context.
4323
+ ${chalk8.dim("\xB7")} Compose workflows where a Rush specialist agent runs as your sub-agent.
4324
+
4325
+ ${chalk8.dim("For agents:")} ${chalk8.cyan("rush-ai skill")} prints ready-to-consume usage playbooks.
4145
4326
  `;
4146
4327
  function showWelcomeGuide() {
4147
4328
  const lines = [
4148
4329
  "",
4149
4330
  ` ${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}`,
4331
+ ` ${chalk8.dim("Call Rush agents from your terminal \u2014 relay the context, not the prompt.")}`,
4150
4332
  "",
4151
4333
  chalk8.dim(" Quick start:"),
4152
- ` ${chalk8.cyan("rush-ai auth login")} Log in to Rush platform`,
4153
- ` ${chalk8.cyan("rush-ai agent list")} Browse available agents`,
4154
- ` ${chalk8.cyan("rush-ai task create -a <agent>")} Create a task`,
4334
+ ` ${chalk8.cyan("rush-ai auth login")} Log in to the Rush platform`,
4335
+ ` ${chalk8.cyan("rush-ai agent list")} Browse available agents`,
4336
+ ` ${chalk8.cyan("rush-ai task create -a web-builder")} Hand a task to a Rush agent`,
4337
+ "",
4338
+ chalk8.dim(" For AI agents:"),
4339
+ ` ${chalk8.cyan("rush-ai skill")} Print usage playbooks (markdown)`,
4340
+ ` ${chalk8.cyan("rush-ai skill hand-off")} IDE \u2192 Rush task hand-off`,
4341
+ ` ${chalk8.cyan("rush-ai skill agent-shelf")} Calling a Rush agent as a sub-agent`,
4155
4342
  ""
4156
4343
  ];
4157
4344
  try {