@robota-sdk/agent-cli 3.0.0-beta.46 → 3.0.0-beta.47

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
@@ -78,10 +78,33 @@ robota --model <model> # Model override (e.g., claude-sonnet-4-6)
78
78
  robota --language <lang> # Response language (ko, en, ja, zh)
79
79
  robota --permission-mode <mode> # plan | default | acceptEdits | bypassPermissions
80
80
  robota --max-turns <n> # Limit agentic turns per interaction
81
+ robota --output-format <fmt> # text | json | stream-json (print mode)
82
+ robota --system-prompt <text> # Replace system prompt (print mode)
83
+ robota --append-system-prompt <text> # Append to system prompt (print mode)
81
84
  robota --reset # Delete user settings and exit
82
85
  robota --version # Show version
83
86
  ```
84
87
 
88
+ ### Print Mode Output Formats
89
+
90
+ Print mode (`-p`) supports three output formats via `--output-format`:
91
+
92
+ | Format | Description |
93
+ | ------------- | ------------------------------------------------------------------ |
94
+ | `text` | Plain text response to stdout (default) |
95
+ | `json` | Single JSON object: `{ type, result, session_id, subtype }` |
96
+ | `stream-json` | Newline-delimited JSON with `content_block_delta` streaming events |
97
+
98
+ ### Stdin Pipe
99
+
100
+ When `-p` is used without a positional argument and stdin is piped, the CLI reads from stdin:
101
+
102
+ ```bash
103
+ echo "Explain this error" | robota -p
104
+ cat file.ts | robota -p "Review this code" --output-format json
105
+ git diff | robota -p "Summarize changes" --output-format stream-json
106
+ ```
107
+
85
108
  ## First-Run Setup
86
109
 
87
110
  When no settings file exists, the CLI prompts for:
@@ -227,7 +250,7 @@ When a session has a name, it appears in three places:
227
250
  | `/rename <name>` | Rename the current session |
228
251
  | `/exit` | Exit CLI |
229
252
 
230
- Typing `/` triggers an autocomplete popup with arrow-key navigation, Tab completion, and Esc to dismiss. Commands with subcommands (e.g., `/mode`, `/model`) show a nested submenu. Skill commands discovered from `.agents/skills/` and `.claude/commands/` appear alongside built-in commands.
253
+ Typing `/` triggers an autocomplete popup with arrow-key navigation and Esc to dismiss. Tab inserts the highlighted command into the input field without executing — continue typing args or press Enter to execute. Enter selects and executes immediately. Commands with subcommands (e.g., `/mode`, `/model`) show a nested submenu. Skill commands discovered from `.agents/skills/` and `.claude/commands/` appear alongside built-in commands.
231
254
 
232
255
  ## Plugin Management
233
256
 
@@ -317,18 +340,19 @@ bin.ts → cli.ts (arg parsing)
317
340
 
318
341
  ## Dependencies
319
342
 
320
- | Package | Purpose |
321
- | --------------------------- | ------------------------------------------ |
322
- | `@robota-sdk/agent-sdk` | Session factory, query, config, context |
323
- | `@robota-sdk/agent-core` | Types (TPermissionMode, TToolArgs) |
324
- | `ink`, `react` | TUI rendering |
325
- | `ink-select-input` | Arrow-key selection (permission prompt) |
326
- | `ink-spinner` | Loading spinner |
327
- | `chalk` | Terminal colors |
328
- | `ink-text-input` | Base text input (extended by CjkTextInput) |
329
- | `marked`, `marked-terminal` | Markdown parsing and terminal rendering |
330
- | `cli-highlight` | Syntax highlighting for code blocks |
331
- | `string-width` | Unicode-aware string width (CJK support) |
343
+ | Package | Purpose |
344
+ | -------------------------------------- | ------------------------------------------ |
345
+ | `@robota-sdk/agent-sdk` | Session factory, query, config, context |
346
+ | `@robota-sdk/agent-core` | Types (TPermissionMode, TToolArgs) |
347
+ | `@robota-sdk/agent-transport-headless` | Headless runner for print mode (`-p`) |
348
+ | `ink`, `react` | TUI rendering |
349
+ | `ink-select-input` | Arrow-key selection (permission prompt) |
350
+ | `ink-spinner` | Loading spinner |
351
+ | `chalk` | Terminal colors |
352
+ | `ink-text-input` | Base text input (extended by CjkTextInput) |
353
+ | `marked`, `marked-terminal` | Markdown parsing and terminal rendering |
354
+ | `cli-highlight` | Syntax highlighting for code blocks |
355
+ | `string-width` | Unicode-aware string width (CJK support) |
332
356
 
333
357
  ## Documentation
334
358
 
package/dist/node/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-L5G6WFOL.js";
4
+ } from "./chunk-74WEPZG2.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {
@@ -40,6 +40,9 @@ function parseCliArgs() {
40
40
  "max-turns": { type: "string" },
41
41
  "fork-session": { type: "boolean", default: false },
42
42
  name: { type: "string", short: "n" },
43
+ "output-format": { type: "string" },
44
+ "system-prompt": { type: "string" },
45
+ "append-system-prompt": { type: "string" },
43
46
  version: { type: "boolean", default: false },
44
47
  reset: { type: "boolean", default: false }
45
48
  }
@@ -55,6 +58,9 @@ function parseCliArgs() {
55
58
  maxTurns: parseMaxTurns(values["max-turns"]),
56
59
  forkSession: values["fork-session"] ?? false,
57
60
  sessionName: values["name"],
61
+ outputFormat: values["output-format"],
62
+ systemPrompt: values["system-prompt"],
63
+ appendSystemPrompt: values["append-system-prompt"],
58
64
  version: values["version"] ?? false,
59
65
  reset: values["reset"] ?? false
60
66
  };
@@ -134,6 +140,9 @@ function createProviderFromSettings(cwd, modelOverride) {
134
140
  }
135
141
  }
136
142
 
143
+ // src/cli.ts
144
+ import { createHeadlessTransport } from "@robota-sdk/agent-transport-headless";
145
+
137
146
  // src/ui/render.tsx
138
147
  import { render } from "ink";
139
148
 
@@ -911,9 +920,7 @@ function CjkTextInput({
911
920
  const pasteBufferRef = useRef2("");
912
921
  if (value !== valueRef.current) {
913
922
  valueRef.current = value;
914
- if (cursorRef.current > value.length) {
915
- cursorRef.current = value.length;
916
- }
923
+ cursorRef.current = value.length;
917
924
  }
918
925
  useInput(
919
926
  (input, key) => {
@@ -1194,23 +1201,23 @@ function InputArea({
1194
1201
  const label = `[Pasted text #${id} +${lineCount} lines]`;
1195
1202
  setValue((prev) => prev ? `${prev} ${label}` : label);
1196
1203
  }, []);
1197
- const handleSubmit = useCallback2(
1198
- (text) => {
1199
- const trimmed = text.trim();
1200
- if (trimmed.length === 0) return;
1201
- if (showPopup && filteredCommands[selectedIndex]) {
1202
- selectCommand(filteredCommands[selectedIndex]);
1204
+ const tabCompleteCommand = useCallback2(
1205
+ (cmd) => {
1206
+ const parsed = parseSlashInput(value);
1207
+ if (parsed.parentCommand) {
1208
+ setValue(`/${parsed.parentCommand} ${cmd.name} `);
1203
1209
  return;
1204
1210
  }
1205
- const expanded = expandPasteLabels(trimmed, pasteStore.current);
1206
- setValue("");
1207
- pasteStore.current.clear();
1208
- pasteIdRef.current = 0;
1209
- onSubmit(expanded);
1211
+ if (cmd.subcommands && cmd.subcommands.length > 0) {
1212
+ setValue(`/${cmd.name} `);
1213
+ setSelectedIndex(0);
1214
+ return;
1215
+ }
1216
+ setValue(`/${cmd.name} `);
1210
1217
  },
1211
- [showPopup, filteredCommands, selectedIndex, onSubmit]
1218
+ [value, setSelectedIndex]
1212
1219
  );
1213
- const selectCommand = useCallback2(
1220
+ const enterSelectCommand = useCallback2(
1214
1221
  (cmd) => {
1215
1222
  const parsed = parseSlashInput(value);
1216
1223
  if (parsed.parentCommand) {
@@ -1229,6 +1236,22 @@ function InputArea({
1229
1236
  },
1230
1237
  [value, onSubmit, setSelectedIndex]
1231
1238
  );
1239
+ const handleSubmit = useCallback2(
1240
+ (text) => {
1241
+ const trimmed = text.trim();
1242
+ if (trimmed.length === 0) return;
1243
+ if (showPopup && filteredCommands[selectedIndex]) {
1244
+ enterSelectCommand(filteredCommands[selectedIndex]);
1245
+ return;
1246
+ }
1247
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1248
+ setValue("");
1249
+ pasteStore.current.clear();
1250
+ pasteIdRef.current = 0;
1251
+ onSubmit(expanded);
1252
+ },
1253
+ [showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
1254
+ );
1232
1255
  useInput2(
1233
1256
  (_input, key) => {
1234
1257
  if (!showPopup) return;
@@ -1240,7 +1263,7 @@ function InputArea({
1240
1263
  setShowPopup(false);
1241
1264
  } else if (key.tab) {
1242
1265
  const cmd = filteredCommands[selectedIndex];
1243
- if (cmd) selectCommand(cmd);
1266
+ if (cmd) tabCompleteCommand(cmd);
1244
1267
  }
1245
1268
  },
1246
1269
  { isActive: showPopup && !isDisabled }
@@ -2446,8 +2469,15 @@ async function startCli() {
2446
2469
  }
2447
2470
  }
2448
2471
  if (args.printMode) {
2449
- const prompt = args.positional.join(" ").trim();
2450
- if (prompt.length === 0) {
2472
+ let prompt = args.positional.join(" ").trim();
2473
+ if (!prompt && !process.stdin.isTTY) {
2474
+ const chunks = [];
2475
+ for await (const chunk of process.stdin) {
2476
+ chunks.push(chunk);
2477
+ }
2478
+ prompt = Buffer.concat(chunks).toString("utf-8").trim();
2479
+ }
2480
+ if (!prompt) {
2451
2481
  process.stderr.write("Print mode (-p) requires a prompt argument.\n");
2452
2482
  process.exit(1);
2453
2483
  }
@@ -2459,19 +2489,13 @@ async function startCli() {
2459
2489
  sessionStore,
2460
2490
  sessionName: args.sessionName
2461
2491
  });
2462
- await new Promise((resolve, reject) => {
2463
- session.on("complete", (result) => {
2464
- process.stdout.write(result.response + "\n");
2465
- resolve();
2466
- });
2467
- session.on("interrupted", (result) => {
2468
- if (result.response) process.stdout.write(result.response + "\n");
2469
- resolve();
2470
- });
2471
- session.on("error", (err) => reject(err));
2472
- session.submit(prompt).catch(reject);
2492
+ const transport = createHeadlessTransport({
2493
+ outputFormat: args.outputFormat ?? "text",
2494
+ prompt
2473
2495
  });
2474
- return;
2496
+ session.attachTransport(transport);
2497
+ await transport.start();
2498
+ process.exit(transport.getExitCode());
2475
2499
  }
2476
2500
  renderApp({
2477
2501
  cwd,
@@ -76,6 +76,9 @@ function parseCliArgs() {
76
76
  "max-turns": { type: "string" },
77
77
  "fork-session": { type: "boolean", default: false },
78
78
  name: { type: "string", short: "n" },
79
+ "output-format": { type: "string" },
80
+ "system-prompt": { type: "string" },
81
+ "append-system-prompt": { type: "string" },
79
82
  version: { type: "boolean", default: false },
80
83
  reset: { type: "boolean", default: false }
81
84
  }
@@ -91,6 +94,9 @@ function parseCliArgs() {
91
94
  maxTurns: parseMaxTurns(values["max-turns"]),
92
95
  forkSession: values["fork-session"] ?? false,
93
96
  sessionName: values["name"],
97
+ outputFormat: values["output-format"],
98
+ systemPrompt: values["system-prompt"],
99
+ appendSystemPrompt: values["append-system-prompt"],
94
100
  version: values["version"] ?? false,
95
101
  reset: values["reset"] ?? false
96
102
  };
@@ -170,6 +176,9 @@ function createProviderFromSettings(cwd, modelOverride) {
170
176
  }
171
177
  }
172
178
 
179
+ // src/cli.ts
180
+ var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
181
+
173
182
  // src/ui/render.tsx
174
183
  var import_ink15 = require("ink");
175
184
 
@@ -934,9 +943,7 @@ function CjkTextInput({
934
943
  const pasteBufferRef = (0, import_react4.useRef)("");
935
944
  if (value !== valueRef.current) {
936
945
  valueRef.current = value;
937
- if (cursorRef.current > value.length) {
938
- cursorRef.current = value.length;
939
- }
946
+ cursorRef.current = value.length;
940
947
  }
941
948
  (0, import_ink4.useInput)(
942
949
  (input, key) => {
@@ -1217,23 +1224,23 @@ function InputArea({
1217
1224
  const label = `[Pasted text #${id} +${lineCount} lines]`;
1218
1225
  setValue((prev) => prev ? `${prev} ${label}` : label);
1219
1226
  }, []);
1220
- const handleSubmit = (0, import_react6.useCallback)(
1221
- (text) => {
1222
- const trimmed = text.trim();
1223
- if (trimmed.length === 0) return;
1224
- if (showPopup && filteredCommands[selectedIndex]) {
1225
- selectCommand(filteredCommands[selectedIndex]);
1227
+ const tabCompleteCommand = (0, import_react6.useCallback)(
1228
+ (cmd) => {
1229
+ const parsed = parseSlashInput(value);
1230
+ if (parsed.parentCommand) {
1231
+ setValue(`/${parsed.parentCommand} ${cmd.name} `);
1226
1232
  return;
1227
1233
  }
1228
- const expanded = expandPasteLabels(trimmed, pasteStore.current);
1229
- setValue("");
1230
- pasteStore.current.clear();
1231
- pasteIdRef.current = 0;
1232
- onSubmit(expanded);
1234
+ if (cmd.subcommands && cmd.subcommands.length > 0) {
1235
+ setValue(`/${cmd.name} `);
1236
+ setSelectedIndex(0);
1237
+ return;
1238
+ }
1239
+ setValue(`/${cmd.name} `);
1233
1240
  },
1234
- [showPopup, filteredCommands, selectedIndex, onSubmit]
1241
+ [value, setSelectedIndex]
1235
1242
  );
1236
- const selectCommand = (0, import_react6.useCallback)(
1243
+ const enterSelectCommand = (0, import_react6.useCallback)(
1237
1244
  (cmd) => {
1238
1245
  const parsed = parseSlashInput(value);
1239
1246
  if (parsed.parentCommand) {
@@ -1252,6 +1259,22 @@ function InputArea({
1252
1259
  },
1253
1260
  [value, onSubmit, setSelectedIndex]
1254
1261
  );
1262
+ const handleSubmit = (0, import_react6.useCallback)(
1263
+ (text) => {
1264
+ const trimmed = text.trim();
1265
+ if (trimmed.length === 0) return;
1266
+ if (showPopup && filteredCommands[selectedIndex]) {
1267
+ enterSelectCommand(filteredCommands[selectedIndex]);
1268
+ return;
1269
+ }
1270
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1271
+ setValue("");
1272
+ pasteStore.current.clear();
1273
+ pasteIdRef.current = 0;
1274
+ onSubmit(expanded);
1275
+ },
1276
+ [showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
1277
+ );
1255
1278
  (0, import_ink7.useInput)(
1256
1279
  (_input, key) => {
1257
1280
  if (!showPopup) return;
@@ -1263,7 +1286,7 @@ function InputArea({
1263
1286
  setShowPopup(false);
1264
1287
  } else if (key.tab) {
1265
1288
  const cmd = filteredCommands[selectedIndex];
1266
- if (cmd) selectCommand(cmd);
1289
+ if (cmd) tabCompleteCommand(cmd);
1267
1290
  }
1268
1291
  },
1269
1292
  { isActive: showPopup && !isDisabled }
@@ -2470,8 +2493,15 @@ async function startCli() {
2470
2493
  }
2471
2494
  }
2472
2495
  if (args.printMode) {
2473
- const prompt = args.positional.join(" ").trim();
2474
- if (prompt.length === 0) {
2496
+ let prompt = args.positional.join(" ").trim();
2497
+ if (!prompt && !process.stdin.isTTY) {
2498
+ const chunks = [];
2499
+ for await (const chunk of process.stdin) {
2500
+ chunks.push(chunk);
2501
+ }
2502
+ prompt = Buffer.concat(chunks).toString("utf-8").trim();
2503
+ }
2504
+ if (!prompt) {
2475
2505
  process.stderr.write("Print mode (-p) requires a prompt argument.\n");
2476
2506
  process.exit(1);
2477
2507
  }
@@ -2483,19 +2513,13 @@ async function startCli() {
2483
2513
  sessionStore,
2484
2514
  sessionName: args.sessionName
2485
2515
  });
2486
- await new Promise((resolve, reject) => {
2487
- session.on("complete", (result) => {
2488
- process.stdout.write(result.response + "\n");
2489
- resolve();
2490
- });
2491
- session.on("interrupted", (result) => {
2492
- if (result.response) process.stdout.write(result.response + "\n");
2493
- resolve();
2494
- });
2495
- session.on("error", (err) => reject(err));
2496
- session.submit(prompt).catch(reject);
2516
+ const transport = (0, import_agent_transport_headless.createHeadlessTransport)({
2517
+ outputFormat: args.outputFormat ?? "text",
2518
+ prompt
2497
2519
  });
2498
- return;
2520
+ session.attachTransport(transport);
2521
+ await transport.start();
2522
+ process.exit(transport.getExitCode());
2499
2523
  }
2500
2524
  renderApp({
2501
2525
  cwd,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-L5G6WFOL.js";
3
+ } from "./chunk-74WEPZG2.js";
4
4
  export {
5
5
  startCli
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-cli",
3
- "version": "3.0.0-beta.46",
3
+ "version": "3.0.0-beta.47",
4
4
  "description": "AI coding assistant CLI built on Robota SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,7 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@robota-sdk/agent-sessions": "3.0.0-beta.46",
28
+ "@robota-sdk/agent-sessions": "3.0.0-beta.47",
29
29
  "chalk": "^5.3.0",
30
30
  "cli-highlight": "^2.1.0",
31
31
  "ink": "^6.8.0",
@@ -36,9 +36,10 @@
36
36
  "marked-terminal": "^7.3.0",
37
37
  "react": "19.2.4",
38
38
  "string-width": "^8.2.0",
39
- "@robota-sdk/agent-core": "3.0.0-beta.46",
40
- "@robota-sdk/agent-sdk": "3.0.0-beta.46",
41
- "@robota-sdk/agent-provider-anthropic": "3.0.0-beta.46"
39
+ "@robota-sdk/agent-core": "3.0.0-beta.47",
40
+ "@robota-sdk/agent-sdk": "3.0.0-beta.47",
41
+ "@robota-sdk/agent-provider-anthropic": "3.0.0-beta.47",
42
+ "@robota-sdk/agent-transport-headless": "3.0.0-beta.47"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@types/marked": "^6.0.0",