@menteeai/menteeswe 0.1.9 → 0.1.11

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.js +358 -109
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -52,7 +52,9 @@ var init_config = __esm({
52
52
  kimi: "MENTEE_KIMI_API_KEY",
53
53
  glm: "MENTEE_GLM_API_KEY",
54
54
  zai: "MENTEE_ZAI_API_KEY",
55
- "zai-coding": "MENTEE_ZAI_API_KEY"
55
+ "zai-coding": "MENTEE_ZAI_API_KEY",
56
+ openrouter: "MENTEE_OPENROUTER_API_KEY",
57
+ nvidia: "MENTEE_NVIDIA_API_KEY"
56
58
  };
57
59
  }
58
60
  });
@@ -319,7 +321,7 @@ function formatEvent(event) {
319
321
  const statusLine = cancelled ? chalk.yellow.bold("\u2716 Task cancelled") : success ? chalk.green.bold("\u2714 Task completed") : chalk.red.bold("\u2716 Task failed");
320
322
  const filesLine = modifiedFiles.length > 0 ? chalk.cyan.dim(` \u{1F4DD} ${modifiedFiles.length} file(s): ${modifiedFiles.join(", ")}`) : "";
321
323
  const tipLine = success ? chalk.dim(" Next: type a new task, or /help for commands") : chalk.yellow.dim(" The task did not finish \u2014 check the steps above and retry");
322
- const detailsLine = toolCalls > 0 ? chalk.dim(` \u{1F50D} press d to toggle ${toolCalls} tool call(s)`) : "";
324
+ const detailsLine = toolCalls > 0 ? chalk.dim(` \u{1F50D} Alt+D to toggle ${toolCalls} tool call(s)`) : "";
323
325
  const block = [sep, statusLine, statsLine, filesLine, tipLine, detailsLine, sep, ""];
324
326
  return block.filter((line) => line !== "").join("\n");
325
327
  }
@@ -528,12 +530,13 @@ ${tree}
528
530
 
529
531
  # Communication (IMPORTANT)
530
532
  - While working, say only what you are doing and why, in ONE short sentence per step. No filler.
531
- - Your FINAL answer must be minimal and useful: 1-3 sentences unless the task explicitly asks for detail. State the outcome and, if you changed anything, the one command used to verify. Do NOT write document-style reports, headers, bullet inventories of the codebase, or repeat tool output. If the user wants more, they will ask.
532
- - NEVER use bullet lists, numbered lists, headings, or markdown headers in your final answer unless the user explicitly asks for one. Plain prose only \u2014 1 to 3 sentences.
533
+ - Your FINAL answer has a HARD limit of 3 sentences. Join related points with commas into a single flowing sentence; never use bullet lists, numbered lists, or markdown headings unless the user explicitly asks. Plain prose only.
534
+ - State the outcome and, if you changed anything, the one command used to verify. Do NOT write document-style reports, inventories of the codebase, or repeat tool output. If the user wants more, they will ask.
535
+ - Never open with preamble such as "Here are", "I can", "Sure", or "Based on". Get straight to the point.
533
536
  - Never dump the whole repo structure or a file-by-file summary unless requested.
534
537
  - If the user refers to something you said or did earlier ("that", "it", "before"), use the prior conversation context provided above.
535
538
 
536
- Be concise in your visible text between tool calls: one short sentence about what you are doing and why is enough.`;
539
+ Be concise in your visible text between tool calls: one short sentence about what you are doing and why is enough. Low token usage is a core goal \u2014 be terse everywhere.`;
537
540
  }
538
541
  var SKIP_DIRS;
539
542
  var init_prompts = __esm({
@@ -593,16 +596,24 @@ function appendTurn(cwd, task, response) {
593
596
  if (turns.length > 50) turns = turns.slice(-50);
594
597
  fs9.writeFileSync(file, JSON.stringify(turns, null, 2));
595
598
  }
596
- function formatConversationContext(cwd, limit = 12) {
599
+ function formatConversationContext(cwd, limit = 10) {
597
600
  const turns = loadConversation(cwd, limit);
598
601
  if (turns.length === 0) return "";
599
- const lines = ["# Prior conversation in this project (oldest first)"];
600
- for (const t of turns) {
601
- lines.push(`User: ${t.task}`);
602
- lines.push(`Assistant: ${t.response.trim()}`);
603
- lines.push("");
602
+ const MAX_PRIOR_CHARS = 5e3;
603
+ const header = "# Prior conversation in this project (most recent shown)";
604
+ const blocks = [];
605
+ let total = header.length;
606
+ for (let i = turns.length - 1; i >= 0; i--) {
607
+ const t = turns[i];
608
+ if (!t) continue;
609
+ const block = `User: ${t.task}
610
+ Assistant: ${t.response.trim()}
611
+ `;
612
+ if (blocks.length > 0 && total + block.length > MAX_PRIOR_CHARS) break;
613
+ blocks.unshift(block);
614
+ total += block.length;
604
615
  }
605
- return lines.join("\n");
616
+ return [header, ...blocks, ""].join("\n");
606
617
  }
607
618
  var init_conversation = __esm({
608
619
  "src/agent/conversation.ts"() {
@@ -700,10 +711,17 @@ function estimateContextChars(messages, system) {
700
711
  return total;
701
712
  }
702
713
  function trimOldToolResults(messages) {
703
- for (const message of messages) {
704
- if (message.role === "tool" && (message.content?.length ?? 0) > 2e3) {
705
- message.content = message.content.slice(0, 500) + "\n...[older tool output trimmed to save context]";
706
- }
714
+ let seen = 0;
715
+ for (let i = messages.length - 1; i >= 0; i--) {
716
+ const message = messages[i];
717
+ if (!message) continue;
718
+ if (message.role !== "tool") continue;
719
+ seen++;
720
+ if (seen <= KEEP_RECENT_TOOL) continue;
721
+ const content = message.content ?? "";
722
+ if (content.length <= 240) continue;
723
+ const head = content.replace(/\s+/g, " ").slice(0, 160);
724
+ message.content = `\u27E8prior tool output trimmed: ${head}\u2026 (${content.length} chars)\u27E9`;
707
725
  }
708
726
  }
709
727
  async function runAgent(options) {
@@ -951,7 +969,7 @@ ${systemExtra}` : "");
951
969
  });
952
970
  return { success, finalText, state };
953
971
  }
954
- var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS;
972
+ var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS, KEEP_RECENT_TOOL;
955
973
  var init_loop = __esm({
956
974
  "src/agent/loop.ts"() {
957
975
  "use strict";
@@ -959,15 +977,200 @@ var init_loop = __esm({
959
977
  init_conversation();
960
978
  init_state();
961
979
  init_base();
962
- MAX_CONTEXT_CHARS = 48e4;
980
+ MAX_CONTEXT_CHARS = 7e4;
963
981
  READ_BUDGET = 6;
964
982
  RATE_LIMIT_MAX_ATTEMPTS = 8;
983
+ KEEP_RECENT_TOOL = 4;
965
984
  }
966
985
  });
967
986
 
968
- // src/tui/Approval.tsx
969
- import { Box, Text, useInput } from "ink";
987
+ // package.json
988
+ var version;
989
+ var init_package = __esm({
990
+ "package.json"() {
991
+ version = "0.1.11";
992
+ }
993
+ });
994
+
995
+ // src/tui/markdown.tsx
996
+ import { Box, Text } from "ink";
970
997
  import { jsx, jsxs } from "react/jsx-runtime";
998
+ function renderInline(text, keyPrefix) {
999
+ const nodes = [];
1000
+ const regex = /(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(`([^`]+)`)/g;
1001
+ let last = 0;
1002
+ let m;
1003
+ let i = 0;
1004
+ while ((m = regex.exec(text)) !== null) {
1005
+ if (m.index > last) nodes.push(text.slice(last, m.index));
1006
+ if (m[2] !== void 0) {
1007
+ nodes.push(
1008
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: m[2] }, `${keyPrefix}-b${i}`)
1009
+ );
1010
+ } else if (m[4] !== void 0) {
1011
+ nodes.push(
1012
+ /* @__PURE__ */ jsx(Text, { italic: true, children: m[4] }, `${keyPrefix}-i${i}`)
1013
+ );
1014
+ } else if (m[6] !== void 0) {
1015
+ nodes.push(
1016
+ /* @__PURE__ */ jsx(Text, { color: "greenBright", children: m[6] }, `${keyPrefix}-c${i}`)
1017
+ );
1018
+ }
1019
+ last = m.index + m[0].length;
1020
+ i++;
1021
+ }
1022
+ if (last < text.length) nodes.push(text.slice(last));
1023
+ return nodes;
1024
+ }
1025
+ function renderMarkdown(md) {
1026
+ const lines = md.split("\n");
1027
+ const blocks = [];
1028
+ let para = [];
1029
+ let key = 0;
1030
+ const flushPara = () => {
1031
+ if (para.length === 0) return;
1032
+ blocks.push(/* @__PURE__ */ jsx(Text, { children: renderInline(para.join(" "), `p${key}`) }, `p${key++}`));
1033
+ para = [];
1034
+ };
1035
+ for (const line of lines) {
1036
+ if (line.trim() === "") {
1037
+ flushPara();
1038
+ continue;
1039
+ }
1040
+ const heading = /^(#{1,6})\s+(.*)$/.exec(line);
1041
+ if (heading) {
1042
+ flushPara();
1043
+ const level = heading[1]?.length ?? 0;
1044
+ blocks.push(
1045
+ /* @__PURE__ */ jsx(Text, { bold: true, color: level <= 2 ? "cyan" : "white", children: renderInline(heading[2] ?? "", `h${key}`) }, `h${key++}`)
1046
+ );
1047
+ continue;
1048
+ }
1049
+ const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
1050
+ if (bullet) {
1051
+ flushPara();
1052
+ blocks.push(
1053
+ /* @__PURE__ */ jsxs(Text, { children: [
1054
+ " \u2022 ",
1055
+ renderInline(bullet[1] ?? "", `b${key}`)
1056
+ ] }, `b${key++}`)
1057
+ );
1058
+ continue;
1059
+ }
1060
+ const numbered = /^\s*\d+\.\s+(.*)$/.exec(line);
1061
+ if (numbered) {
1062
+ flushPara();
1063
+ blocks.push(
1064
+ /* @__PURE__ */ jsxs(Text, { children: [
1065
+ " ",
1066
+ renderInline(numbered[1] ?? "", `n${key}`)
1067
+ ] }, `n${key++}`)
1068
+ );
1069
+ continue;
1070
+ }
1071
+ para.push(line.trim());
1072
+ }
1073
+ flushPara();
1074
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: blocks });
1075
+ }
1076
+ function AnswerBlock({ text }) {
1077
+ return /* @__PURE__ */ jsx(
1078
+ Box,
1079
+ {
1080
+ flexDirection: "column",
1081
+ borderStyle: "round",
1082
+ borderColor: "green",
1083
+ paddingX: 1,
1084
+ marginY: 0,
1085
+ children: renderMarkdown(text)
1086
+ }
1087
+ );
1088
+ }
1089
+ var init_markdown = __esm({
1090
+ "src/tui/markdown.tsx"() {
1091
+ "use strict";
1092
+ }
1093
+ });
1094
+
1095
+ // src/tui/diff.tsx
1096
+ import { Box as Box2, Text as Text2 } from "ink";
1097
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1098
+ function compactChanges(oldStr, newStr) {
1099
+ const oldLines = oldStr.split(/\r?\n/);
1100
+ const newLines = newStr.split(/\r?\n/);
1101
+ let start = 0;
1102
+ const maxStart = Math.min(oldLines.length, newLines.length);
1103
+ while (start < maxStart && oldLines[start] === newLines[start]) start++;
1104
+ let endOld = oldLines.length;
1105
+ let endNew = newLines.length;
1106
+ while (endOld > start && endNew > start && oldLines[endOld - 1] === newLines[endNew - 1]) {
1107
+ endOld--;
1108
+ endNew--;
1109
+ }
1110
+ return {
1111
+ removed: oldLines.slice(start, endOld),
1112
+ added: newLines.slice(start, endNew)
1113
+ };
1114
+ }
1115
+ function FileDiffView({ diffs }) {
1116
+ const files = Object.entries(diffs);
1117
+ if (files.length === 0) return null;
1118
+ const blocks = [];
1119
+ let key = 0;
1120
+ let lineBudget = MAX_LINES;
1121
+ const shown = files.slice(0, MAX_FILES);
1122
+ shown.forEach(([file, edits], fi) => {
1123
+ let removedTotal = 0;
1124
+ let addedTotal = 0;
1125
+ const lines = [];
1126
+ for (const edit of edits) {
1127
+ const c = compactChanges(edit.old, edit.new);
1128
+ removedTotal += c.removed.length;
1129
+ addedTotal += c.added.length;
1130
+ if (lineBudget > 0) {
1131
+ for (const l of c.removed) {
1132
+ if (lineBudget <= 0) break;
1133
+ lines.push(
1134
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "redBright", color: "white", children: ` - ${l}` }, `r${key++}`)
1135
+ );
1136
+ lineBudget--;
1137
+ }
1138
+ for (const l of c.added) {
1139
+ if (lineBudget <= 0) break;
1140
+ lines.push(
1141
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "greenBright", color: "black", children: ` + ${l}` }, `a${key++}`)
1142
+ );
1143
+ lineBudget--;
1144
+ }
1145
+ }
1146
+ }
1147
+ blocks.push(
1148
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
1149
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: "magenta", children: `\u270E ${file}` }),
1150
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ` ${edits.length} edit(s) \xB7 +${addedTotal} / -${removedTotal}` }),
1151
+ lines
1152
+ ] }, `f${fi}`)
1153
+ );
1154
+ });
1155
+ if (files.length > MAX_FILES) {
1156
+ blocks.push(
1157
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ` \u2026 and ${files.length - MAX_FILES} more file(s)` }, "more")
1158
+ );
1159
+ }
1160
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: blocks });
1161
+ }
1162
+ var MAX_FILES, MAX_LINES;
1163
+ var init_diff = __esm({
1164
+ "src/tui/diff.tsx"() {
1165
+ "use strict";
1166
+ MAX_FILES = 12;
1167
+ MAX_LINES = 160;
1168
+ }
1169
+ });
1170
+
1171
+ // src/tui/Approval.tsx
1172
+ import { Box as Box3, Text as Text3, useInput } from "ink";
1173
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
971
1174
  function argPreview(request) {
972
1175
  const args = request.args;
973
1176
  if (request.tool === "apply_patch" && typeof args.old_string === "string" && typeof args.new_string === "string") {
@@ -985,8 +1188,8 @@ function ApprovalPrompt({ request, onDecision }) {
985
1188
  else if (input === "a") onDecision("always");
986
1189
  else if (input === "n" || key.escape) onDecision("deny");
987
1190
  });
988
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
989
- /* @__PURE__ */ jsxs(Text, { bold: true, color: "magenta", children: [
1191
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1192
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "magenta", children: [
990
1193
  "Permission needed: ",
991
1194
  friendlyToolName(request.tool),
992
1195
  " (",
@@ -994,14 +1197,14 @@ function ApprovalPrompt({ request, onDecision }) {
994
1197
  ") \u2014 ",
995
1198
  request.risk
996
1199
  ] }),
997
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: argPreview(request) }) }),
998
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
999
- /* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "[y]" }),
1200
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { children: argPreview(request) }) }),
1201
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs3(Text3, { children: [
1202
+ /* @__PURE__ */ jsx3(Text3, { color: "green", bold: true, children: "[y]" }),
1000
1203
  " allow once \xB7 ",
1001
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "[a]" }),
1204
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "[a]" }),
1002
1205
  " allow this session \xB7",
1003
1206
  " ",
1004
- /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "[n]" }),
1207
+ /* @__PURE__ */ jsx3(Text3, { color: "red", bold: true, children: "[n]" }),
1005
1208
  " deny"
1006
1209
  ] }) })
1007
1210
  ] });
@@ -1015,8 +1218,8 @@ var init_Approval = __esm({
1015
1218
 
1016
1219
  // src/tui/SelectDialog.tsx
1017
1220
  import { useEffect, useMemo, useState } from "react";
1018
- import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
1019
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1221
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
1222
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1020
1223
  function SelectDialog({
1021
1224
  title,
1022
1225
  items,
@@ -1051,34 +1254,34 @@ function SelectDialog({
1051
1254
  Math.min(cursor - Math.floor(VISIBLE / 2), Math.max(0, filtered.length - VISIBLE))
1052
1255
  );
1053
1256
  const visible = filtered.slice(start, start + VISIBLE);
1054
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1, children: [
1055
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: title }),
1056
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
1057
- /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
1257
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1, children: [
1258
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: title }),
1259
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1260
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1058
1261
  "search: ",
1059
1262
  filter
1060
1263
  ] }),
1061
- /* @__PURE__ */ jsx2(Text2, { children: "\u258F" })
1264
+ /* @__PURE__ */ jsx4(Text4, { children: "\u258F" })
1062
1265
  ] }),
1063
- loading ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "loading\u2026" }) : null,
1064
- error ? /* @__PURE__ */ jsx2(Text2, { color: "red", children: error }) : null,
1065
- !loading && !error ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1266
+ loading ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "loading\u2026" }) : null,
1267
+ error ? /* @__PURE__ */ jsx4(Text4, { color: "red", children: error }) : null,
1268
+ !loading && !error ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1066
1269
  visible.map((item, i) => {
1067
1270
  const index = start + i;
1068
1271
  const isCursor = index === cursor;
1069
1272
  const isCurrent = item === current;
1070
1273
  const description = descriptions?.[item];
1071
- return /* @__PURE__ */ jsxs2(Text2, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1274
+ return /* @__PURE__ */ jsxs4(Text4, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1072
1275
  isCursor ? "\u276F " : " ",
1073
1276
  isCurrent ? "\u25CF " : "\u25CB ",
1074
1277
  item,
1075
1278
  isCurrent ? " (current)" : "",
1076
- description ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ` \u2014 ${description}` }) : null
1279
+ description ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: ` \u2014 ${description}` }) : null
1077
1280
  ] }, item);
1078
1281
  }),
1079
- filtered.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "no matches" }) : null
1282
+ filtered.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "no matches" }) : null
1080
1283
  ] }) : null,
1081
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2191\u2193 move \xB7 type to filter \xB7 enter select \xB7 esc cancel" }) })
1284
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\u2191\u2193 move \xB7 type to filter \xB7 enter select \xB7 esc cancel" }) })
1082
1285
  ] });
1083
1286
  }
1084
1287
  var VISIBLE;
@@ -1091,22 +1294,22 @@ var init_SelectDialog = __esm({
1091
1294
 
1092
1295
  // src/tui/KeyDialog.tsx
1093
1296
  import { useState as useState2 } from "react";
1094
- import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
1297
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
1095
1298
  import TextInput from "ink-text-input";
1096
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1299
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1097
1300
  function KeyDialog({ providerName, onSubmit, onClose }) {
1098
1301
  const [value, setValue] = useState2("");
1099
1302
  useInput3((_input, key) => {
1100
1303
  if (key.escape) onClose();
1101
1304
  });
1102
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1103
- /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "magenta", children: [
1305
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1306
+ /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
1104
1307
  "API key for ",
1105
1308
  providerName
1106
1309
  ] }),
1107
- /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
1108
- /* @__PURE__ */ jsx3(Text3, { children: "key: " }),
1109
- /* @__PURE__ */ jsx3(
1310
+ /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1311
+ /* @__PURE__ */ jsx5(Text5, { children: "key: " }),
1312
+ /* @__PURE__ */ jsx5(
1110
1313
  TextInput,
1111
1314
  {
1112
1315
  mask: "*",
@@ -1119,7 +1322,7 @@ function KeyDialog({ providerName, onSubmit, onClose }) {
1119
1322
  }
1120
1323
  )
1121
1324
  ] }),
1122
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "enter confirm \xB7 esc cancel \xB7 saved to ~/.mentee/config.json" }) })
1325
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "enter confirm \xB7 esc cancel \xB7 saved to ~/.mentee/config.json" }) })
1123
1326
  ] });
1124
1327
  }
1125
1328
  var init_KeyDialog = __esm({
@@ -1128,23 +1331,15 @@ var init_KeyDialog = __esm({
1128
1331
  }
1129
1332
  });
1130
1333
 
1131
- // package.json
1132
- var version;
1133
- var init_package = __esm({
1134
- "package.json"() {
1135
- version = "0.1.9";
1136
- }
1137
- });
1138
-
1139
1334
  // src/tui/App.tsx
1140
1335
  var App_exports = {};
1141
1336
  __export(App_exports, {
1142
1337
  App: () => App
1143
1338
  });
1144
1339
  import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
1145
- import { Box as Box4, Static, Text as Text4, useApp, useInput as useInput4, useStdout } from "ink";
1340
+ import { Box as Box6, Static, Text as Text6, useApp, useInput as useInput4, useStdout } from "ink";
1146
1341
  import TextInput2 from "ink-text-input";
1147
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1342
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1148
1343
  function App(props) {
1149
1344
  const { exit } = useApp();
1150
1345
  const { stdout } = useStdout();
@@ -1172,15 +1367,17 @@ function App(props) {
1172
1367
  const [detailsOpen, setDetailsOpen] = useState3(false);
1173
1368
  const [showDetails, setShowDetails] = useState3(false);
1174
1369
  const [thinkingText, setThinkingText] = useState3("");
1370
+ const [fileDiffs, setFileDiffs] = useState3({});
1371
+ const finalTextRef = useRef(null);
1175
1372
  const pendingTool = useRef(null);
1176
1373
  const counter = useRef(0);
1177
1374
  const stateRef = useRef({ running, approval, dialog, factoryResult, providerName, model });
1178
1375
  stateRef.current = { running, approval, dialog, factoryResult, providerName, model };
1179
1376
  const abortRef = useRef(null);
1180
- const appendLog = (text, noise = false) => {
1377
+ const appendLog = (text, noise = false, kind) => {
1181
1378
  if (!text) return;
1182
1379
  counter.current += 1;
1183
- const line = { id: counter.current, text, noise };
1380
+ const line = { id: counter.current, text, noise, kind };
1184
1381
  setLog((prev) => [...prev, line]);
1185
1382
  };
1186
1383
  useEffect2(() => {
@@ -1194,6 +1391,16 @@ function App(props) {
1194
1391
  if (event.type === "model_request" || event.type === "task_completed") {
1195
1392
  setThinkingText("");
1196
1393
  }
1394
+ if (event.type === "patch") {
1395
+ const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
1396
+ const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
1397
+ const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
1398
+ setFileDiffs((prev) => {
1399
+ const arr = prev[filePath] ? [...prev[filePath], { old: oldStr, new: newStr }] : [{ old: oldStr, new: newStr }];
1400
+ return { ...prev, [filePath]: arr };
1401
+ });
1402
+ return;
1403
+ }
1197
1404
  appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
1198
1405
  if (event.type === "tool_started") {
1199
1406
  const name = typeof event.data?.tool === "string" ? event.data.tool : "";
@@ -1211,10 +1418,12 @@ function App(props) {
1211
1418
  } else if (event.type === "task_completed") {
1212
1419
  const text = typeof event.data?.finalText === "string" ? event.data.finalText.trim() : "";
1213
1420
  if (text) {
1214
- appendLog(text);
1421
+ finalTextRef.current = text;
1215
1422
  setFinalPending(text);
1216
1423
  setTypedText("");
1217
1424
  setTyping(true);
1425
+ } else {
1426
+ finalTextRef.current = null;
1218
1427
  }
1219
1428
  }
1220
1429
  });
@@ -1389,6 +1598,10 @@ function App(props) {
1389
1598
  appendLog(factoryResult.error ?? "No provider configured. Press Ctrl+K to add an API key.");
1390
1599
  return;
1391
1600
  }
1601
+ if (finalTextRef.current) {
1602
+ appendLog(finalTextRef.current, false, "answer");
1603
+ finalTextRef.current = null;
1604
+ }
1392
1605
  setFinalPending(null);
1393
1606
  setTypedText("");
1394
1607
  setTyping(false);
@@ -1397,6 +1610,7 @@ function App(props) {
1397
1610
  setDetailsOpen(false);
1398
1611
  setShowDetails(false);
1399
1612
  setRunning(true);
1613
+ setFileDiffs({});
1400
1614
  const ac = new AbortController();
1401
1615
  abortRef.current = ac;
1402
1616
  try {
@@ -1468,9 +1682,9 @@ function App(props) {
1468
1682
  else if (key.ctrl && input === "k") setDialog({ kind: "key" });
1469
1683
  });
1470
1684
  const modelLabel = model ?? factoryResult.provider?.defaultModel ?? (factoryResult.error ? "no key" : "");
1471
- const header = /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
1472
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: BANNER }),
1473
- /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1685
+ const header = /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
1686
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: BANNER }),
1687
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1474
1688
  "v",
1475
1689
  version,
1476
1690
  " \xB7 ",
@@ -1481,42 +1695,45 @@ function App(props) {
1481
1695
  " \xB7 ",
1482
1696
  props.cwd
1483
1697
  ] }),
1484
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
1698
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
1485
1699
  ] }, "header");
1486
1700
  const cols = stdout.columns || 80;
1487
1701
  const marquee = thinkingText.slice(-Math.max(1, cols - 4));
1488
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1489
- /* @__PURE__ */ jsx4(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx4(Box4, { children: item.text }, item.key) }),
1490
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1491
- log.filter((l) => showDetails || !l.noise).map((line) => /* @__PURE__ */ jsx4(Text4, { children: line.text }, line.id)),
1492
- finalPending !== null ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1493
- /* @__PURE__ */ jsx4(Text4, { color: "green", children: typedText }),
1494
- typing ? /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u258C" }) : null
1495
- ] }) : null,
1702
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1703
+ /* @__PURE__ */ jsx6(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx6(Box6, { children: item.text }, item.key) }),
1704
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1705
+ log.filter((l) => showDetails || !l.noise).map(
1706
+ (line) => line.kind === "answer" ? /* @__PURE__ */ jsx6(AnswerBlock, { text: line.text }, line.id) : /* @__PURE__ */ jsx6(Text6, { children: line.text }, line.id)
1707
+ ),
1708
+ finalPending !== null ? /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
1709
+ typedText,
1710
+ typing ? "\u258C" : ""
1711
+ ] }) }) : null,
1712
+ /* @__PURE__ */ jsx6(FileDiffView, { diffs: fileDiffs }),
1496
1713
  detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
1497
1714
  const rec = toolHistory[inspectorIndex];
1498
1715
  const outLines = rec.output.split("\n").slice(0, 24).join("\n");
1499
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1500
- /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1716
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1717
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1501
1718
  " tool details (",
1502
1719
  inspectorIndex + 1,
1503
1720
  "/",
1504
1721
  toolHistory.length,
1505
- ") \xB7 press d to cycle "
1722
+ ") \xB7 Alt+D to cycle "
1506
1723
  ] }),
1507
- /* @__PURE__ */ jsxs4(Text4, { color: toolColorName(rec.name), bold: true, children: [
1724
+ /* @__PURE__ */ jsxs6(Text6, { color: toolColorName(rec.name), bold: true, children: [
1508
1725
  rec.name,
1509
1726
  " ",
1510
1727
  rec.target
1511
1728
  ] }),
1512
- /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1729
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1513
1730
  "args: ",
1514
1731
  rec.args.slice(0, 400) || "(none)"
1515
1732
  ] }),
1516
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: outLines || "(no output)" })
1733
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: outLines || "(no output)" })
1517
1734
  ] });
1518
1735
  })() : null,
1519
- dialog?.kind === "model" ? /* @__PURE__ */ jsx4(
1736
+ dialog?.kind === "model" ? /* @__PURE__ */ jsx6(
1520
1737
  SelectDialog,
1521
1738
  {
1522
1739
  title: `Model \u2014 ${providerName}`,
@@ -1530,7 +1747,7 @@ function App(props) {
1530
1747
  },
1531
1748
  onClose: () => setDialog(null)
1532
1749
  }
1533
- ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
1750
+ ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx6(
1534
1751
  SelectDialog,
1535
1752
  {
1536
1753
  title: "Provider",
@@ -1543,7 +1760,7 @@ function App(props) {
1543
1760
  },
1544
1761
  onClose: () => setDialog(null)
1545
1762
  }
1546
- ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
1763
+ ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx6(
1547
1764
  KeyDialog,
1548
1765
  {
1549
1766
  providerName,
@@ -1553,7 +1770,7 @@ function App(props) {
1553
1770
  },
1554
1771
  onClose: () => setDialog(null)
1555
1772
  }
1556
- ) : approval ? /* @__PURE__ */ jsx4(
1773
+ ) : approval ? /* @__PURE__ */ jsx6(
1557
1774
  ApprovalPrompt,
1558
1775
  {
1559
1776
  request: approval,
@@ -1562,22 +1779,22 @@ function App(props) {
1562
1779
  if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
1563
1780
  }
1564
1781
  }
1565
- ) : running ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1566
- /* @__PURE__ */ jsxs4(Box4, { children: [
1567
- /* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
1782
+ ) : running ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1783
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1784
+ /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
1568
1785
  SPINNER_FRAMES[frame],
1569
1786
  " "
1570
1787
  ] }),
1571
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1788
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1572
1789
  ] }),
1573
- thinkingText ? /* @__PURE__ */ jsxs4(Box4, { children: [
1574
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u{1F4AD} " }),
1575
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", dimColor: true, children: marquee }),
1576
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u258C" })
1790
+ thinkingText ? /* @__PURE__ */ jsxs6(Box6, { children: [
1791
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u{1F4AD} " }),
1792
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", dimColor: true, children: marquee }),
1793
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u258C" })
1577
1794
  ] }) : null
1578
- ] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
1579
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
1580
- /* @__PURE__ */ jsx4(
1795
+ ] }) : /* @__PURE__ */ jsxs6(Box6, { children: [
1796
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: "\u25B8 " }),
1797
+ /* @__PURE__ */ jsx6(
1581
1798
  TextInput2,
1582
1799
  {
1583
1800
  value: taskInput,
@@ -1597,6 +1814,8 @@ var init_App = __esm({
1597
1814
  init_events();
1598
1815
  init_logging();
1599
1816
  init_render();
1817
+ init_markdown();
1818
+ init_diff();
1600
1819
  init_approval();
1601
1820
  init_loop();
1602
1821
  init_conversation();
@@ -1607,12 +1826,14 @@ var init_App = __esm({
1607
1826
  init_KeyDialog();
1608
1827
  init_package();
1609
1828
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1610
- PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "mock"];
1829
+ PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"];
1611
1830
  PROVIDER_DESCRIPTIONS = {
1612
1831
  kimi: "Moonshot \xB7 api.moonshot.ai",
1613
1832
  glm: "Zhipu China \xB7 open.bigmodel.cn \xB7 separate account & keys",
1614
1833
  zai: "Z.ai standard plan \xB7 api.z.ai/api/paas/v4",
1615
1834
  "zai-coding": "Z.ai GLM Coding Plan subscription \xB7 api.z.ai/api/coding",
1835
+ openrouter: "OpenRouter \xB7 100+ models via one key (Claude, GPT, Llama\u2026)",
1836
+ nvidia: "NVIDIA NIM \xB7 build.nvidia.com hosted models",
1616
1837
  mock: "offline testing \xB7 no network"
1617
1838
  };
1618
1839
  BANNER = ` __ __ \u{1F380}
@@ -1784,6 +2005,16 @@ function createKimiProvider(apiKey, model) {
1784
2005
  });
1785
2006
  }
1786
2007
 
2008
+ // src/models/nvidia.ts
2009
+ function createNvidiaProvider(apiKey, model) {
2010
+ return new OpenAICompatProvider({
2011
+ name: "nvidia",
2012
+ defaultModel: model ?? "nvidia/llama-3.1-nemotron-70b-instruct",
2013
+ baseURL: "https://integrate.api.nvidia.com/v1",
2014
+ apiKey
2015
+ });
2016
+ }
2017
+
1787
2018
  // src/models/mock.ts
1788
2019
  var callCounter = 0;
1789
2020
  var MockProvider = class {
@@ -1820,6 +2051,16 @@ var MockProvider = class {
1820
2051
  }
1821
2052
  };
1822
2053
 
2054
+ // src/models/openrouter.ts
2055
+ function createOpenRouterProvider(apiKey, model) {
2056
+ return new OpenAICompatProvider({
2057
+ name: "openrouter",
2058
+ defaultModel: model ?? "anthropic/claude-3.5-sonnet",
2059
+ baseURL: "https://openrouter.ai/api/v1",
2060
+ apiKey
2061
+ });
2062
+ }
2063
+
1823
2064
  // src/models/zai.ts
1824
2065
  function createZaiProvider(apiKey, model) {
1825
2066
  return new OpenAICompatProvider({
@@ -1843,7 +2084,9 @@ var ENV_NAMES2 = {
1843
2084
  kimi: "MENTEE_KIMI_API_KEY",
1844
2085
  glm: "MENTEE_GLM_API_KEY",
1845
2086
  zai: "MENTEE_ZAI_API_KEY",
1846
- "zai-coding": "MENTEE_ZAI_API_KEY"
2087
+ "zai-coding": "MENTEE_ZAI_API_KEY",
2088
+ openrouter: "MENTEE_OPENROUTER_API_KEY",
2089
+ nvidia: "MENTEE_NVIDIA_API_KEY"
1847
2090
  };
1848
2091
  var MissingApiKeyError = class extends Error {
1849
2092
  constructor(provider) {
@@ -1871,6 +2114,12 @@ function createProvider(provider, config, modelOverride) {
1871
2114
  if (provider === "zai-coding") {
1872
2115
  return createZaiCodingProvider(apiKey, model);
1873
2116
  }
2117
+ if (provider === "openrouter") {
2118
+ return createOpenRouterProvider(apiKey, model);
2119
+ }
2120
+ if (provider === "nvidia") {
2121
+ return createNvidiaProvider(apiKey, model);
2122
+ }
1874
2123
  return createGlmProvider(apiKey, model);
1875
2124
  }
1876
2125
 
@@ -3287,12 +3536,12 @@ async function runConfigWizard() {
3287
3536
  const config = loadConfig() ?? blankConfig();
3288
3537
  config.keys = config.keys ?? {};
3289
3538
  config.models = config.models ?? {};
3290
- console.log("Providers: 1) Kimi (Moonshot) 2) GLM (Zhipu CN) 3) Z.ai (GLM international)");
3291
- const defaultChoice = config.defaultProvider === "glm" ? "2" : config.defaultProvider === "zai" ? "3" : "1";
3539
+ console.log("Providers: 1) Kimi (Moonshot) 2) GLM (Zhipu CN) 3) Z.ai (GLM international) 4) OpenRouter 5) NVIDIA NIM");
3540
+ const defaultChoice = config.defaultProvider === "glm" ? "2" : config.defaultProvider === "zai" ? "3" : config.defaultProvider === "openrouter" ? "4" : config.defaultProvider === "nvidia" ? "5" : "1";
3292
3541
  const providerChoice = await ask(rl, "Default provider [1]", defaultChoice);
3293
- const provider = providerChoice === "2" ? "glm" : providerChoice === "3" ? "zai" : "kimi";
3542
+ const provider = providerChoice === "2" ? "glm" : providerChoice === "3" ? "zai" : providerChoice === "4" ? "openrouter" : providerChoice === "5" ? "nvidia" : "kimi";
3294
3543
  config.defaultProvider = provider;
3295
- const defaultModel = provider === "kimi" ? "kimi-k2.7-code" : "glm-4.6";
3544
+ const defaultModel = provider === "kimi" ? "kimi-k2.7-code" : provider === "openrouter" ? "anthropic/claude-3.5-sonnet" : provider === "nvidia" ? "nvidia/llama-3.1-nemotron-70b-instruct" : "glm-4.6";
3296
3545
  const key = await ask(rl, `API key for ${provider}`);
3297
3546
  if (key) config.keys[provider] = key;
3298
3547
  const model = await ask(rl, `Model for ${provider}`, defaultModel);
@@ -3304,7 +3553,7 @@ Saved to ${configFilePath()}
3304
3553
  if (config.keys[provider]) {
3305
3554
  console.log("Testing connection...");
3306
3555
  try {
3307
- const providerInstance = provider === "kimi" ? createKimiProvider(config.keys[provider], model) : provider === "zai" ? createZaiProvider(config.keys[provider], model) : createGlmProvider(config.keys[provider], model);
3556
+ const providerInstance = provider === "kimi" ? createKimiProvider(config.keys[provider], model) : provider === "zai" ? createZaiProvider(config.keys[provider], model) : provider === "openrouter" ? createOpenRouterProvider(config.keys[provider], model) : provider === "nvidia" ? createNvidiaProvider(config.keys[provider], model) : createGlmProvider(config.keys[provider], model);
3308
3557
  await providerInstance.generate(
3309
3558
  {
3310
3559
  system: "You are a ping endpoint.",
@@ -3331,20 +3580,20 @@ Saved to ${configFilePath()}
3331
3580
  }
3332
3581
 
3333
3582
  // src/cli.tsx
3583
+ init_package();
3334
3584
  import path9 from "path";
3335
- import { jsx as jsx5 } from "react/jsx-runtime";
3336
- var packageJson = { version: "0.1.0" };
3585
+ import { jsx as jsx7 } from "react/jsx-runtime";
3337
3586
  var program = new Command();
3338
- program.name("mentee").description("MenteE SWE \u2014 an autonomous SWE agent in your terminal. Bring your own model: Kimi, GLM, and more.").version(packageJson.version);
3587
+ program.name("mentee").description("MenteE SWE \u2014 an autonomous SWE agent in your terminal. Bring your own model: Kimi, GLM, Z.ai, OpenRouter, NVIDIA, and more.").version(version);
3339
3588
  program.command("config").description("Configure providers, API keys, and models (interactive wizard)").action(async () => {
3340
3589
  await runConfigWizard();
3341
3590
  });
3342
- program.argument("[task...]", "the software task to perform (omit to type it interactively)").option("-p, --provider <name>", "model provider: kimi | glm | mock").option("-m, --model <id>", "model id override for the chosen provider").option("-y, --yes", "auto-approve restricted actions (installs, git history changes)").option("--no-tui", "run in plain text mode instead of the interactive UI").option("--max-iterations <n>", "maximum agent iterations", "40").action(async (taskParts, options) => {
3591
+ program.argument("[task...]", "the software task to perform (omit to type it interactively)").option("-p, --provider <name>", "model provider: kimi | glm | zai | zai-coding | openrouter | nvidia | mock").option("-m, --model <id>", "model id override for the chosen provider").option("-y, --yes", "auto-approve restricted actions (installs, git history changes)").option("--no-tui", "run in plain text mode instead of the interactive UI").option("--max-iterations <n>", "maximum agent iterations", "40").action(async (taskParts, options) => {
3343
3592
  const task = taskParts.join(" ").trim();
3344
3593
  const config = loadConfig();
3345
3594
  const providerName = options.provider ?? config?.defaultProvider ?? "zai-coding";
3346
- if (!["kimi", "glm", "zai", "zai-coding", "mock"].includes(providerName)) {
3347
- console.error(chalk4.red(`Unknown provider "${providerName}". Use kimi, glm, zai, zai-coding, or mock.`));
3595
+ if (!["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"].includes(providerName)) {
3596
+ console.error(chalk4.red(`Unknown provider "${providerName}". Use kimi, glm, zai, zai-coding, openrouter, nvidia, or mock.`));
3348
3597
  process.exit(1);
3349
3598
  }
3350
3599
  const tools = new ToolRegistry(createDefaultTools());
@@ -3389,7 +3638,7 @@ program.argument("[task...]", "the software task to perform (omit to type it int
3389
3638
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
3390
3639
  const { render } = await import("ink");
3391
3640
  const { waitUntilExit } = render(
3392
- /* @__PURE__ */ jsx5(
3641
+ /* @__PURE__ */ jsx7(
3393
3642
  App2,
3394
3643
  {
3395
3644
  initialTask: task || void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@menteeai/menteeswe",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "MenteE SWE — a model-agnostic autonomous software-engineering agent CLI. Bring your own intelligence: Kimi, GLM/Z.ai, and more.",
5
5
  "type": "module",
6
6
  "license": "MIT",