@menteeai/menteeswe 0.1.10 → 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 +217 -94
  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.11";
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,85 @@ 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 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";
1087
1174
  function argPreview(request) {
1088
1175
  const args = request.args;
1089
1176
  if (request.tool === "apply_patch" && typeof args.old_string === "string" && typeof args.new_string === "string") {
@@ -1101,8 +1188,8 @@ function ApprovalPrompt({ request, onDecision }) {
1101
1188
  else if (input === "a") onDecision("always");
1102
1189
  else if (input === "n" || key.escape) onDecision("deny");
1103
1190
  });
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: [
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: [
1106
1193
  "Permission needed: ",
1107
1194
  friendlyToolName(request.tool),
1108
1195
  " (",
@@ -1110,14 +1197,14 @@ function ApprovalPrompt({ request, onDecision }) {
1110
1197
  ") \u2014 ",
1111
1198
  request.risk
1112
1199
  ] }),
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]" }),
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]" }),
1116
1203
  " allow once \xB7 ",
1117
- /* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "[a]" }),
1204
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "[a]" }),
1118
1205
  " allow this session \xB7",
1119
1206
  " ",
1120
- /* @__PURE__ */ jsx2(Text2, { color: "red", bold: true, children: "[n]" }),
1207
+ /* @__PURE__ */ jsx3(Text3, { color: "red", bold: true, children: "[n]" }),
1121
1208
  " deny"
1122
1209
  ] }) })
1123
1210
  ] });
@@ -1131,8 +1218,8 @@ var init_Approval = __esm({
1131
1218
 
1132
1219
  // src/tui/SelectDialog.tsx
1133
1220
  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";
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";
1136
1223
  function SelectDialog({
1137
1224
  title,
1138
1225
  items,
@@ -1167,34 +1254,34 @@ function SelectDialog({
1167
1254
  Math.min(cursor - Math.floor(VISIBLE / 2), Math.max(0, filtered.length - VISIBLE))
1168
1255
  );
1169
1256
  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: [
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: [
1174
1261
  "search: ",
1175
1262
  filter
1176
1263
  ] }),
1177
- /* @__PURE__ */ jsx3(Text3, { children: "\u258F" })
1264
+ /* @__PURE__ */ jsx4(Text4, { children: "\u258F" })
1178
1265
  ] }),
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: [
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: [
1182
1269
  visible.map((item, i) => {
1183
1270
  const index = start + i;
1184
1271
  const isCursor = index === cursor;
1185
1272
  const isCurrent = item === current;
1186
1273
  const description = descriptions?.[item];
1187
- return /* @__PURE__ */ jsxs3(Text3, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1274
+ return /* @__PURE__ */ jsxs4(Text4, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
1188
1275
  isCursor ? "\u276F " : " ",
1189
1276
  isCurrent ? "\u25CF " : "\u25CB ",
1190
1277
  item,
1191
1278
  isCurrent ? " (current)" : "",
1192
- description ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` \u2014 ${description}` }) : null
1279
+ description ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: ` \u2014 ${description}` }) : null
1193
1280
  ] }, item);
1194
1281
  }),
1195
- filtered.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "no matches" }) : null
1282
+ filtered.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "no matches" }) : null
1196
1283
  ] }) : 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" }) })
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" }) })
1198
1285
  ] });
1199
1286
  }
1200
1287
  var VISIBLE;
@@ -1207,22 +1294,22 @@ var init_SelectDialog = __esm({
1207
1294
 
1208
1295
  // src/tui/KeyDialog.tsx
1209
1296
  import { useState as useState2 } from "react";
1210
- import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
1297
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
1211
1298
  import TextInput from "ink-text-input";
1212
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1299
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1213
1300
  function KeyDialog({ providerName, onSubmit, onClose }) {
1214
1301
  const [value, setValue] = useState2("");
1215
1302
  useInput3((_input, key) => {
1216
1303
  if (key.escape) onClose();
1217
1304
  });
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: [
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: [
1220
1307
  "API key for ",
1221
1308
  providerName
1222
1309
  ] }),
1223
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1224
- /* @__PURE__ */ jsx4(Text4, { children: "key: " }),
1225
- /* @__PURE__ */ jsx4(
1310
+ /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1311
+ /* @__PURE__ */ jsx5(Text5, { children: "key: " }),
1312
+ /* @__PURE__ */ jsx5(
1226
1313
  TextInput,
1227
1314
  {
1228
1315
  mask: "*",
@@ -1235,7 +1322,7 @@ function KeyDialog({ providerName, onSubmit, onClose }) {
1235
1322
  }
1236
1323
  )
1237
1324
  ] }),
1238
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { 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" }) })
1239
1326
  ] });
1240
1327
  }
1241
1328
  var init_KeyDialog = __esm({
@@ -1244,23 +1331,15 @@ var init_KeyDialog = __esm({
1244
1331
  }
1245
1332
  });
1246
1333
 
1247
- // package.json
1248
- var version;
1249
- var init_package = __esm({
1250
- "package.json"() {
1251
- version = "0.1.10";
1252
- }
1253
- });
1254
-
1255
1334
  // src/tui/App.tsx
1256
1335
  var App_exports = {};
1257
1336
  __export(App_exports, {
1258
1337
  App: () => App
1259
1338
  });
1260
1339
  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";
1340
+ import { Box as Box6, Static, Text as Text6, useApp, useInput as useInput4, useStdout } from "ink";
1262
1341
  import TextInput2 from "ink-text-input";
1263
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1342
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1264
1343
  function App(props) {
1265
1344
  const { exit } = useApp();
1266
1345
  const { stdout } = useStdout();
@@ -1288,6 +1367,7 @@ function App(props) {
1288
1367
  const [detailsOpen, setDetailsOpen] = useState3(false);
1289
1368
  const [showDetails, setShowDetails] = useState3(false);
1290
1369
  const [thinkingText, setThinkingText] = useState3("");
1370
+ const [fileDiffs, setFileDiffs] = useState3({});
1291
1371
  const finalTextRef = useRef(null);
1292
1372
  const pendingTool = useRef(null);
1293
1373
  const counter = useRef(0);
@@ -1311,6 +1391,16 @@ function App(props) {
1311
1391
  if (event.type === "model_request" || event.type === "task_completed") {
1312
1392
  setThinkingText("");
1313
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
+ }
1314
1404
  appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
1315
1405
  if (event.type === "tool_started") {
1316
1406
  const name = typeof event.data?.tool === "string" ? event.data.tool : "";
@@ -1520,6 +1610,7 @@ function App(props) {
1520
1610
  setDetailsOpen(false);
1521
1611
  setShowDetails(false);
1522
1612
  setRunning(true);
1613
+ setFileDiffs({});
1523
1614
  const ac = new AbortController();
1524
1615
  abortRef.current = ac;
1525
1616
  try {
@@ -1591,9 +1682,9 @@ function App(props) {
1591
1682
  else if (key.ctrl && input === "k") setDialog({ kind: "key" });
1592
1683
  });
1593
1684
  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: [
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: [
1597
1688
  "v",
1598
1689
  version,
1599
1690
  " \xB7 ",
@@ -1604,44 +1695,45 @@ function App(props) {
1604
1695
  " \xB7 ",
1605
1696
  props.cwd
1606
1697
  ] }),
1607
- /* @__PURE__ */ jsx5(Text5, { 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" })
1608
1699
  ] }, "header");
1609
1700
  const cols = stdout.columns || 80;
1610
1701
  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: [
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: [
1614
1705
  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)
1706
+ (line) => line.kind === "answer" ? /* @__PURE__ */ jsx6(AnswerBlock, { text: line.text }, line.id) : /* @__PURE__ */ jsx6(Text6, { children: line.text }, line.id)
1616
1707
  ),
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,
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 }),
1621
1713
  detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
1622
1714
  const rec = toolHistory[inspectorIndex];
1623
1715
  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: [
1716
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1717
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1626
1718
  " tool details (",
1627
1719
  inspectorIndex + 1,
1628
1720
  "/",
1629
1721
  toolHistory.length,
1630
- ") \xB7 press d to cycle "
1722
+ ") \xB7 Alt+D to cycle "
1631
1723
  ] }),
1632
- /* @__PURE__ */ jsxs5(Text5, { color: toolColorName(rec.name), bold: true, children: [
1724
+ /* @__PURE__ */ jsxs6(Text6, { color: toolColorName(rec.name), bold: true, children: [
1633
1725
  rec.name,
1634
1726
  " ",
1635
1727
  rec.target
1636
1728
  ] }),
1637
- /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1729
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1638
1730
  "args: ",
1639
1731
  rec.args.slice(0, 400) || "(none)"
1640
1732
  ] }),
1641
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: outLines || "(no output)" })
1733
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: outLines || "(no output)" })
1642
1734
  ] });
1643
1735
  })() : null,
1644
- dialog?.kind === "model" ? /* @__PURE__ */ jsx5(
1736
+ dialog?.kind === "model" ? /* @__PURE__ */ jsx6(
1645
1737
  SelectDialog,
1646
1738
  {
1647
1739
  title: `Model \u2014 ${providerName}`,
@@ -1655,7 +1747,7 @@ function App(props) {
1655
1747
  },
1656
1748
  onClose: () => setDialog(null)
1657
1749
  }
1658
- ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx5(
1750
+ ) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx6(
1659
1751
  SelectDialog,
1660
1752
  {
1661
1753
  title: "Provider",
@@ -1668,7 +1760,7 @@ function App(props) {
1668
1760
  },
1669
1761
  onClose: () => setDialog(null)
1670
1762
  }
1671
- ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx5(
1763
+ ) : dialog?.kind === "key" ? /* @__PURE__ */ jsx6(
1672
1764
  KeyDialog,
1673
1765
  {
1674
1766
  providerName,
@@ -1678,7 +1770,7 @@ function App(props) {
1678
1770
  },
1679
1771
  onClose: () => setDialog(null)
1680
1772
  }
1681
- ) : approval ? /* @__PURE__ */ jsx5(
1773
+ ) : approval ? /* @__PURE__ */ jsx6(
1682
1774
  ApprovalPrompt,
1683
1775
  {
1684
1776
  request: approval,
@@ -1687,22 +1779,22 @@ function App(props) {
1687
1779
  if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
1688
1780
  }
1689
1781
  }
1690
- ) : running ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1691
- /* @__PURE__ */ jsxs5(Box5, { children: [
1692
- /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
1782
+ ) : running ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1783
+ /* @__PURE__ */ jsxs6(Box6, { children: [
1784
+ /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
1693
1785
  SPINNER_FRAMES[frame],
1694
1786
  " "
1695
1787
  ] }),
1696
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1788
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
1697
1789
  ] }),
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" })
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" })
1702
1794
  ] }) : null
1703
- ] }) : /* @__PURE__ */ jsxs5(Box5, { children: [
1704
- /* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "\u25B8 " }),
1705
- /* @__PURE__ */ jsx5(
1795
+ ] }) : /* @__PURE__ */ jsxs6(Box6, { children: [
1796
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: "\u25B8 " }),
1797
+ /* @__PURE__ */ jsx6(
1706
1798
  TextInput2,
1707
1799
  {
1708
1800
  value: taskInput,
@@ -1723,6 +1815,7 @@ var init_App = __esm({
1723
1815
  init_logging();
1724
1816
  init_render();
1725
1817
  init_markdown();
1818
+ init_diff();
1726
1819
  init_approval();
1727
1820
  init_loop();
1728
1821
  init_conversation();
@@ -1733,12 +1826,14 @@ var init_App = __esm({
1733
1826
  init_KeyDialog();
1734
1827
  init_package();
1735
1828
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1736
- PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "mock"];
1829
+ PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"];
1737
1830
  PROVIDER_DESCRIPTIONS = {
1738
1831
  kimi: "Moonshot \xB7 api.moonshot.ai",
1739
1832
  glm: "Zhipu China \xB7 open.bigmodel.cn \xB7 separate account & keys",
1740
1833
  zai: "Z.ai standard plan \xB7 api.z.ai/api/paas/v4",
1741
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",
1742
1837
  mock: "offline testing \xB7 no network"
1743
1838
  };
1744
1839
  BANNER = ` __ __ \u{1F380}
@@ -1910,6 +2005,16 @@ function createKimiProvider(apiKey, model) {
1910
2005
  });
1911
2006
  }
1912
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
+
1913
2018
  // src/models/mock.ts
1914
2019
  var callCounter = 0;
1915
2020
  var MockProvider = class {
@@ -1946,6 +2051,16 @@ var MockProvider = class {
1946
2051
  }
1947
2052
  };
1948
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
+
1949
2064
  // src/models/zai.ts
1950
2065
  function createZaiProvider(apiKey, model) {
1951
2066
  return new OpenAICompatProvider({
@@ -1969,7 +2084,9 @@ var ENV_NAMES2 = {
1969
2084
  kimi: "MENTEE_KIMI_API_KEY",
1970
2085
  glm: "MENTEE_GLM_API_KEY",
1971
2086
  zai: "MENTEE_ZAI_API_KEY",
1972
- "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"
1973
2090
  };
1974
2091
  var MissingApiKeyError = class extends Error {
1975
2092
  constructor(provider) {
@@ -1997,6 +2114,12 @@ function createProvider(provider, config, modelOverride) {
1997
2114
  if (provider === "zai-coding") {
1998
2115
  return createZaiCodingProvider(apiKey, model);
1999
2116
  }
2117
+ if (provider === "openrouter") {
2118
+ return createOpenRouterProvider(apiKey, model);
2119
+ }
2120
+ if (provider === "nvidia") {
2121
+ return createNvidiaProvider(apiKey, model);
2122
+ }
2000
2123
  return createGlmProvider(apiKey, model);
2001
2124
  }
2002
2125
 
@@ -3413,12 +3536,12 @@ async function runConfigWizard() {
3413
3536
  const config = loadConfig() ?? blankConfig();
3414
3537
  config.keys = config.keys ?? {};
3415
3538
  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";
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";
3418
3541
  const providerChoice = await ask(rl, "Default provider [1]", defaultChoice);
3419
- 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";
3420
3543
  config.defaultProvider = provider;
3421
- 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";
3422
3545
  const key = await ask(rl, `API key for ${provider}`);
3423
3546
  if (key) config.keys[provider] = key;
3424
3547
  const model = await ask(rl, `Model for ${provider}`, defaultModel);
@@ -3430,7 +3553,7 @@ Saved to ${configFilePath()}
3430
3553
  if (config.keys[provider]) {
3431
3554
  console.log("Testing connection...");
3432
3555
  try {
3433
- 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);
3434
3557
  await providerInstance.generate(
3435
3558
  {
3436
3559
  system: "You are a ping endpoint.",
@@ -3457,20 +3580,20 @@ Saved to ${configFilePath()}
3457
3580
  }
3458
3581
 
3459
3582
  // src/cli.tsx
3583
+ init_package();
3460
3584
  import path9 from "path";
3461
- import { jsx as jsx6 } from "react/jsx-runtime";
3462
- var packageJson = { version: "0.1.0" };
3585
+ import { jsx as jsx7 } from "react/jsx-runtime";
3463
3586
  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);
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);
3465
3588
  program.command("config").description("Configure providers, API keys, and models (interactive wizard)").action(async () => {
3466
3589
  await runConfigWizard();
3467
3590
  });
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) => {
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) => {
3469
3592
  const task = taskParts.join(" ").trim();
3470
3593
  const config = loadConfig();
3471
3594
  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.`));
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.`));
3474
3597
  process.exit(1);
3475
3598
  }
3476
3599
  const tools = new ToolRegistry(createDefaultTools());
@@ -3515,7 +3638,7 @@ program.argument("[task...]", "the software task to perform (omit to type it int
3515
3638
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
3516
3639
  const { render } = await import("ink");
3517
3640
  const { waitUntilExit } = render(
3518
- /* @__PURE__ */ jsx6(
3641
+ /* @__PURE__ */ jsx7(
3519
3642
  App2,
3520
3643
  {
3521
3644
  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.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",