md4ai 0.7.8 → 0.8.0

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.
@@ -1232,7 +1232,7 @@ var CURRENT_VERSION;
1232
1232
  var init_check_update = __esm({
1233
1233
  "dist/check-update.js"() {
1234
1234
  "use strict";
1235
- CURRENT_VERSION = true ? "0.7.8" : "0.0.0-dev";
1235
+ CURRENT_VERSION = true ? "0.8.0" : "0.0.0-dev";
1236
1236
  }
1237
1237
  });
1238
1238
 
@@ -1750,7 +1750,7 @@ function buildRows(configs, httpResults) {
1750
1750
  return rows;
1751
1751
  }
1752
1752
  function printTable(rows, deviceName) {
1753
- process.stdout.write("\x1B[2J\x1B[H");
1753
+ process.stdout.write("\x1B[3J\x1B[2J\x1B[H");
1754
1754
  console.log(chalk18.bold.cyan(`
1755
1755
  MCP Monitor \u2014 ${deviceName}`));
1756
1756
  console.log(chalk18.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
@@ -1798,14 +1798,8 @@ function printTable(rows, deviceName) {
1798
1798
  console.log(chalk18.yellow(" No MCP servers configured."));
1799
1799
  console.log(chalk18.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
1800
1800
  }
1801
- }
1802
- function timeAgo(dateStr) {
1803
- const diff = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1e3);
1804
- if (diff < 60)
1805
- return `${diff}s ago`;
1806
- if (diff < 3600)
1807
- return `${Math.floor(diff / 60)}m ago`;
1808
- return `${Math.floor(diff / 3600)}h ago`;
1801
+ console.log(chalk18.bgYellow.black.bold(" \u26A0 DO NOT CLOSE THIS WINDOW \u2014 it feeds live data to the dashboard \u26A0 "));
1802
+ console.log("");
1809
1803
  }
1810
1804
  function formatUptime(seconds) {
1811
1805
  if (seconds < 60)
@@ -1826,21 +1820,8 @@ async function mcpWatchCommand() {
1826
1820
  await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).lt("last_heartbeat", staleThreshold);
1827
1821
  const { data: existingWatchers } = await supabase.from("mcp_watchers").select("pid, tty, cli_version, started_at").eq("device_id", deviceId);
1828
1822
  if (existingWatchers && existingWatchers.length > 0) {
1829
- const { confirm: confirm2 } = await import("@inquirer/prompts");
1830
- console.log("");
1831
- console.log(chalk18.yellow(" Another watcher is already running on this device:"));
1832
- for (const w of existingWatchers) {
1833
- console.log(chalk18.dim(` PID ${w.pid} \xB7 ${w.tty ?? "unknown tty"} \xB7 v${w.cli_version} \xB7 started ${timeAgo(w.started_at)}`));
1834
- }
1835
1823
  console.log("");
1836
- const takeOver = await confirm2({
1837
- message: "Stop the existing watcher and start a new one?",
1838
- default: true
1839
- });
1840
- if (!takeOver) {
1841
- console.log(chalk18.dim("\nKeeping existing watcher. Exiting.\n"));
1842
- return;
1843
- }
1824
+ console.log(chalk18.yellow(` Replacing ${existingWatchers.length} existing watcher${existingWatchers.length !== 1 ? "s" : ""} on this device...`));
1844
1825
  for (const w of existingWatchers) {
1845
1826
  try {
1846
1827
  process.kill(w.pid, "SIGTERM");
@@ -1908,7 +1889,14 @@ async function mcpWatchCommand() {
1908
1889
  }
1909
1890
  await envCycle();
1910
1891
  const envInterval = setInterval(envCycle, ENV_POLL_INTERVAL_MS);
1911
- const interval = setInterval(cycle, POLL_INTERVAL_MS);
1892
+ async function rescanCycle() {
1893
+ await checkPendingRescans(supabase, deviceId, deviceName);
1894
+ }
1895
+ async function fullCycle() {
1896
+ await cycle();
1897
+ await rescanCycle();
1898
+ }
1899
+ const interval = setInterval(fullCycle, POLL_INTERVAL_MS);
1912
1900
  const shutdown = async () => {
1913
1901
  clearInterval(interval);
1914
1902
  clearInterval(envInterval);
@@ -1923,6 +1911,41 @@ async function mcpWatchCommand() {
1923
1911
  void shutdown();
1924
1912
  });
1925
1913
  }
1914
+ async function checkPendingRescans(supabase, deviceId, deviceName) {
1915
+ const { data: paths } = await supabase.from("device_paths").select("folder_id, path").eq("device_name", deviceName);
1916
+ if (!paths?.length)
1917
+ return;
1918
+ const folderIds = paths.map((p) => p.folder_id);
1919
+ const { data: folders } = await supabase.from("claude_folders").select("id, last_scanned, rescan_requested_at").in("id", folderIds).not("rescan_requested_at", "is", null);
1920
+ if (!folders?.length)
1921
+ return;
1922
+ for (const folder of folders) {
1923
+ const requested = new Date(folder.rescan_requested_at).getTime();
1924
+ const scanned = folder.last_scanned ? new Date(folder.last_scanned).getTime() : 0;
1925
+ if (requested <= scanned)
1926
+ continue;
1927
+ const dp = paths.find((p) => p.folder_id === folder.id);
1928
+ if (!dp?.path)
1929
+ continue;
1930
+ try {
1931
+ const result = await scanProject(dp.path);
1932
+ await supabase.from("claude_folders").update({
1933
+ graph_json: result.graph,
1934
+ orphans_json: result.orphans,
1935
+ skills_table_json: result.skills,
1936
+ stale_files_json: result.staleFiles,
1937
+ env_manifest_json: result.envManifest,
1938
+ last_scanned: result.scannedAt,
1939
+ data_hash: result.dataHash,
1940
+ rescan_requested_at: null
1941
+ }).eq("id", folder.id);
1942
+ await pushToolings(supabase, folder.id, result.toolings);
1943
+ await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", folder.id).eq("device_name", deviceName);
1944
+ } catch {
1945
+ await supabase.from("claude_folders").update({ rescan_requested_at: null }).eq("id", folder.id);
1946
+ }
1947
+ }
1948
+ }
1926
1949
  var POLL_INTERVAL_MS, ENV_POLL_INTERVAL_MS;
1927
1950
  var init_mcp_watch = __esm({
1928
1951
  "dist/commands/mcp-watch.js"() {
@@ -1933,6 +1956,8 @@ var init_mcp_watch = __esm({
1933
1956
  init_scan_processes();
1934
1957
  init_check_update();
1935
1958
  init_env_manifest_scanner();
1959
+ init_scanner();
1960
+ init_push_toolings();
1936
1961
  init_config();
1937
1962
  POLL_INTERVAL_MS = 3e4;
1938
1963
  ENV_POLL_INTERVAL_MS = 3e5;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md4ai",
3
- "version": "0.7.8",
3
+ "version": "0.8.0",
4
4
  "description": "CLI for MD4AI — scan Claude projects and sync to your dashboard",
5
5
  "type": "module",
6
6
  "bin": {