rafaygen-cli 1.3.2 → 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 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("--dangerously-bypass-approvals-and-sandbox", "Bypass all safety checks (DANGEROUS)")
25
- .option("--full-auto", "Run without pausing for user confirmation")
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
- program.hook('preAction', (thisCommand, actionCommand) => {
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 = opts.verbose;
41
- if (opts.compact) newState.compactMode = opts.compact;
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("<token>", "Your Personal Access Token")
125
+ .argument("[token]", "Your Personal Access Token")
58
126
  .option("--url <url>", "Set custom API URL")
59
- .action((token, options) => {
60
- setToken(token);
61
- if (options.url) setApiUrl(options.url);
62
- printSuccess("Successfully logged in to RafayGen!");
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).then(() => process.exit(0));
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
- // Handle default command if no args provided or just flags -> Start Chat Loop
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
- program.parse(process.argv);
86
- // If parsing resulted in no subcommands being called (e.g. `rgcli --model groq`), launch loop
87
- if (!process.argv.slice(2).some(arg => ['login', 'ask', 'chat'].includes(arg)) && !program.opts().help) {
88
- startInteractiveLoop();
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.2",
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": "./bin/rgcli.js"
14
+ "rgcli": "bin/rgcli.js"
15
15
  },
16
16
  "dependencies": {
17
17
  "axios": "^1.16.1",