@modelstatus/cli 0.1.26 → 0.1.27
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 +1 -1
- package/src/tui/app.js +2 -2
- package/src/tui/ui.js +7 -1
- package/src/tui/views/alerts.js +1 -1
- package/src/tui/views/inventory.js +5 -2
- package/src/tui/views/local.js +9 -1
- package/src/tui/views/scan.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelstatus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
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/tui/app.js
CHANGED
|
@@ -171,8 +171,8 @@ export function App({ apiBase, apiKey, dir, initialView, onSignedIn, fresh }) {
|
|
|
171
171
|
} else if (showSignInGate) {
|
|
172
172
|
body = h(EmptyCard, {
|
|
173
173
|
icon: GLYPH.spark,
|
|
174
|
-
title:
|
|
175
|
-
lines: [
|
|
174
|
+
title: "Track your AI models across every project",
|
|
175
|
+
lines: [`${current2.label} syncs with your account — press 7 to sign in with your browser.`, "No account needed: tabs 1-2 (Here, What's New) work offline."],
|
|
176
176
|
width: W,
|
|
177
177
|
});
|
|
178
178
|
keys = GATE_KEYS;
|
package/src/tui/ui.js
CHANGED
|
@@ -614,11 +614,17 @@ export function ModelDetailBar({ title, health, model, refs = [], width, height
|
|
|
614
614
|
const W = Math.max(20, width);
|
|
615
615
|
const focused = refCursor >= 0;
|
|
616
616
|
const rt = model && model.retires_date ? relativeTime(model.retires_date) : null;
|
|
617
|
+
// A model retiring within 7 days earns a quiet ✦ on its detail header — a rare,
|
|
618
|
+
// earned "this one's hot" marker. Static (no animation); ASCII-safe (GLYPH.spark → *).
|
|
619
|
+
const retiresInDays = model && model.retires_date
|
|
620
|
+
? Math.round((new Date(model.retires_date).getTime() - Date.now()) / 86_400_000)
|
|
621
|
+
: null;
|
|
622
|
+
const imminent = retiresInDays !== null && retiresInDays >= 0 && retiresInDays <= 7;
|
|
617
623
|
const lines = [];
|
|
618
624
|
// separator rule
|
|
619
625
|
lines.push(h(Text, { key: "rule", color: C.BORDER }, "─".repeat(W)));
|
|
620
626
|
// header: health glyph + slug (strong) + health word, padded to W
|
|
621
|
-
const hg = `${healthGlyph(health)} `;
|
|
627
|
+
const hg = `${imminent ? `${GLYPH.spark} ` : ""}${healthGlyph(health)} `;
|
|
622
628
|
const hw = ` ${health}`;
|
|
623
629
|
const slugW = Math.max(8, W - hg.length - hw.length);
|
|
624
630
|
lines.push(
|
package/src/tui/views/alerts.js
CHANGED
|
@@ -135,7 +135,7 @@ export function AlertsView({ client, ui, active, width = 78, height = 14 }) {
|
|
|
135
135
|
body = h(StateLine, { kind: "loading", spin: SPINNER[tick % SPINNER.length], text: tab === 0 ? "loading rules…" : "loading channels…" });
|
|
136
136
|
} else if (tab === 0) {
|
|
137
137
|
if (!ruleList.length) {
|
|
138
|
-
body = h(EmptyCard, { title: "No alert rules", lines: ["
|
|
138
|
+
body = h(EmptyCard, { title: "No alert rules yet", lines: ["Stay ahead of your model timeline — a heads-up 90, 30, 7, and 1 day before anything you use is deprecated or retired.", "Press n to set the sensible default (your models · in-app + email · those lead times)."], width });
|
|
139
139
|
} else {
|
|
140
140
|
const curIdx = clampCursor(cursor, ruleList.length);
|
|
141
141
|
const fixed = 2 + 26 + 1 + 10 + 1; // glyph + name + gap + delivery + gap
|
|
@@ -95,8 +95,11 @@ export function InventoryView({ client, ui, dir = ".", active, width = 78, heigh
|
|
|
95
95
|
if (!usages.length)
|
|
96
96
|
return h(EmptyCard, {
|
|
97
97
|
icon: GLYPH.spark,
|
|
98
|
-
title: "
|
|
99
|
-
lines: [
|
|
98
|
+
title: "Let's find your AI models",
|
|
99
|
+
lines: [
|
|
100
|
+
"Press 4 Scan to auto-detect every model used in this repo.",
|
|
101
|
+
"Or press 5 Add to enter one by name — takes about 30 seconds.",
|
|
102
|
+
],
|
|
100
103
|
width,
|
|
101
104
|
});
|
|
102
105
|
|
package/src/tui/views/local.js
CHANGED
|
@@ -220,7 +220,15 @@ export function LocalView({ dir, ui, width = 78, height = 14, active = true, fre
|
|
|
220
220
|
Box,
|
|
221
221
|
{ flexDirection: "column" },
|
|
222
222
|
strip,
|
|
223
|
-
emptyDone
|
|
223
|
+
emptyDone
|
|
224
|
+
? h(
|
|
225
|
+
Text,
|
|
226
|
+
{ color: C.FG_DIM },
|
|
227
|
+
search.query
|
|
228
|
+
? ` No matches for "${search.query}" — try a broader term, or esc to clear.`
|
|
229
|
+
: " No AI model calls here yet — this folder looks clean.",
|
|
230
|
+
)
|
|
231
|
+
: null,
|
|
224
232
|
...rowNodes,
|
|
225
233
|
showingLine,
|
|
226
234
|
cur ? h(ModelDetailBar, { title: cur.slug, health: cur.health, model: cur.model, refs: cur.refs, width, height: panelH, refCursor: focus === "refs" ? clampCursor(refIdx, drefs.length) : -1, snippet }) : null,
|
package/src/tui/views/scan.js
CHANGED
|
@@ -240,7 +240,7 @@ export function ScanView({ client, dir, ui, active, width = 78, height = 14, fre
|
|
|
240
240
|
if (scan.phase === "error") return h(StateLine, { kind: "error", text: scan.error });
|
|
241
241
|
if (running && !items.length) return h(StateLine, { kind: "scanning", spin: SPINNER[tick % SPINNER.length], text: `scanning ${dir}…` });
|
|
242
242
|
if (!items.length)
|
|
243
|
-
return h(EmptyCard, { icon: GLYPH.spark, title: `No
|
|
243
|
+
return h(EmptyCard, { icon: GLYPH.spark, title: `No models found in ${path.basename(dir)} — yet`, lines: ["We looked through code, config, and prompt files for model ids.", "Press g to rescan, 1 Here to try another project, or 5 Add to enter one by hand."], width });
|
|
244
244
|
|
|
245
245
|
const L = layout(width);
|
|
246
246
|
const view = filtered.slice(nav.start, nav.start + pageSize);
|