@menteeai/menteeswe 0.1.10 → 0.1.12

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 +219 -103
  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({
@@ -981,6 +984,14 @@ var init_loop = __esm({
981
984
  }
982
985
  });
983
986
 
987
+ // package.json
988
+ var version;
989
+ var init_package = __esm({
990
+ "package.json"() {
991
+ version = "0.1.12";
992
+ }
993
+ });
994
+
984
995
  // src/tui/markdown.tsx
985
996
  import { Box, Text } from "ink";
986
997
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1068,7 +1079,7 @@ function AnswerBlock({ text }) {
1068
1079
  {
1069
1080
  flexDirection: "column",
1070
1081
  borderStyle: "round",
1071
- borderColor: "cyan",
1082
+ borderColor: "green",
1072
1083
  paddingX: 1,
1073
1084
  marginY: 0,
1074
1085
  children: renderMarkdown(text)
@@ -1081,9 +1092,71 @@ var init_markdown = __esm({
1081
1092
  }
1082
1093
  });
1083
1094
 
1084
- // src/tui/Approval.tsx
1085
- import { Box as Box2, Text as Text2, useInput } from "ink";
1095
+ // src/tui/diff.tsx
1096
+ import { Box as Box2, Text as Text2 } from "ink";
1086
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 CurrentEditBox({ edit }) {
1116
+ if (!edit) return null;
1117
+ const c = compactChanges(edit.old, edit.new);
1118
+ const removed = c.removed.slice(0, MAX_LINES);
1119
+ const added = c.added.slice(0, MAX_LINES);
1120
+ const lines = [];
1121
+ let key = 0;
1122
+ for (const l of removed) {
1123
+ lines.push(
1124
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "redBright", color: "white", children: ` - ${l}` }, `r${key++}`)
1125
+ );
1126
+ }
1127
+ for (const l of added) {
1128
+ lines.push(
1129
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "greenBright", color: "black", children: ` + ${l}` }, `a${key++}`)
1130
+ );
1131
+ }
1132
+ return /* @__PURE__ */ jsxs2(
1133
+ Box2,
1134
+ {
1135
+ flexDirection: "column",
1136
+ marginTop: 1,
1137
+ borderStyle: "single",
1138
+ borderColor: "gray",
1139
+ borderLeft: false,
1140
+ borderRight: false,
1141
+ paddingX: 1,
1142
+ children: [
1143
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: "magenta", children: `\u270E ${edit.path}` }),
1144
+ lines.length > 0 ? lines : /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " (no text change)" })
1145
+ ]
1146
+ }
1147
+ );
1148
+ }
1149
+ var MAX_LINES;
1150
+ var init_diff = __esm({
1151
+ "src/tui/diff.tsx"() {
1152
+ "use strict";
1153
+ MAX_LINES = 24;
1154
+ }
1155
+ });
1156
+
1157
+ // src/tui/Approval.tsx
1158
+ import { Box as Box3, Text as Text3, useInput } from "ink";
1159
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1087
1160
  function argPreview(request) {
1088
1161
  const args = request.args;
1089
1162
  if (request.tool === "apply_patch" && typeof args.old_string === "string" && typeof args.new_string === "string") {
@@ -1101,8 +1174,8 @@ function ApprovalPrompt({ request, onDecision }) {
1101
1174
  else if (input === "a") onDecision("always");
1102
1175
  else if (input === "n" || key.escape) onDecision("deny");
1103
1176
  });
1104
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1105
- /* @__PURE__ */ jsxs2(Text2, { bold: true, color: "magenta", children: [
1177
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1178
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "magenta", children: [
1106
1179
  "Permission needed: ",
1107
1180
  friendlyToolName(request.tool),
1108
1181
  " (",
@@ -1110,14 +1183,14 @@ function ApprovalPrompt({ request, onDecision }) {
1110
1183
  ") \u2014 ",
1111
1184
  request.risk
1112
1185
  ] }),
1113
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: argPreview(request) }) }),
1114
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { children: [
1115
- /* @__PURE__ */ jsx2(Text2, { color: "green", bold: true, children: "[y]" }),
1186
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { children: argPreview(request) }) }),
1187
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs3(Text3, { children: [
1188
+ /* @__PURE__ */ jsx3(Text3, { color: "green", bold: true, children: "[y]" }),
1116
1189
  " allow once \xB7 ",
1117
- /* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "[a]" }),
1190
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "[a]" }),
1118
1191
  " allow this session \xB7",
1119
1192
  " ",
1120
- /* @__PURE__ */ jsx2(Text2, { color: "red", bold: true, children: "[n]" }),
1193
+ /* @__PURE__ */ jsx3(Text3, { color: "red", bold: true, children: "[n]" }),
1121
1194
  " deny"
1122
1195
  ] }) })
1123
1196
  ] });
@@ -1131,8 +1204,8 @@ var init_Approval = __esm({
1131
1204
 
1132
1205
  // src/tui/SelectDialog.tsx
1133
1206
  import { useEffect, useMemo, useState } from "react";
1134
- import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
1135
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1207
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
1208
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1136
1209
  function SelectDialog({
1137
1210
  title,
1138
1211
  items,
@@ -1167,34 +1240,34 @@ function SelectDialog({
1167
1240
  Math.min(cursor - Math.floor(VISIBLE / 2), Math.max(0, filtered.length - VISIBLE))
1168
1241
  );
1169
1242
  const visible = filtered.slice(start, start + VISIBLE);
1170
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1, children: [
1171
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: title }),
1172
- /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
1173
- /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1243
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1, children: [
1244
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: title }),
1245
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1246
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1174
1247
  "search: ",
1175
1248
  filter
1176
1249
  ] }),
1177
- /* @__PURE__ */ jsx3(Text3, { children: "\u258F" })
1250
+ /* @__PURE__ */ jsx4(Text4, { children: "\u258F" })
1178
1251
  ] }),
1179
- loading ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "loading\u2026" }) : null,
1180
- error ? /* @__PURE__ */ jsx3(Text3, { color: "red", children: error }) : null,
1181
- !loading && !error ? /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
1252
+ loading ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "loading\u2026" }) : null,
1253
+ error ? /* @__PURE__ */ jsx4(Text4, { color: "red", children: error }) : null,
1254
+ !loading && !error ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1182
1255
  visible.map((item, i) => {
1183
1256
  const index = start + i;
1184
1257
  const isCursor = index === cursor;
1185
1258
  const isCurrent = item === current;
1186
1259
  const description = descriptions?.[item];
1187
- return /* @__PURE__ */ jsxs3(Text3, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1260
+ return /* @__PURE__ */ jsxs4(Text4, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1188
1261
  isCursor ? "\u276F " : " ",
1189
1262
  isCurrent ? "\u25CF " : "\u25CB ",
1190
1263
  item,
1191
1264
  isCurrent ? " (current)" : "",
1192
- description ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` \u2014 ${description}` }) : null
1265
+ description ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: ` \u2014 ${description}` }) : null
1193
1266
  ] }, item);
1194
1267
  }),
1195
- filtered.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "no matches" }) : null
1268
+ filtered.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "no matches" }) : null
1196
1269
  ] }) : null,
1197
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2191\u2193 move \xB7 type to filter \xB7 enter select \xB7 esc cancel" }) })
1270
+ /* @__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" }) })
1198
1271
  ] });
1199
1272
  }
1200
1273
  var VISIBLE;
@@ -1207,22 +1280,22 @@ var init_SelectDialog = __esm({
1207
1280
 
1208
1281
  // src/tui/KeyDialog.tsx
1209
1282
  import { useState as useState2 } from "react";
1210
- import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
1283
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
1211
1284
  import TextInput from "ink-text-input";
1212
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1285
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1213
1286
  function KeyDialog({ providerName, onSubmit, onClose }) {
1214
1287
  const [value, setValue] = useState2("");
1215
1288
  useInput3((_input, key) => {
1216
1289
  if (key.escape) onClose();
1217
1290
  });
1218
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1219
- /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "magenta", children: [
1291
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
1292
+ /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "magenta", children: [
1220
1293
  "API key for ",
1221
1294
  providerName
1222
1295
  ] }),
1223
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1224
- /* @__PURE__ */ jsx4(Text4, { children: "key: " }),
1225
- /* @__PURE__ */ jsx4(
1296
+ /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1297
+ /* @__PURE__ */ jsx5(Text5, { children: "key: " }),
1298
+ /* @__PURE__ */ jsx5(
1226
1299
  TextInput,
1227
1300
  {
1228
1301
  mask: "*",
@@ -1235,7 +1308,7 @@ function KeyDialog({ providerName, onSubmit, onClose }) {
1235
1308
  }
1236
1309
  )
1237
1310
  ] }),
1238
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "enter confirm \xB7 esc cancel \xB7 saved to ~/.mentee/config.json" }) })
1311
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "enter confirm \xB7 esc cancel \xB7 saved to ~/.mentee/config.json" }) })
1239
1312
  ] });
1240
1313
  }
1241
1314
  var init_KeyDialog = __esm({
@@ -1244,23 +1317,15 @@ var init_KeyDialog = __esm({
1244
1317
  }
1245
1318
  });
1246
1319
 
1247
- // package.json
1248
- var version;
1249
- var init_package = __esm({
1250
- "package.json"() {
1251
- version = "0.1.10";
1252
- }
1253
- });
1254
-
1255
1320
  // src/tui/App.tsx
1256
1321
  var App_exports = {};
1257
1322
  __export(App_exports, {
1258
1323
  App: () => App
1259
1324
  });
1260
1325
  import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
1261
- import { Box as Box5, Static, Text as Text5, useApp, useInput as useInput4, useStdout } from "ink";
1326
+ import { Box as Box6, Static, Text as Text6, useApp, useInput as useInput4, useStdout } from "ink";
1262
1327
  import TextInput2 from "ink-text-input";
1263
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1328
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1264
1329
  function App(props) {
1265
1330
  const { exit } = useApp();
1266
1331
  const { stdout } = useStdout();
@@ -1288,6 +1353,7 @@ function App(props) {
1288
1353
  const [detailsOpen, setDetailsOpen] = useState3(false);
1289
1354
  const [showDetails, setShowDetails] = useState3(false);
1290
1355
  const [thinkingText, setThinkingText] = useState3("");
1356
+ const [currentEdit, setCurrentEdit] = useState3(null);
1291
1357
  const finalTextRef = useRef(null);
1292
1358
  const pendingTool = useRef(null);
1293
1359
  const counter = useRef(0);
@@ -1311,6 +1377,13 @@ function App(props) {
1311
1377
  if (event.type === "model_request" || event.type === "task_completed") {
1312
1378
  setThinkingText("");
1313
1379
  }
1380
+ if (event.type === "patch") {
1381
+ const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
1382
+ const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
1383
+ const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
1384
+ setCurrentEdit({ path: filePath, old: oldStr, new: newStr });
1385
+ return;
1386
+ }
1314
1387
  appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
1315
1388
  if (event.type === "tool_started") {
1316
1389
  const name = typeof event.data?.tool === "string" ? event.data.tool : "";
@@ -1518,8 +1591,8 @@ function App(props) {
1518
1591
  setToolHistory([]);
1519
1592
  setInspectorIndex(null);
1520
1593
  setDetailsOpen(false);
1521
- setShowDetails(false);
1522
1594
  setRunning(true);
1595
+ setCurrentEdit(null);
1523
1596
  const ac = new AbortController();
1524
1597
  abortRef.current = ac;
1525
1598
  try {
@@ -1591,9 +1664,9 @@ function App(props) {
1591
1664
  else if (key.ctrl && input === "k") setDialog({ kind: "key" });
1592
1665
  });
1593
1666
  const modelLabel = model ?? factoryResult.provider?.defaultModel ?? (factoryResult.error ? "no key" : "");
1594
- const header = /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1595
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: BANNER }),
1596
- /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1667
+ const header = /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
1668
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: BANNER }),
1669
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1597
1670
  "v",
1598
1671
  version,
1599
1672
  " \xB7 ",
@@ -1604,44 +1677,45 @@ function App(props) {
1604
1677
  " \xB7 ",
1605
1678
  props.cwd
1606
1679
  ] }),
1607
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
1680
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
1608
1681
  ] }, "header");
1609
1682
  const cols = stdout.columns || 80;
1610
1683
  const marquee = thinkingText.slice(-Math.max(1, cols - 4));
1611
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1612
- /* @__PURE__ */ jsx5(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx5(Box5, { children: item.text }, item.key) }),
1613
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1684
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1685
+ /* @__PURE__ */ jsx6(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx6(Box6, { children: item.text }, item.key) }),
1686
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1614
1687
  log.filter((l) => showDetails || !l.noise).map(
1615
- (line) => line.kind === "answer" ? /* @__PURE__ */ jsx5(AnswerBlock, { text: line.text }, line.id) : /* @__PURE__ */ jsx5(Text5, { children: line.text }, line.id)
1688
+ (line) => line.kind === "answer" ? /* @__PURE__ */ jsx6(AnswerBlock, { text: line.text }, line.id) : /* @__PURE__ */ jsx6(Text6, { children: line.text }, line.id)
1616
1689
  ),
1617
- finalPending !== null ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1618
- /* @__PURE__ */ jsx5(Text5, { color: "green", children: typedText }),
1619
- typing ? /* @__PURE__ */ jsx5(Text5, { color: "green", children: "\u258C" }) : null
1620
- ] }) : null,
1690
+ finalPending !== null ? /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
1691
+ typedText,
1692
+ typing ? "\u258C" : ""
1693
+ ] }) }) : null,
1694
+ /* @__PURE__ */ jsx6(CurrentEditBox, { edit: currentEdit }),
1621
1695
  detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
1622
1696
  const rec = toolHistory[inspectorIndex];
1623
1697
  const outLines = rec.output.split("\n").slice(0, 24).join("\n");
1624
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1625
- /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1698
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1699
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1626
1700
  " tool details (",
1627
1701
  inspectorIndex + 1,
1628
1702
  "/",
1629
1703
  toolHistory.length,
1630
- ") \xB7 press d to cycle "
1704
+ ") \xB7 Alt+D to cycle "
1631
1705
  ] }),
1632
- /* @__PURE__ */ jsxs5(Text5, { color: toolColorName(rec.name), bold: true, children: [
1706
+ /* @__PURE__ */ jsxs6(Text6, { color: toolColorName(rec.name), bold: true, children: [
1633
1707
  rec.name,
1634
1708
  " ",
1635
1709
  rec.target
1636
1710
  ] }),
1637
- /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1711
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1638
1712
  "args: ",
1639
1713
  rec.args.slice(0, 400) || "(none)"
1640
1714
  ] }),
1641
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: outLines || "(no output)" })
1715
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: outLines || "(no output)" })
1642
1716
  ] });
1643
1717
  })() : null,
1644
- dialog?.kind === "model" ? /* @__PURE__ */ jsx5(
1718
+ dialog?.kind === "model" ? /* @__PURE__ */ jsx6(
1645
1719
  SelectDialog,
1646
1720
  {
1647
1721
  title: `Model \u2014 ${providerName}`,
@@ -1655,7 +1729,7 @@ function App(props) {
1655
1729
  },
1656
1730
  onClose: () => setDialog(null)
1657
1731
  }
1658
- ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx5(
1732
+ ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx6(
1659
1733
  SelectDialog,
1660
1734
  {
1661
1735
  title: "Provider",
@@ -1668,7 +1742,7 @@ function App(props) {
1668
1742
  },
1669
1743
  onClose: () => setDialog(null)
1670
1744
  }
1671
- ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx5(
1745
+ ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx6(
1672
1746
  KeyDialog,
1673
1747
  {
1674
1748
  providerName,
@@ -1678,7 +1752,7 @@ function App(props) {
1678
1752
  },
1679
1753
  onClose: () => setDialog(null)
1680
1754
  }
1681
- ) : approval ? /* @__PURE__ */ jsx5(
1755
+ ) : approval ? /* @__PURE__ */ jsx6(
1682
1756
  ApprovalPrompt,
1683
1757
  {
1684
1758
  request: approval,
@@ -1687,22 +1761,22 @@ function App(props) {
1687
1761
  if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
1688
1762
  }
1689
1763
  }
1690
- ) : running ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1691
- /* @__PURE__ */ jsxs5(Box5, { children: [
1692
- /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
1764
+ ) : running ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1765
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1766
+ /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
1693
1767
  SPINNER_FRAMES[frame],
1694
1768
  " "
1695
1769
  ] }),
1696
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1770
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1697
1771
  ] }),
1698
- thinkingText ? /* @__PURE__ */ jsxs5(Box5, { children: [
1699
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u{1F4AD} " }),
1700
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", dimColor: true, children: marquee }),
1701
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u258C" })
1772
+ thinkingText ? /* @__PURE__ */ jsxs6(Box6, { children: [
1773
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u{1F4AD} " }),
1774
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", dimColor: true, children: marquee }),
1775
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u258C" })
1702
1776
  ] }) : null
1703
- ] }) : /* @__PURE__ */ jsxs5(Box5, { children: [
1704
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "\u25B8 " }),
1705
- /* @__PURE__ */ jsx5(
1777
+ ] }) : /* @__PURE__ */ jsxs6(Box6, { children: [
1778
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: "\u25B8 " }),
1779
+ /* @__PURE__ */ jsx6(
1706
1780
  TextInput2,
1707
1781
  {
1708
1782
  value: taskInput,
@@ -1723,6 +1797,7 @@ var init_App = __esm({
1723
1797
  init_logging();
1724
1798
  init_render();
1725
1799
  init_markdown();
1800
+ init_diff();
1726
1801
  init_approval();
1727
1802
  init_loop();
1728
1803
  init_conversation();
@@ -1733,12 +1808,14 @@ var init_App = __esm({
1733
1808
  init_KeyDialog();
1734
1809
  init_package();
1735
1810
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1736
- PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "mock"];
1811
+ PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"];
1737
1812
  PROVIDER_DESCRIPTIONS = {
1738
1813
  kimi: "Moonshot \xB7 api.moonshot.ai",
1739
1814
  glm: "Zhipu China \xB7 open.bigmodel.cn \xB7 separate account & keys",
1740
1815
  zai: "Z.ai standard plan \xB7 api.z.ai/api/paas/v4",
1741
1816
  "zai-coding": "Z.ai GLM Coding Plan subscription \xB7 api.z.ai/api/coding",
1817
+ openrouter: "OpenRouter \xB7 100+ models via one key (Claude, GPT, Llama\u2026)",
1818
+ nvidia: "NVIDIA NIM \xB7 build.nvidia.com hosted models",
1742
1819
  mock: "offline testing \xB7 no network"
1743
1820
  };
1744
1821
  BANNER = ` __ __ \u{1F380}
@@ -1823,14 +1900,14 @@ var OpenAICompatProvider = class {
1823
1900
  ...request.maxTokens ? { max_tokens: request.maxTokens } : {}
1824
1901
  };
1825
1902
  if (!request.onToken) {
1826
- const response = await this.client.chat.completions.create(baseParams);
1827
- return parseResponse(response);
1903
+ const response2 = await this.client.chat.completions.create(baseParams);
1904
+ return parseResponse(response2);
1828
1905
  }
1829
- try {
1906
+ const streamOnce = async (withUsage) => {
1830
1907
  const stream = await this.client.chat.completions.create({
1831
1908
  ...baseParams,
1832
1909
  stream: true,
1833
- stream_options: { include_usage: true }
1910
+ ...withUsage ? { stream_options: { include_usage: true } } : {}
1834
1911
  });
1835
1912
  let content = "";
1836
1913
  let finishReason = "stop";
@@ -1841,7 +1918,7 @@ var OpenAICompatProvider = class {
1841
1918
  const delta = choice?.delta;
1842
1919
  if (delta?.content) {
1843
1920
  content += delta.content;
1844
- request.onToken(delta.content);
1921
+ request.onToken?.(delta.content);
1845
1922
  }
1846
1923
  if (delta?.tool_calls) {
1847
1924
  for (const tc of delta.tool_calls) {
@@ -1875,10 +1952,21 @@ var OpenAICompatProvider = class {
1875
1952
  finishReason: mapFinishReason(finishReason),
1876
1953
  usage
1877
1954
  };
1878
- } catch {
1879
- const response = await this.client.chat.completions.create(baseParams);
1880
- return parseResponse(response);
1955
+ };
1956
+ for (const withUsage of [true, false]) {
1957
+ try {
1958
+ return await streamOnce(withUsage);
1959
+ } catch {
1960
+ }
1961
+ }
1962
+ const response = await this.client.chat.completions.create(baseParams);
1963
+ const parsed = parseResponse(response);
1964
+ if (parsed.content) {
1965
+ for (let i = 0; i < parsed.content.length; i += 3) {
1966
+ request.onToken?.(parsed.content.slice(i, i + 3));
1967
+ }
1881
1968
  }
1969
+ return parsed;
1882
1970
  }
1883
1971
  async listModels() {
1884
1972
  const page = await this.client.models.list();
@@ -1910,6 +1998,16 @@ function createKimiProvider(apiKey, model) {
1910
1998
  });
1911
1999
  }
1912
2000
 
2001
+ // src/models/nvidia.ts
2002
+ function createNvidiaProvider(apiKey, model) {
2003
+ return new OpenAICompatProvider({
2004
+ name: "nvidia",
2005
+ defaultModel: model ?? "nvidia/llama-3.1-nemotron-70b-instruct",
2006
+ baseURL: "https://integrate.api.nvidia.com/v1",
2007
+ apiKey
2008
+ });
2009
+ }
2010
+
1913
2011
  // src/models/mock.ts
1914
2012
  var callCounter = 0;
1915
2013
  var MockProvider = class {
@@ -1946,6 +2044,16 @@ var MockProvider = class {
1946
2044
  }
1947
2045
  };
1948
2046
 
2047
+ // src/models/openrouter.ts
2048
+ function createOpenRouterProvider(apiKey, model) {
2049
+ return new OpenAICompatProvider({
2050
+ name: "openrouter",
2051
+ defaultModel: model ?? "anthropic/claude-3.5-sonnet",
2052
+ baseURL: "https://openrouter.ai/api/v1",
2053
+ apiKey
2054
+ });
2055
+ }
2056
+
1949
2057
  // src/models/zai.ts
1950
2058
  function createZaiProvider(apiKey, model) {
1951
2059
  return new OpenAICompatProvider({
@@ -1969,7 +2077,9 @@ var ENV_NAMES2 = {
1969
2077
  kimi: "MENTEE_KIMI_API_KEY",
1970
2078
  glm: "MENTEE_GLM_API_KEY",
1971
2079
  zai: "MENTEE_ZAI_API_KEY",
1972
- "zai-coding": "MENTEE_ZAI_API_KEY"
2080
+ "zai-coding": "MENTEE_ZAI_API_KEY",
2081
+ openrouter: "MENTEE_OPENROUTER_API_KEY",
2082
+ nvidia: "MENTEE_NVIDIA_API_KEY"
1973
2083
  };
1974
2084
  var MissingApiKeyError = class extends Error {
1975
2085
  constructor(provider) {
@@ -1997,6 +2107,12 @@ function createProvider(provider, config, modelOverride) {
1997
2107
  if (provider === "zai-coding") {
1998
2108
  return createZaiCodingProvider(apiKey, model);
1999
2109
  }
2110
+ if (provider === "openrouter") {
2111
+ return createOpenRouterProvider(apiKey, model);
2112
+ }
2113
+ if (provider === "nvidia") {
2114
+ return createNvidiaProvider(apiKey, model);
2115
+ }
2000
2116
  return createGlmProvider(apiKey, model);
2001
2117
  }
2002
2118
 
@@ -3413,12 +3529,12 @@ async function runConfigWizard() {
3413
3529
  const config = loadConfig() ?? blankConfig();
3414
3530
  config.keys = config.keys ?? {};
3415
3531
  config.models = config.models ?? {};
3416
- console.log("Providers: 1) Kimi (Moonshot) 2) GLM (Zhipu CN) 3) Z.ai (GLM international)");
3417
- const defaultChoice = config.defaultProvider === "glm" ? "2" : config.defaultProvider === "zai" ? "3" : "1";
3532
+ console.log("Providers: 1) Kimi (Moonshot) 2) GLM (Zhipu CN) 3) Z.ai (GLM international) 4) OpenRouter 5) NVIDIA NIM");
3533
+ const defaultChoice = config.defaultProvider === "glm" ? "2" : config.defaultProvider === "zai" ? "3" : config.defaultProvider === "openrouter" ? "4" : config.defaultProvider === "nvidia" ? "5" : "1";
3418
3534
  const providerChoice = await ask(rl, "Default provider [1]", defaultChoice);
3419
- const provider = providerChoice === "2" ? "glm" : providerChoice === "3" ? "zai" : "kimi";
3535
+ const provider = providerChoice === "2" ? "glm" : providerChoice === "3" ? "zai" : providerChoice === "4" ? "openrouter" : providerChoice === "5" ? "nvidia" : "kimi";
3420
3536
  config.defaultProvider = provider;
3421
- const defaultModel = provider === "kimi" ? "kimi-k2.7-code" : "glm-4.6";
3537
+ 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";
3422
3538
  const key = await ask(rl, `API key for ${provider}`);
3423
3539
  if (key) config.keys[provider] = key;
3424
3540
  const model = await ask(rl, `Model for ${provider}`, defaultModel);
@@ -3430,7 +3546,7 @@ Saved to ${configFilePath()}
3430
3546
  if (config.keys[provider]) {
3431
3547
  console.log("Testing connection...");
3432
3548
  try {
3433
- const providerInstance = provider === "kimi" ? createKimiProvider(config.keys[provider], model) : provider === "zai" ? createZaiProvider(config.keys[provider], model) : createGlmProvider(config.keys[provider], model);
3549
+ 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);
3434
3550
  await providerInstance.generate(
3435
3551
  {
3436
3552
  system: "You are a ping endpoint.",
@@ -3457,20 +3573,20 @@ Saved to ${configFilePath()}
3457
3573
  }
3458
3574
 
3459
3575
  // src/cli.tsx
3576
+ init_package();
3460
3577
  import path9 from "path";
3461
- import { jsx as jsx6 } from "react/jsx-runtime";
3462
- var packageJson = { version: "0.1.0" };
3578
+ import { jsx as jsx7 } from "react/jsx-runtime";
3463
3579
  var program = new Command();
3464
- 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);
3580
+ 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);
3465
3581
  program.command("config").description("Configure providers, API keys, and models (interactive wizard)").action(async () => {
3466
3582
  await runConfigWizard();
3467
3583
  });
3468
- 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) => {
3584
+ 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) => {
3469
3585
  const task = taskParts.join(" ").trim();
3470
3586
  const config = loadConfig();
3471
3587
  const providerName = options.provider ?? config?.defaultProvider ?? "zai-coding";
3472
- if (!["kimi", "glm", "zai", "zai-coding", "mock"].includes(providerName)) {
3473
- console.error(chalk4.red(`Unknown provider "${providerName}". Use kimi, glm, zai, zai-coding, or mock.`));
3588
+ if (!["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"].includes(providerName)) {
3589
+ console.error(chalk4.red(`Unknown provider "${providerName}". Use kimi, glm, zai, zai-coding, openrouter, nvidia, or mock.`));
3474
3590
  process.exit(1);
3475
3591
  }
3476
3592
  const tools = new ToolRegistry(createDefaultTools());
@@ -3515,7 +3631,7 @@ program.argument("[task...]", "the software task to perform (omit to type it int
3515
3631
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
3516
3632
  const { render } = await import("ink");
3517
3633
  const { waitUntilExit } = render(
3518
- /* @__PURE__ */ jsx6(
3634
+ /* @__PURE__ */ jsx7(
3519
3635
  App2,
3520
3636
  {
3521
3637
  initialTask: task || void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@menteeai/menteeswe",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
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",