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 +37 -0
- package/README.md +54 -37
- package/dist/index.js +232 -45
- package/dist/index.js.map +1 -1
- package/dist/plugin-assets/SKILL.md +66 -59
- package/dist/skills/README.md +48 -0
- package/dist/skills/agent-shelf.md +102 -0
- package/dist/skills/hand-off.md +163 -0
- package/package.json +3 -1
- package/skills/README.md +48 -0
- package/skills/agent-shelf.md +102 -0
- package/skills/hand-off.md +163 -0
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
|
-
**
|
|
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
|
[](https://www.npmjs.com/package/rush-ai)
|
|
10
8
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -13,23 +11,60 @@
|
|
|
13
11
|
|
|
14
12
|
---
|
|
15
13
|
|
|
16
|
-
##
|
|
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
|
-
|
|
23
|
+
# From inside Cursor / Claude Code, the local agent runs:
|
|
24
|
+
npx rush-ai task create -a web-builder -p "做一个公司官网,风格大气专业"
|
|
25
|
+
```
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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
|
-
|
|
43
|
-
npx rush-ai <command>
|
|
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
|
-
|
|
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").
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
3451
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3307
3452
|
import { join as join8 } from "path";
|
|
3308
3453
|
function isGitRepo(projectPath) {
|
|
3309
|
-
return
|
|
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 =
|
|
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").
|
|
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", "
|
|
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("
|
|
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")}
|
|
4153
|
-
` ${chalk8.cyan("rush-ai agent list")}
|
|
4154
|
-
` ${chalk8.cyan("rush-ai task create -a
|
|
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 {
|