md4ai 0.9.3 → 0.9.5

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/index.bundled.js +599 -268
  2. package/package.json +1 -1
@@ -89,6 +89,13 @@ async function loadCredentials() {
89
89
  return null;
90
90
  }
91
91
  }
92
+ async function mergeCredentials(partial) {
93
+ await ensureConfigDir();
94
+ const existing = await loadCredentials();
95
+ const merged = { ...existing, ...partial };
96
+ await writeFile(credentialsPath, JSON.stringify(merged, null, 2), "utf-8");
97
+ await chmod(credentialsPath, 384);
98
+ }
92
99
  async function clearCredentials() {
93
100
  try {
94
101
  await writeFile(credentialsPath, "{}", "utf-8");
@@ -812,9 +819,9 @@ async function detectFromMcpSettings(projectRoot) {
812
819
  continue;
813
820
  seen.add(serverName);
814
821
  let version = null;
815
- const config = mcpServers[serverName];
816
- if (config?.command && typeof config.command === "string") {
817
- const args = config.args ?? [];
822
+ const config2 = mcpServers[serverName];
823
+ if (config2?.command && typeof config2.command === "string") {
824
+ const args = config2.args ?? [];
818
825
  for (const arg of args) {
819
826
  const versionMatch = arg.match(/@(\d+\.\d+[^\s]*?)$/);
820
827
  if (versionMatch) {
@@ -849,16 +856,110 @@ var init_tooling_detector = __esm({
849
856
  }
850
857
  });
851
858
 
859
+ // dist/vercel/auth.js
860
+ import { readFile as readFile5 } from "node:fs/promises";
861
+ import { join as join7 } from "node:path";
862
+ import { homedir as homedir5 } from "node:os";
863
+ async function resolveVercelToken() {
864
+ const creds = await loadCredentials();
865
+ if (creds?.vercelToken)
866
+ return creds.vercelToken;
867
+ const possiblePaths = [
868
+ join7(homedir5(), ".config", "com.vercel.cli", "auth.json"),
869
+ join7(homedir5(), ".local", "share", "com.vercel.cli", "auth.json")
870
+ ];
871
+ for (const authPath of possiblePaths) {
872
+ try {
873
+ const data = await readFile5(authPath, "utf-8");
874
+ const parsed = JSON.parse(data);
875
+ if (parsed.token)
876
+ return parsed.token;
877
+ } catch {
878
+ }
879
+ }
880
+ return null;
881
+ }
882
+ var init_auth2 = __esm({
883
+ "dist/vercel/auth.js"() {
884
+ "use strict";
885
+ init_config();
886
+ }
887
+ });
888
+
889
+ // dist/vercel/discover-projects.js
890
+ import { readFile as readFile6, glob } from "node:fs/promises";
891
+ import { join as join8, dirname as dirname2, relative } from "node:path";
892
+ async function discoverVercelProjects(projectRoot) {
893
+ const found = /* @__PURE__ */ new Map();
894
+ for await (const filePath of glob(join8(projectRoot, "**/.vercel/project.json"))) {
895
+ try {
896
+ const data = await readFile6(filePath, "utf-8");
897
+ const parsed = JSON.parse(data);
898
+ if (!parsed.projectId || !parsed.orgId)
899
+ continue;
900
+ const vercelDir = dirname2(filePath);
901
+ const parentDir = dirname2(vercelDir);
902
+ const relDir = relative(projectRoot, parentDir);
903
+ const label = parsed.projectName || (relDir === "" ? "root" : relDir.split("/").pop() || "unknown");
904
+ const existing = found.get(parsed.projectId);
905
+ if (!existing || relDir.length > 0) {
906
+ found.set(parsed.projectId, {
907
+ projectId: parsed.projectId,
908
+ orgId: parsed.orgId,
909
+ projectName: label
910
+ });
911
+ }
912
+ } catch {
913
+ }
914
+ }
915
+ return [...found.values()].sort((a, b) => a.projectName.localeCompare(b.projectName));
916
+ }
917
+ var init_discover_projects = __esm({
918
+ "dist/vercel/discover-projects.js"() {
919
+ "use strict";
920
+ }
921
+ });
922
+
923
+ // dist/vercel/fetch-env-vars.js
924
+ async function fetchVercelEnvVars(projectId, orgId, token) {
925
+ const url = new URL(`https://api.vercel.com/v9/projects/${projectId}/env`);
926
+ url.searchParams.set("teamId", orgId);
927
+ const res = await fetch(url.toString(), {
928
+ headers: { Authorization: `Bearer ${token}` }
929
+ });
930
+ if (!res.ok) {
931
+ const body = await res.text().catch(() => "");
932
+ throw new Error(`Vercel API ${res.status}: ${body.slice(0, 200)}`);
933
+ }
934
+ const data = await res.json();
935
+ const envs = data.envs ?? [];
936
+ return envs.map((e) => ({
937
+ key: e.key,
938
+ targets: e.target ?? ["production", "preview", "development"],
939
+ type: e.type ?? "plain",
940
+ inManifest: false
941
+ // populated later by the scanner
942
+ }));
943
+ }
944
+ var init_fetch_env_vars = __esm({
945
+ "dist/vercel/fetch-env-vars.js"() {
946
+ "use strict";
947
+ }
948
+ });
949
+
852
950
  // dist/scanner/env-manifest-scanner.js
853
- import { readFile as readFile5, glob } from "node:fs/promises";
854
- import { join as join7, relative } from "node:path";
951
+ import { readFile as readFile7, glob as glob2 } from "node:fs/promises";
952
+ import { execFile } from "node:child_process";
953
+ import { promisify } from "node:util";
954
+ import { join as join9, relative as relative2 } from "node:path";
855
955
  import { existsSync as existsSync5 } from "node:fs";
956
+ import chalk8 from "chalk";
856
957
  async function scanEnvManifest(projectRoot) {
857
- const manifestFullPath = join7(projectRoot, MANIFEST_PATH);
958
+ const manifestFullPath = join9(projectRoot, MANIFEST_PATH);
858
959
  if (!existsSync5(manifestFullPath)) {
859
960
  return null;
860
961
  }
861
- const manifestContent = await readFile5(manifestFullPath, "utf-8");
962
+ const manifestContent = await readFile7(manifestFullPath, "utf-8");
862
963
  const apps = parseApps(manifestContent);
863
964
  const variables = parseVariables(manifestContent);
864
965
  const localPresence = await checkLocalEnvFiles(projectRoot, apps);
@@ -870,6 +971,8 @@ async function scanEnvManifest(projectRoot) {
870
971
  }
871
972
  const workflowRefs = await parseWorkflowSecrets(projectRoot);
872
973
  const drift = computeDrift(variables, apps, localPresence, workflowRefs);
974
+ const vercelDiscovered = await checkVercelEnvVars(projectRoot, variables);
975
+ const githubRepoSlug = await checkGitHubSecrets(projectRoot, variables);
873
976
  return {
874
977
  manifestFound: true,
875
978
  manifestPath: MANIFEST_PATH,
@@ -877,9 +980,98 @@ async function scanEnvManifest(projectRoot) {
877
980
  variables,
878
981
  drift,
879
982
  workflowRefs,
983
+ vercelDiscovered: vercelDiscovered ?? void 0,
984
+ githubRepoSlug: githubRepoSlug ?? void 0,
880
985
  checkedAt: (/* @__PURE__ */ new Date()).toISOString()
881
986
  };
882
987
  }
988
+ async function checkVercelEnvVars(projectRoot, variables) {
989
+ const token = await resolveVercelToken();
990
+ if (!token) {
991
+ console.log(chalk8.dim(" Vercel checks skipped (no token configured)"));
992
+ return null;
993
+ }
994
+ const projects = await discoverVercelProjects(projectRoot);
995
+ if (projects.length === 0)
996
+ return null;
997
+ console.log(chalk8.dim(` Checking ${projects.length} Vercel project(s)...`));
998
+ const discovered = [];
999
+ const manifestVarNames = new Set(variables.map((v) => v.name));
1000
+ for (const proj of projects) {
1001
+ try {
1002
+ const vars = await fetchVercelEnvVars(proj.projectId, proj.orgId, token);
1003
+ for (const v of vars) {
1004
+ v.inManifest = manifestVarNames.has(v.key);
1005
+ }
1006
+ discovered.push({
1007
+ projectId: proj.projectId,
1008
+ projectName: proj.projectName,
1009
+ orgId: proj.orgId,
1010
+ vars
1011
+ });
1012
+ const vercelKeySet = new Set(vars.map((v) => v.key));
1013
+ for (const mv of variables) {
1014
+ if (!mv.vercelStatus)
1015
+ mv.vercelStatus = {};
1016
+ mv.vercelStatus[proj.projectName] = vercelKeySet.has(mv.name) ? "present" : "missing";
1017
+ }
1018
+ console.log(chalk8.dim(` ${proj.projectName}: ${vars.length} var(s)`));
1019
+ } catch (err) {
1020
+ console.log(chalk8.yellow(` ${proj.projectName}: ${err instanceof Error ? err.message : "API error"}`));
1021
+ }
1022
+ }
1023
+ return discovered.length > 0 ? discovered : null;
1024
+ }
1025
+ async function detectGitHubRepoSlug(projectRoot) {
1026
+ try {
1027
+ const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], { cwd: projectRoot });
1028
+ const url = stdout.trim();
1029
+ const sshMatch = url.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
1030
+ if (sshMatch)
1031
+ return sshMatch[1];
1032
+ return null;
1033
+ } catch {
1034
+ return null;
1035
+ }
1036
+ }
1037
+ async function listGitHubSecretNames(repoSlug) {
1038
+ try {
1039
+ const { stdout } = await execFileAsync("gh", [
1040
+ "secret",
1041
+ "list",
1042
+ "--repo",
1043
+ repoSlug,
1044
+ "--json",
1045
+ "name",
1046
+ "-q",
1047
+ ".[].name"
1048
+ ], { timeout: 15e3 });
1049
+ const names = stdout.trim().split("\n").filter(Boolean);
1050
+ return names;
1051
+ } catch {
1052
+ return null;
1053
+ }
1054
+ }
1055
+ async function checkGitHubSecrets(projectRoot, variables) {
1056
+ const slug = await detectGitHubRepoSlug(projectRoot);
1057
+ if (!slug) {
1058
+ console.log(chalk8.dim(" GitHub checks skipped (no GitHub remote detected)"));
1059
+ return null;
1060
+ }
1061
+ const secretNames = await listGitHubSecretNames(slug);
1062
+ if (!secretNames) {
1063
+ console.log(chalk8.dim(" GitHub checks skipped (gh CLI not available or not authenticated)"));
1064
+ return slug;
1065
+ }
1066
+ const secretSet = new Set(secretNames);
1067
+ console.log(chalk8.dim(` GitHub secrets: ${secretNames.length} found in ${slug}`));
1068
+ for (const v of variables) {
1069
+ if (v.requiredIn.github) {
1070
+ v.githubStatus = secretSet.has(v.name) ? "present" : "missing";
1071
+ }
1072
+ }
1073
+ return slug;
1074
+ }
883
1075
  function parseApps(content) {
884
1076
  const apps = [];
885
1077
  const appsSection = extractSection(content, "Apps");
@@ -959,10 +1151,10 @@ function extractSection(content, heading) {
959
1151
  async function checkLocalEnvFiles(projectRoot, apps) {
960
1152
  const result = /* @__PURE__ */ new Map();
961
1153
  for (const app of apps) {
962
- const envPath = join7(projectRoot, app.envFilePath);
1154
+ const envPath = join9(projectRoot, app.envFilePath);
963
1155
  const varNames = /* @__PURE__ */ new Set();
964
1156
  if (existsSync5(envPath)) {
965
- const content = await readFile5(envPath, "utf-8");
1157
+ const content = await readFile7(envPath, "utf-8");
966
1158
  for (const line of content.split("\n")) {
967
1159
  const m = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=/);
968
1160
  if (m) {
@@ -975,14 +1167,14 @@ async function checkLocalEnvFiles(projectRoot, apps) {
975
1167
  return result;
976
1168
  }
977
1169
  async function parseWorkflowSecrets(projectRoot) {
978
- const workflowsDir = join7(projectRoot, ".github", "workflows");
1170
+ const workflowsDir = join9(projectRoot, ".github", "workflows");
979
1171
  if (!existsSync5(workflowsDir))
980
1172
  return [];
981
1173
  const refs = [];
982
1174
  const secretRe = /\$\{\{\s*secrets\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
983
1175
  for (const ext of ["*.yml", "*.yaml"]) {
984
- for await (const filePath of glob(join7(workflowsDir, ext))) {
985
- const content = await readFile5(filePath, "utf-8");
1176
+ for await (const filePath of glob2(join9(workflowsDir, ext))) {
1177
+ const content = await readFile7(filePath, "utf-8");
986
1178
  const secrets = /* @__PURE__ */ new Set();
987
1179
  let m;
988
1180
  while ((m = secretRe.exec(content)) !== null) {
@@ -991,7 +1183,7 @@ async function parseWorkflowSecrets(projectRoot) {
991
1183
  secretRe.lastIndex = 0;
992
1184
  if (secrets.size > 0) {
993
1185
  refs.push({
994
- workflowFile: relative(projectRoot, filePath),
1186
+ workflowFile: relative2(projectRoot, filePath),
995
1187
  secretsUsed: [...secrets].sort()
996
1188
  });
997
1189
  }
@@ -1045,26 +1237,30 @@ function computeDrift(variables, apps, localPresence, workflowRefs) {
1045
1237
  workflowBrokenRefs: workflowBrokenRefs.sort()
1046
1238
  };
1047
1239
  }
1048
- var MANIFEST_PATH;
1240
+ var execFileAsync, MANIFEST_PATH;
1049
1241
  var init_env_manifest_scanner = __esm({
1050
1242
  "dist/scanner/env-manifest-scanner.js"() {
1051
1243
  "use strict";
1244
+ init_auth2();
1245
+ init_discover_projects();
1246
+ init_fetch_env_vars();
1247
+ execFileAsync = promisify(execFile);
1052
1248
  MANIFEST_PATH = "docs/reference/env-manifest.md";
1053
1249
  }
1054
1250
  });
1055
1251
 
1056
1252
  // dist/scanner/index.js
1057
1253
  import { readdir as readdir2 } from "node:fs/promises";
1058
- import { join as join8, relative as relative2 } from "node:path";
1254
+ import { join as join10, relative as relative3 } from "node:path";
1059
1255
  import { existsSync as existsSync6 } from "node:fs";
1060
- import { homedir as homedir5 } from "node:os";
1256
+ import { homedir as homedir6 } from "node:os";
1061
1257
  import { createHash } from "node:crypto";
1062
1258
  async function scanProject(projectRoot) {
1063
1259
  const allFiles = await discoverFiles(projectRoot);
1064
1260
  const rootFiles = identifyRoots(allFiles, projectRoot);
1065
1261
  const allRefs = [];
1066
1262
  for (const file of allFiles) {
1067
- const fullPath = file.startsWith("/") ? file : join8(projectRoot, file);
1263
+ const fullPath = file.startsWith("/") ? file : join10(projectRoot, file);
1068
1264
  try {
1069
1265
  const refs = await parseFileReferences(fullPath, projectRoot);
1070
1266
  allRefs.push(...refs);
@@ -1092,17 +1288,17 @@ async function scanProject(projectRoot) {
1092
1288
  }
1093
1289
  async function discoverFiles(projectRoot) {
1094
1290
  const files = [];
1095
- const claudeDir = join8(projectRoot, ".claude");
1291
+ const claudeDir = join10(projectRoot, ".claude");
1096
1292
  if (existsSync6(claudeDir)) {
1097
1293
  await walkDir(claudeDir, projectRoot, files);
1098
1294
  }
1099
- if (existsSync6(join8(projectRoot, "CLAUDE.md"))) {
1295
+ if (existsSync6(join10(projectRoot, "CLAUDE.md"))) {
1100
1296
  files.push("CLAUDE.md");
1101
1297
  }
1102
- if (existsSync6(join8(projectRoot, "skills.md"))) {
1298
+ if (existsSync6(join10(projectRoot, "skills.md"))) {
1103
1299
  files.push("skills.md");
1104
1300
  }
1105
- const plansDir = join8(projectRoot, "docs", "plans");
1301
+ const plansDir = join10(projectRoot, "docs", "plans");
1106
1302
  if (existsSync6(plansDir)) {
1107
1303
  await walkDir(plansDir, projectRoot, files);
1108
1304
  }
@@ -1111,13 +1307,13 @@ async function discoverFiles(projectRoot) {
1111
1307
  async function walkDir(dir, projectRoot, files) {
1112
1308
  const entries = await readdir2(dir, { withFileTypes: true });
1113
1309
  for (const entry of entries) {
1114
- const fullPath = join8(dir, entry.name);
1310
+ const fullPath = join10(dir, entry.name);
1115
1311
  if (entry.isDirectory()) {
1116
1312
  if (["node_modules", ".git", ".turbo", "cache", "session-env"].includes(entry.name))
1117
1313
  continue;
1118
1314
  await walkDir(fullPath, projectRoot, files);
1119
1315
  } else {
1120
- const relPath = relative2(projectRoot, fullPath);
1316
+ const relPath = relative3(projectRoot, fullPath);
1121
1317
  files.push(relPath);
1122
1318
  }
1123
1319
  }
@@ -1130,7 +1326,7 @@ function identifyRoots(allFiles, projectRoot) {
1130
1326
  }
1131
1327
  }
1132
1328
  for (const globalFile of GLOBAL_ROOT_FILES) {
1133
- const expanded = globalFile.replace("~", homedir5());
1329
+ const expanded = globalFile.replace("~", homedir6());
1134
1330
  if (existsSync6(expanded)) {
1135
1331
  roots.push(globalFile);
1136
1332
  }
@@ -1138,8 +1334,8 @@ function identifyRoots(allFiles, projectRoot) {
1138
1334
  return roots;
1139
1335
  }
1140
1336
  async function readClaudeConfigFiles(projectRoot) {
1141
- const { readFile: readFile10, stat, glob: glob2 } = await import("node:fs/promises");
1142
- const { join: join14, relative: relative4 } = await import("node:path");
1337
+ const { readFile: readFile12, stat, glob: glob3 } = await import("node:fs/promises");
1338
+ const { join: join16, relative: relative5 } = await import("node:path");
1143
1339
  const { existsSync: existsSync13 } = await import("node:fs");
1144
1340
  const configPatterns = [
1145
1341
  "CLAUDE.md",
@@ -1149,22 +1345,43 @@ async function readClaudeConfigFiles(projectRoot) {
1149
1345
  ".claude/skills/*.md"
1150
1346
  ];
1151
1347
  const files = [];
1348
+ const seen = /* @__PURE__ */ new Set();
1349
+ async function addFile(fullPath) {
1350
+ const relPath = relative5(projectRoot, fullPath);
1351
+ if (seen.has(relPath))
1352
+ return;
1353
+ seen.add(relPath);
1354
+ try {
1355
+ const content = await readFile12(fullPath, "utf-8");
1356
+ const fileStat = await stat(fullPath);
1357
+ const lastMod = getGitLastModified(fullPath, projectRoot);
1358
+ files.push({ filePath: relPath, content, sizeBytes: fileStat.size, lastModified: lastMod });
1359
+ } catch {
1360
+ }
1361
+ }
1152
1362
  for (const pattern of configPatterns) {
1153
- for await (const fullPath of glob2(join14(projectRoot, pattern))) {
1363
+ for await (const fullPath of glob3(join16(projectRoot, pattern))) {
1154
1364
  if (!existsSync13(fullPath))
1155
1365
  continue;
1156
- try {
1157
- const content = await readFile10(fullPath, "utf-8");
1158
- const fileStat = await stat(fullPath);
1159
- const lastMod = getGitLastModified(fullPath, projectRoot);
1160
- files.push({
1161
- filePath: relative4(projectRoot, fullPath),
1162
- content,
1163
- sizeBytes: fileStat.size,
1164
- lastModified: lastMod
1165
- });
1166
- } catch {
1366
+ await addFile(fullPath);
1367
+ }
1368
+ }
1369
+ const pkgPath = join16(projectRoot, "package.json");
1370
+ if (existsSync13(pkgPath)) {
1371
+ try {
1372
+ const pkgContent = await readFile12(pkgPath, "utf-8");
1373
+ const pkg = JSON.parse(pkgContent);
1374
+ const preflightCmd = pkg.scripts?.preflight;
1375
+ if (preflightCmd) {
1376
+ const match = preflightCmd.match(/(?:bash\s+|sh\s+|\.\/)?(\S+\.(?:sh|bash|ts|js|mjs))/);
1377
+ if (match) {
1378
+ const scriptPath = join16(projectRoot, match[1]);
1379
+ if (existsSync13(scriptPath)) {
1380
+ await addFile(scriptPath);
1381
+ }
1382
+ }
1167
1383
  }
1384
+ } catch {
1168
1385
  }
1169
1386
  }
1170
1387
  return files;
@@ -1173,7 +1390,7 @@ function detectStaleFiles(allFiles, projectRoot) {
1173
1390
  const stale = [];
1174
1391
  const now = Date.now();
1175
1392
  for (const file of allFiles) {
1176
- const fullPath = join8(projectRoot, file);
1393
+ const fullPath = join10(projectRoot, file);
1177
1394
  const lastMod = getGitLastModified(fullPath, projectRoot);
1178
1395
  if (lastMod) {
1179
1396
  const days = Math.floor((now - new Date(lastMod).getTime()) / (1e3 * 60 * 60 * 24));
@@ -1315,7 +1532,7 @@ var init_html_generator = __esm({
1315
1532
  });
1316
1533
 
1317
1534
  // dist/check-update.js
1318
- import chalk8 from "chalk";
1535
+ import chalk9 from "chalk";
1319
1536
  async function fetchLatest() {
1320
1537
  try {
1321
1538
  const controller = new AbortController();
@@ -1334,12 +1551,12 @@ async function fetchLatest() {
1334
1551
  }
1335
1552
  function printUpdateBanner(latest) {
1336
1553
  console.log("");
1337
- console.log(chalk8.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
1338
- console.log(chalk8.yellow("\u2502") + chalk8.bold(" Update available! ") + chalk8.dim(`${CURRENT_VERSION}`) + chalk8.white(" \u2192 ") + chalk8.green.bold(`${latest}`) + " " + chalk8.yellow("\u2502"));
1339
- console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
1340
- console.log(chalk8.yellow("\u2502") + " Run: " + chalk8.cyan("md4ai update") + " " + chalk8.yellow("\u2502"));
1341
- console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
1342
- console.log(chalk8.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
1554
+ console.log(chalk9.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
1555
+ console.log(chalk9.yellow("\u2502") + chalk9.bold(" Update available! ") + chalk9.dim(`${CURRENT_VERSION}`) + chalk9.white(" \u2192 ") + chalk9.green.bold(`${latest}`) + " " + chalk9.yellow("\u2502"));
1556
+ console.log(chalk9.yellow("\u2502") + " " + chalk9.yellow("\u2502"));
1557
+ console.log(chalk9.yellow("\u2502") + " Run: " + chalk9.cyan("md4ai update") + " " + chalk9.yellow("\u2502"));
1558
+ console.log(chalk9.yellow("\u2502") + " " + chalk9.yellow("\u2502"));
1559
+ console.log(chalk9.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
1343
1560
  console.log("");
1344
1561
  }
1345
1562
  async function checkForUpdate() {
@@ -1349,7 +1566,7 @@ async function checkForUpdate() {
1349
1566
  if (latest !== CURRENT_VERSION && isNewer(latest, CURRENT_VERSION)) {
1350
1567
  printUpdateBanner(latest);
1351
1568
  } else {
1352
- console.log(chalk8.green(`md4ai v${CURRENT_VERSION} \u2014 you're on the latest version.`));
1569
+ console.log(chalk9.green(`md4ai v${CURRENT_VERSION} \u2014 you're on the latest version.`));
1353
1570
  }
1354
1571
  }
1355
1572
  async function autoCheckForUpdate() {
@@ -1378,7 +1595,7 @@ var CURRENT_VERSION;
1378
1595
  var init_check_update = __esm({
1379
1596
  "dist/check-update.js"() {
1380
1597
  "use strict";
1381
- CURRENT_VERSION = true ? "0.9.3" : "0.0.0-dev";
1598
+ CURRENT_VERSION = true ? "0.9.5" : "0.0.0-dev";
1382
1599
  }
1383
1600
  });
1384
1601
 
@@ -1406,6 +1623,87 @@ var init_push_toolings = __esm({
1406
1623
  }
1407
1624
  });
1408
1625
 
1626
+ // dist/commands/push-health-results.js
1627
+ import chalk10 from "chalk";
1628
+ async function pushHealthResults(supabase, folderId, manifest) {
1629
+ const { data: checks } = await supabase.from("env_health_checks").select("id, check_type, check_config").eq("folder_id", folderId).eq("enabled", true);
1630
+ if (!checks?.length)
1631
+ return;
1632
+ const results = [];
1633
+ const ranAt = manifest.checkedAt;
1634
+ for (const chk of checks) {
1635
+ const config2 = chk.check_config;
1636
+ if (chk.check_type === "env_var_set") {
1637
+ const app = config2.app;
1638
+ const variable = config2.variable;
1639
+ const v = manifest.variables.find((mv) => mv.name === variable);
1640
+ if (v) {
1641
+ const localStatus = v.localStatus[app];
1642
+ results.push({
1643
+ check_id: chk.id,
1644
+ status: localStatus === "present" ? "pass" : "fail",
1645
+ message: localStatus === "present" ? "Set" : "Missing",
1646
+ ran_at: ranAt
1647
+ });
1648
+ }
1649
+ } else if (chk.check_type === "file_exists") {
1650
+ results.push({
1651
+ check_id: chk.id,
1652
+ status: "pass",
1653
+ message: "Found",
1654
+ ran_at: ranAt
1655
+ });
1656
+ } else if (chk.check_type === "gh_secret") {
1657
+ const variable = config2.secret;
1658
+ const v = manifest.variables.find((mv) => mv.name === variable);
1659
+ const ghStatus = v?.githubStatus;
1660
+ results.push({
1661
+ check_id: chk.id,
1662
+ status: ghStatus === "present" ? "pass" : ghStatus === "missing" ? "fail" : "warn",
1663
+ message: ghStatus === "present" ? "Exists" : ghStatus === "missing" ? "Missing" : "Unknown",
1664
+ ran_at: ranAt
1665
+ });
1666
+ } else if (chk.check_type === "vercel_env") {
1667
+ const variable = config2.variable;
1668
+ const projectName = config2.projectName;
1669
+ const target = config2.target;
1670
+ const proj = manifest.vercelDiscovered?.find((p) => p.projectName === projectName);
1671
+ if (proj) {
1672
+ const vercelVar = proj.vars.find((v) => v.key === variable);
1673
+ if (vercelVar) {
1674
+ const hasTarget = vercelVar.targets.includes(target);
1675
+ results.push({
1676
+ check_id: chk.id,
1677
+ status: hasTarget ? "pass" : "warn",
1678
+ message: hasTarget ? `Present (${target})` : `Present but not in ${target}`,
1679
+ ran_at: ranAt
1680
+ });
1681
+ } else {
1682
+ results.push({
1683
+ check_id: chk.id,
1684
+ status: "fail",
1685
+ message: `Not found in ${projectName}`,
1686
+ ran_at: ranAt
1687
+ });
1688
+ }
1689
+ }
1690
+ }
1691
+ }
1692
+ if (results.length > 0) {
1693
+ const { error } = await supabase.from("env_health_results").insert(results);
1694
+ if (error) {
1695
+ console.error(chalk10.yellow(`Health results warning: ${error.message}`));
1696
+ } else {
1697
+ console.log(chalk10.green(` Updated ${results.length} health check result(s).`));
1698
+ }
1699
+ }
1700
+ }
1701
+ var init_push_health_results = __esm({
1702
+ "dist/commands/push-health-results.js"() {
1703
+ "use strict";
1704
+ }
1705
+ });
1706
+
1409
1707
  // dist/device-utils.js
1410
1708
  import { hostname as hostname2, platform as platform2 } from "node:os";
1411
1709
  function detectOs2() {
@@ -1452,16 +1750,16 @@ __export(map_exports, {
1452
1750
  import { resolve as resolve3, basename } from "node:path";
1453
1751
  import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
1454
1752
  import { existsSync as existsSync7 } from "node:fs";
1455
- import chalk9 from "chalk";
1753
+ import chalk11 from "chalk";
1456
1754
  import { select as select3, input as input5, confirm as confirm2 } from "@inquirer/prompts";
1457
1755
  async function mapCommand(path, options) {
1458
1756
  await checkForUpdate();
1459
1757
  const projectRoot = resolve3(path ?? process.cwd());
1460
1758
  if (!existsSync7(projectRoot)) {
1461
- console.error(chalk9.red(`Path not found: ${projectRoot}`));
1759
+ console.error(chalk11.red(`Path not found: ${projectRoot}`));
1462
1760
  process.exit(1);
1463
1761
  }
1464
- console.log(chalk9.blue(`Scanning: ${projectRoot}
1762
+ console.log(chalk11.blue(`Scanning: ${projectRoot}
1465
1763
  `));
1466
1764
  const result = await scanProject(projectRoot);
1467
1765
  console.log(` Files found: ${result.graph.nodes.length}`);
@@ -1479,7 +1777,7 @@ async function mapCommand(path, options) {
1479
1777
  const htmlPath = resolve3(outputDir, "index.html");
1480
1778
  const html = generateOfflineHtml(result, projectRoot);
1481
1779
  await writeFile2(htmlPath, html, "utf-8");
1482
- console.log(chalk9.green(`
1780
+ console.log(chalk11.green(`
1483
1781
  Local preview: ${htmlPath}`));
1484
1782
  if (!options.offline) {
1485
1783
  try {
@@ -1488,9 +1786,9 @@ Local preview: ${htmlPath}`));
1488
1786
  if (devicePaths?.length) {
1489
1787
  const { folder_id, device_name } = devicePaths[0];
1490
1788
  const { data: proposedFiles } = await supabase.from("folder_files").select("id, file_path, proposed_at").eq("folder_id", folder_id).eq("proposed_for_deletion", true);
1491
- if (proposedFiles?.length) {
1789
+ if (proposedFiles?.length && process.stdin.isTTY) {
1492
1790
  const { checkbox } = await import("@inquirer/prompts");
1493
- console.log(chalk9.yellow(`
1791
+ console.log(chalk11.yellow(`
1494
1792
  ${proposedFiles.length} file(s) proposed for deletion:
1495
1793
  `));
1496
1794
  const toDelete = await checkbox({
@@ -1506,9 +1804,9 @@ ${proposedFiles.length} file(s) proposed for deletion:
1506
1804
  const { unlink } = await import("node:fs/promises");
1507
1805
  await unlink(fullPath);
1508
1806
  await supabase.from("folder_files").delete().eq("id", file.id);
1509
- console.log(chalk9.green(` Deleted: ${file.file_path}`));
1807
+ console.log(chalk11.green(` Deleted: ${file.file_path}`));
1510
1808
  } catch (err) {
1511
- console.error(chalk9.red(` Failed to delete ${file.file_path}: ${err}`));
1809
+ console.error(chalk11.red(` Failed to delete ${file.file_path}: ${err}`));
1512
1810
  }
1513
1811
  }
1514
1812
  const keptIds = proposedFiles.filter((f) => !toDelete.some((d) => d.id === f.id)).map((f) => f.id);
@@ -1516,9 +1814,11 @@ ${proposedFiles.length} file(s) proposed for deletion:
1516
1814
  for (const id of keptIds) {
1517
1815
  await supabase.from("folder_files").update({ proposed_for_deletion: false, proposed_at: null, proposed_by: null }).eq("id", id);
1518
1816
  }
1519
- console.log(chalk9.cyan(` Kept ${keptIds.length} file(s) \u2014 proposals cleared.`));
1817
+ console.log(chalk11.cyan(` Kept ${keptIds.length} file(s) \u2014 proposals cleared.`));
1520
1818
  }
1521
1819
  console.log("");
1820
+ } else if (proposedFiles?.length) {
1821
+ console.log(chalk11.dim(` ${proposedFiles.length} file(s) proposed for deletion \u2014 skipped (non-interactive mode).`));
1522
1822
  }
1523
1823
  const { error } = await supabase.from("claude_folders").update({
1524
1824
  graph_json: result.graph,
@@ -1530,17 +1830,20 @@ ${proposedFiles.length} file(s) proposed for deletion:
1530
1830
  data_hash: result.dataHash
1531
1831
  }).eq("id", folder_id);
1532
1832
  if (error) {
1533
- console.error(chalk9.yellow(`Sync warning: ${error.message}`));
1833
+ console.error(chalk11.yellow(`Sync warning: ${error.message}`));
1534
1834
  } else {
1535
1835
  await pushToolings(supabase, folder_id, result.toolings);
1836
+ if (result.envManifest) {
1837
+ await pushHealthResults(supabase, folder_id, result.envManifest);
1838
+ }
1536
1839
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", folder_id).eq("device_name", device_name);
1537
1840
  await saveState({
1538
1841
  lastFolderId: folder_id,
1539
1842
  lastDeviceName: device_name,
1540
1843
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
1541
1844
  });
1542
- console.log(chalk9.green("Synced to Supabase."));
1543
- console.log(chalk9.cyan(`
1845
+ console.log(chalk11.green("Synced to Supabase."));
1846
+ console.log(chalk11.cyan(`
1544
1847
  https://www.md4ai.com/project/${folder_id}
1545
1848
  `));
1546
1849
  const configFiles = await readClaudeConfigFiles(projectRoot);
@@ -1554,14 +1857,18 @@ ${proposedFiles.length} file(s) proposed for deletion:
1554
1857
  last_modified: file.lastModified
1555
1858
  }, { onConflict: "folder_id,file_path" });
1556
1859
  }
1557
- console.log(chalk9.green(` Uploaded ${configFiles.length} config file(s).`));
1860
+ console.log(chalk11.green(` Uploaded ${configFiles.length} config file(s).`));
1558
1861
  }
1559
1862
  }
1560
1863
  } else {
1561
- console.log(chalk9.yellow("\nThis folder is not linked to a project on your dashboard."));
1864
+ console.log(chalk11.yellow("\nThis folder is not linked to a project on your dashboard."));
1865
+ if (!process.stdin.isTTY) {
1866
+ console.log(chalk11.dim("Non-interactive mode \u2014 skipping link prompt."));
1867
+ return;
1868
+ }
1562
1869
  const shouldLink = await confirm2({ message: "Would you like to link it now?" });
1563
1870
  if (!shouldLink) {
1564
- console.log(chalk9.dim("Skipped \u2014 local preview still generated."));
1871
+ console.log(chalk11.dim("Skipped \u2014 local preview still generated."));
1565
1872
  } else {
1566
1873
  const { supabase: sb, userId } = await getAuthenticatedClient();
1567
1874
  const { data: folders } = await sb.from("claude_folders").select("id, name").order("name");
@@ -1581,11 +1888,11 @@ ${proposedFiles.length} file(s) proposed for deletion:
1581
1888
  });
1582
1889
  const { data: newFolder, error: createErr } = await sb.from("claude_folders").insert({ user_id: userId, name: projectName }).select("id").single();
1583
1890
  if (createErr || !newFolder) {
1584
- console.error(chalk9.red(`Failed to create project: ${createErr?.message}`));
1891
+ console.error(chalk11.red(`Failed to create project: ${createErr?.message}`));
1585
1892
  return;
1586
1893
  }
1587
1894
  folderId = newFolder.id;
1588
- console.log(chalk9.green(`Created project "${projectName}".`));
1895
+ console.log(chalk11.green(`Created project "${projectName}".`));
1589
1896
  } else {
1590
1897
  folderId = chosen;
1591
1898
  }
@@ -1614,6 +1921,9 @@ ${proposedFiles.length} file(s) proposed for deletion:
1614
1921
  data_hash: result.dataHash
1615
1922
  }).eq("id", folderId);
1616
1923
  await pushToolings(sb, folderId, result.toolings);
1924
+ if (result.envManifest) {
1925
+ await pushHealthResults(sb, folderId, result.envManifest);
1926
+ }
1617
1927
  const configFiles = await readClaudeConfigFiles(projectRoot);
1618
1928
  for (const file of configFiles) {
1619
1929
  await sb.from("folder_files").upsert({
@@ -1629,14 +1939,14 @@ ${proposedFiles.length} file(s) proposed for deletion:
1629
1939
  lastDeviceName: deviceName,
1630
1940
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
1631
1941
  });
1632
- console.log(chalk9.green("\nLinked and synced."));
1633
- console.log(chalk9.cyan(`
1942
+ console.log(chalk11.green("\nLinked and synced."));
1943
+ console.log(chalk11.cyan(`
1634
1944
  https://www.md4ai.com/project/${folderId}
1635
1945
  `));
1636
1946
  }
1637
1947
  }
1638
1948
  } catch {
1639
- console.log(chalk9.yellow("Not logged in \u2014 local preview only."));
1949
+ console.log(chalk11.yellow("Not logged in \u2014 local preview only."));
1640
1950
  }
1641
1951
  }
1642
1952
  }
@@ -1649,6 +1959,7 @@ var init_map = __esm({
1649
1959
  init_html_generator();
1650
1960
  init_check_update();
1651
1961
  init_push_toolings();
1962
+ init_push_health_results();
1652
1963
  init_device_utils();
1653
1964
  }
1654
1965
  });
@@ -1658,20 +1969,20 @@ var sync_exports = {};
1658
1969
  __export(sync_exports, {
1659
1970
  syncCommand: () => syncCommand
1660
1971
  });
1661
- import chalk12 from "chalk";
1972
+ import chalk14 from "chalk";
1662
1973
  async function syncCommand(options) {
1663
1974
  const { supabase } = await getAuthenticatedClient();
1664
1975
  if (options.all) {
1665
1976
  const { data: devices, error } = await supabase.from("device_paths").select("folder_id, device_name, path");
1666
1977
  if (error || !devices?.length) {
1667
- console.error(chalk12.red("No devices found."));
1978
+ console.error(chalk14.red("No devices found."));
1668
1979
  process.exit(1);
1669
1980
  }
1670
1981
  for (const device of devices) {
1671
- console.log(chalk12.blue(`Syncing: ${device.path}`));
1982
+ console.log(chalk14.blue(`Syncing: ${device.path}`));
1672
1983
  const { data: proposedAll } = await supabase.from("folder_files").select("file_path").eq("folder_id", device.folder_id).eq("proposed_for_deletion", true);
1673
1984
  if (proposedAll?.length) {
1674
- console.log(chalk12.yellow(` ${proposedAll.length} file(s) proposed for deletion \u2014 run \`md4ai scan\` to review.`));
1985
+ console.log(chalk14.yellow(` ${proposedAll.length} file(s) proposed for deletion \u2014 run \`md4ai scan\` to review.`));
1675
1986
  }
1676
1987
  try {
1677
1988
  const result = await scanProject(device.path);
@@ -1686,28 +1997,28 @@ async function syncCommand(options) {
1686
1997
  }).eq("id", device.folder_id);
1687
1998
  await pushToolings(supabase, device.folder_id, result.toolings);
1688
1999
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", device.folder_id).eq("device_name", device.device_name);
1689
- console.log(chalk12.green(` Done: ${device.device_name}`));
2000
+ console.log(chalk14.green(` Done: ${device.device_name}`));
1690
2001
  } catch (err) {
1691
- console.error(chalk12.red(` Failed: ${device.path}: ${err}`));
2002
+ console.error(chalk14.red(` Failed: ${device.path}: ${err}`));
1692
2003
  }
1693
2004
  }
1694
2005
  await saveState({ lastSyncAt: (/* @__PURE__ */ new Date()).toISOString() });
1695
- console.log(chalk12.green("\nAll devices synced."));
2006
+ console.log(chalk14.green("\nAll devices synced."));
1696
2007
  } else {
1697
2008
  const state = await loadState();
1698
2009
  if (!state.lastFolderId) {
1699
- console.error(chalk12.yellow("No recent sync. Use: md4ai sync --all, or md4ai scan <path> first."));
2010
+ console.error(chalk14.yellow("No recent sync. Use: md4ai sync --all, or md4ai scan <path> first."));
1700
2011
  process.exit(1);
1701
2012
  }
1702
2013
  const { data: device } = await supabase.from("device_paths").select("folder_id, device_name, path").eq("folder_id", state.lastFolderId).eq("device_name", state.lastDeviceName).single();
1703
2014
  if (!device) {
1704
- console.error(chalk12.red("Could not find last synced device/folder."));
2015
+ console.error(chalk14.red("Could not find last synced device/folder."));
1705
2016
  process.exit(1);
1706
2017
  }
1707
- console.log(chalk12.blue(`Syncing: ${device.path}`));
2018
+ console.log(chalk14.blue(`Syncing: ${device.path}`));
1708
2019
  const { data: proposedSingle } = await supabase.from("folder_files").select("file_path").eq("folder_id", device.folder_id).eq("proposed_for_deletion", true);
1709
2020
  if (proposedSingle?.length) {
1710
- console.log(chalk12.yellow(` ${proposedSingle.length} file(s) proposed for deletion \u2014 run \`md4ai scan\` to review.`));
2021
+ console.log(chalk14.yellow(` ${proposedSingle.length} file(s) proposed for deletion \u2014 run \`md4ai scan\` to review.`));
1711
2022
  }
1712
2023
  const result = await scanProject(device.path);
1713
2024
  await supabase.from("claude_folders").update({
@@ -1721,7 +2032,7 @@ async function syncCommand(options) {
1721
2032
  await pushToolings(supabase, device.folder_id, result.toolings);
1722
2033
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", device.folder_id).eq("device_name", device.device_name);
1723
2034
  await saveState({ lastSyncAt: (/* @__PURE__ */ new Date()).toISOString() });
1724
- console.log(chalk12.green("Synced."));
2035
+ console.log(chalk14.green("Synced."));
1725
2036
  }
1726
2037
  }
1727
2038
  var init_sync = __esm({
@@ -1735,16 +2046,16 @@ var init_sync = __esm({
1735
2046
  });
1736
2047
 
1737
2048
  // dist/mcp/read-configs.js
1738
- import { readFile as readFile8 } from "node:fs/promises";
1739
- import { join as join12 } from "node:path";
1740
- import { homedir as homedir7 } from "node:os";
2049
+ import { readFile as readFile10 } from "node:fs/promises";
2050
+ import { join as join14 } from "node:path";
2051
+ import { homedir as homedir8 } from "node:os";
1741
2052
  import { existsSync as existsSync11 } from "node:fs";
1742
2053
  import { readdir as readdir3 } from "node:fs/promises";
1743
2054
  async function readJsonSafe(path) {
1744
2055
  try {
1745
2056
  if (!existsSync11(path))
1746
2057
  return null;
1747
- const raw = await readFile8(path, "utf-8");
2058
+ const raw = await readFile10(path, "utf-8");
1748
2059
  return JSON.parse(raw);
1749
2060
  } catch {
1750
2061
  return null;
@@ -1804,52 +2115,52 @@ function parseFlatConfig(data, source) {
1804
2115
  return entries;
1805
2116
  }
1806
2117
  async function readAllMcpConfigs() {
1807
- const home = homedir7();
2118
+ const home = homedir8();
1808
2119
  const entries = [];
1809
- const userConfig = await readJsonSafe(join12(home, ".claude.json"));
2120
+ const userConfig = await readJsonSafe(join14(home, ".claude.json"));
1810
2121
  entries.push(...parseServers(userConfig, "global"));
1811
- const globalMcp = await readJsonSafe(join12(home, ".claude", "mcp.json"));
2122
+ const globalMcp = await readJsonSafe(join14(home, ".claude", "mcp.json"));
1812
2123
  entries.push(...parseServers(globalMcp, "global"));
1813
- const cwdMcp = await readJsonSafe(join12(process.cwd(), ".mcp.json"));
2124
+ const cwdMcp = await readJsonSafe(join14(process.cwd(), ".mcp.json"));
1814
2125
  entries.push(...parseServers(cwdMcp, "project"));
1815
- const pluginsBase = join12(home, ".claude", "plugins", "marketplaces");
2126
+ const pluginsBase = join14(home, ".claude", "plugins", "marketplaces");
1816
2127
  if (existsSync11(pluginsBase)) {
1817
2128
  try {
1818
2129
  const marketplaces = await readdir3(pluginsBase, { withFileTypes: true });
1819
2130
  for (const mp of marketplaces) {
1820
2131
  if (!mp.isDirectory())
1821
2132
  continue;
1822
- const extDir = join12(pluginsBase, mp.name, "external_plugins");
2133
+ const extDir = join14(pluginsBase, mp.name, "external_plugins");
1823
2134
  if (!existsSync11(extDir))
1824
2135
  continue;
1825
2136
  const plugins = await readdir3(extDir, { withFileTypes: true });
1826
2137
  for (const plugin of plugins) {
1827
2138
  if (!plugin.isDirectory())
1828
2139
  continue;
1829
- const pluginMcp = await readJsonSafe(join12(extDir, plugin.name, ".mcp.json"));
2140
+ const pluginMcp = await readJsonSafe(join14(extDir, plugin.name, ".mcp.json"));
1830
2141
  entries.push(...parseServers(pluginMcp, "plugin"));
1831
2142
  }
1832
2143
  }
1833
2144
  } catch {
1834
2145
  }
1835
2146
  }
1836
- const cacheBase = join12(home, ".claude", "plugins", "cache");
2147
+ const cacheBase = join14(home, ".claude", "plugins", "cache");
1837
2148
  if (existsSync11(cacheBase)) {
1838
2149
  try {
1839
2150
  const registries = await readdir3(cacheBase, { withFileTypes: true });
1840
2151
  for (const reg of registries) {
1841
2152
  if (!reg.isDirectory())
1842
2153
  continue;
1843
- const regDir = join12(cacheBase, reg.name);
2154
+ const regDir = join14(cacheBase, reg.name);
1844
2155
  const plugins = await readdir3(regDir, { withFileTypes: true });
1845
2156
  for (const plugin of plugins) {
1846
2157
  if (!plugin.isDirectory())
1847
2158
  continue;
1848
- const versionDirs = await readdir3(join12(regDir, plugin.name), { withFileTypes: true });
2159
+ const versionDirs = await readdir3(join14(regDir, plugin.name), { withFileTypes: true });
1849
2160
  for (const ver of versionDirs) {
1850
2161
  if (!ver.isDirectory())
1851
2162
  continue;
1852
- const mcpPath = join12(regDir, plugin.name, ver.name, ".mcp.json");
2163
+ const mcpPath = join14(regDir, plugin.name, ver.name, ".mcp.json");
1853
2164
  const flatData = await readJsonSafe(mcpPath);
1854
2165
  if (flatData) {
1855
2166
  entries.push(...parseFlatConfig(flatData, "plugin"));
@@ -1913,23 +2224,23 @@ function parseEtime(etime) {
1913
2224
  }
1914
2225
  return days * 86400 + (parts[0] ?? 0);
1915
2226
  }
1916
- function findProcessesForConfig(config, processes) {
1917
- if (config.type === "http")
2227
+ function findProcessesForConfig(config2, processes) {
2228
+ if (config2.type === "http")
1918
2229
  return [];
1919
- const packageName = config.args ? extractPackageName(config.args) : null;
2230
+ const packageName = config2.args ? extractPackageName(config2.args) : null;
1920
2231
  const matches = [];
1921
2232
  const searchTerms = [];
1922
2233
  if (packageName)
1923
2234
  searchTerms.push(packageName);
1924
- if (config.command === "node" && config.args?.[0]) {
1925
- searchTerms.push(config.args[0]);
2235
+ if (config2.command === "node" && config2.args?.[0]) {
2236
+ searchTerms.push(config2.args[0]);
1926
2237
  }
1927
- if (config.name) {
1928
- searchTerms.push(`mcp-server-${config.name}`);
1929
- searchTerms.push(`${config.name}-mcp`);
2238
+ if (config2.name) {
2239
+ searchTerms.push(`mcp-server-${config2.name}`);
2240
+ searchTerms.push(`${config2.name}-mcp`);
1930
2241
  }
1931
- if ((config.command === "uvx" || config.command === "pipx") && config.args) {
1932
- for (const arg of config.args) {
2242
+ if ((config2.command === "uvx" || config2.command === "pipx") && config2.args) {
2243
+ for (const arg of config2.args) {
1933
2244
  if (!arg.startsWith("-") && arg !== "run")
1934
2245
  searchTerms.push(arg);
1935
2246
  }
@@ -1979,7 +2290,7 @@ var mcp_watch_exports = {};
1979
2290
  __export(mcp_watch_exports, {
1980
2291
  mcpWatchCommand: () => mcpWatchCommand
1981
2292
  });
1982
- import chalk18 from "chalk";
2293
+ import chalk20 from "chalk";
1983
2294
  import { execFileSync as execFileSync5 } from "node:child_process";
1984
2295
  import { createHash as createHash2 } from "node:crypto";
1985
2296
  function detectTty() {
@@ -1993,10 +2304,10 @@ function detectTty() {
1993
2304
  return null;
1994
2305
  }
1995
2306
  }
1996
- function checkEnvVars(config) {
1997
- const required = config.env ? Object.keys(config.env) : [];
2307
+ function checkEnvVars(config2) {
2308
+ const required = config2.env ? Object.keys(config2.env) : [];
1998
2309
  const missing = required.filter((key) => {
1999
- const configValue = config.env?.[key];
2310
+ const configValue = config2.env?.[key];
2000
2311
  const hasConfigValue = configValue && !configValue.startsWith("${");
2001
2312
  return !hasConfigValue && !process.env[key];
2002
2313
  });
@@ -2031,20 +2342,20 @@ async function checkChromeCdp() {
2031
2342
  function buildRows(configs, httpResults, cdpStatus) {
2032
2343
  const processes = getProcessTable();
2033
2344
  const rows = [];
2034
- for (const config of configs) {
2035
- const { required, missing } = checkEnvVars(config);
2036
- const packageName = config.args ? extractPackageName(config.args) : null;
2037
- if (config.type === "http") {
2038
- const reachability = httpResults.get(config.name) ?? "unknown";
2345
+ for (const config2 of configs) {
2346
+ const { required, missing } = checkEnvVars(config2);
2347
+ const packageName = config2.args ? extractPackageName(config2.args) : null;
2348
+ if (config2.type === "http") {
2349
+ const reachability = httpResults.get(config2.name) ?? "unknown";
2039
2350
  const status = reachability === "reachable" ? "running" : "stopped";
2040
2351
  const detail = reachability === "reachable" ? "HTTP \u2014 remote service reachable" : reachability === "unreachable" ? "HTTP \u2014 remote service unreachable" : "HTTP \u2014 could not verify";
2041
2352
  rows.push({
2042
- server_name: config.name,
2043
- config_source: config.source,
2353
+ server_name: config2.name,
2354
+ config_source: config2.source,
2044
2355
  server_type: "http",
2045
2356
  command: null,
2046
2357
  package_name: null,
2047
- http_url: config.url ?? null,
2358
+ http_url: config2.url ?? null,
2048
2359
  status,
2049
2360
  pid: null,
2050
2361
  session_tty: null,
@@ -2058,10 +2369,10 @@ function buildRows(configs, httpResults, cdpStatus) {
2058
2369
  }
2059
2370
  if (missing.length > 0) {
2060
2371
  rows.push({
2061
- server_name: config.name,
2062
- config_source: config.source,
2372
+ server_name: config2.name,
2373
+ config_source: config2.source,
2063
2374
  server_type: "stdio",
2064
- command: config.command ?? null,
2375
+ command: config2.command ?? null,
2065
2376
  package_name: packageName,
2066
2377
  http_url: null,
2067
2378
  status: "error",
@@ -2075,15 +2386,15 @@ function buildRows(configs, httpResults, cdpStatus) {
2075
2386
  });
2076
2387
  continue;
2077
2388
  }
2078
- const isChromeDevtools = config.name === "chrome-devtools" || (config.args ?? []).some((a) => a.includes("chrome-devtools"));
2389
+ const isChromeDevtools = config2.name === "chrome-devtools" || (config2.args ?? []).some((a) => a.includes("chrome-devtools"));
2079
2390
  const cdpDetail = isChromeDevtools && cdpStatus ? cdpStatus.status === "reachable" ? "Chrome CDP reachable" : "Chrome not reachable on port 9222" : null;
2080
- const matches = findProcessesForConfig(config, processes);
2391
+ const matches = findProcessesForConfig(config2, processes);
2081
2392
  if (matches.length === 0) {
2082
2393
  rows.push({
2083
- server_name: config.name,
2084
- config_source: config.source,
2394
+ server_name: config2.name,
2395
+ config_source: config2.source,
2085
2396
  server_type: "stdio",
2086
- command: config.command ?? null,
2397
+ command: config2.command ?? null,
2087
2398
  package_name: packageName,
2088
2399
  http_url: null,
2089
2400
  status: "stopped",
@@ -2106,10 +2417,10 @@ function buildRows(configs, httpResults, cdpStatus) {
2106
2417
  }
2107
2418
  for (const match of byTty.values()) {
2108
2419
  rows.push({
2109
- server_name: config.name,
2110
- config_source: config.source,
2420
+ server_name: config2.name,
2421
+ config_source: config2.source,
2111
2422
  server_type: "stdio",
2112
- command: config.command ?? null,
2423
+ command: config2.command ?? null,
2113
2424
  package_name: packageName,
2114
2425
  http_url: null,
2115
2426
  status: "running",
@@ -2128,20 +2439,20 @@ function buildRows(configs, httpResults, cdpStatus) {
2128
2439
  }
2129
2440
  function printTable(rows, deviceName, watcherPid, cdpResult) {
2130
2441
  process.stdout.write("\x1B[3J\x1B[2J\x1B[H");
2131
- console.log(chalk18.bold.cyan(`
2132
- MCP Monitor v${CURRENT_VERSION} \u2014 ${deviceName}`) + chalk18.dim(` (PID ${watcherPid})`));
2133
- console.log(chalk18.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
2442
+ console.log(chalk20.bold.cyan(`
2443
+ MCP Monitor v${CURRENT_VERSION} \u2014 ${deviceName}`) + chalk20.dim(` (PID ${watcherPid})`));
2444
+ console.log(chalk20.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
2134
2445
  `));
2135
2446
  if (cdpResult) {
2136
2447
  if (cdpResult.status === "reachable") {
2137
2448
  const browserInfo = cdpResult.browser ? ` (${cdpResult.browser})` : "";
2138
- console.log(chalk18.green(" \u2714 Chrome browser connected") + chalk18.dim(browserInfo));
2139
- console.log(chalk18.dim(" Claude Code can control Chrome via DevTools Protocol on localhost:9222"));
2449
+ console.log(chalk20.green(" \u2714 Chrome browser connected") + chalk20.dim(browserInfo));
2450
+ console.log(chalk20.dim(" Claude Code can control Chrome via DevTools Protocol on localhost:9222"));
2140
2451
  } else {
2141
- console.log(chalk18.red(" \u2717 Chrome browser not connected"));
2142
- console.log(chalk18.dim(" Claude Code cannot reach Chrome. To fix:"));
2143
- console.log(chalk18.dim(" 1. Open Chrome with: google-chrome --remote-debugging-port=9222"));
2144
- console.log(chalk18.dim(" 2. Or add --remote-debugging-port=9222 to your Chrome shortcut"));
2452
+ console.log(chalk20.red(" \u2717 Chrome browser not connected"));
2453
+ console.log(chalk20.dim(" Claude Code cannot reach Chrome. To fix:"));
2454
+ console.log(chalk20.dim(" 1. Open Chrome with: google-chrome --remote-debugging-port=9222"));
2455
+ console.log(chalk20.dim(" 2. Or add --remote-debugging-port=9222 to your Chrome shortcut"));
2145
2456
  }
2146
2457
  console.log("");
2147
2458
  }
@@ -2162,41 +2473,41 @@ function printTable(rows, deviceName, watcherPid, cdpResult) {
2162
2473
  sessionNum++;
2163
2474
  const totalMem = servers.reduce((sum, s) => sum + (s.memory_mb ?? 0), 0);
2164
2475
  const label = byTty.size === 1 ? "Claude Code session" : `Claude Code session ${sessionNum}`;
2165
- console.log(chalk18.green(` ${label}`) + chalk18.dim(` \u2014 ${servers.length} server${servers.length !== 1 ? "s" : ""} \xB7 ${totalMem} MB \xB7 terminal ${tty}`));
2476
+ console.log(chalk20.green(` ${label}`) + chalk20.dim(` \u2014 ${servers.length} server${servers.length !== 1 ? "s" : ""} \xB7 ${totalMem} MB \xB7 terminal ${tty}`));
2166
2477
  for (const s of servers) {
2167
2478
  const uptime = formatUptime(s.uptime_seconds ?? 0);
2168
- const source = chalk18.dim(`[${s.config_source}]`);
2169
- console.log(` ${chalk18.green("\u25CF")} ${s.server_name.padEnd(20)} ${source} ${chalk18.dim((s.package_name ?? "").padEnd(25))} ${String(s.memory_mb ?? 0).padStart(4)} MB ${uptime}`);
2479
+ const source = chalk20.dim(`[${s.config_source}]`);
2480
+ console.log(` ${chalk20.green("\u25CF")} ${s.server_name.padEnd(20)} ${source} ${chalk20.dim((s.package_name ?? "").padEnd(25))} ${String(s.memory_mb ?? 0).padStart(4)} MB ${uptime}`);
2170
2481
  }
2171
2482
  console.log("");
2172
2483
  }
2173
2484
  if (byTty.size > 1) {
2174
- console.log(chalk18.dim(" Each open Claude Code window runs its own set of MCP servers.\n"));
2485
+ console.log(chalk20.dim(" Each open Claude Code window runs its own set of MCP servers.\n"));
2175
2486
  }
2176
2487
  }
2177
2488
  if (runningHttp.length > 0) {
2178
- console.log(chalk18.blue(` Remote Services (${runningHttp.length})`) + chalk18.dim(" \u2014 HTTP endpoints reachable"));
2489
+ console.log(chalk20.blue(` Remote Services (${runningHttp.length})`) + chalk20.dim(" \u2014 HTTP endpoints reachable"));
2179
2490
  for (const s of runningHttp) {
2180
- console.log(` ${chalk18.blue("\u25CF")} ${s.server_name.padEnd(20)} ${chalk18.dim((s.http_url ?? "").padEnd(30))}`);
2491
+ console.log(` ${chalk20.blue("\u25CF")} ${s.server_name.padEnd(20)} ${chalk20.dim((s.http_url ?? "").padEnd(30))}`);
2181
2492
  }
2182
2493
  console.log("");
2183
2494
  }
2184
2495
  if (stopped.length > 0 || errored.length > 0) {
2185
2496
  const notRunning = [...stopped, ...errored];
2186
- console.log(chalk18.yellow(` Not Running (${notRunning.length})`) + chalk18.dim(" \u2014 configured but no process detected"));
2497
+ console.log(chalk20.yellow(` Not Running (${notRunning.length})`) + chalk20.dim(" \u2014 configured but no process detected"));
2187
2498
  for (const s of notRunning) {
2188
- const icon = s.status === "error" ? chalk18.red("\u2717") : chalk18.yellow("\u25CB");
2189
- const source = chalk18.dim(`[${s.config_source}]`);
2190
- const detail = s.error_detail ? chalk18.dim(` \u2014 ${s.error_detail}`) : "";
2499
+ const icon = s.status === "error" ? chalk20.red("\u2717") : chalk20.yellow("\u25CB");
2500
+ const source = chalk20.dim(`[${s.config_source}]`);
2501
+ const detail = s.error_detail ? chalk20.dim(` \u2014 ${s.error_detail}`) : "";
2191
2502
  console.log(` ${icon} ${s.server_name.padEnd(20)} ${source}${detail}`);
2192
2503
  }
2193
2504
  console.log("");
2194
2505
  }
2195
2506
  if (rows.length === 0) {
2196
- console.log(chalk18.yellow(" No MCP servers configured."));
2197
- console.log(chalk18.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
2507
+ console.log(chalk20.yellow(" No MCP servers configured."));
2508
+ console.log(chalk20.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
2198
2509
  }
2199
- console.log(chalk18.bgYellow.black.bold(" \u26A0 DO NOT CLOSE THIS WINDOW \u2014 it feeds live data to the dashboard \u26A0 "));
2510
+ console.log(chalk20.bgYellow.black.bold(" \u26A0 DO NOT CLOSE THIS WINDOW \u2014 it feeds live data to the dashboard \u26A0 "));
2200
2511
  console.log("");
2201
2512
  }
2202
2513
  function formatUptime(seconds) {
@@ -2219,7 +2530,7 @@ async function mcpWatchCommand() {
2219
2530
  const { data: existingWatchers } = await supabase.from("mcp_watchers").select("pid, tty, cli_version, started_at").eq("device_id", deviceId);
2220
2531
  if (existingWatchers && existingWatchers.length > 0) {
2221
2532
  console.log("");
2222
- console.log(chalk18.yellow(` Replacing ${existingWatchers.length} existing watcher${existingWatchers.length !== 1 ? "s" : ""} on this device...`));
2533
+ console.log(chalk20.yellow(` Replacing ${existingWatchers.length} existing watcher${existingWatchers.length !== 1 ? "s" : ""} on this device...`));
2223
2534
  for (const w of existingWatchers) {
2224
2535
  try {
2225
2536
  process.kill(w.pid, "SIGTERM");
@@ -2228,15 +2539,15 @@ async function mcpWatchCommand() {
2228
2539
  }
2229
2540
  await supabase.from("mcp_watchers").delete().eq("device_id", deviceId);
2230
2541
  await new Promise((r) => setTimeout(r, 1e3));
2231
- console.log(chalk18.dim(" Previous watcher stopped.\n"));
2542
+ console.log(chalk20.dim(" Previous watcher stopped.\n"));
2232
2543
  }
2233
2544
  process.stdout.write(`\x1B]0;MCP mon\x07`);
2234
- console.log(chalk18.blue(`Starting MCP monitor for ${deviceName}...`));
2545
+ console.log(chalk20.blue(`Starting MCP monitor for ${deviceName}...`));
2235
2546
  console.log("");
2236
- console.log(chalk18.dim(" View this in the online dashboard at:"));
2237
- console.log(chalk18.cyan(` https://www.md4ai.com/device/${deviceId}`));
2547
+ console.log(chalk20.dim(" View this in the online dashboard at:"));
2548
+ console.log(chalk20.cyan(` https://www.md4ai.com/device/${deviceId}`));
2238
2549
  console.log("");
2239
- console.log(chalk18.yellow(" Please note, closing this window will stop ALL watch reports."));
2550
+ console.log(chalk20.yellow(" Please note, closing this window will stop ALL watch reports."));
2240
2551
  console.log("");
2241
2552
  await supabase.from("mcp_watchers").upsert({
2242
2553
  device_id: deviceId,
@@ -2262,7 +2573,7 @@ async function mcpWatchCommand() {
2262
2573
  const now = (/* @__PURE__ */ new Date()).toISOString();
2263
2574
  const { error: deleteError } = await supabase.from("mcp_server_status").delete().eq("device_id", deviceId);
2264
2575
  if (deleteError) {
2265
- console.error(chalk18.red(` [debug] Failed to delete old status: ${deleteError.message}`));
2576
+ console.error(chalk20.red(` [debug] Failed to delete old status: ${deleteError.message}`));
2266
2577
  }
2267
2578
  if (rows.length > 0) {
2268
2579
  const { error: insertError } = await supabase.from("mcp_server_status").insert(rows.map((row) => ({
@@ -2271,7 +2582,7 @@ async function mcpWatchCommand() {
2271
2582
  checked_at: now
2272
2583
  })));
2273
2584
  if (insertError) {
2274
- console.error(chalk18.red(` [debug] Failed to write MCP status: ${insertError.message}`));
2585
+ console.error(chalk20.red(` [debug] Failed to write MCP status: ${insertError.message}`));
2275
2586
  }
2276
2587
  }
2277
2588
  await supabase.from("mcp_watchers").update({ last_heartbeat: now }).eq("device_id", deviceId).eq("pid", myPid);
@@ -2309,7 +2620,7 @@ async function mcpWatchCommand() {
2309
2620
  clearInterval(interval);
2310
2621
  clearInterval(envInterval);
2311
2622
  await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).eq("pid", myPid);
2312
- console.log(chalk18.dim("\nMCP monitor stopped."));
2623
+ console.log(chalk20.dim("\nMCP monitor stopped."));
2313
2624
  process.exit(0);
2314
2625
  };
2315
2626
  process.on("SIGINT", () => {
@@ -2573,71 +2884,71 @@ init_map();
2573
2884
 
2574
2885
  // dist/commands/simulate.js
2575
2886
  init_dist();
2576
- import { join as join9 } from "node:path";
2887
+ import { join as join11 } from "node:path";
2577
2888
  import { existsSync as existsSync8 } from "node:fs";
2578
- import { homedir as homedir6 } from "node:os";
2579
- import chalk10 from "chalk";
2889
+ import { homedir as homedir7 } from "node:os";
2890
+ import chalk12 from "chalk";
2580
2891
  async function simulateCommand(prompt) {
2581
2892
  const projectRoot = process.cwd();
2582
- console.log(chalk10.blue(`Simulating prompt: "${prompt}"
2893
+ console.log(chalk12.blue(`Simulating prompt: "${prompt}"
2583
2894
  `));
2584
- console.log(chalk10.dim("Files Claude would load:\n"));
2585
- const globalClaude = join9(homedir6(), ".claude", "CLAUDE.md");
2895
+ console.log(chalk12.dim("Files Claude would load:\n"));
2896
+ const globalClaude = join11(homedir7(), ".claude", "CLAUDE.md");
2586
2897
  if (existsSync8(globalClaude)) {
2587
- console.log(chalk10.green(" \u2713 ~/.claude/CLAUDE.md (global)"));
2898
+ console.log(chalk12.green(" \u2713 ~/.claude/CLAUDE.md (global)"));
2588
2899
  }
2589
2900
  for (const rootFile of ROOT_FILES) {
2590
- const fullPath = join9(projectRoot, rootFile);
2901
+ const fullPath = join11(projectRoot, rootFile);
2591
2902
  if (existsSync8(fullPath)) {
2592
- console.log(chalk10.green(` \u2713 ${rootFile} (project root)`));
2903
+ console.log(chalk12.green(` \u2713 ${rootFile} (project root)`));
2593
2904
  }
2594
2905
  }
2595
2906
  const settingsFiles = [
2596
- join9(homedir6(), ".claude", "settings.json"),
2597
- join9(homedir6(), ".claude", "settings.local.json"),
2598
- join9(projectRoot, ".claude", "settings.json"),
2599
- join9(projectRoot, ".claude", "settings.local.json")
2907
+ join11(homedir7(), ".claude", "settings.json"),
2908
+ join11(homedir7(), ".claude", "settings.local.json"),
2909
+ join11(projectRoot, ".claude", "settings.json"),
2910
+ join11(projectRoot, ".claude", "settings.local.json")
2600
2911
  ];
2601
2912
  for (const sf of settingsFiles) {
2602
2913
  if (existsSync8(sf)) {
2603
- const display = sf.startsWith(homedir6()) ? sf.replace(homedir6(), "~") : sf.replace(projectRoot + "/", "");
2604
- console.log(chalk10.green(` \u2713 ${display} (settings)`));
2914
+ const display = sf.startsWith(homedir7()) ? sf.replace(homedir7(), "~") : sf.replace(projectRoot + "/", "");
2915
+ console.log(chalk12.green(` \u2713 ${display} (settings)`));
2605
2916
  }
2606
2917
  }
2607
2918
  const words = prompt.split(/\s+/);
2608
2919
  for (const word of words) {
2609
2920
  const cleaned = word.replace(/['"]/g, "");
2610
- const candidatePath = join9(projectRoot, cleaned);
2921
+ const candidatePath = join11(projectRoot, cleaned);
2611
2922
  if (existsSync8(candidatePath) && cleaned.includes("/")) {
2612
- console.log(chalk10.cyan(` \u2192 ${cleaned} (referenced in prompt)`));
2923
+ console.log(chalk12.cyan(` \u2192 ${cleaned} (referenced in prompt)`));
2613
2924
  }
2614
2925
  }
2615
- console.log(chalk10.dim("\nNote: This is an approximation. Actual file loading depends on Claude Code internals."));
2926
+ console.log(chalk12.dim("\nNote: This is an approximation. Actual file loading depends on Claude Code internals."));
2616
2927
  }
2617
2928
 
2618
2929
  // dist/commands/print.js
2619
- import { join as join10 } from "node:path";
2620
- import { readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
2930
+ import { join as join12 } from "node:path";
2931
+ import { readFile as readFile8, writeFile as writeFile3 } from "node:fs/promises";
2621
2932
  import { existsSync as existsSync9 } from "node:fs";
2622
- import chalk11 from "chalk";
2933
+ import chalk13 from "chalk";
2623
2934
  async function printCommand(title) {
2624
2935
  const projectRoot = process.cwd();
2625
- const scanDataPath = join10(projectRoot, "output", "index.html");
2936
+ const scanDataPath = join12(projectRoot, "output", "index.html");
2626
2937
  if (!existsSync9(scanDataPath)) {
2627
- console.error(chalk11.red("No scan data found. Run: md4ai scan"));
2938
+ console.error(chalk13.red("No scan data found. Run: md4ai scan"));
2628
2939
  process.exit(1);
2629
2940
  }
2630
- const html = await readFile6(scanDataPath, "utf-8");
2941
+ const html = await readFile8(scanDataPath, "utf-8");
2631
2942
  const match = html.match(/<script type="application\/json" id="scan-data">([\s\S]*?)<\/script>/);
2632
2943
  if (!match) {
2633
- console.error(chalk11.red("Could not extract scan data from output/index.html"));
2944
+ console.error(chalk13.red("Could not extract scan data from output/index.html"));
2634
2945
  process.exit(1);
2635
2946
  }
2636
2947
  const result = JSON.parse(match[1]);
2637
2948
  const printHtml = generatePrintHtml(result, title);
2638
- const outputPath = join10(projectRoot, "output", `print-${Date.now()}.html`);
2949
+ const outputPath = join12(projectRoot, "output", `print-${Date.now()}.html`);
2639
2950
  await writeFile3(outputPath, printHtml, "utf-8");
2640
- console.log(chalk11.green(`Print-ready wall sheet: ${outputPath}`));
2951
+ console.log(chalk13.green(`Print-ready wall sheet: ${outputPath}`));
2641
2952
  }
2642
2953
  function escapeHtml2(text) {
2643
2954
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -2703,7 +3014,7 @@ init_scanner();
2703
3014
  init_push_toolings();
2704
3015
  init_device_utils();
2705
3016
  import { resolve as resolve4 } from "node:path";
2706
- import chalk13 from "chalk";
3017
+ import chalk15 from "chalk";
2707
3018
  async function linkCommand(projectId) {
2708
3019
  const { supabase, userId } = await getAuthenticatedClient();
2709
3020
  const cwd = resolve4(process.cwd());
@@ -2711,11 +3022,11 @@ async function linkCommand(projectId) {
2711
3022
  const osType = detectOs2();
2712
3023
  const { data: folder, error: folderErr } = await supabase.from("claude_folders").select("id, name").eq("id", projectId).single();
2713
3024
  if (folderErr || !folder) {
2714
- console.error(chalk13.red("Project not found, or you do not have access."));
2715
- console.error(chalk13.yellow("Check the project ID in the MD4AI web dashboard."));
3025
+ console.error(chalk15.red("Project not found, or you do not have access."));
3026
+ console.error(chalk15.yellow("Check the project ID in the MD4AI web dashboard."));
2716
3027
  process.exit(1);
2717
3028
  }
2718
- console.log(chalk13.blue(`
3029
+ console.log(chalk15.blue(`
2719
3030
  Linking "${folder.name}" to this device...
2720
3031
  `));
2721
3032
  console.log(` Project: ${folder.name}`);
@@ -2739,12 +3050,12 @@ Linking "${folder.name}" to this device...
2739
3050
  path: cwd
2740
3051
  });
2741
3052
  if (pathErr) {
2742
- console.error(chalk13.red(`Failed to link: ${pathErr.message}`));
3053
+ console.error(chalk15.red(`Failed to link: ${pathErr.message}`));
2743
3054
  process.exit(1);
2744
3055
  }
2745
3056
  }
2746
- console.log(chalk13.green("\nLinked successfully."));
2747
- console.log(chalk13.blue("\nRunning initial scan...\n"));
3057
+ console.log(chalk15.green("\nLinked successfully."));
3058
+ console.log(chalk15.blue("\nRunning initial scan...\n"));
2748
3059
  const result = await scanProject(cwd);
2749
3060
  console.log(` Files: ${result.graph.nodes.length}`);
2750
3061
  console.log(` References:${result.graph.edges.length}`);
@@ -2762,7 +3073,7 @@ Linking "${folder.name}" to this device...
2762
3073
  data_hash: result.dataHash
2763
3074
  }).eq("id", folder.id);
2764
3075
  if (scanErr) {
2765
- console.error(chalk13.yellow(`Scan upload warning: ${scanErr.message}`));
3076
+ console.error(chalk15.yellow(`Scan upload warning: ${scanErr.message}`));
2766
3077
  }
2767
3078
  await pushToolings(supabase, folder.id, result.toolings);
2768
3079
  const configFiles = await readClaudeConfigFiles(cwd);
@@ -2776,7 +3087,7 @@ Linking "${folder.name}" to this device...
2776
3087
  last_modified: file.lastModified
2777
3088
  }, { onConflict: "folder_id,file_path" });
2778
3089
  }
2779
- console.log(chalk13.green(` Uploaded ${configFiles.length} config file(s).`));
3090
+ console.log(chalk15.green(` Uploaded ${configFiles.length} config file(s).`));
2780
3091
  }
2781
3092
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", folder.id).eq("device_name", deviceName);
2782
3093
  await saveState({
@@ -2785,34 +3096,34 @@ Linking "${folder.name}" to this device...
2785
3096
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
2786
3097
  });
2787
3098
  const projectUrl = `https://www.md4ai.com/project/${folder.id}`;
2788
- console.log(chalk13.green("\nDone! Project linked and scanned."));
2789
- console.log(chalk13.cyan(`
3099
+ console.log(chalk15.green("\nDone! Project linked and scanned."));
3100
+ console.log(chalk15.cyan(`
2790
3101
  ${projectUrl}
2791
3102
  `));
2792
- console.log(chalk13.grey('Run "md4ai scan" to rescan at any time.'));
3103
+ console.log(chalk15.grey('Run "md4ai scan" to rescan at any time.'));
2793
3104
  }
2794
3105
 
2795
3106
  // dist/commands/import-bundle.js
2796
- import { readFile as readFile7, writeFile as writeFile4, mkdir as mkdir3 } from "node:fs/promises";
2797
- import { join as join11, dirname as dirname2 } from "node:path";
3107
+ import { readFile as readFile9, writeFile as writeFile4, mkdir as mkdir3 } from "node:fs/promises";
3108
+ import { join as join13, dirname as dirname3 } from "node:path";
2798
3109
  import { existsSync as existsSync10 } from "node:fs";
2799
- import chalk14 from "chalk";
3110
+ import chalk16 from "chalk";
2800
3111
  import { confirm as confirm3, input as input6 } from "@inquirer/prompts";
2801
3112
  async function importBundleCommand(zipPath) {
2802
3113
  if (!existsSync10(zipPath)) {
2803
- console.error(chalk14.red(`File not found: ${zipPath}`));
3114
+ console.error(chalk16.red(`File not found: ${zipPath}`));
2804
3115
  process.exit(1);
2805
3116
  }
2806
3117
  const JSZip = (await import("jszip")).default;
2807
- const zipData = await readFile7(zipPath);
3118
+ const zipData = await readFile9(zipPath);
2808
3119
  const zip = await JSZip.loadAsync(zipData);
2809
3120
  const manifestFile = zip.file("manifest.json");
2810
3121
  if (!manifestFile) {
2811
- console.error(chalk14.red("Invalid bundle: missing manifest.json"));
3122
+ console.error(chalk16.red("Invalid bundle: missing manifest.json"));
2812
3123
  process.exit(1);
2813
3124
  }
2814
3125
  const manifest = JSON.parse(await manifestFile.async("string"));
2815
- console.log(chalk14.blue(`
3126
+ console.log(chalk16.blue(`
2816
3127
  Bundle: ${manifest.folderName}`));
2817
3128
  console.log(` Exported by: ${manifest.ownerEmail}`);
2818
3129
  console.log(` Exported at: ${manifest.exportedAt}`);
@@ -2826,10 +3137,10 @@ Bundle: ${manifest.folderName}`));
2826
3137
  }
2827
3138
  }
2828
3139
  if (files.length === 0) {
2829
- console.log(chalk14.yellow("No Claude config files found in bundle."));
3140
+ console.log(chalk16.yellow("No Claude config files found in bundle."));
2830
3141
  return;
2831
3142
  }
2832
- console.log(chalk14.blue(`
3143
+ console.log(chalk16.blue(`
2833
3144
  Files to extract:`));
2834
3145
  for (const f of files) {
2835
3146
  console.log(` ${f.filePath}`);
@@ -2842,34 +3153,34 @@ Files to extract:`));
2842
3153
  message: `Extract ${files.length} file(s) to ${targetDir}?`
2843
3154
  });
2844
3155
  if (!proceed) {
2845
- console.log(chalk14.yellow("Cancelled."));
3156
+ console.log(chalk16.yellow("Cancelled."));
2846
3157
  return;
2847
3158
  }
2848
3159
  for (const file of files) {
2849
- const fullPath = join11(targetDir, file.filePath);
2850
- const dir = dirname2(fullPath);
3160
+ const fullPath = join13(targetDir, file.filePath);
3161
+ const dir = dirname3(fullPath);
2851
3162
  if (!existsSync10(dir)) {
2852
3163
  await mkdir3(dir, { recursive: true });
2853
3164
  }
2854
3165
  await writeFile4(fullPath, file.content, "utf-8");
2855
- console.log(chalk14.green(` \u2713 ${file.filePath}`));
3166
+ console.log(chalk16.green(` \u2713 ${file.filePath}`));
2856
3167
  }
2857
- console.log(chalk14.green(`
3168
+ console.log(chalk16.green(`
2858
3169
  Done! ${files.length} file(s) extracted to ${targetDir}`));
2859
3170
  }
2860
3171
 
2861
3172
  // dist/commands/admin-update-tool.js
2862
3173
  init_auth();
2863
- import chalk15 from "chalk";
3174
+ import chalk17 from "chalk";
2864
3175
  var VALID_CATEGORIES = ["framework", "runtime", "cli", "mcp", "package", "database", "other"];
2865
3176
  async function adminUpdateToolCommand(options) {
2866
3177
  const { supabase } = await getAuthenticatedClient();
2867
3178
  if (!options.name) {
2868
- console.error(chalk15.red("--name is required."));
3179
+ console.error(chalk17.red("--name is required."));
2869
3180
  process.exit(1);
2870
3181
  }
2871
3182
  if (options.category && !VALID_CATEGORIES.includes(options.category)) {
2872
- console.error(chalk15.red(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(", ")}`));
3183
+ console.error(chalk17.red(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(", ")}`));
2873
3184
  process.exit(1);
2874
3185
  }
2875
3186
  const { data: existing } = await supabase.from("tools_registry").select("id, name, display_name, category").eq("name", options.name).maybeSingle();
@@ -2891,13 +3202,13 @@ async function adminUpdateToolCommand(options) {
2891
3202
  updates.notes = options.notes;
2892
3203
  const { error } = await supabase.from("tools_registry").update(updates).eq("id", existing.id);
2893
3204
  if (error) {
2894
- console.error(chalk15.red(`Failed to update: ${error.message}`));
3205
+ console.error(chalk17.red(`Failed to update: ${error.message}`));
2895
3206
  process.exit(1);
2896
3207
  }
2897
- console.log(chalk15.green(`Updated "${existing.display_name}" in the registry.`));
3208
+ console.log(chalk17.green(`Updated "${existing.display_name}" in the registry.`));
2898
3209
  } else {
2899
3210
  if (!options.display || !options.category) {
2900
- console.error(chalk15.red("New tools require --display and --category."));
3211
+ console.error(chalk17.red("New tools require --display and --category."));
2901
3212
  process.exit(1);
2902
3213
  }
2903
3214
  const { error } = await supabase.from("tools_registry").insert({
@@ -2911,25 +3222,25 @@ async function adminUpdateToolCommand(options) {
2911
3222
  notes: options.notes ?? null
2912
3223
  });
2913
3224
  if (error) {
2914
- console.error(chalk15.red(`Failed to create: ${error.message}`));
3225
+ console.error(chalk17.red(`Failed to create: ${error.message}`));
2915
3226
  process.exit(1);
2916
3227
  }
2917
- console.log(chalk15.green(`Added "${options.display}" to the registry.`));
3228
+ console.log(chalk17.green(`Added "${options.display}" to the registry.`));
2918
3229
  }
2919
3230
  }
2920
3231
 
2921
3232
  // dist/commands/admin-list-tools.js
2922
3233
  init_auth();
2923
- import chalk16 from "chalk";
3234
+ import chalk18 from "chalk";
2924
3235
  async function adminListToolsCommand() {
2925
3236
  const { supabase } = await getAuthenticatedClient();
2926
3237
  const { data: tools, error } = await supabase.from("tools_registry").select("*").order("category").order("display_name");
2927
3238
  if (error) {
2928
- console.error(chalk16.red(`Failed to fetch tools: ${error.message}`));
3239
+ console.error(chalk18.red(`Failed to fetch tools: ${error.message}`));
2929
3240
  process.exit(1);
2930
3241
  }
2931
3242
  if (!tools?.length) {
2932
- console.log(chalk16.yellow("No tools in the registry."));
3243
+ console.log(chalk18.yellow("No tools in the registry."));
2933
3244
  return;
2934
3245
  }
2935
3246
  const nameW = Math.max(16, ...tools.map((t) => t.display_name.length));
@@ -2943,7 +3254,7 @@ async function adminListToolsCommand() {
2943
3254
  "Beta".padEnd(betaW),
2944
3255
  "Last Checked"
2945
3256
  ].join(" ");
2946
- console.log(chalk16.bold(header));
3257
+ console.log(chalk18.bold(header));
2947
3258
  console.log("\u2500".repeat(header.length));
2948
3259
  for (const tool of tools) {
2949
3260
  const lastChecked = tool.updated_at ? formatRelative(new Date(tool.updated_at)) : "\u2014";
@@ -2956,7 +3267,7 @@ async function adminListToolsCommand() {
2956
3267
  ].join(" ");
2957
3268
  console.log(row);
2958
3269
  }
2959
- console.log(chalk16.grey(`
3270
+ console.log(chalk18.grey(`
2960
3271
  ${tools.length} tool(s) in registry.`));
2961
3272
  }
2962
3273
  function formatRelative(date) {
@@ -2974,7 +3285,7 @@ function formatRelative(date) {
2974
3285
 
2975
3286
  // dist/commands/admin-fetch-versions.js
2976
3287
  init_auth();
2977
- import chalk17 from "chalk";
3288
+ import chalk19 from "chalk";
2978
3289
 
2979
3290
  // dist/commands/version-sources.js
2980
3291
  var VERSION_SOURCES = {
@@ -3003,11 +3314,11 @@ async function adminFetchVersionsCommand() {
3003
3314
  const { supabase } = await getAuthenticatedClient();
3004
3315
  const { data: tools, error } = await supabase.from("tools_registry").select("*").order("display_name");
3005
3316
  if (error) {
3006
- console.error(chalk17.red(`Failed to fetch tools: ${error.message}`));
3317
+ console.error(chalk19.red(`Failed to fetch tools: ${error.message}`));
3007
3318
  process.exit(1);
3008
3319
  }
3009
3320
  if (!tools?.length) {
3010
- console.log(chalk17.yellow("No tools in the registry."));
3321
+ console.log(chalk19.yellow("No tools in the registry."));
3011
3322
  }
3012
3323
  const { data: allProjectToolings } = await supabase.from("project_toolings").select("tool_name, detection_source").is("tool_id", null);
3013
3324
  const registeredNames = new Set((tools ?? []).map((t) => t.name));
@@ -3018,7 +3329,7 @@ async function adminFetchVersionsCommand() {
3018
3329
  }
3019
3330
  }
3020
3331
  if (unregisteredNames.size > 0) {
3021
- console.log(chalk17.blue(`Auto-registering ${unregisteredNames.size} unverified package(s)...
3332
+ console.log(chalk19.blue(`Auto-registering ${unregisteredNames.size} unverified package(s)...
3022
3333
  `));
3023
3334
  for (const name of unregisteredNames) {
3024
3335
  const displayName = name;
@@ -3036,10 +3347,10 @@ async function adminFetchVersionsCommand() {
3036
3347
  }
3037
3348
  }
3038
3349
  if (!tools?.length) {
3039
- console.log(chalk17.yellow("No tools to fetch."));
3350
+ console.log(chalk19.yellow("No tools to fetch."));
3040
3351
  return;
3041
3352
  }
3042
- console.log(chalk17.bold(`Fetching versions for ${tools.length} tool(s)...
3353
+ console.log(chalk19.bold(`Fetching versions for ${tools.length} tool(s)...
3043
3354
  `));
3044
3355
  const results = await Promise.all(tools.map(async (tool) => {
3045
3356
  const source = VERSION_SOURCES[tool.name] ?? { type: "npm", package: tool.name };
@@ -3078,10 +3389,10 @@ async function adminFetchVersionsCommand() {
3078
3389
  "Beta".padEnd(betaW),
3079
3390
  "Status".padEnd(statusW)
3080
3391
  ].join(" ");
3081
- console.log(chalk17.bold(header));
3392
+ console.log(chalk19.bold(header));
3082
3393
  console.log("\u2500".repeat(header.length));
3083
3394
  for (const result of results) {
3084
- const statusColour = result.status === "updated" ? chalk17.green : result.status === "unchanged" ? chalk17.grey : result.status === "failed" ? chalk17.red : chalk17.yellow;
3395
+ const statusColour = result.status === "updated" ? chalk19.green : result.status === "unchanged" ? chalk19.grey : result.status === "failed" ? chalk19.red : chalk19.yellow;
3085
3396
  const row = [
3086
3397
  result.displayName.padEnd(nameW),
3087
3398
  (result.stable ?? "\u2014").padEnd(stableW),
@@ -3094,8 +3405,8 @@ async function adminFetchVersionsCommand() {
3094
3405
  const unchanged = results.filter((r) => r.status === "unchanged").length;
3095
3406
  const failed = results.filter((r) => r.status === "failed").length;
3096
3407
  const noSource = results.filter((r) => r.status === "no source").length;
3097
- console.log(chalk17.grey(`
3098
- ${results.length} tool(s): `) + chalk17.green(`${updated} updated`) + ", " + chalk17.grey(`${unchanged} unchanged`) + ", " + chalk17.red(`${failed} failed`) + ", " + chalk17.yellow(`${noSource} no source`));
3408
+ console.log(chalk19.grey(`
3409
+ ${results.length} tool(s): `) + chalk19.green(`${updated} updated`) + ", " + chalk19.grey(`${unchanged} unchanged`) + ", " + chalk19.red(`${failed} failed`) + ", " + chalk19.yellow(`${noSource} no source`));
3099
3410
  }
3100
3411
  async function fetchVersions(source) {
3101
3412
  const controller = new AbortController();
@@ -3153,10 +3464,10 @@ async function fetchGitHubVersions(repo, signal) {
3153
3464
  init_mcp_watch();
3154
3465
 
3155
3466
  // dist/commands/init-manifest.js
3156
- import { resolve as resolve5, join as join13, relative as relative3, dirname as dirname3 } from "node:path";
3157
- import { readFile as readFile9, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
3467
+ import { resolve as resolve5, join as join15, relative as relative4, dirname as dirname4 } from "node:path";
3468
+ import { readFile as readFile11, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
3158
3469
  import { existsSync as existsSync12 } from "node:fs";
3159
- import chalk19 from "chalk";
3470
+ import chalk21 from "chalk";
3160
3471
  var SECRET_PATTERNS = [
3161
3472
  /_KEY$/i,
3162
3473
  /_SECRET$/i,
@@ -3172,7 +3483,7 @@ function isLikelySecret(name) {
3172
3483
  async function discoverEnvFiles(projectRoot) {
3173
3484
  const apps = [];
3174
3485
  for (const envName of [".env", ".env.local", ".env.example"]) {
3175
- const envPath = join13(projectRoot, envName);
3486
+ const envPath = join15(projectRoot, envName);
3176
3487
  if (existsSync12(envPath)) {
3177
3488
  const vars = await extractVarNames(envPath);
3178
3489
  if (vars.length > 0) {
@@ -3183,11 +3494,11 @@ async function discoverEnvFiles(projectRoot) {
3183
3494
  }
3184
3495
  const subdirs = ["web", "cli", "api", "app", "server", "packages"];
3185
3496
  for (const sub of subdirs) {
3186
- const subDir = join13(projectRoot, sub);
3497
+ const subDir = join15(projectRoot, sub);
3187
3498
  if (!existsSync12(subDir))
3188
3499
  continue;
3189
3500
  for (const envName of [".env.local", ".env", ".env.example"]) {
3190
- const envPath = join13(subDir, envName);
3501
+ const envPath = join15(subDir, envName);
3191
3502
  if (existsSync12(envPath)) {
3192
3503
  const vars = await extractVarNames(envPath);
3193
3504
  if (vars.length > 0) {
@@ -3204,7 +3515,7 @@ async function discoverEnvFiles(projectRoot) {
3204
3515
  return apps;
3205
3516
  }
3206
3517
  async function extractVarNames(envPath) {
3207
- const content = await readFile9(envPath, "utf-8");
3518
+ const content = await readFile11(envPath, "utf-8");
3208
3519
  const vars = [];
3209
3520
  for (const line of content.split("\n")) {
3210
3521
  const trimmed = line.trim();
@@ -3249,38 +3560,38 @@ function generateManifest(apps) {
3249
3560
  }
3250
3561
  async function initManifestCommand() {
3251
3562
  const projectRoot = resolve5(process.cwd());
3252
- const manifestPath = join13(projectRoot, "docs", "reference", "env-manifest.md");
3563
+ const manifestPath = join15(projectRoot, "docs", "reference", "env-manifest.md");
3253
3564
  if (existsSync12(manifestPath)) {
3254
- console.log(chalk19.yellow(`Manifest already exists: ${relative3(projectRoot, manifestPath)}`));
3255
- console.log(chalk19.dim("Edit it directly to make changes."));
3565
+ console.log(chalk21.yellow(`Manifest already exists: ${relative4(projectRoot, manifestPath)}`));
3566
+ console.log(chalk21.dim("Edit it directly to make changes."));
3256
3567
  return;
3257
3568
  }
3258
- console.log(chalk19.blue("Scanning for .env files...\n"));
3569
+ console.log(chalk21.blue("Scanning for .env files...\n"));
3259
3570
  const apps = await discoverEnvFiles(projectRoot);
3260
3571
  if (apps.length === 0) {
3261
- console.log(chalk19.yellow("No .env files found. Create a manifest manually at:"));
3262
- console.log(chalk19.cyan(` docs/reference/env-manifest.md`));
3572
+ console.log(chalk21.yellow("No .env files found. Create a manifest manually at:"));
3573
+ console.log(chalk21.cyan(` docs/reference/env-manifest.md`));
3263
3574
  return;
3264
3575
  }
3265
3576
  for (const app of apps) {
3266
- console.log(` ${chalk19.green(app.name)}: ${app.envFilePath} (${app.vars.length} vars)`);
3577
+ console.log(` ${chalk21.green(app.name)}: ${app.envFilePath} (${app.vars.length} vars)`);
3267
3578
  }
3268
3579
  const content = generateManifest(apps);
3269
- const dir = dirname3(manifestPath);
3580
+ const dir = dirname4(manifestPath);
3270
3581
  if (!existsSync12(dir)) {
3271
3582
  await mkdir4(dir, { recursive: true });
3272
3583
  }
3273
3584
  await writeFile5(manifestPath, content, "utf-8");
3274
- console.log(chalk19.green(`
3275
- Manifest created: ${relative3(projectRoot, manifestPath)}`));
3276
- console.log(chalk19.dim("Review and edit the file \u2014 it is your source of truth."));
3277
- console.log(chalk19.dim("Then run `md4ai scan` to verify against your environments."));
3585
+ console.log(chalk21.green(`
3586
+ Manifest created: ${relative4(projectRoot, manifestPath)}`));
3587
+ console.log(chalk21.dim("Review and edit the file \u2014 it is your source of truth."));
3588
+ console.log(chalk21.dim("Then run `md4ai scan` to verify against your environments."));
3278
3589
  }
3279
3590
 
3280
3591
  // dist/commands/update.js
3281
3592
  init_check_update();
3282
3593
  init_config();
3283
- import chalk20 from "chalk";
3594
+ import chalk22 from "chalk";
3284
3595
  import { execFileSync as execFileSync6, spawn } from "node:child_process";
3285
3596
  async function fetchLatestVersion() {
3286
3597
  try {
@@ -3329,13 +3640,13 @@ function spawnPostUpdate() {
3329
3640
  }
3330
3641
  async function postUpdateFlow() {
3331
3642
  const { confirm: confirm4 } = await import("@inquirer/prompts");
3332
- console.log(chalk20.green(`
3643
+ console.log(chalk22.green(`
3333
3644
  md4ai v${CURRENT_VERSION} \u2014 you're on the latest version.
3334
3645
  `));
3335
3646
  const creds = await loadCredentials();
3336
3647
  const isLoggedIn = !!creds?.accessToken && Date.now() < creds.expiresAt;
3337
3648
  if (!isLoggedIn) {
3338
- console.log(chalk20.yellow(" You are not logged in.\n"));
3649
+ console.log(chalk22.yellow(" You are not logged in.\n"));
3339
3650
  const wantLogin = await confirm4({
3340
3651
  message: "Log in now?",
3341
3652
  default: true
@@ -3346,7 +3657,7 @@ async function postUpdateFlow() {
3346
3657
  await loginCommand2();
3347
3658
  console.log("");
3348
3659
  } else {
3349
- console.log(chalk20.dim("\nRun md4ai login when you're ready.\n"));
3660
+ console.log(chalk22.dim("\nRun md4ai login when you're ready.\n"));
3350
3661
  return;
3351
3662
  }
3352
3663
  }
@@ -3380,7 +3691,7 @@ async function postUpdateFlow() {
3380
3691
  const { mcpWatchCommand: mcpWatchCommand2 } = await Promise.resolve().then(() => (init_mcp_watch(), mcp_watch_exports));
3381
3692
  await mcpWatchCommand2();
3382
3693
  } else {
3383
- console.log(chalk20.dim("\nYou can start monitoring later with: md4ai mcp-watch\n"));
3694
+ console.log(chalk22.dim("\nYou can start monitoring later with: md4ai mcp-watch\n"));
3384
3695
  }
3385
3696
  }
3386
3697
  async function updateCommand(options) {
@@ -3388,19 +3699,19 @@ async function updateCommand(options) {
3388
3699
  await postUpdateFlow();
3389
3700
  return;
3390
3701
  }
3391
- console.log(chalk20.blue(`
3702
+ console.log(chalk22.blue(`
3392
3703
  Current version: v${CURRENT_VERSION}`));
3393
- console.log(chalk20.dim(" Checking for updates...\n"));
3704
+ console.log(chalk22.dim(" Checking for updates...\n"));
3394
3705
  const latest = await fetchLatestVersion();
3395
3706
  if (!latest) {
3396
- console.log(chalk20.yellow(" Could not reach the npm registry. Check your internet connection."));
3707
+ console.log(chalk22.yellow(" Could not reach the npm registry. Check your internet connection."));
3397
3708
  process.exit(1);
3398
3709
  }
3399
3710
  if (!isNewer2(latest, CURRENT_VERSION)) {
3400
3711
  await postUpdateFlow();
3401
3712
  return;
3402
3713
  }
3403
- console.log(chalk20.white(" Update available: ") + chalk20.dim(`v${CURRENT_VERSION}`) + chalk20.white(" \u2192 ") + chalk20.green.bold(`v${latest}
3714
+ console.log(chalk22.white(" Update available: ") + chalk22.dim(`v${CURRENT_VERSION}`) + chalk22.white(" \u2192 ") + chalk22.green.bold(`v${latest}
3404
3715
  `));
3405
3716
  const { confirm: confirm4 } = await import("@inquirer/prompts");
3406
3717
  const wantUpdate = await confirm4({
@@ -3408,20 +3719,38 @@ async function updateCommand(options) {
3408
3719
  default: true
3409
3720
  });
3410
3721
  if (!wantUpdate) {
3411
- console.log(chalk20.dim("\nUpdate skipped.\n"));
3722
+ console.log(chalk22.dim("\nUpdate skipped.\n"));
3412
3723
  return;
3413
3724
  }
3414
- console.log(chalk20.blue("\n Installing...\n"));
3725
+ console.log(chalk22.blue("\n Installing...\n"));
3415
3726
  const ok = runNpmInstall();
3416
3727
  if (!ok) {
3417
- console.log(chalk20.red("\n npm install failed. Try running manually:"));
3418
- console.log(chalk20.cyan(" npm install -g md4ai\n"));
3728
+ console.log(chalk22.red("\n npm install failed. Try running manually:"));
3729
+ console.log(chalk22.cyan(" npm install -g md4ai\n"));
3419
3730
  process.exit(1);
3420
3731
  }
3421
- console.log(chalk20.green("\n Updated successfully.\n"));
3732
+ console.log(chalk22.green("\n Updated successfully.\n"));
3422
3733
  spawnPostUpdate();
3423
3734
  }
3424
3735
 
3736
+ // dist/commands/config.js
3737
+ init_config();
3738
+ import chalk23 from "chalk";
3739
+ var ALLOWED_KEYS = ["vercel-token"];
3740
+ var KEY_MAP = {
3741
+ "vercel-token": "vercelToken"
3742
+ };
3743
+ async function configSetCommand(key, value) {
3744
+ if (!ALLOWED_KEYS.includes(key)) {
3745
+ console.error(chalk23.red(`Unknown config key: ${key}`));
3746
+ console.log(` Allowed keys: ${ALLOWED_KEYS.join(", ")}`);
3747
+ process.exit(1);
3748
+ }
3749
+ const credKey = KEY_MAP[key];
3750
+ await mergeCredentials({ [credKey]: value });
3751
+ console.log(chalk23.green(`Saved ${key}.`));
3752
+ }
3753
+
3425
3754
  // dist/index.js
3426
3755
  init_check_update();
3427
3756
  var program = new Command();
@@ -3444,6 +3773,8 @@ program.command("update").description("Check for updates, install, and optionall
3444
3773
  program.command("check-update").description("Check if a newer version of md4ai is available").action(checkForUpdate);
3445
3774
  program.command("mcp-watch").description("Monitor MCP server status on this device (runs until Ctrl+C)").action(mcpWatchCommand);
3446
3775
  program.command("init-manifest").description("Scaffold a starter env-manifest.md from detected .env files").action(initManifestCommand);
3776
+ var config = program.command("config").description("Manage CLI configuration");
3777
+ config.command("set <key> <value>").description("Set a configuration value (e.g. vercel-token)").action(configSetCommand);
3447
3778
  var admin = program.command("admin").description("Admin commands for managing the tools registry");
3448
3779
  admin.command("update-tool").description("Add or update a tool in the master registry").requiredOption("--name <name>", "Canonical tool name (e.g. next, playwright)").option("--display <display>", 'Human-friendly display name (e.g. "Next.js")').option("--category <category>", "Tool category (framework|runtime|cli|mcp|package|database|other)").option("--stable <version>", "Latest stable version").option("--beta <version>", "Latest beta/RC version").option("--source <url>", "Source of truth URL for checking versions").option("--install <url>", "Download/install link").option("--notes <text>", "Compatibility notes or warnings").action(adminUpdateToolCommand);
3449
3780
  admin.command("list-tools").description("List all tools in the master registry").action(adminListToolsCommand);