@modelstatus/cli 0.1.61 → 0.1.62
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/scan-stream.js +18 -1
- package/src/tui/views/local.js +13 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelstatus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.62",
|
|
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/scan-stream.js
CHANGED
|
@@ -230,5 +230,22 @@ export function useStreamingScan(dir, opts = {}) {
|
|
|
230
230
|
}, [dir, nonce]);
|
|
231
231
|
|
|
232
232
|
const reload = React.useCallback(() => { SCAN_CACHE.delete(dir); setNonce((n) => n + 1); }, [dir]);
|
|
233
|
-
|
|
233
|
+
|
|
234
|
+
/** Surgically transform the in-memory candidates (e.g. after `f` fix rewrites
|
|
235
|
+
* a model id on disk) and re-summarize — NO filesystem walk. Keeps the
|
|
236
|
+
* session + disk caches in sync so the edit survives tab switches/restarts.
|
|
237
|
+
* A full `reload()` stays the way to re-verify against the real tree. */
|
|
238
|
+
const applyEdit = React.useCallback((updater) => {
|
|
239
|
+
setState((s) => {
|
|
240
|
+
if (!s.snapshot) return s;
|
|
241
|
+
const next = updater(s.candidates || []);
|
|
242
|
+
const sum = summarize(s.snapshot, next);
|
|
243
|
+
const prev = SCAN_CACHE.get(dir) || {};
|
|
244
|
+
SCAN_CACHE.set(dir, { ...prev, snapshot: s.snapshot, candidates: next, scannedAt: s.scannedAt || Date.now(), fromCache: s.fromCache });
|
|
245
|
+
try { writeDiskScan(dir, next); } catch { /* best effort */ }
|
|
246
|
+
return { ...s, candidates: next, candidateCount: next.length, ...sum };
|
|
247
|
+
});
|
|
248
|
+
}, [dir]);
|
|
249
|
+
|
|
250
|
+
return { ...state, paused, reload, togglePause, applyEdit };
|
|
234
251
|
}
|
package/src/tui/views/local.js
CHANGED
|
@@ -27,7 +27,7 @@ export const meta = {
|
|
|
27
27
|
keys: [
|
|
28
28
|
{ k: "↑↓", label: "nav" },
|
|
29
29
|
{ k: "↵", label: "refs" },
|
|
30
|
-
{ k: "f", label: "fix
|
|
30
|
+
{ k: "f", label: "fix all" },
|
|
31
31
|
{ k: "u", label: "push → Inv" },
|
|
32
32
|
{ k: "g", label: "rescan" },
|
|
33
33
|
{ k: "/", label: "search" },
|
|
@@ -87,7 +87,7 @@ export function LocalView({ client, me, dir, ui, width = 78, height = 14, active
|
|
|
87
87
|
if (!cur?.model) return ui?.showToast?.("custom model — no registry replacement known", "#d97706");
|
|
88
88
|
if (cur.health === "ok") return ui?.showToast?.(`${cur.slug} is current — nothing to fix`, "#d97706");
|
|
89
89
|
if (!repl) return ui?.showToast?.("no replacement in the registry yet", "#d97706");
|
|
90
|
-
import("../../fix.js").then(({ planFixes, applyFixes }) => {
|
|
90
|
+
import("../../fix.js").then(({ planFixes, applyFixes, styleReplacement }) => {
|
|
91
91
|
const plans = planFixes(refs, repl);
|
|
92
92
|
if (!plans.length) return ui?.showToast?.("no rewritable file refs (integration-sourced?)", "#d97706");
|
|
93
93
|
ui?.askPrompt?.(`Rewrite ${what} → ${repl}? type y`, {
|
|
@@ -96,9 +96,18 @@ export function LocalView({ client, me, dir, ui, width = 78, height = 14, active
|
|
|
96
96
|
const res = applyFixes(dir, plans);
|
|
97
97
|
const files = new Set(res.applied.map((p) => p.file)).size;
|
|
98
98
|
if (res.applied.length) {
|
|
99
|
+
// Update the in-memory scan IN PLACE (no filesystem re-walk — on a big
|
|
100
|
+
// workspace a surprise full rescan reads as "what is it doing?!").
|
|
101
|
+
// Any candidate at a rewritten file:line whose string overlaps the
|
|
102
|
+
// applied rewrite re-styles to the replacement; g still re-verifies.
|
|
103
|
+
scan.applyEdit?.((cands) => cands.map((c) => {
|
|
104
|
+
const hit = res.applied.find((p) => p.file === c.source_path && p.line === c.source_line
|
|
105
|
+
&& (p.from === c.model_string || p.from.includes(c.model_string) || c.model_string.includes(p.from)));
|
|
106
|
+
if (!hit) return c;
|
|
107
|
+
const to = styleReplacement(c.model_string, repl);
|
|
108
|
+
return to ? { ...c, model_string: to } : c;
|
|
109
|
+
}));
|
|
99
110
|
ui?.showToast?.(`${GLYPH.check} rewrote ${res.applied.length} ref${res.applied.length === 1 ? "" : "s"} in ${files} file${files === 1 ? "" : "s"} → ${repl}${res.stale.length ? ` · ${res.stale.length} stale skipped` : ""}`);
|
|
100
|
-
justReloadedRef.current = true;
|
|
101
|
-
scan.reload(); // re-scan so the list reflects the rewritten files
|
|
102
111
|
} else {
|
|
103
112
|
ui?.showToast?.(res.stale.length ? "files changed since the scan — press g to rescan first" : "nothing rewritten", "#dc2626");
|
|
104
113
|
}
|