@modelstatus/cli 0.1.64 → 0.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelstatus/cli",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "Track which AI models you use, where, and never get surprised by a retirement. Free offline model-health for any repo (mm status), browser sign-in for cloud inventory + alerts.",
5
5
  "keywords": [
6
6
  "llm",
package/src/fix.js CHANGED
@@ -55,7 +55,8 @@ export function recordFixes(dir, applied, { source = "tui" } = {}) {
55
55
  if (Array.isArray(j)) log = j;
56
56
  } catch { /* first write */ }
57
57
  const ts = Date.now();
58
- const entries = applied.map((p) => ({ ts, dir: path.resolve(dir), file: p.file, line: p.line, from: p.from, to: p.to, source }));
58
+ const clip = (t) => (typeof t === "string" && t.length > 240 ? t.slice(0, 237) + "…" : t);
59
+ const entries = applied.map((p) => ({ ts, dir: path.resolve(dir), file: p.file, line: p.line, from: p.from, to: p.to, before: clip(p.before), after: clip(p.after), source }));
59
60
  log = [...entries, ...log].slice(0, FIXES_CAP);
60
61
  fs.mkdirSync(path.dirname(FIXES_FILE), { recursive: true, mode: 0o700 });
61
62
  fs.writeFileSync(FIXES_FILE, JSON.stringify(log));
@@ -190,14 +191,15 @@ export function applyFixes(dir, plans) {
190
191
  stale.push({ ...p, error: "line out of range — rescan" });
191
192
  continue;
192
193
  }
193
- const { out, n } = replaceOnLine(lines[idx], p.from, p.to);
194
+ const before = lines[idx];
195
+ const { out, n } = replaceOnLine(before, p.from, p.to);
194
196
  if (n === 0) {
195
197
  stale.push({ ...p, error: "string not on that line anymore — rescan" });
196
198
  continue;
197
199
  }
198
200
  lines[idx] = out;
199
201
  dirty = true;
200
- applied.push({ ...p, count: n });
202
+ applied.push({ ...p, count: n, before, after: out });
201
203
  }
202
204
  if (dirty) {
203
205
  try {
package/src/tui/app.js CHANGED
@@ -129,7 +129,11 @@ export function App({ apiBase, apiKey, dir, initialView, onSignedIn, fresh }) {
129
129
  const { exit } = useApp();
130
130
  const { stdout } = useStdout();
131
131
  const client = React.useMemo(() => createClient({ apiBase, apiKey }), [apiBase, apiKey]);
132
- const fallbackKey = apiKey ? "inventory" : "local";
132
+ // Everyone lands on Here (the local scan) — it's the richest view (live scan,
133
+ // fix preview, push) and needs no network. Inventory is one keypress away (3).
134
+ // (Pre-0.1.66 signed-in users landed on Inventory; that dated from before the
135
+ // Here tab existed.)
136
+ const fallbackKey = "local";
133
137
  const startIdx = Math.max(0, VIEWS.findIndex((v) => v.key === (initialView || fallbackKey)));
134
138
  const [idx, setIdx] = React.useState(startIdx);
135
139
  const [redraw, setRedraw] = React.useState(0); // bumped by ctrl-L to force a clean repaint
@@ -206,9 +206,11 @@ export function WhatsNewView({ client, dir, ui, active, width = 78, height = 14
206
206
  if (!fixes.length) {
207
207
  body = h(Text, { color: C.FG_DIM }, " No fixes applied yet. Press f on the Here tab (or run mm fix) to rewrite dying model ids.");
208
208
  } else {
209
- // Windowed like the Alerts section so ↑↓ reaches every row.
209
+ // Windowed like the Alerts section so ↑↓ reaches every row — minus 4 rows
210
+ // reserved for the selected fix's diff underneath.
211
+ const FROWS = Math.max(3, ROWS - 4);
210
212
  const cur = clampCursor(cursor, fixes.length);
211
- const start = Math.max(0, Math.min(cur - ROWS + 1, fixes.length - ROWS));
213
+ const start = Math.max(0, Math.min(cur - FROWS + 1, fixes.length - FROWS));
212
214
  const ago = (ts) => {
213
215
  const m = Math.max(0, Math.round((Date.now() - ts) / 60000));
214
216
  if (m < 1) return "now";
@@ -220,7 +222,7 @@ export function WhatsNewView({ client, dir, ui, active, width = 78, height = 14
220
222
  body = h(
221
223
  Box,
222
224
  { flexDirection: "column" },
223
- ...fixes.slice(start, start + ROWS).map((f, i) => {
225
+ ...fixes.slice(start, start + FROWS).map((f, i) => {
224
226
  const cells = [
225
227
  { text: `${GLYPH.check} `, color: "#16a34a" },
226
228
  { text: cellE(`${path.basename(f.dir)} · ${f.file}:${f.line}`, 38), color: C.FG },
@@ -230,6 +232,19 @@ export function WhatsNewView({ client, dir, ui, active, width = 78, height = 14
230
232
  ];
231
233
  return h(ListRow, { key: `f${start + i}`, active: start + i === cur, cells, width });
232
234
  }),
235
+ // The selected fix's diff — same red/green visual as the f preview + the fix PR.
236
+ h(Text, { key: "drule" }, ""),
237
+ ...(() => {
238
+ const sel = fixes[cur];
239
+ if (!sel) return [];
240
+ if (!sel.before || !sel.after) return [h(Text, { key: "dnone", color: C.FG_DIM }, " (diff not recorded for this entry — older fix)")];
241
+ const w = Math.max(20, width - 4);
242
+ return [
243
+ h(Text, { key: "dh", color: C.FG_DIM }, ` ${cellE(`${sel.file}:${sel.line}`, w)}`),
244
+ h(Text, { key: "dd", color: "#f87171" }, cellE(` - ${sel.before.trimEnd()}`, w)),
245
+ h(Text, { key: "da", color: "#4ade80" }, cellE(` + ${sel.after.trimEnd()}`, w)),
246
+ ];
247
+ })(),
233
248
  );
234
249
  }
235
250
  } else {