@robinpath/cli 1.77.0 → 1.78.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +191 -281
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -18598,7 +18598,7 @@ function getNativeModules() {
18598
18598
  import { join as join3, basename as basename2 } from "node:path";
18599
18599
  import { homedir as homedir2, platform as platform2 } from "node:os";
18600
18600
  import { existsSync as existsSync2 } from "node:fs";
18601
- var CLI_VERSION = true ? "1.77.0" : "1.77.0";
18601
+ var CLI_VERSION = true ? "1.78.0" : "1.78.0";
18602
18602
  var FLAG_QUIET = false;
18603
18603
  var FLAG_VERBOSE = false;
18604
18604
  var FLAG_AUTO_ACCEPT = false;
@@ -24132,89 +24132,72 @@ ${resultSummary}`
24132
24132
  }
24133
24133
 
24134
24134
  // src/ink-repl.tsx
24135
- import { useState, useCallback, useEffect } from "react";
24135
+ import { useState, useEffect } from "react";
24136
24136
  import { render, Box, Text, Static, useInput, useApp } from "ink";
24137
24137
  import InkSpinner from "ink-spinner";
24138
24138
  import { platform as platform7 } from "node:os";
24139
24139
  import { randomUUID as randomUUID4 } from "node:crypto";
24140
24140
  import { jsx, jsxs } from "react/jsx-runtime";
24141
24141
  var nextId = 0;
24142
- function InputBox({ onSubmit, active, placeholder }) {
24143
- const [value, setValue] = useState("");
24144
- useInput((input, key) => {
24145
- if (!active) return;
24142
+ function ChatApp({ onMessage }) {
24143
+ const [input, setInput] = useState("");
24144
+ const [messages, setMessages] = useState([]);
24145
+ const [streaming, setStreaming] = useState("");
24146
+ const [loading, setLoading] = useState(false);
24147
+ const { exit } = useApp();
24148
+ useEffect(() => {
24149
+ global.__rpUI = {
24150
+ setStreaming,
24151
+ setLoading,
24152
+ addMessage: (text) => setMessages((prev) => [...prev, { id: ++nextId, text }])
24153
+ };
24154
+ }, []);
24155
+ useInput((character, key) => {
24156
+ if (loading) return;
24146
24157
  if (key.return) {
24147
- if (value.endsWith("\\")) {
24148
- setValue((p) => p.slice(0, -1) + "\n");
24158
+ if (!input.trim()) return;
24159
+ if (input.endsWith("\\")) {
24160
+ setInput((prev) => prev.slice(0, -1) + "\n");
24149
24161
  return;
24150
24162
  }
24151
- const text = value.trim();
24152
- if (text) {
24153
- onSubmit(text);
24154
- setValue("");
24163
+ const text = input.trim();
24164
+ setInput("");
24165
+ if (text === "exit" || text === "quit") {
24166
+ exit();
24167
+ return;
24155
24168
  }
24169
+ setMessages((prev) => [...prev, { id: ++nextId, text: `\u276F ${text}` }]);
24170
+ setLoading(true);
24171
+ setStreaming("");
24172
+ onMessage(text).then((response) => {
24173
+ if (response) {
24174
+ setMessages((prev) => [...prev, { id: ++nextId, text: response }]);
24175
+ }
24176
+ setLoading(false);
24177
+ setStreaming("");
24178
+ }).catch((err) => {
24179
+ setMessages((prev) => [...prev, { id: ++nextId, text: `Error: ${err.message}` }]);
24180
+ setLoading(false);
24181
+ setStreaming("");
24182
+ });
24156
24183
  return;
24157
24184
  }
24158
- if (input === "\n") {
24159
- setValue((p) => p + "\n");
24185
+ if (input.length > 0 && (key.backspace || key.delete)) {
24186
+ setInput((prev) => prev.slice(0, -1));
24160
24187
  return;
24161
24188
  }
24162
24189
  if (key.escape) {
24163
- setValue("");
24164
- return;
24165
- }
24166
- if (key.backspace || key.delete) {
24167
- setValue((p) => p.slice(0, -1));
24190
+ setInput("");
24168
24191
  return;
24169
24192
  }
24170
24193
  if (key.tab) return;
24171
- if (input === "") {
24172
- setValue("");
24173
- return;
24174
- }
24175
- if (input === "") {
24176
- setValue((p) => p.replace(/\S+\s*$/, ""));
24177
- return;
24194
+ if (character && !key.ctrl && !key.meta) {
24195
+ setInput((prev) => prev + character);
24178
24196
  }
24179
- if (input && !key.ctrl && !key.meta) setValue((p) => p + input);
24180
- }, { isActive: active });
24181
- const lines = value.split("\n");
24182
- const empty = value === "";
24183
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
24184
- /* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: active ? "cyan" : "gray", flexDirection: "column", paddingX: 1, marginX: 1, children: empty ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: placeholder }) : lines.map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
24185
- line,
24186
- i === lines.length - 1 && active ? /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u2588" }) : null
24187
- ] }, i)) }),
24188
- /* @__PURE__ */ jsx(Box, { marginX: 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "enter send \xB7 \\ newline \xB7 esc clear \xB7 / commands" }) })
24189
- ] });
24190
- }
24191
- function App({ engine }) {
24192
- const [messages, setMessages] = useState([]);
24193
- const [streaming, setStreaming] = useState("");
24194
- const [loading, setLoading] = useState(false);
24195
- const [loadingLabel, setLoadingLabel] = useState("Thinking");
24196
- const [status, setStatus] = useState("");
24197
- const { exit } = useApp();
24198
- useEffect(() => {
24199
- engine.ui = {
24200
- addMessage: (role, text) => setMessages((p) => [...p, { id: ++nextId, role, text }]),
24201
- setStreaming,
24202
- setLoading,
24203
- setLoadingLabel,
24204
- setStatus,
24205
- exit
24206
- };
24207
- }, []);
24208
- const handleSubmit = useCallback(async (text) => {
24209
- if (text === "exit" || text === "quit") {
24210
- engine.exit();
24211
- return;
24212
- }
24213
- await engine.handleInput(text);
24214
- }, [engine]);
24215
- const isFirst = messages.length === 0;
24216
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
24217
- /* @__PURE__ */ jsx(Box, { marginTop: 1, marginBottom: 1, marginX: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
24197
+ });
24198
+ const lines = input.split("\n");
24199
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
24200
+ /* @__PURE__ */ jsxs(Text, { children: [
24218
24201
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u25C6" }),
24219
24202
  " ",
24220
24203
  /* @__PURE__ */ jsx(Text, { bold: true, children: "RobinPath" }),
@@ -24223,261 +24206,188 @@ function App({ engine }) {
24223
24206
  "v",
24224
24207
  CLI_VERSION
24225
24208
  ] })
24226
- ] }) }),
24227
- /* @__PURE__ */ jsx(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx(Box, { marginX: 2, marginBottom: msg.role === "assistant" ? 1 : 0, children: msg.role === "user" ? /* @__PURE__ */ jsxs(Text, { children: [
24228
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u276F " }),
24229
- /* @__PURE__ */ jsx(Text, { bold: true, children: msg.text })
24230
- ] }) : msg.role === "info" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: msg.text }) : /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: msg.text }) }, msg.id) }),
24231
- loading && streaming ? /* @__PURE__ */ jsx(Box, { marginX: 2, marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { wrap: "wrap", children: [
24232
- streaming,
24233
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u258D" })
24234
- ] }) }) : null,
24235
- loading && !streaming ? /* @__PURE__ */ jsx(Box, { marginX: 2, marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
24209
+ ] }),
24210
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
24211
+ /* @__PURE__ */ jsx(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: msg.text }, msg.id) }),
24212
+ loading && streaming ? /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: streaming }) : null,
24213
+ loading && !streaming ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
24236
24214
  /* @__PURE__ */ jsx(InkSpinner, { type: "dots" }),
24237
- " ",
24238
- loadingLabel
24239
- ] }) }) : null,
24240
- /* @__PURE__ */ jsx(
24241
- InputBox,
24242
- {
24243
- onSubmit: handleSubmit,
24244
- active: !loading,
24245
- placeholder: isFirst ? "What do you want to automate?" : "Ask anything..."
24246
- }
24247
- ),
24248
- status ? /* @__PURE__ */ jsx(Box, { marginX: 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: status }) }) : null
24215
+ " Thinking"
24216
+ ] }) : null,
24217
+ !loading ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
24218
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u276F " }),
24219
+ input === "" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: messages.length === 0 ? "What do you want to automate?" : "Ask anything..." }) : /* @__PURE__ */ jsxs(Text, { children: [
24220
+ input,
24221
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u258E" })
24222
+ ] })
24223
+ ] }) }) : null
24249
24224
  ] });
24250
24225
  }
24251
- var ReplEngine = class {
24252
- config;
24253
- autoAccept;
24254
- devMode;
24255
- apiKey;
24256
- model;
24257
- provider;
24258
- sessionId;
24259
- sessionName;
24260
- usage;
24261
- conversationMessages;
24262
- cliContext;
24263
- ui = null;
24264
- constructor(resumeSessionId, opts) {
24265
- this.config = readAiConfig();
24266
- this.autoAccept = opts.autoAccept || false;
24267
- this.devMode = opts.devMode || false;
24268
- if (this.devMode) setFlags({ verbose: true });
24269
- this.apiKey = this.config.apiKey || null;
24270
- this.provider = this.resolveProvider(this.apiKey);
24271
- this.model = this.apiKey ? this.config.model || "anthropic/claude-sonnet-4.6" : "robinpath-default";
24272
- this.sessionId = resumeSessionId || randomUUID4().slice(0, 8);
24273
- this.sessionName = `session-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
24274
- this.usage = createUsageTracker();
24275
- this.conversationMessages = [];
24276
- this.cliContext = {
24277
- platform: platform7(),
24278
- shell: getShellConfig().name,
24279
- cwd: process.cwd(),
24280
- cliVersion: CLI_VERSION,
24281
- nativeModules: getNativeModules().map((m) => m.name),
24282
- installedModules: Object.keys(readModulesManifest())
24283
- };
24284
- const mem = buildMemoryContext();
24285
- if (mem.trim()) {
24286
- this.conversationMessages.push({ role: "user", content: `[Context] ${mem.trim()}` });
24287
- this.conversationMessages.push({ role: "assistant", content: "Preferences loaded." });
24288
- }
24289
- if (resumeSessionId) {
24290
- const session = loadSession(resumeSessionId);
24291
- if (session) {
24292
- this.sessionName = session.name;
24293
- for (const msg of session.messages) this.conversationMessages.push(msg);
24294
- if (session.usage) {
24295
- this.usage.promptTokens = session.usage.promptTokens || 0;
24296
- this.usage.completionTokens = session.usage.completionTokens || 0;
24297
- this.usage.totalTokens = session.usage.totalTokens || 0;
24298
- this.usage.requests = session.usage.requests || 0;
24299
- }
24300
- }
24301
- }
24302
- }
24303
- resolveProvider(key) {
24226
+ async function startInkREPL(initialPrompt, resumeSessionId, opts = {}) {
24227
+ const config = readAiConfig();
24228
+ let autoAccept = opts.autoAccept || false;
24229
+ const devMode = opts.devMode || false;
24230
+ if (devMode) setFlags({ verbose: true });
24231
+ const resolveProvider = (key) => {
24304
24232
  if (!key) return "gemini";
24305
24233
  if (key.startsWith("sk-or-")) return "openrouter";
24306
24234
  if (key.startsWith("sk-ant-")) return "anthropic";
24307
24235
  if (key.startsWith("sk-")) return "openai";
24308
- return this.config.provider || "gemini";
24309
- }
24310
- updateStatus() {
24311
- const model = this.model.includes("/") ? this.model.split("/").pop() : this.model;
24312
- const cost = this.usage.cost > 0 ? ` \xB7 $${this.usage.cost.toFixed(4)}` : "";
24313
- const tokens = this.usage.totalTokens > 0 ? ` \xB7 ${this.usage.totalTokens.toLocaleString()} tokens` : "";
24314
- this.ui?.setStatus(`${model} \xB7 ${getShellConfig().name} \xB7 ${this.autoAccept ? "auto" : "confirm"}${tokens}${cost}`);
24236
+ return config.provider || "gemini";
24237
+ };
24238
+ const apiKey = config.apiKey || null;
24239
+ const model = apiKey ? config.model || "anthropic/claude-sonnet-4.6" : "robinpath-default";
24240
+ const cliContext = {
24241
+ platform: platform7(),
24242
+ shell: getShellConfig().name,
24243
+ cwd: process.cwd(),
24244
+ cliVersion: CLI_VERSION,
24245
+ nativeModules: getNativeModules().map((m) => m.name),
24246
+ installedModules: Object.keys(readModulesManifest())
24247
+ };
24248
+ let sessionId = resumeSessionId || randomUUID4().slice(0, 8);
24249
+ let sessionName = `session-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
24250
+ const usage = createUsageTracker();
24251
+ const conversationMessages = [];
24252
+ const mem = buildMemoryContext();
24253
+ if (mem.trim()) {
24254
+ conversationMessages.push({ role: "user", content: `[Context] ${mem.trim()}` });
24255
+ conversationMessages.push({ role: "assistant", content: "Preferences loaded." });
24315
24256
  }
24316
- exit() {
24317
- if (this.conversationMessages.length > 1) {
24318
- saveSession(this.sessionId, this.sessionName, this.conversationMessages, this.usage);
24257
+ if (resumeSessionId) {
24258
+ const session = loadSession(resumeSessionId);
24259
+ if (session) {
24260
+ sessionName = session.name;
24261
+ for (const msg of session.messages) conversationMessages.push(msg);
24262
+ if (session.usage) Object.assign(usage, session.usage);
24319
24263
  }
24320
- this.ui?.exit();
24321
24264
  }
24322
- async handleInput(text) {
24323
- if (text === "/" || text === "/help") {
24324
- const cmds = ["/model", "/shell", "/auto", "/clear", "/save", "/sessions", "/memory", "/usage", "exit"];
24325
- this.ui?.addMessage("info", cmds.join(" "));
24326
- return;
24327
- }
24265
+ async function handleMessage(text) {
24266
+ const ui = global.__rpUI;
24267
+ if (text === "/" || text === "/help") return "/model /auto /clear /save /usage /memory exit";
24328
24268
  if (text === "/clear") {
24329
- this.conversationMessages.length = 0;
24330
- this.ui?.addMessage("info", "Cleared.");
24331
- return;
24269
+ conversationMessages.length = 0;
24270
+ return "Cleared.";
24332
24271
  }
24333
24272
  if (text === "/usage") {
24334
- const c = this.usage.cost > 0 ? `$${this.usage.cost.toFixed(4)}` : "$0 (free)";
24335
- this.ui?.addMessage("info", `${this.usage.totalTokens.toLocaleString()} tokens \xB7 ${this.usage.requests} requests \xB7 ${c}`);
24336
- return;
24273
+ const c = usage.cost > 0 ? `$${usage.cost.toFixed(4)}` : "$0 (free)";
24274
+ return `${usage.totalTokens.toLocaleString()} tokens \xB7 ${usage.requests} requests \xB7 ${c}`;
24337
24275
  }
24338
24276
  if (text === "/auto") {
24339
- this.autoAccept = !this.autoAccept;
24340
- this.ui?.addMessage("info", `Auto-accept: ${this.autoAccept ? "ON" : "OFF"}`);
24341
- this.updateStatus();
24342
- return;
24277
+ autoAccept = !autoAccept;
24278
+ return `Auto-accept: ${autoAccept ? "ON" : "OFF"}`;
24343
24279
  }
24344
24280
  if (text === "/model") {
24345
24281
  const hasKey = !!readAiConfig().apiKey;
24346
24282
  const models = hasKey ? AI_MODELS : AI_MODELS.filter((m) => !m.requiresKey);
24347
- this.ui?.addMessage("info", models.map((m, i) => `${i + 1}. ${m.name} \u2014 ${m.desc}`).join("\n"));
24348
- return;
24283
+ return models.map((m, i) => `${i + 1}. ${m.name} \u2014 ${m.desc}`).join("\n");
24349
24284
  }
24350
24285
  if (text.match(/^\/model \d+$/)) {
24351
24286
  const hasKey = !!readAiConfig().apiKey;
24352
24287
  const models = hasKey ? AI_MODELS : AI_MODELS.filter((m) => !m.requiresKey);
24353
24288
  const idx = parseInt(text.split(" ")[1], 10) - 1;
24354
24289
  if (idx >= 0 && idx < models.length) {
24355
- this.config.model = models[idx].id;
24356
- this.model = models[idx].id;
24357
- writeAiConfig(this.config);
24358
- this.ui?.addMessage("info", `Model: ${models[idx].id}`);
24359
- this.updateStatus();
24290
+ config.model = models[idx].id;
24291
+ writeAiConfig(config);
24292
+ return `Model: ${models[idx].id}`;
24360
24293
  }
24361
- return;
24294
+ return "Invalid number.";
24362
24295
  }
24363
24296
  if (text === "/memory") {
24364
- const mem = loadMemory();
24365
- this.ui?.addMessage("info", mem.facts.length ? mem.facts.map((f, i) => `${i + 1}. ${f}`).join("\n") : "No memories.");
24366
- return;
24297
+ const m = loadMemory();
24298
+ return m.facts.length ? m.facts.map((f, i) => `${i + 1}. ${f}`).join("\n") : "No memories.";
24367
24299
  }
24368
24300
  if (text.startsWith("/save")) {
24369
- if (text.length > 5) this.sessionName = text.slice(5).trim();
24370
- saveSession(this.sessionId, this.sessionName, this.conversationMessages, this.usage);
24371
- this.ui?.addMessage("info", `Saved: ${this.sessionName}`);
24372
- return;
24373
- }
24374
- if (text.startsWith("/")) {
24375
- this.ui?.addMessage("info", `Unknown: ${text}. Type / for help.`);
24376
- return;
24301
+ if (text.length > 5) sessionName = text.slice(5).trim();
24302
+ saveSession(sessionId, sessionName, conversationMessages, usage);
24303
+ return `Saved: ${sessionName}`;
24377
24304
  }
24378
- this.ui?.addMessage("user", text);
24379
- this.ui?.setLoading(true);
24380
- this.ui?.setStreaming("");
24381
- this.ui?.setLoadingLabel("Thinking");
24382
- try {
24383
- const { expanded } = expandFileRefs(text);
24384
- this.conversationMessages.push({ role: "user", content: expanded });
24385
- await autoCompact(this.conversationMessages);
24386
- const activeModel = readAiConfig().model || this.model;
24387
- const activeKey = readAiConfig().apiKey || this.apiKey;
24388
- const activeProvider = this.resolveProvider(activeKey);
24389
- let fullResponse = "";
24390
- for (let loop = 0; loop < 15; loop++) {
24391
- this.ui?.setLoadingLabel(loop === 0 ? "Thinking" : "Processing");
24392
- fullResponse = "";
24393
- const result = await fetchBrainStream(
24394
- loop === 0 ? expanded : this.conversationMessages[this.conversationMessages.length - 1].content,
24395
- {
24396
- onToken: (delta) => {
24397
- if (delta === "\x1B[RETRY]") {
24398
- fullResponse = "";
24399
- this.ui?.setStreaming("");
24400
- return;
24401
- }
24402
- fullResponse += delta;
24403
- const clean = fullResponse.replace(/<memory>[\s\S]*?<\/memory>/g, "").replace(/<cmd>[\s\S]*?<\/cmd>/g, "").replace(/\n{3,}/g, "\n\n").trim();
24404
- this.ui?.setStreaming(clean);
24405
- },
24406
- conversationHistory: this.conversationMessages.slice(0, -1),
24407
- provider: activeProvider,
24408
- model: activeModel,
24409
- apiKey: activeKey,
24410
- cliContext: this.cliContext
24411
- }
24412
- );
24413
- if (!result || !result.code) {
24414
- this.ui?.addMessage("assistant", fullResponse || "No response. Check connection or API key.");
24415
- break;
24416
- }
24417
- if (result.usage) {
24418
- const pt2 = result.usage.prompt_tokens || 0;
24419
- const ct2 = result.usage.completion_tokens || 0;
24420
- this.usage.promptTokens += pt2;
24421
- this.usage.completionTokens += ct2;
24422
- this.usage.totalTokens += pt2 + ct2;
24423
- this.usage.requests++;
24424
- this.usage.cost += estimateCost(activeModel, pt2, ct2);
24425
- this.updateStatus();
24426
- }
24427
- const { cleaned } = extractMemoryTags(stripCommandTags(result.code));
24428
- const commands = extractCommands(result.code);
24429
- if (cleaned) {
24430
- this.conversationMessages.push({ role: "assistant", content: cleaned });
24305
+ if (text.startsWith("/")) return `Unknown: ${text}. Type / for help.`;
24306
+ const { expanded } = expandFileRefs(text);
24307
+ conversationMessages.push({ role: "user", content: expanded });
24308
+ await autoCompact(conversationMessages);
24309
+ const activeModel = readAiConfig().model || model;
24310
+ const activeKey = readAiConfig().apiKey || apiKey;
24311
+ const activeProvider = resolveProvider(activeKey);
24312
+ let finalResponse = "";
24313
+ for (let loop = 0; loop < 15; loop++) {
24314
+ let fullText = "";
24315
+ const result = await fetchBrainStream(
24316
+ loop === 0 ? expanded : conversationMessages[conversationMessages.length - 1].content,
24317
+ {
24318
+ onToken: (delta) => {
24319
+ if (delta === "\x1B[RETRY]") {
24320
+ fullText = "";
24321
+ ui?.setStreaming("");
24322
+ return;
24323
+ }
24324
+ fullText += delta;
24325
+ const clean = fullText.replace(/<memory>[\s\S]*?<\/memory>/g, "").replace(/<cmd>[\s\S]*?<\/cmd>/g, "").replace(/\n{3,}/g, "\n\n").trim();
24326
+ ui?.setStreaming(clean);
24327
+ },
24328
+ conversationHistory: conversationMessages.slice(0, -1),
24329
+ provider: activeProvider,
24330
+ model: activeModel,
24331
+ apiKey: activeKey,
24332
+ cliContext
24431
24333
  }
24432
- if (commands.length === 0) {
24433
- if (cleaned) this.ui?.addMessage("assistant", cleaned);
24434
- break;
24334
+ );
24335
+ if (!result || !result.code) {
24336
+ finalResponse = fullText || "No response.";
24337
+ break;
24338
+ }
24339
+ if (result.usage) {
24340
+ const pt2 = result.usage.prompt_tokens || 0;
24341
+ const ct2 = result.usage.completion_tokens || 0;
24342
+ usage.promptTokens += pt2;
24343
+ usage.completionTokens += ct2;
24344
+ usage.totalTokens += pt2 + ct2;
24345
+ usage.requests++;
24346
+ usage.cost += estimateCost(activeModel, pt2, ct2);
24347
+ }
24348
+ const { cleaned } = extractMemoryTags(stripCommandTags(result.code));
24349
+ const commands = extractCommands(result.code);
24350
+ if (cleaned) conversationMessages.push({ role: "assistant", content: cleaned });
24351
+ if (commands.length === 0) {
24352
+ finalResponse = cleaned || fullText;
24353
+ break;
24354
+ }
24355
+ if (cleaned) ui?.addMessage(cleaned);
24356
+ for (const cmd of commands) {
24357
+ const preview = cmd.split("\n")[0].slice(0, 80);
24358
+ ui?.addMessage(`$ ${preview}${cmd.includes("\n") ? " ..." : ""}`);
24359
+ const r = await executeShellCommand(cmd);
24360
+ if (r.exitCode === 0 && r.stdout?.trim()) {
24361
+ ui?.addMessage(r.stdout.trim().split("\n").slice(0, 5).join("\n"));
24362
+ } else if (r.exitCode !== 0) {
24363
+ ui?.addMessage(`exit ${r.exitCode}: ${(r.stderr || "").slice(0, 100)}`);
24435
24364
  }
24436
- if (cleaned) this.ui?.addMessage("assistant", cleaned);
24437
- const cmdResults = [];
24438
- for (const cmd of commands) {
24439
- this.ui?.addMessage("info", `$ ${cmd.split("\n")[0]}${cmd.includes("\n") ? ` (+${cmd.split("\n").length - 1} lines)` : ""}`);
24440
- const r = await executeShellCommand(cmd);
24441
- if (r.exitCode === 0 && r.stdout?.trim()) {
24442
- this.ui?.addMessage("info", r.stdout.trim().split("\n").slice(0, 5).join("\n"));
24443
- } else if (r.exitCode !== 0) {
24444
- this.ui?.addMessage("info", `exit ${r.exitCode}: ${(r.stderr || "").slice(0, 100)}`);
24445
- }
24446
- cmdResults.push({ command: cmd, stdout: r.stdout || "", stderr: r.stderr || "", exitCode: r.exitCode });
24447
- }
24448
- const summary = cmdResults.map((r) => {
24449
- let o = `$ ${r.command}
24450
- `;
24451
- if (r.exitCode === 0) o += r.stdout || "(no output)";
24452
- else {
24453
- o += `Exit: ${r.exitCode}
24454
- `;
24455
- if (r.stderr) o += r.stderr;
24456
- }
24457
- return o;
24458
- }).join("\n\n");
24459
- this.conversationMessages.push({ role: "user", content: `[Command results]
24460
- ${summary}` });
24461
- this.ui?.setStreaming("");
24462
24365
  }
24463
- } catch (err) {
24464
- this.ui?.addMessage("info", `Error: ${err.message}`);
24465
- } finally {
24466
- this.ui?.setLoading(false);
24467
- this.ui?.setStreaming("");
24468
- saveSession(this.sessionId, this.sessionName, this.conversationMessages, this.usage);
24366
+ const summary = commands.map((cmd, i) => `$ ${cmd}
24367
+ (executed)`).join("\n");
24368
+ conversationMessages.push({ role: "user", content: `[Results]
24369
+ ${summary}` });
24370
+ ui?.setStreaming("");
24371
+ finalResponse = "";
24469
24372
  }
24373
+ saveSession(sessionId, sessionName, conversationMessages, usage);
24374
+ return finalResponse;
24470
24375
  }
24471
- };
24472
- async function startInkREPL(initialPrompt, resumeSessionId, opts = {}) {
24473
- const engine = new ReplEngine(resumeSessionId, opts);
24474
- const { waitUntilExit } = render(/* @__PURE__ */ jsx(App, { engine }));
24475
- await new Promise((r) => setTimeout(r, 100));
24476
- engine.updateStatus();
24376
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx(ChatApp, { onMessage: handleMessage }));
24477
24377
  if (initialPrompt) {
24478
- await engine.handleInput(initialPrompt);
24378
+ await new Promise((r) => setTimeout(r, 200));
24379
+ const ui = global.__rpUI;
24380
+ ui?.addMessage(`\u276F ${initialPrompt}`);
24381
+ ui?.setLoading(true);
24382
+ try {
24383
+ const response = await handleMessage(initialPrompt);
24384
+ if (response) ui?.addMessage(response);
24385
+ } finally {
24386
+ ui?.setLoading(false);
24387
+ }
24479
24388
  }
24480
24389
  await waitUntilExit();
24390
+ saveSession(sessionId, sessionName, conversationMessages, usage);
24481
24391
  }
24482
24392
 
24483
24393
  // src/commands-modules.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinpath/cli",
3
- "version": "1.77.0",
3
+ "version": "1.78.0",
4
4
  "description": "AI-powered scripting CLI — automate anything from your terminal",
5
5
  "type": "module",
6
6
  "license": "MIT",