rafaygen-cli 1.3.1 → 1.3.3
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/bin/rgcli.js +302 -29
- package/package.json +2 -2
- package/src/agent.js +1297 -191
- package/src/auth.js +446 -105
- package/src/executor.js +737 -0
- package/src/state.js +254 -10
- package/src/ui.js +419 -51
package/bin/rgcli.js
CHANGED
|
@@ -2,13 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
-
import { setToken, setApiUrl, setModel } from "../src/auth.js";
|
|
5
|
+
import { setToken, setApiUrl, setModel, clearConfig, loadConfig } from "../src/auth.js";
|
|
6
6
|
import { askAgent, startInteractiveLoop } from "../src/agent.js";
|
|
7
|
-
import { printSuccess } from "../src/ui.js";
|
|
7
|
+
import { printSuccess, printAsciiLogo } from "../src/ui.js";
|
|
8
8
|
import { updateSessionState } from "../src/state.js";
|
|
9
9
|
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
|
|
13
|
+
const pkg = JSON.parse(
|
|
14
|
+
fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const MCP_CONFIG_PATH = path.join(os.homedir(), ".rgcli", "mcp.json");
|
|
18
|
+
|
|
19
|
+
function loadMcpConfig() {
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(MCP_CONFIG_PATH)) {
|
|
22
|
+
return JSON.parse(fs.readFileSync(MCP_CONFIG_PATH, "utf-8"));
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// corrupted file – reset
|
|
26
|
+
}
|
|
27
|
+
return { servers: {} };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function saveMcpConfig(config) {
|
|
31
|
+
const dir = path.dirname(MCP_CONFIG_PATH);
|
|
32
|
+
if (!fs.existsSync(dir)) {
|
|
33
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
fs.writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const KNOWN_COMMANDS = [
|
|
39
|
+
"login",
|
|
40
|
+
"logout",
|
|
41
|
+
"ask",
|
|
42
|
+
"chat",
|
|
43
|
+
"exec",
|
|
44
|
+
"proto",
|
|
45
|
+
"mcp",
|
|
46
|
+
];
|
|
10
47
|
|
|
11
|
-
const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
12
48
|
const program = new Command();
|
|
13
49
|
|
|
14
50
|
program
|
|
@@ -16,61 +52,111 @@ program
|
|
|
16
52
|
.description("RafayGen - The Ultimate Agentic Coding CLI")
|
|
17
53
|
.version(pkg.version)
|
|
18
54
|
.option("-m, --model <name>", "Override the default AI model")
|
|
19
|
-
.option("--sandbox <mode>", "Set sandbox mode: read-only, workspace-write, danger-full-access")
|
|
20
|
-
.option("--approval-mode <mode>", "Set approval mode: suggest, auto-edit, full-auto, never")
|
|
21
55
|
.option("--reasoning <level>", "Reasoning effort: low, medium, high")
|
|
56
|
+
.option(
|
|
57
|
+
"--approval-mode <mode>",
|
|
58
|
+
"Approval mode: suggest, auto-edit, full-auto, never"
|
|
59
|
+
)
|
|
60
|
+
.option(
|
|
61
|
+
"--sandbox <mode>",
|
|
62
|
+
"Sandbox mode: read-only, workspace-write, danger-full-access"
|
|
63
|
+
)
|
|
64
|
+
.option("--full-auto", "Run without pausing for user confirmation")
|
|
65
|
+
.option("--auto-edit", "Auto-approve file edits without prompting")
|
|
66
|
+
.option(
|
|
67
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
68
|
+
"Bypass all safety checks (DANGEROUS)"
|
|
69
|
+
)
|
|
70
|
+
.option("--search", "Enable web search for context")
|
|
71
|
+
.option("--image <path>", "Attach an image file to the prompt")
|
|
72
|
+
.option("--config", "Show current configuration")
|
|
73
|
+
.option("--profile <name>", "Load a named user profile")
|
|
22
74
|
.option("--cwd <path>", "Set explicit working directory")
|
|
75
|
+
.option("--skip-git-repo-check", "Skip git repository detection")
|
|
76
|
+
.option("--json", "Output results as JSON")
|
|
23
77
|
.option("-v, --verbose", "Enable verbose debugging output")
|
|
24
|
-
.option("--
|
|
25
|
-
.option("--
|
|
78
|
+
.option("-q, --quiet", "Suppress non-essential output")
|
|
79
|
+
.option("--color", "Force color output")
|
|
80
|
+
.option("--no-color", "Disable color output")
|
|
26
81
|
.option("-c, --compact", "Compact conversation context")
|
|
27
|
-
.option("-p, --profile <name>", "Load specific user profile")
|
|
28
|
-
.option("-q, --quiet", "Suppress non-essential output");
|
|
82
|
+
.option("-p, --profile <name>", "Load specific user profile");
|
|
29
83
|
|
|
30
|
-
|
|
84
|
+
// ── preAction hook ─────────────────────────────────────────────────────
|
|
85
|
+
program.hook("preAction", (_thisCommand, _actionCommand) => {
|
|
31
86
|
const opts = program.opts();
|
|
32
|
-
|
|
87
|
+
|
|
33
88
|
if (opts.model) setModel(opts.model);
|
|
34
|
-
|
|
89
|
+
|
|
35
90
|
const newState = {};
|
|
91
|
+
|
|
36
92
|
if (opts.sandbox) newState.sandboxMode = opts.sandbox;
|
|
37
93
|
if (opts.approvalMode) newState.approvalMode = opts.approvalMode;
|
|
38
94
|
if (opts.reasoning) newState.reasoningEffort = opts.reasoning;
|
|
39
|
-
if (opts.cwd) newState.cwd = opts.cwd;
|
|
40
|
-
if (opts.verbose) newState.verbose =
|
|
41
|
-
if (opts.
|
|
42
|
-
|
|
95
|
+
if (opts.cwd) newState.cwd = path.resolve(opts.cwd);
|
|
96
|
+
if (opts.verbose) newState.verbose = true;
|
|
97
|
+
if (opts.quiet) newState.quiet = true;
|
|
98
|
+
if (opts.compact) newState.compactMode = true;
|
|
99
|
+
if (opts.json) newState.jsonOutput = true;
|
|
100
|
+
if (opts.skipGitRepoCheck) newState.skipGitCheck = true;
|
|
101
|
+
if (opts.autoEdit) newState.autoEdit = true;
|
|
102
|
+
if (opts.search) newState.searchEnabled = true;
|
|
103
|
+
if (opts.image) newState.imageAttached = path.resolve(opts.image);
|
|
104
|
+
|
|
105
|
+
if (typeof opts.color === "boolean") {
|
|
106
|
+
newState.colorEnabled = opts.color;
|
|
107
|
+
}
|
|
108
|
+
|
|
43
109
|
if (opts.fullAuto) {
|
|
44
110
|
newState.approvalMode = "full-auto";
|
|
45
111
|
}
|
|
112
|
+
|
|
46
113
|
if (opts.dangerouslyBypassApprovalsAndSandbox) {
|
|
47
114
|
newState.sandboxMode = "danger-full-access";
|
|
48
115
|
newState.approvalMode = "full-auto";
|
|
49
116
|
}
|
|
50
|
-
|
|
117
|
+
|
|
51
118
|
updateSessionState(newState);
|
|
52
119
|
});
|
|
53
120
|
|
|
121
|
+
// ── login ──────────────────────────────────────────────────────────────
|
|
54
122
|
program
|
|
55
123
|
.command("login")
|
|
56
124
|
.description("Authenticate with your RafayGen live app")
|
|
57
|
-
.argument("
|
|
125
|
+
.argument("[token]", "Your Personal Access Token")
|
|
58
126
|
.option("--url <url>", "Set custom API URL")
|
|
59
|
-
.action((token, options) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
127
|
+
.action(async (token, options) => {
|
|
128
|
+
if (token) {
|
|
129
|
+
setToken(token);
|
|
130
|
+
if (options.url) setApiUrl(options.url);
|
|
131
|
+
printSuccess("Successfully logged in to RafayGen!");
|
|
132
|
+
} else {
|
|
133
|
+
if (options.url) setApiUrl(options.url);
|
|
134
|
+
const { startAuthLoop } = await import("../src/auth.js");
|
|
135
|
+
await startAuthLoop();
|
|
136
|
+
}
|
|
63
137
|
});
|
|
64
138
|
|
|
139
|
+
// ── logout ─────────────────────────────────────────────────────────────
|
|
140
|
+
program
|
|
141
|
+
.command("logout")
|
|
142
|
+
.description("Logout and clear stored credentials")
|
|
143
|
+
.action(() => {
|
|
144
|
+
clearConfig();
|
|
145
|
+
printSuccess("Successfully logged out. All credentials cleared.");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ── ask ────────────────────────────────────────────────────────────────
|
|
65
149
|
program
|
|
66
150
|
.command("ask")
|
|
67
151
|
.description("Ask RafayGen a one-off question")
|
|
68
152
|
.argument("<prompt...>", "What do you want to build?")
|
|
69
|
-
.action((promptWords) => {
|
|
153
|
+
.action(async (promptWords) => {
|
|
70
154
|
const prompt = promptWords.join(" ");
|
|
71
|
-
askAgent(prompt)
|
|
155
|
+
await askAgent(prompt);
|
|
156
|
+
process.exit(0);
|
|
72
157
|
});
|
|
73
158
|
|
|
159
|
+
// ── chat ───────────────────────────────────────────────────────────────
|
|
74
160
|
program
|
|
75
161
|
.command("chat")
|
|
76
162
|
.description("Start an interactive chat session")
|
|
@@ -78,13 +164,200 @@ program
|
|
|
78
164
|
startInteractiveLoop();
|
|
79
165
|
});
|
|
80
166
|
|
|
81
|
-
//
|
|
167
|
+
// ── exec ───────────────────────────────────────────────────────────────
|
|
168
|
+
program
|
|
169
|
+
.command("exec")
|
|
170
|
+
.description("Execute a single action directly without chat")
|
|
171
|
+
.argument("<action...>", "The action to execute")
|
|
172
|
+
.action(async (actionWords) => {
|
|
173
|
+
const prompt =
|
|
174
|
+
"[EXEC MODE: Execute exactly this task with no conversational filler] " +
|
|
175
|
+
actionWords.join(" ");
|
|
176
|
+
await askAgent(prompt);
|
|
177
|
+
process.exit(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ── proto ──────────────────────────────────────────────────────────────
|
|
181
|
+
program
|
|
182
|
+
.command("proto")
|
|
183
|
+
.description("Prototype a complete module/app quickly")
|
|
184
|
+
.argument("<description...>", "What are we prototyping?")
|
|
185
|
+
.action(async (descWords) => {
|
|
186
|
+
const prompt =
|
|
187
|
+
"[PROTOTYPE MODE: Generate a complete, production-ready MVP for this] " +
|
|
188
|
+
descWords.join(" ");
|
|
189
|
+
await askAgent(prompt);
|
|
190
|
+
process.exit(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ── mcp (parent command) ──────────────────────────────────────────────
|
|
194
|
+
const mcpCmd = program
|
|
195
|
+
.command("mcp")
|
|
196
|
+
.description("Manage MCP (Model Context Protocol) server configurations");
|
|
197
|
+
|
|
198
|
+
// mcp add <name>
|
|
199
|
+
mcpCmd
|
|
200
|
+
.command("add")
|
|
201
|
+
.description("Add or update an MCP server configuration")
|
|
202
|
+
.argument("<name>", "Unique name for the MCP server")
|
|
203
|
+
.option("--url <url>", "Server endpoint URL")
|
|
204
|
+
.option("--command <cmd>", "Shell command to launch the server")
|
|
205
|
+
.option("--args <args...>", "Arguments for the launch command")
|
|
206
|
+
.option("--env <pairs...>", "Environment variables as KEY=VALUE pairs")
|
|
207
|
+
.action((name, options) => {
|
|
208
|
+
const config = loadMcpConfig();
|
|
209
|
+
|
|
210
|
+
const entry = {};
|
|
211
|
+
if (options.url) {
|
|
212
|
+
entry.type = "url";
|
|
213
|
+
entry.url = options.url;
|
|
214
|
+
} else if (options.command) {
|
|
215
|
+
entry.type = "stdio";
|
|
216
|
+
entry.command = options.command;
|
|
217
|
+
entry.args = options.args || [];
|
|
218
|
+
} else {
|
|
219
|
+
entry.type = "stdio";
|
|
220
|
+
entry.command = name;
|
|
221
|
+
entry.args = options.args || [];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (options.env && options.env.length > 0) {
|
|
225
|
+
entry.env = {};
|
|
226
|
+
for (const pair of options.env) {
|
|
227
|
+
const eqIdx = pair.indexOf("=");
|
|
228
|
+
if (eqIdx > 0) {
|
|
229
|
+
entry.env[pair.slice(0, eqIdx)] = pair.slice(eqIdx + 1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
config.servers[name] = entry;
|
|
235
|
+
saveMcpConfig(config);
|
|
236
|
+
printSuccess(`MCP server '${name}' added successfully.`);
|
|
237
|
+
console.log(chalk.gray(JSON.stringify(entry, null, 2)));
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// mcp remove <name>
|
|
241
|
+
mcpCmd
|
|
242
|
+
.command("remove")
|
|
243
|
+
.description("Remove an MCP server configuration")
|
|
244
|
+
.argument("<name>", "Name of the MCP server to remove")
|
|
245
|
+
.action((name) => {
|
|
246
|
+
const config = loadMcpConfig();
|
|
247
|
+
if (config.servers[name]) {
|
|
248
|
+
delete config.servers[name];
|
|
249
|
+
saveMcpConfig(config);
|
|
250
|
+
printSuccess(`MCP server '${name}' removed.`);
|
|
251
|
+
} else {
|
|
252
|
+
console.log(
|
|
253
|
+
chalk.yellow(`\n⚠ MCP server '${name}' not found in configuration.\n`)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// mcp list
|
|
259
|
+
mcpCmd
|
|
260
|
+
.command("list")
|
|
261
|
+
.description("List all configured MCP servers")
|
|
262
|
+
.action(() => {
|
|
263
|
+
const config = loadMcpConfig();
|
|
264
|
+
const names = Object.keys(config.servers);
|
|
265
|
+
if (names.length === 0) {
|
|
266
|
+
console.log(
|
|
267
|
+
chalk.gray(
|
|
268
|
+
"\nNo MCP servers configured. Use 'rgcli mcp add <name>' to add one.\n"
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
console.log(chalk.cyan.bold("\n── Configured MCP Servers ──\n"));
|
|
274
|
+
for (const name of names) {
|
|
275
|
+
const srv = config.servers[name];
|
|
276
|
+
const typeLabel = srv.type === "url" ? chalk.blue("URL") : chalk.green("STDIO");
|
|
277
|
+
const detail =
|
|
278
|
+
srv.type === "url"
|
|
279
|
+
? srv.url
|
|
280
|
+
: `${srv.command}${srv.args && srv.args.length > 0 ? " " + srv.args.join(" ") : ""}`;
|
|
281
|
+
console.log(` ${chalk.white.bold(name)} ${typeLabel} ${chalk.gray(detail)}`);
|
|
282
|
+
}
|
|
283
|
+
console.log("");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// mcp inspect <name>
|
|
287
|
+
mcpCmd
|
|
288
|
+
.command("inspect")
|
|
289
|
+
.description("Show detailed configuration for an MCP server")
|
|
290
|
+
.argument("<name>", "Name of the MCP server to inspect")
|
|
291
|
+
.action((name) => {
|
|
292
|
+
const config = loadMcpConfig();
|
|
293
|
+
const srv = config.servers[name];
|
|
294
|
+
if (!srv) {
|
|
295
|
+
console.log(
|
|
296
|
+
chalk.yellow(`\n⚠ MCP server '${name}' not found in configuration.\n`)
|
|
297
|
+
);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
console.log(chalk.cyan.bold(`\n── MCP Server: ${name} ──\n`));
|
|
301
|
+
console.log(JSON.stringify(srv, null, 2));
|
|
302
|
+
console.log("");
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// ── --config flag handler ──────────────────────────────────────────────
|
|
306
|
+
function handleConfigFlag() {
|
|
307
|
+
const config = loadConfig();
|
|
308
|
+
console.log(chalk.cyan.bold("\n── Current Configuration ──\n"));
|
|
309
|
+
console.log(
|
|
310
|
+
chalk.white(` Token: ${config.token ? chalk.green("set (" + config.token.slice(0, 12) + "…)") : chalk.red("not set")}`)
|
|
311
|
+
);
|
|
312
|
+
console.log(
|
|
313
|
+
chalk.white(` API URL: ${config.apiUrl || chalk.gray("http://localhost:3000/api/cli (default)")}`)
|
|
314
|
+
);
|
|
315
|
+
console.log(
|
|
316
|
+
chalk.white(` Model: ${config.model || chalk.gray("default")}`)
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
// Show any extra keys
|
|
320
|
+
const knownKeys = new Set(["token", "apiUrl", "model"]);
|
|
321
|
+
for (const [key, value] of Object.entries(config)) {
|
|
322
|
+
if (!knownKeys.has(key)) {
|
|
323
|
+
console.log(chalk.white(` ${key}: ${chalk.gray(JSON.stringify(value))}`));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log(
|
|
328
|
+
chalk.white(`\n Config file: ${chalk.gray(path.join(os.homedir(), ".rgcli.json"))}`)
|
|
329
|
+
);
|
|
330
|
+
console.log(
|
|
331
|
+
chalk.white(` MCP config: ${chalk.gray(MCP_CONFIG_PATH)}`)
|
|
332
|
+
);
|
|
333
|
+
console.log(
|
|
334
|
+
chalk.white(` Skills dir: ${chalk.gray(path.join(os.homedir(), ".rgcli", "skills"))}`)
|
|
335
|
+
);
|
|
336
|
+
console.log(
|
|
337
|
+
chalk.white(` Sessions: ${chalk.gray(path.join(os.homedir(), ".rgcli", "sessions"))}`)
|
|
338
|
+
);
|
|
339
|
+
console.log("");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Default behaviour / entry point ────────────────────────────────────
|
|
82
343
|
if (process.argv.length === 2) {
|
|
344
|
+
// No arguments at all → launch interactive loop
|
|
83
345
|
startInteractiveLoop();
|
|
84
346
|
} else {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
347
|
+
// Check for --config before parsing (since it's a flag, not a command)
|
|
348
|
+
if (process.argv.includes("--config")) {
|
|
349
|
+
handleConfigFlag();
|
|
350
|
+
} else {
|
|
351
|
+
program.parse(process.argv);
|
|
352
|
+
|
|
353
|
+
// After parsing, check if a known subcommand was actually invoked.
|
|
354
|
+
// If not (e.g. user just passed flags like `rgcli --model groq`),
|
|
355
|
+
// fall through to interactive loop.
|
|
356
|
+
const userArgs = process.argv.slice(2);
|
|
357
|
+
const hasSubcommand = userArgs.some((arg) => KNOWN_COMMANDS.includes(arg));
|
|
358
|
+
|
|
359
|
+
if (!hasSubcommand) {
|
|
360
|
+
startInteractiveLoop();
|
|
361
|
+
}
|
|
89
362
|
}
|
|
90
363
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rafaygen-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"license": "ISC",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
14
|
-
"rgcli": "
|
|
14
|
+
"rgcli": "bin/rgcli.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"axios": "^1.16.1",
|