md4ai 0.9.4 → 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 +572 -262
  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",
@@ -1151,12 +1347,12 @@ async function readClaudeConfigFiles(projectRoot) {
1151
1347
  const files = [];
1152
1348
  const seen = /* @__PURE__ */ new Set();
1153
1349
  async function addFile(fullPath) {
1154
- const relPath = relative4(projectRoot, fullPath);
1350
+ const relPath = relative5(projectRoot, fullPath);
1155
1351
  if (seen.has(relPath))
1156
1352
  return;
1157
1353
  seen.add(relPath);
1158
1354
  try {
1159
- const content = await readFile10(fullPath, "utf-8");
1355
+ const content = await readFile12(fullPath, "utf-8");
1160
1356
  const fileStat = await stat(fullPath);
1161
1357
  const lastMod = getGitLastModified(fullPath, projectRoot);
1162
1358
  files.push({ filePath: relPath, content, sizeBytes: fileStat.size, lastModified: lastMod });
@@ -1164,22 +1360,22 @@ async function readClaudeConfigFiles(projectRoot) {
1164
1360
  }
1165
1361
  }
1166
1362
  for (const pattern of configPatterns) {
1167
- for await (const fullPath of glob2(join14(projectRoot, pattern))) {
1363
+ for await (const fullPath of glob3(join16(projectRoot, pattern))) {
1168
1364
  if (!existsSync13(fullPath))
1169
1365
  continue;
1170
1366
  await addFile(fullPath);
1171
1367
  }
1172
1368
  }
1173
- const pkgPath = join14(projectRoot, "package.json");
1369
+ const pkgPath = join16(projectRoot, "package.json");
1174
1370
  if (existsSync13(pkgPath)) {
1175
1371
  try {
1176
- const pkgContent = await readFile10(pkgPath, "utf-8");
1372
+ const pkgContent = await readFile12(pkgPath, "utf-8");
1177
1373
  const pkg = JSON.parse(pkgContent);
1178
1374
  const preflightCmd = pkg.scripts?.preflight;
1179
1375
  if (preflightCmd) {
1180
1376
  const match = preflightCmd.match(/(?:bash\s+|sh\s+|\.\/)?(\S+\.(?:sh|bash|ts|js|mjs))/);
1181
1377
  if (match) {
1182
- const scriptPath = join14(projectRoot, match[1]);
1378
+ const scriptPath = join16(projectRoot, match[1]);
1183
1379
  if (existsSync13(scriptPath)) {
1184
1380
  await addFile(scriptPath);
1185
1381
  }
@@ -1194,7 +1390,7 @@ function detectStaleFiles(allFiles, projectRoot) {
1194
1390
  const stale = [];
1195
1391
  const now = Date.now();
1196
1392
  for (const file of allFiles) {
1197
- const fullPath = join8(projectRoot, file);
1393
+ const fullPath = join10(projectRoot, file);
1198
1394
  const lastMod = getGitLastModified(fullPath, projectRoot);
1199
1395
  if (lastMod) {
1200
1396
  const days = Math.floor((now - new Date(lastMod).getTime()) / (1e3 * 60 * 60 * 24));
@@ -1336,7 +1532,7 @@ var init_html_generator = __esm({
1336
1532
  });
1337
1533
 
1338
1534
  // dist/check-update.js
1339
- import chalk8 from "chalk";
1535
+ import chalk9 from "chalk";
1340
1536
  async function fetchLatest() {
1341
1537
  try {
1342
1538
  const controller = new AbortController();
@@ -1355,12 +1551,12 @@ async function fetchLatest() {
1355
1551
  }
1356
1552
  function printUpdateBanner(latest) {
1357
1553
  console.log("");
1358
- 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"));
1359
- console.log(chalk8.yellow("\u2502") + chalk8.bold(" Update available! ") + chalk8.dim(`${CURRENT_VERSION}`) + chalk8.white(" \u2192 ") + chalk8.green.bold(`${latest}`) + " " + chalk8.yellow("\u2502"));
1360
- console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
1361
- console.log(chalk8.yellow("\u2502") + " Run: " + chalk8.cyan("md4ai update") + " " + chalk8.yellow("\u2502"));
1362
- console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
1363
- 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"));
1364
1560
  console.log("");
1365
1561
  }
1366
1562
  async function checkForUpdate() {
@@ -1370,7 +1566,7 @@ async function checkForUpdate() {
1370
1566
  if (latest !== CURRENT_VERSION && isNewer(latest, CURRENT_VERSION)) {
1371
1567
  printUpdateBanner(latest);
1372
1568
  } else {
1373
- 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.`));
1374
1570
  }
1375
1571
  }
1376
1572
  async function autoCheckForUpdate() {
@@ -1399,7 +1595,7 @@ var CURRENT_VERSION;
1399
1595
  var init_check_update = __esm({
1400
1596
  "dist/check-update.js"() {
1401
1597
  "use strict";
1402
- CURRENT_VERSION = true ? "0.9.4" : "0.0.0-dev";
1598
+ CURRENT_VERSION = true ? "0.9.5" : "0.0.0-dev";
1403
1599
  }
1404
1600
  });
1405
1601
 
@@ -1427,6 +1623,87 @@ var init_push_toolings = __esm({
1427
1623
  }
1428
1624
  });
1429
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
+
1430
1707
  // dist/device-utils.js
1431
1708
  import { hostname as hostname2, platform as platform2 } from "node:os";
1432
1709
  function detectOs2() {
@@ -1473,16 +1750,16 @@ __export(map_exports, {
1473
1750
  import { resolve as resolve3, basename } from "node:path";
1474
1751
  import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
1475
1752
  import { existsSync as existsSync7 } from "node:fs";
1476
- import chalk9 from "chalk";
1753
+ import chalk11 from "chalk";
1477
1754
  import { select as select3, input as input5, confirm as confirm2 } from "@inquirer/prompts";
1478
1755
  async function mapCommand(path, options) {
1479
1756
  await checkForUpdate();
1480
1757
  const projectRoot = resolve3(path ?? process.cwd());
1481
1758
  if (!existsSync7(projectRoot)) {
1482
- console.error(chalk9.red(`Path not found: ${projectRoot}`));
1759
+ console.error(chalk11.red(`Path not found: ${projectRoot}`));
1483
1760
  process.exit(1);
1484
1761
  }
1485
- console.log(chalk9.blue(`Scanning: ${projectRoot}
1762
+ console.log(chalk11.blue(`Scanning: ${projectRoot}
1486
1763
  `));
1487
1764
  const result = await scanProject(projectRoot);
1488
1765
  console.log(` Files found: ${result.graph.nodes.length}`);
@@ -1500,7 +1777,7 @@ async function mapCommand(path, options) {
1500
1777
  const htmlPath = resolve3(outputDir, "index.html");
1501
1778
  const html = generateOfflineHtml(result, projectRoot);
1502
1779
  await writeFile2(htmlPath, html, "utf-8");
1503
- console.log(chalk9.green(`
1780
+ console.log(chalk11.green(`
1504
1781
  Local preview: ${htmlPath}`));
1505
1782
  if (!options.offline) {
1506
1783
  try {
@@ -1509,9 +1786,9 @@ Local preview: ${htmlPath}`));
1509
1786
  if (devicePaths?.length) {
1510
1787
  const { folder_id, device_name } = devicePaths[0];
1511
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);
1512
- if (proposedFiles?.length) {
1789
+ if (proposedFiles?.length && process.stdin.isTTY) {
1513
1790
  const { checkbox } = await import("@inquirer/prompts");
1514
- console.log(chalk9.yellow(`
1791
+ console.log(chalk11.yellow(`
1515
1792
  ${proposedFiles.length} file(s) proposed for deletion:
1516
1793
  `));
1517
1794
  const toDelete = await checkbox({
@@ -1527,9 +1804,9 @@ ${proposedFiles.length} file(s) proposed for deletion:
1527
1804
  const { unlink } = await import("node:fs/promises");
1528
1805
  await unlink(fullPath);
1529
1806
  await supabase.from("folder_files").delete().eq("id", file.id);
1530
- console.log(chalk9.green(` Deleted: ${file.file_path}`));
1807
+ console.log(chalk11.green(` Deleted: ${file.file_path}`));
1531
1808
  } catch (err) {
1532
- console.error(chalk9.red(` Failed to delete ${file.file_path}: ${err}`));
1809
+ console.error(chalk11.red(` Failed to delete ${file.file_path}: ${err}`));
1533
1810
  }
1534
1811
  }
1535
1812
  const keptIds = proposedFiles.filter((f) => !toDelete.some((d) => d.id === f.id)).map((f) => f.id);
@@ -1537,9 +1814,11 @@ ${proposedFiles.length} file(s) proposed for deletion:
1537
1814
  for (const id of keptIds) {
1538
1815
  await supabase.from("folder_files").update({ proposed_for_deletion: false, proposed_at: null, proposed_by: null }).eq("id", id);
1539
1816
  }
1540
- 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.`));
1541
1818
  }
1542
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).`));
1543
1822
  }
1544
1823
  const { error } = await supabase.from("claude_folders").update({
1545
1824
  graph_json: result.graph,
@@ -1551,17 +1830,20 @@ ${proposedFiles.length} file(s) proposed for deletion:
1551
1830
  data_hash: result.dataHash
1552
1831
  }).eq("id", folder_id);
1553
1832
  if (error) {
1554
- console.error(chalk9.yellow(`Sync warning: ${error.message}`));
1833
+ console.error(chalk11.yellow(`Sync warning: ${error.message}`));
1555
1834
  } else {
1556
1835
  await pushToolings(supabase, folder_id, result.toolings);
1836
+ if (result.envManifest) {
1837
+ await pushHealthResults(supabase, folder_id, result.envManifest);
1838
+ }
1557
1839
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", folder_id).eq("device_name", device_name);
1558
1840
  await saveState({
1559
1841
  lastFolderId: folder_id,
1560
1842
  lastDeviceName: device_name,
1561
1843
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
1562
1844
  });
1563
- console.log(chalk9.green("Synced to Supabase."));
1564
- console.log(chalk9.cyan(`
1845
+ console.log(chalk11.green("Synced to Supabase."));
1846
+ console.log(chalk11.cyan(`
1565
1847
  https://www.md4ai.com/project/${folder_id}
1566
1848
  `));
1567
1849
  const configFiles = await readClaudeConfigFiles(projectRoot);
@@ -1575,14 +1857,18 @@ ${proposedFiles.length} file(s) proposed for deletion:
1575
1857
  last_modified: file.lastModified
1576
1858
  }, { onConflict: "folder_id,file_path" });
1577
1859
  }
1578
- console.log(chalk9.green(` Uploaded ${configFiles.length} config file(s).`));
1860
+ console.log(chalk11.green(` Uploaded ${configFiles.length} config file(s).`));
1579
1861
  }
1580
1862
  }
1581
1863
  } else {
1582
- 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
+ }
1583
1869
  const shouldLink = await confirm2({ message: "Would you like to link it now?" });
1584
1870
  if (!shouldLink) {
1585
- console.log(chalk9.dim("Skipped \u2014 local preview still generated."));
1871
+ console.log(chalk11.dim("Skipped \u2014 local preview still generated."));
1586
1872
  } else {
1587
1873
  const { supabase: sb, userId } = await getAuthenticatedClient();
1588
1874
  const { data: folders } = await sb.from("claude_folders").select("id, name").order("name");
@@ -1602,11 +1888,11 @@ ${proposedFiles.length} file(s) proposed for deletion:
1602
1888
  });
1603
1889
  const { data: newFolder, error: createErr } = await sb.from("claude_folders").insert({ user_id: userId, name: projectName }).select("id").single();
1604
1890
  if (createErr || !newFolder) {
1605
- console.error(chalk9.red(`Failed to create project: ${createErr?.message}`));
1891
+ console.error(chalk11.red(`Failed to create project: ${createErr?.message}`));
1606
1892
  return;
1607
1893
  }
1608
1894
  folderId = newFolder.id;
1609
- console.log(chalk9.green(`Created project "${projectName}".`));
1895
+ console.log(chalk11.green(`Created project "${projectName}".`));
1610
1896
  } else {
1611
1897
  folderId = chosen;
1612
1898
  }
@@ -1635,6 +1921,9 @@ ${proposedFiles.length} file(s) proposed for deletion:
1635
1921
  data_hash: result.dataHash
1636
1922
  }).eq("id", folderId);
1637
1923
  await pushToolings(sb, folderId, result.toolings);
1924
+ if (result.envManifest) {
1925
+ await pushHealthResults(sb, folderId, result.envManifest);
1926
+ }
1638
1927
  const configFiles = await readClaudeConfigFiles(projectRoot);
1639
1928
  for (const file of configFiles) {
1640
1929
  await sb.from("folder_files").upsert({
@@ -1650,14 +1939,14 @@ ${proposedFiles.length} file(s) proposed for deletion:
1650
1939
  lastDeviceName: deviceName,
1651
1940
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
1652
1941
  });
1653
- console.log(chalk9.green("\nLinked and synced."));
1654
- console.log(chalk9.cyan(`
1942
+ console.log(chalk11.green("\nLinked and synced."));
1943
+ console.log(chalk11.cyan(`
1655
1944
  https://www.md4ai.com/project/${folderId}
1656
1945
  `));
1657
1946
  }
1658
1947
  }
1659
1948
  } catch {
1660
- console.log(chalk9.yellow("Not logged in \u2014 local preview only."));
1949
+ console.log(chalk11.yellow("Not logged in \u2014 local preview only."));
1661
1950
  }
1662
1951
  }
1663
1952
  }
@@ -1670,6 +1959,7 @@ var init_map = __esm({
1670
1959
  init_html_generator();
1671
1960
  init_check_update();
1672
1961
  init_push_toolings();
1962
+ init_push_health_results();
1673
1963
  init_device_utils();
1674
1964
  }
1675
1965
  });
@@ -1679,20 +1969,20 @@ var sync_exports = {};
1679
1969
  __export(sync_exports, {
1680
1970
  syncCommand: () => syncCommand
1681
1971
  });
1682
- import chalk12 from "chalk";
1972
+ import chalk14 from "chalk";
1683
1973
  async function syncCommand(options) {
1684
1974
  const { supabase } = await getAuthenticatedClient();
1685
1975
  if (options.all) {
1686
1976
  const { data: devices, error } = await supabase.from("device_paths").select("folder_id, device_name, path");
1687
1977
  if (error || !devices?.length) {
1688
- console.error(chalk12.red("No devices found."));
1978
+ console.error(chalk14.red("No devices found."));
1689
1979
  process.exit(1);
1690
1980
  }
1691
1981
  for (const device of devices) {
1692
- console.log(chalk12.blue(`Syncing: ${device.path}`));
1982
+ console.log(chalk14.blue(`Syncing: ${device.path}`));
1693
1983
  const { data: proposedAll } = await supabase.from("folder_files").select("file_path").eq("folder_id", device.folder_id).eq("proposed_for_deletion", true);
1694
1984
  if (proposedAll?.length) {
1695
- 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.`));
1696
1986
  }
1697
1987
  try {
1698
1988
  const result = await scanProject(device.path);
@@ -1707,28 +1997,28 @@ async function syncCommand(options) {
1707
1997
  }).eq("id", device.folder_id);
1708
1998
  await pushToolings(supabase, device.folder_id, result.toolings);
1709
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);
1710
- console.log(chalk12.green(` Done: ${device.device_name}`));
2000
+ console.log(chalk14.green(` Done: ${device.device_name}`));
1711
2001
  } catch (err) {
1712
- console.error(chalk12.red(` Failed: ${device.path}: ${err}`));
2002
+ console.error(chalk14.red(` Failed: ${device.path}: ${err}`));
1713
2003
  }
1714
2004
  }
1715
2005
  await saveState({ lastSyncAt: (/* @__PURE__ */ new Date()).toISOString() });
1716
- console.log(chalk12.green("\nAll devices synced."));
2006
+ console.log(chalk14.green("\nAll devices synced."));
1717
2007
  } else {
1718
2008
  const state = await loadState();
1719
2009
  if (!state.lastFolderId) {
1720
- 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."));
1721
2011
  process.exit(1);
1722
2012
  }
1723
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();
1724
2014
  if (!device) {
1725
- console.error(chalk12.red("Could not find last synced device/folder."));
2015
+ console.error(chalk14.red("Could not find last synced device/folder."));
1726
2016
  process.exit(1);
1727
2017
  }
1728
- console.log(chalk12.blue(`Syncing: ${device.path}`));
2018
+ console.log(chalk14.blue(`Syncing: ${device.path}`));
1729
2019
  const { data: proposedSingle } = await supabase.from("folder_files").select("file_path").eq("folder_id", device.folder_id).eq("proposed_for_deletion", true);
1730
2020
  if (proposedSingle?.length) {
1731
- 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.`));
1732
2022
  }
1733
2023
  const result = await scanProject(device.path);
1734
2024
  await supabase.from("claude_folders").update({
@@ -1742,7 +2032,7 @@ async function syncCommand(options) {
1742
2032
  await pushToolings(supabase, device.folder_id, result.toolings);
1743
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);
1744
2034
  await saveState({ lastSyncAt: (/* @__PURE__ */ new Date()).toISOString() });
1745
- console.log(chalk12.green("Synced."));
2035
+ console.log(chalk14.green("Synced."));
1746
2036
  }
1747
2037
  }
1748
2038
  var init_sync = __esm({
@@ -1756,16 +2046,16 @@ var init_sync = __esm({
1756
2046
  });
1757
2047
 
1758
2048
  // dist/mcp/read-configs.js
1759
- import { readFile as readFile8 } from "node:fs/promises";
1760
- import { join as join12 } from "node:path";
1761
- 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";
1762
2052
  import { existsSync as existsSync11 } from "node:fs";
1763
2053
  import { readdir as readdir3 } from "node:fs/promises";
1764
2054
  async function readJsonSafe(path) {
1765
2055
  try {
1766
2056
  if (!existsSync11(path))
1767
2057
  return null;
1768
- const raw = await readFile8(path, "utf-8");
2058
+ const raw = await readFile10(path, "utf-8");
1769
2059
  return JSON.parse(raw);
1770
2060
  } catch {
1771
2061
  return null;
@@ -1825,52 +2115,52 @@ function parseFlatConfig(data, source) {
1825
2115
  return entries;
1826
2116
  }
1827
2117
  async function readAllMcpConfigs() {
1828
- const home = homedir7();
2118
+ const home = homedir8();
1829
2119
  const entries = [];
1830
- const userConfig = await readJsonSafe(join12(home, ".claude.json"));
2120
+ const userConfig = await readJsonSafe(join14(home, ".claude.json"));
1831
2121
  entries.push(...parseServers(userConfig, "global"));
1832
- const globalMcp = await readJsonSafe(join12(home, ".claude", "mcp.json"));
2122
+ const globalMcp = await readJsonSafe(join14(home, ".claude", "mcp.json"));
1833
2123
  entries.push(...parseServers(globalMcp, "global"));
1834
- const cwdMcp = await readJsonSafe(join12(process.cwd(), ".mcp.json"));
2124
+ const cwdMcp = await readJsonSafe(join14(process.cwd(), ".mcp.json"));
1835
2125
  entries.push(...parseServers(cwdMcp, "project"));
1836
- const pluginsBase = join12(home, ".claude", "plugins", "marketplaces");
2126
+ const pluginsBase = join14(home, ".claude", "plugins", "marketplaces");
1837
2127
  if (existsSync11(pluginsBase)) {
1838
2128
  try {
1839
2129
  const marketplaces = await readdir3(pluginsBase, { withFileTypes: true });
1840
2130
  for (const mp of marketplaces) {
1841
2131
  if (!mp.isDirectory())
1842
2132
  continue;
1843
- const extDir = join12(pluginsBase, mp.name, "external_plugins");
2133
+ const extDir = join14(pluginsBase, mp.name, "external_plugins");
1844
2134
  if (!existsSync11(extDir))
1845
2135
  continue;
1846
2136
  const plugins = await readdir3(extDir, { withFileTypes: true });
1847
2137
  for (const plugin of plugins) {
1848
2138
  if (!plugin.isDirectory())
1849
2139
  continue;
1850
- const pluginMcp = await readJsonSafe(join12(extDir, plugin.name, ".mcp.json"));
2140
+ const pluginMcp = await readJsonSafe(join14(extDir, plugin.name, ".mcp.json"));
1851
2141
  entries.push(...parseServers(pluginMcp, "plugin"));
1852
2142
  }
1853
2143
  }
1854
2144
  } catch {
1855
2145
  }
1856
2146
  }
1857
- const cacheBase = join12(home, ".claude", "plugins", "cache");
2147
+ const cacheBase = join14(home, ".claude", "plugins", "cache");
1858
2148
  if (existsSync11(cacheBase)) {
1859
2149
  try {
1860
2150
  const registries = await readdir3(cacheBase, { withFileTypes: true });
1861
2151
  for (const reg of registries) {
1862
2152
  if (!reg.isDirectory())
1863
2153
  continue;
1864
- const regDir = join12(cacheBase, reg.name);
2154
+ const regDir = join14(cacheBase, reg.name);
1865
2155
  const plugins = await readdir3(regDir, { withFileTypes: true });
1866
2156
  for (const plugin of plugins) {
1867
2157
  if (!plugin.isDirectory())
1868
2158
  continue;
1869
- const versionDirs = await readdir3(join12(regDir, plugin.name), { withFileTypes: true });
2159
+ const versionDirs = await readdir3(join14(regDir, plugin.name), { withFileTypes: true });
1870
2160
  for (const ver of versionDirs) {
1871
2161
  if (!ver.isDirectory())
1872
2162
  continue;
1873
- const mcpPath = join12(regDir, plugin.name, ver.name, ".mcp.json");
2163
+ const mcpPath = join14(regDir, plugin.name, ver.name, ".mcp.json");
1874
2164
  const flatData = await readJsonSafe(mcpPath);
1875
2165
  if (flatData) {
1876
2166
  entries.push(...parseFlatConfig(flatData, "plugin"));
@@ -1934,23 +2224,23 @@ function parseEtime(etime) {
1934
2224
  }
1935
2225
  return days * 86400 + (parts[0] ?? 0);
1936
2226
  }
1937
- function findProcessesForConfig(config, processes) {
1938
- if (config.type === "http")
2227
+ function findProcessesForConfig(config2, processes) {
2228
+ if (config2.type === "http")
1939
2229
  return [];
1940
- const packageName = config.args ? extractPackageName(config.args) : null;
2230
+ const packageName = config2.args ? extractPackageName(config2.args) : null;
1941
2231
  const matches = [];
1942
2232
  const searchTerms = [];
1943
2233
  if (packageName)
1944
2234
  searchTerms.push(packageName);
1945
- if (config.command === "node" && config.args?.[0]) {
1946
- searchTerms.push(config.args[0]);
2235
+ if (config2.command === "node" && config2.args?.[0]) {
2236
+ searchTerms.push(config2.args[0]);
1947
2237
  }
1948
- if (config.name) {
1949
- searchTerms.push(`mcp-server-${config.name}`);
1950
- searchTerms.push(`${config.name}-mcp`);
2238
+ if (config2.name) {
2239
+ searchTerms.push(`mcp-server-${config2.name}`);
2240
+ searchTerms.push(`${config2.name}-mcp`);
1951
2241
  }
1952
- if ((config.command === "uvx" || config.command === "pipx") && config.args) {
1953
- for (const arg of config.args) {
2242
+ if ((config2.command === "uvx" || config2.command === "pipx") && config2.args) {
2243
+ for (const arg of config2.args) {
1954
2244
  if (!arg.startsWith("-") && arg !== "run")
1955
2245
  searchTerms.push(arg);
1956
2246
  }
@@ -2000,7 +2290,7 @@ var mcp_watch_exports = {};
2000
2290
  __export(mcp_watch_exports, {
2001
2291
  mcpWatchCommand: () => mcpWatchCommand
2002
2292
  });
2003
- import chalk18 from "chalk";
2293
+ import chalk20 from "chalk";
2004
2294
  import { execFileSync as execFileSync5 } from "node:child_process";
2005
2295
  import { createHash as createHash2 } from "node:crypto";
2006
2296
  function detectTty() {
@@ -2014,10 +2304,10 @@ function detectTty() {
2014
2304
  return null;
2015
2305
  }
2016
2306
  }
2017
- function checkEnvVars(config) {
2018
- const required = config.env ? Object.keys(config.env) : [];
2307
+ function checkEnvVars(config2) {
2308
+ const required = config2.env ? Object.keys(config2.env) : [];
2019
2309
  const missing = required.filter((key) => {
2020
- const configValue = config.env?.[key];
2310
+ const configValue = config2.env?.[key];
2021
2311
  const hasConfigValue = configValue && !configValue.startsWith("${");
2022
2312
  return !hasConfigValue && !process.env[key];
2023
2313
  });
@@ -2052,20 +2342,20 @@ async function checkChromeCdp() {
2052
2342
  function buildRows(configs, httpResults, cdpStatus) {
2053
2343
  const processes = getProcessTable();
2054
2344
  const rows = [];
2055
- for (const config of configs) {
2056
- const { required, missing } = checkEnvVars(config);
2057
- const packageName = config.args ? extractPackageName(config.args) : null;
2058
- if (config.type === "http") {
2059
- 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";
2060
2350
  const status = reachability === "reachable" ? "running" : "stopped";
2061
2351
  const detail = reachability === "reachable" ? "HTTP \u2014 remote service reachable" : reachability === "unreachable" ? "HTTP \u2014 remote service unreachable" : "HTTP \u2014 could not verify";
2062
2352
  rows.push({
2063
- server_name: config.name,
2064
- config_source: config.source,
2353
+ server_name: config2.name,
2354
+ config_source: config2.source,
2065
2355
  server_type: "http",
2066
2356
  command: null,
2067
2357
  package_name: null,
2068
- http_url: config.url ?? null,
2358
+ http_url: config2.url ?? null,
2069
2359
  status,
2070
2360
  pid: null,
2071
2361
  session_tty: null,
@@ -2079,10 +2369,10 @@ function buildRows(configs, httpResults, cdpStatus) {
2079
2369
  }
2080
2370
  if (missing.length > 0) {
2081
2371
  rows.push({
2082
- server_name: config.name,
2083
- config_source: config.source,
2372
+ server_name: config2.name,
2373
+ config_source: config2.source,
2084
2374
  server_type: "stdio",
2085
- command: config.command ?? null,
2375
+ command: config2.command ?? null,
2086
2376
  package_name: packageName,
2087
2377
  http_url: null,
2088
2378
  status: "error",
@@ -2096,15 +2386,15 @@ function buildRows(configs, httpResults, cdpStatus) {
2096
2386
  });
2097
2387
  continue;
2098
2388
  }
2099
- 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"));
2100
2390
  const cdpDetail = isChromeDevtools && cdpStatus ? cdpStatus.status === "reachable" ? "Chrome CDP reachable" : "Chrome not reachable on port 9222" : null;
2101
- const matches = findProcessesForConfig(config, processes);
2391
+ const matches = findProcessesForConfig(config2, processes);
2102
2392
  if (matches.length === 0) {
2103
2393
  rows.push({
2104
- server_name: config.name,
2105
- config_source: config.source,
2394
+ server_name: config2.name,
2395
+ config_source: config2.source,
2106
2396
  server_type: "stdio",
2107
- command: config.command ?? null,
2397
+ command: config2.command ?? null,
2108
2398
  package_name: packageName,
2109
2399
  http_url: null,
2110
2400
  status: "stopped",
@@ -2127,10 +2417,10 @@ function buildRows(configs, httpResults, cdpStatus) {
2127
2417
  }
2128
2418
  for (const match of byTty.values()) {
2129
2419
  rows.push({
2130
- server_name: config.name,
2131
- config_source: config.source,
2420
+ server_name: config2.name,
2421
+ config_source: config2.source,
2132
2422
  server_type: "stdio",
2133
- command: config.command ?? null,
2423
+ command: config2.command ?? null,
2134
2424
  package_name: packageName,
2135
2425
  http_url: null,
2136
2426
  status: "running",
@@ -2149,20 +2439,20 @@ function buildRows(configs, httpResults, cdpStatus) {
2149
2439
  }
2150
2440
  function printTable(rows, deviceName, watcherPid, cdpResult) {
2151
2441
  process.stdout.write("\x1B[3J\x1B[2J\x1B[H");
2152
- console.log(chalk18.bold.cyan(`
2153
- MCP Monitor v${CURRENT_VERSION} \u2014 ${deviceName}`) + chalk18.dim(` (PID ${watcherPid})`));
2154
- 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
2155
2445
  `));
2156
2446
  if (cdpResult) {
2157
2447
  if (cdpResult.status === "reachable") {
2158
2448
  const browserInfo = cdpResult.browser ? ` (${cdpResult.browser})` : "";
2159
- console.log(chalk18.green(" \u2714 Chrome browser connected") + chalk18.dim(browserInfo));
2160
- 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"));
2161
2451
  } else {
2162
- console.log(chalk18.red(" \u2717 Chrome browser not connected"));
2163
- console.log(chalk18.dim(" Claude Code cannot reach Chrome. To fix:"));
2164
- console.log(chalk18.dim(" 1. Open Chrome with: google-chrome --remote-debugging-port=9222"));
2165
- 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"));
2166
2456
  }
2167
2457
  console.log("");
2168
2458
  }
@@ -2183,41 +2473,41 @@ function printTable(rows, deviceName, watcherPid, cdpResult) {
2183
2473
  sessionNum++;
2184
2474
  const totalMem = servers.reduce((sum, s) => sum + (s.memory_mb ?? 0), 0);
2185
2475
  const label = byTty.size === 1 ? "Claude Code session" : `Claude Code session ${sessionNum}`;
2186
- 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}`));
2187
2477
  for (const s of servers) {
2188
2478
  const uptime = formatUptime(s.uptime_seconds ?? 0);
2189
- const source = chalk18.dim(`[${s.config_source}]`);
2190
- 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}`);
2191
2481
  }
2192
2482
  console.log("");
2193
2483
  }
2194
2484
  if (byTty.size > 1) {
2195
- 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"));
2196
2486
  }
2197
2487
  }
2198
2488
  if (runningHttp.length > 0) {
2199
- 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"));
2200
2490
  for (const s of runningHttp) {
2201
- 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))}`);
2202
2492
  }
2203
2493
  console.log("");
2204
2494
  }
2205
2495
  if (stopped.length > 0 || errored.length > 0) {
2206
2496
  const notRunning = [...stopped, ...errored];
2207
- 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"));
2208
2498
  for (const s of notRunning) {
2209
- const icon = s.status === "error" ? chalk18.red("\u2717") : chalk18.yellow("\u25CB");
2210
- const source = chalk18.dim(`[${s.config_source}]`);
2211
- 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}`) : "";
2212
2502
  console.log(` ${icon} ${s.server_name.padEnd(20)} ${source}${detail}`);
2213
2503
  }
2214
2504
  console.log("");
2215
2505
  }
2216
2506
  if (rows.length === 0) {
2217
- console.log(chalk18.yellow(" No MCP servers configured."));
2218
- 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"));
2219
2509
  }
2220
- 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 "));
2221
2511
  console.log("");
2222
2512
  }
2223
2513
  function formatUptime(seconds) {
@@ -2240,7 +2530,7 @@ async function mcpWatchCommand() {
2240
2530
  const { data: existingWatchers } = await supabase.from("mcp_watchers").select("pid, tty, cli_version, started_at").eq("device_id", deviceId);
2241
2531
  if (existingWatchers && existingWatchers.length > 0) {
2242
2532
  console.log("");
2243
- 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...`));
2244
2534
  for (const w of existingWatchers) {
2245
2535
  try {
2246
2536
  process.kill(w.pid, "SIGTERM");
@@ -2249,15 +2539,15 @@ async function mcpWatchCommand() {
2249
2539
  }
2250
2540
  await supabase.from("mcp_watchers").delete().eq("device_id", deviceId);
2251
2541
  await new Promise((r) => setTimeout(r, 1e3));
2252
- console.log(chalk18.dim(" Previous watcher stopped.\n"));
2542
+ console.log(chalk20.dim(" Previous watcher stopped.\n"));
2253
2543
  }
2254
2544
  process.stdout.write(`\x1B]0;MCP mon\x07`);
2255
- console.log(chalk18.blue(`Starting MCP monitor for ${deviceName}...`));
2545
+ console.log(chalk20.blue(`Starting MCP monitor for ${deviceName}...`));
2256
2546
  console.log("");
2257
- console.log(chalk18.dim(" View this in the online dashboard at:"));
2258
- 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}`));
2259
2549
  console.log("");
2260
- 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."));
2261
2551
  console.log("");
2262
2552
  await supabase.from("mcp_watchers").upsert({
2263
2553
  device_id: deviceId,
@@ -2283,7 +2573,7 @@ async function mcpWatchCommand() {
2283
2573
  const now = (/* @__PURE__ */ new Date()).toISOString();
2284
2574
  const { error: deleteError } = await supabase.from("mcp_server_status").delete().eq("device_id", deviceId);
2285
2575
  if (deleteError) {
2286
- 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}`));
2287
2577
  }
2288
2578
  if (rows.length > 0) {
2289
2579
  const { error: insertError } = await supabase.from("mcp_server_status").insert(rows.map((row) => ({
@@ -2292,7 +2582,7 @@ async function mcpWatchCommand() {
2292
2582
  checked_at: now
2293
2583
  })));
2294
2584
  if (insertError) {
2295
- 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}`));
2296
2586
  }
2297
2587
  }
2298
2588
  await supabase.from("mcp_watchers").update({ last_heartbeat: now }).eq("device_id", deviceId).eq("pid", myPid);
@@ -2330,7 +2620,7 @@ async function mcpWatchCommand() {
2330
2620
  clearInterval(interval);
2331
2621
  clearInterval(envInterval);
2332
2622
  await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).eq("pid", myPid);
2333
- console.log(chalk18.dim("\nMCP monitor stopped."));
2623
+ console.log(chalk20.dim("\nMCP monitor stopped."));
2334
2624
  process.exit(0);
2335
2625
  };
2336
2626
  process.on("SIGINT", () => {
@@ -2594,71 +2884,71 @@ init_map();
2594
2884
 
2595
2885
  // dist/commands/simulate.js
2596
2886
  init_dist();
2597
- import { join as join9 } from "node:path";
2887
+ import { join as join11 } from "node:path";
2598
2888
  import { existsSync as existsSync8 } from "node:fs";
2599
- import { homedir as homedir6 } from "node:os";
2600
- import chalk10 from "chalk";
2889
+ import { homedir as homedir7 } from "node:os";
2890
+ import chalk12 from "chalk";
2601
2891
  async function simulateCommand(prompt) {
2602
2892
  const projectRoot = process.cwd();
2603
- console.log(chalk10.blue(`Simulating prompt: "${prompt}"
2893
+ console.log(chalk12.blue(`Simulating prompt: "${prompt}"
2604
2894
  `));
2605
- console.log(chalk10.dim("Files Claude would load:\n"));
2606
- 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");
2607
2897
  if (existsSync8(globalClaude)) {
2608
- console.log(chalk10.green(" \u2713 ~/.claude/CLAUDE.md (global)"));
2898
+ console.log(chalk12.green(" \u2713 ~/.claude/CLAUDE.md (global)"));
2609
2899
  }
2610
2900
  for (const rootFile of ROOT_FILES) {
2611
- const fullPath = join9(projectRoot, rootFile);
2901
+ const fullPath = join11(projectRoot, rootFile);
2612
2902
  if (existsSync8(fullPath)) {
2613
- console.log(chalk10.green(` \u2713 ${rootFile} (project root)`));
2903
+ console.log(chalk12.green(` \u2713 ${rootFile} (project root)`));
2614
2904
  }
2615
2905
  }
2616
2906
  const settingsFiles = [
2617
- join9(homedir6(), ".claude", "settings.json"),
2618
- join9(homedir6(), ".claude", "settings.local.json"),
2619
- join9(projectRoot, ".claude", "settings.json"),
2620
- 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")
2621
2911
  ];
2622
2912
  for (const sf of settingsFiles) {
2623
2913
  if (existsSync8(sf)) {
2624
- const display = sf.startsWith(homedir6()) ? sf.replace(homedir6(), "~") : sf.replace(projectRoot + "/", "");
2625
- 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)`));
2626
2916
  }
2627
2917
  }
2628
2918
  const words = prompt.split(/\s+/);
2629
2919
  for (const word of words) {
2630
2920
  const cleaned = word.replace(/['"]/g, "");
2631
- const candidatePath = join9(projectRoot, cleaned);
2921
+ const candidatePath = join11(projectRoot, cleaned);
2632
2922
  if (existsSync8(candidatePath) && cleaned.includes("/")) {
2633
- console.log(chalk10.cyan(` \u2192 ${cleaned} (referenced in prompt)`));
2923
+ console.log(chalk12.cyan(` \u2192 ${cleaned} (referenced in prompt)`));
2634
2924
  }
2635
2925
  }
2636
- 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."));
2637
2927
  }
2638
2928
 
2639
2929
  // dist/commands/print.js
2640
- import { join as join10 } from "node:path";
2641
- 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";
2642
2932
  import { existsSync as existsSync9 } from "node:fs";
2643
- import chalk11 from "chalk";
2933
+ import chalk13 from "chalk";
2644
2934
  async function printCommand(title) {
2645
2935
  const projectRoot = process.cwd();
2646
- const scanDataPath = join10(projectRoot, "output", "index.html");
2936
+ const scanDataPath = join12(projectRoot, "output", "index.html");
2647
2937
  if (!existsSync9(scanDataPath)) {
2648
- console.error(chalk11.red("No scan data found. Run: md4ai scan"));
2938
+ console.error(chalk13.red("No scan data found. Run: md4ai scan"));
2649
2939
  process.exit(1);
2650
2940
  }
2651
- const html = await readFile6(scanDataPath, "utf-8");
2941
+ const html = await readFile8(scanDataPath, "utf-8");
2652
2942
  const match = html.match(/<script type="application\/json" id="scan-data">([\s\S]*?)<\/script>/);
2653
2943
  if (!match) {
2654
- 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"));
2655
2945
  process.exit(1);
2656
2946
  }
2657
2947
  const result = JSON.parse(match[1]);
2658
2948
  const printHtml = generatePrintHtml(result, title);
2659
- const outputPath = join10(projectRoot, "output", `print-${Date.now()}.html`);
2949
+ const outputPath = join12(projectRoot, "output", `print-${Date.now()}.html`);
2660
2950
  await writeFile3(outputPath, printHtml, "utf-8");
2661
- console.log(chalk11.green(`Print-ready wall sheet: ${outputPath}`));
2951
+ console.log(chalk13.green(`Print-ready wall sheet: ${outputPath}`));
2662
2952
  }
2663
2953
  function escapeHtml2(text) {
2664
2954
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -2724,7 +3014,7 @@ init_scanner();
2724
3014
  init_push_toolings();
2725
3015
  init_device_utils();
2726
3016
  import { resolve as resolve4 } from "node:path";
2727
- import chalk13 from "chalk";
3017
+ import chalk15 from "chalk";
2728
3018
  async function linkCommand(projectId) {
2729
3019
  const { supabase, userId } = await getAuthenticatedClient();
2730
3020
  const cwd = resolve4(process.cwd());
@@ -2732,11 +3022,11 @@ async function linkCommand(projectId) {
2732
3022
  const osType = detectOs2();
2733
3023
  const { data: folder, error: folderErr } = await supabase.from("claude_folders").select("id, name").eq("id", projectId).single();
2734
3024
  if (folderErr || !folder) {
2735
- console.error(chalk13.red("Project not found, or you do not have access."));
2736
- 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."));
2737
3027
  process.exit(1);
2738
3028
  }
2739
- console.log(chalk13.blue(`
3029
+ console.log(chalk15.blue(`
2740
3030
  Linking "${folder.name}" to this device...
2741
3031
  `));
2742
3032
  console.log(` Project: ${folder.name}`);
@@ -2760,12 +3050,12 @@ Linking "${folder.name}" to this device...
2760
3050
  path: cwd
2761
3051
  });
2762
3052
  if (pathErr) {
2763
- console.error(chalk13.red(`Failed to link: ${pathErr.message}`));
3053
+ console.error(chalk15.red(`Failed to link: ${pathErr.message}`));
2764
3054
  process.exit(1);
2765
3055
  }
2766
3056
  }
2767
- console.log(chalk13.green("\nLinked successfully."));
2768
- console.log(chalk13.blue("\nRunning initial scan...\n"));
3057
+ console.log(chalk15.green("\nLinked successfully."));
3058
+ console.log(chalk15.blue("\nRunning initial scan...\n"));
2769
3059
  const result = await scanProject(cwd);
2770
3060
  console.log(` Files: ${result.graph.nodes.length}`);
2771
3061
  console.log(` References:${result.graph.edges.length}`);
@@ -2783,7 +3073,7 @@ Linking "${folder.name}" to this device...
2783
3073
  data_hash: result.dataHash
2784
3074
  }).eq("id", folder.id);
2785
3075
  if (scanErr) {
2786
- console.error(chalk13.yellow(`Scan upload warning: ${scanErr.message}`));
3076
+ console.error(chalk15.yellow(`Scan upload warning: ${scanErr.message}`));
2787
3077
  }
2788
3078
  await pushToolings(supabase, folder.id, result.toolings);
2789
3079
  const configFiles = await readClaudeConfigFiles(cwd);
@@ -2797,7 +3087,7 @@ Linking "${folder.name}" to this device...
2797
3087
  last_modified: file.lastModified
2798
3088
  }, { onConflict: "folder_id,file_path" });
2799
3089
  }
2800
- console.log(chalk13.green(` Uploaded ${configFiles.length} config file(s).`));
3090
+ console.log(chalk15.green(` Uploaded ${configFiles.length} config file(s).`));
2801
3091
  }
2802
3092
  await supabase.from("device_paths").update({ last_synced: (/* @__PURE__ */ new Date()).toISOString() }).eq("folder_id", folder.id).eq("device_name", deviceName);
2803
3093
  await saveState({
@@ -2806,34 +3096,34 @@ Linking "${folder.name}" to this device...
2806
3096
  lastSyncAt: (/* @__PURE__ */ new Date()).toISOString()
2807
3097
  });
2808
3098
  const projectUrl = `https://www.md4ai.com/project/${folder.id}`;
2809
- console.log(chalk13.green("\nDone! Project linked and scanned."));
2810
- console.log(chalk13.cyan(`
3099
+ console.log(chalk15.green("\nDone! Project linked and scanned."));
3100
+ console.log(chalk15.cyan(`
2811
3101
  ${projectUrl}
2812
3102
  `));
2813
- 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.'));
2814
3104
  }
2815
3105
 
2816
3106
  // dist/commands/import-bundle.js
2817
- import { readFile as readFile7, writeFile as writeFile4, mkdir as mkdir3 } from "node:fs/promises";
2818
- 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";
2819
3109
  import { existsSync as existsSync10 } from "node:fs";
2820
- import chalk14 from "chalk";
3110
+ import chalk16 from "chalk";
2821
3111
  import { confirm as confirm3, input as input6 } from "@inquirer/prompts";
2822
3112
  async function importBundleCommand(zipPath) {
2823
3113
  if (!existsSync10(zipPath)) {
2824
- console.error(chalk14.red(`File not found: ${zipPath}`));
3114
+ console.error(chalk16.red(`File not found: ${zipPath}`));
2825
3115
  process.exit(1);
2826
3116
  }
2827
3117
  const JSZip = (await import("jszip")).default;
2828
- const zipData = await readFile7(zipPath);
3118
+ const zipData = await readFile9(zipPath);
2829
3119
  const zip = await JSZip.loadAsync(zipData);
2830
3120
  const manifestFile = zip.file("manifest.json");
2831
3121
  if (!manifestFile) {
2832
- console.error(chalk14.red("Invalid bundle: missing manifest.json"));
3122
+ console.error(chalk16.red("Invalid bundle: missing manifest.json"));
2833
3123
  process.exit(1);
2834
3124
  }
2835
3125
  const manifest = JSON.parse(await manifestFile.async("string"));
2836
- console.log(chalk14.blue(`
3126
+ console.log(chalk16.blue(`
2837
3127
  Bundle: ${manifest.folderName}`));
2838
3128
  console.log(` Exported by: ${manifest.ownerEmail}`);
2839
3129
  console.log(` Exported at: ${manifest.exportedAt}`);
@@ -2847,10 +3137,10 @@ Bundle: ${manifest.folderName}`));
2847
3137
  }
2848
3138
  }
2849
3139
  if (files.length === 0) {
2850
- console.log(chalk14.yellow("No Claude config files found in bundle."));
3140
+ console.log(chalk16.yellow("No Claude config files found in bundle."));
2851
3141
  return;
2852
3142
  }
2853
- console.log(chalk14.blue(`
3143
+ console.log(chalk16.blue(`
2854
3144
  Files to extract:`));
2855
3145
  for (const f of files) {
2856
3146
  console.log(` ${f.filePath}`);
@@ -2863,34 +3153,34 @@ Files to extract:`));
2863
3153
  message: `Extract ${files.length} file(s) to ${targetDir}?`
2864
3154
  });
2865
3155
  if (!proceed) {
2866
- console.log(chalk14.yellow("Cancelled."));
3156
+ console.log(chalk16.yellow("Cancelled."));
2867
3157
  return;
2868
3158
  }
2869
3159
  for (const file of files) {
2870
- const fullPath = join11(targetDir, file.filePath);
2871
- const dir = dirname2(fullPath);
3160
+ const fullPath = join13(targetDir, file.filePath);
3161
+ const dir = dirname3(fullPath);
2872
3162
  if (!existsSync10(dir)) {
2873
3163
  await mkdir3(dir, { recursive: true });
2874
3164
  }
2875
3165
  await writeFile4(fullPath, file.content, "utf-8");
2876
- console.log(chalk14.green(` \u2713 ${file.filePath}`));
3166
+ console.log(chalk16.green(` \u2713 ${file.filePath}`));
2877
3167
  }
2878
- console.log(chalk14.green(`
3168
+ console.log(chalk16.green(`
2879
3169
  Done! ${files.length} file(s) extracted to ${targetDir}`));
2880
3170
  }
2881
3171
 
2882
3172
  // dist/commands/admin-update-tool.js
2883
3173
  init_auth();
2884
- import chalk15 from "chalk";
3174
+ import chalk17 from "chalk";
2885
3175
  var VALID_CATEGORIES = ["framework", "runtime", "cli", "mcp", "package", "database", "other"];
2886
3176
  async function adminUpdateToolCommand(options) {
2887
3177
  const { supabase } = await getAuthenticatedClient();
2888
3178
  if (!options.name) {
2889
- console.error(chalk15.red("--name is required."));
3179
+ console.error(chalk17.red("--name is required."));
2890
3180
  process.exit(1);
2891
3181
  }
2892
3182
  if (options.category && !VALID_CATEGORIES.includes(options.category)) {
2893
- 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(", ")}`));
2894
3184
  process.exit(1);
2895
3185
  }
2896
3186
  const { data: existing } = await supabase.from("tools_registry").select("id, name, display_name, category").eq("name", options.name).maybeSingle();
@@ -2912,13 +3202,13 @@ async function adminUpdateToolCommand(options) {
2912
3202
  updates.notes = options.notes;
2913
3203
  const { error } = await supabase.from("tools_registry").update(updates).eq("id", existing.id);
2914
3204
  if (error) {
2915
- console.error(chalk15.red(`Failed to update: ${error.message}`));
3205
+ console.error(chalk17.red(`Failed to update: ${error.message}`));
2916
3206
  process.exit(1);
2917
3207
  }
2918
- console.log(chalk15.green(`Updated "${existing.display_name}" in the registry.`));
3208
+ console.log(chalk17.green(`Updated "${existing.display_name}" in the registry.`));
2919
3209
  } else {
2920
3210
  if (!options.display || !options.category) {
2921
- console.error(chalk15.red("New tools require --display and --category."));
3211
+ console.error(chalk17.red("New tools require --display and --category."));
2922
3212
  process.exit(1);
2923
3213
  }
2924
3214
  const { error } = await supabase.from("tools_registry").insert({
@@ -2932,25 +3222,25 @@ async function adminUpdateToolCommand(options) {
2932
3222
  notes: options.notes ?? null
2933
3223
  });
2934
3224
  if (error) {
2935
- console.error(chalk15.red(`Failed to create: ${error.message}`));
3225
+ console.error(chalk17.red(`Failed to create: ${error.message}`));
2936
3226
  process.exit(1);
2937
3227
  }
2938
- console.log(chalk15.green(`Added "${options.display}" to the registry.`));
3228
+ console.log(chalk17.green(`Added "${options.display}" to the registry.`));
2939
3229
  }
2940
3230
  }
2941
3231
 
2942
3232
  // dist/commands/admin-list-tools.js
2943
3233
  init_auth();
2944
- import chalk16 from "chalk";
3234
+ import chalk18 from "chalk";
2945
3235
  async function adminListToolsCommand() {
2946
3236
  const { supabase } = await getAuthenticatedClient();
2947
3237
  const { data: tools, error } = await supabase.from("tools_registry").select("*").order("category").order("display_name");
2948
3238
  if (error) {
2949
- console.error(chalk16.red(`Failed to fetch tools: ${error.message}`));
3239
+ console.error(chalk18.red(`Failed to fetch tools: ${error.message}`));
2950
3240
  process.exit(1);
2951
3241
  }
2952
3242
  if (!tools?.length) {
2953
- console.log(chalk16.yellow("No tools in the registry."));
3243
+ console.log(chalk18.yellow("No tools in the registry."));
2954
3244
  return;
2955
3245
  }
2956
3246
  const nameW = Math.max(16, ...tools.map((t) => t.display_name.length));
@@ -2964,7 +3254,7 @@ async function adminListToolsCommand() {
2964
3254
  "Beta".padEnd(betaW),
2965
3255
  "Last Checked"
2966
3256
  ].join(" ");
2967
- console.log(chalk16.bold(header));
3257
+ console.log(chalk18.bold(header));
2968
3258
  console.log("\u2500".repeat(header.length));
2969
3259
  for (const tool of tools) {
2970
3260
  const lastChecked = tool.updated_at ? formatRelative(new Date(tool.updated_at)) : "\u2014";
@@ -2977,7 +3267,7 @@ async function adminListToolsCommand() {
2977
3267
  ].join(" ");
2978
3268
  console.log(row);
2979
3269
  }
2980
- console.log(chalk16.grey(`
3270
+ console.log(chalk18.grey(`
2981
3271
  ${tools.length} tool(s) in registry.`));
2982
3272
  }
2983
3273
  function formatRelative(date) {
@@ -2995,7 +3285,7 @@ function formatRelative(date) {
2995
3285
 
2996
3286
  // dist/commands/admin-fetch-versions.js
2997
3287
  init_auth();
2998
- import chalk17 from "chalk";
3288
+ import chalk19 from "chalk";
2999
3289
 
3000
3290
  // dist/commands/version-sources.js
3001
3291
  var VERSION_SOURCES = {
@@ -3024,11 +3314,11 @@ async function adminFetchVersionsCommand() {
3024
3314
  const { supabase } = await getAuthenticatedClient();
3025
3315
  const { data: tools, error } = await supabase.from("tools_registry").select("*").order("display_name");
3026
3316
  if (error) {
3027
- console.error(chalk17.red(`Failed to fetch tools: ${error.message}`));
3317
+ console.error(chalk19.red(`Failed to fetch tools: ${error.message}`));
3028
3318
  process.exit(1);
3029
3319
  }
3030
3320
  if (!tools?.length) {
3031
- console.log(chalk17.yellow("No tools in the registry."));
3321
+ console.log(chalk19.yellow("No tools in the registry."));
3032
3322
  }
3033
3323
  const { data: allProjectToolings } = await supabase.from("project_toolings").select("tool_name, detection_source").is("tool_id", null);
3034
3324
  const registeredNames = new Set((tools ?? []).map((t) => t.name));
@@ -3039,7 +3329,7 @@ async function adminFetchVersionsCommand() {
3039
3329
  }
3040
3330
  }
3041
3331
  if (unregisteredNames.size > 0) {
3042
- console.log(chalk17.blue(`Auto-registering ${unregisteredNames.size} unverified package(s)...
3332
+ console.log(chalk19.blue(`Auto-registering ${unregisteredNames.size} unverified package(s)...
3043
3333
  `));
3044
3334
  for (const name of unregisteredNames) {
3045
3335
  const displayName = name;
@@ -3057,10 +3347,10 @@ async function adminFetchVersionsCommand() {
3057
3347
  }
3058
3348
  }
3059
3349
  if (!tools?.length) {
3060
- console.log(chalk17.yellow("No tools to fetch."));
3350
+ console.log(chalk19.yellow("No tools to fetch."));
3061
3351
  return;
3062
3352
  }
3063
- console.log(chalk17.bold(`Fetching versions for ${tools.length} tool(s)...
3353
+ console.log(chalk19.bold(`Fetching versions for ${tools.length} tool(s)...
3064
3354
  `));
3065
3355
  const results = await Promise.all(tools.map(async (tool) => {
3066
3356
  const source = VERSION_SOURCES[tool.name] ?? { type: "npm", package: tool.name };
@@ -3099,10 +3389,10 @@ async function adminFetchVersionsCommand() {
3099
3389
  "Beta".padEnd(betaW),
3100
3390
  "Status".padEnd(statusW)
3101
3391
  ].join(" ");
3102
- console.log(chalk17.bold(header));
3392
+ console.log(chalk19.bold(header));
3103
3393
  console.log("\u2500".repeat(header.length));
3104
3394
  for (const result of results) {
3105
- 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;
3106
3396
  const row = [
3107
3397
  result.displayName.padEnd(nameW),
3108
3398
  (result.stable ?? "\u2014").padEnd(stableW),
@@ -3115,8 +3405,8 @@ async function adminFetchVersionsCommand() {
3115
3405
  const unchanged = results.filter((r) => r.status === "unchanged").length;
3116
3406
  const failed = results.filter((r) => r.status === "failed").length;
3117
3407
  const noSource = results.filter((r) => r.status === "no source").length;
3118
- console.log(chalk17.grey(`
3119
- ${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`));
3120
3410
  }
3121
3411
  async function fetchVersions(source) {
3122
3412
  const controller = new AbortController();
@@ -3174,10 +3464,10 @@ async function fetchGitHubVersions(repo, signal) {
3174
3464
  init_mcp_watch();
3175
3465
 
3176
3466
  // dist/commands/init-manifest.js
3177
- import { resolve as resolve5, join as join13, relative as relative3, dirname as dirname3 } from "node:path";
3178
- 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";
3179
3469
  import { existsSync as existsSync12 } from "node:fs";
3180
- import chalk19 from "chalk";
3470
+ import chalk21 from "chalk";
3181
3471
  var SECRET_PATTERNS = [
3182
3472
  /_KEY$/i,
3183
3473
  /_SECRET$/i,
@@ -3193,7 +3483,7 @@ function isLikelySecret(name) {
3193
3483
  async function discoverEnvFiles(projectRoot) {
3194
3484
  const apps = [];
3195
3485
  for (const envName of [".env", ".env.local", ".env.example"]) {
3196
- const envPath = join13(projectRoot, envName);
3486
+ const envPath = join15(projectRoot, envName);
3197
3487
  if (existsSync12(envPath)) {
3198
3488
  const vars = await extractVarNames(envPath);
3199
3489
  if (vars.length > 0) {
@@ -3204,11 +3494,11 @@ async function discoverEnvFiles(projectRoot) {
3204
3494
  }
3205
3495
  const subdirs = ["web", "cli", "api", "app", "server", "packages"];
3206
3496
  for (const sub of subdirs) {
3207
- const subDir = join13(projectRoot, sub);
3497
+ const subDir = join15(projectRoot, sub);
3208
3498
  if (!existsSync12(subDir))
3209
3499
  continue;
3210
3500
  for (const envName of [".env.local", ".env", ".env.example"]) {
3211
- const envPath = join13(subDir, envName);
3501
+ const envPath = join15(subDir, envName);
3212
3502
  if (existsSync12(envPath)) {
3213
3503
  const vars = await extractVarNames(envPath);
3214
3504
  if (vars.length > 0) {
@@ -3225,7 +3515,7 @@ async function discoverEnvFiles(projectRoot) {
3225
3515
  return apps;
3226
3516
  }
3227
3517
  async function extractVarNames(envPath) {
3228
- const content = await readFile9(envPath, "utf-8");
3518
+ const content = await readFile11(envPath, "utf-8");
3229
3519
  const vars = [];
3230
3520
  for (const line of content.split("\n")) {
3231
3521
  const trimmed = line.trim();
@@ -3270,38 +3560,38 @@ function generateManifest(apps) {
3270
3560
  }
3271
3561
  async function initManifestCommand() {
3272
3562
  const projectRoot = resolve5(process.cwd());
3273
- const manifestPath = join13(projectRoot, "docs", "reference", "env-manifest.md");
3563
+ const manifestPath = join15(projectRoot, "docs", "reference", "env-manifest.md");
3274
3564
  if (existsSync12(manifestPath)) {
3275
- console.log(chalk19.yellow(`Manifest already exists: ${relative3(projectRoot, manifestPath)}`));
3276
- 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."));
3277
3567
  return;
3278
3568
  }
3279
- console.log(chalk19.blue("Scanning for .env files...\n"));
3569
+ console.log(chalk21.blue("Scanning for .env files...\n"));
3280
3570
  const apps = await discoverEnvFiles(projectRoot);
3281
3571
  if (apps.length === 0) {
3282
- console.log(chalk19.yellow("No .env files found. Create a manifest manually at:"));
3283
- 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`));
3284
3574
  return;
3285
3575
  }
3286
3576
  for (const app of apps) {
3287
- 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)`);
3288
3578
  }
3289
3579
  const content = generateManifest(apps);
3290
- const dir = dirname3(manifestPath);
3580
+ const dir = dirname4(manifestPath);
3291
3581
  if (!existsSync12(dir)) {
3292
3582
  await mkdir4(dir, { recursive: true });
3293
3583
  }
3294
3584
  await writeFile5(manifestPath, content, "utf-8");
3295
- console.log(chalk19.green(`
3296
- Manifest created: ${relative3(projectRoot, manifestPath)}`));
3297
- console.log(chalk19.dim("Review and edit the file \u2014 it is your source of truth."));
3298
- 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."));
3299
3589
  }
3300
3590
 
3301
3591
  // dist/commands/update.js
3302
3592
  init_check_update();
3303
3593
  init_config();
3304
- import chalk20 from "chalk";
3594
+ import chalk22 from "chalk";
3305
3595
  import { execFileSync as execFileSync6, spawn } from "node:child_process";
3306
3596
  async function fetchLatestVersion() {
3307
3597
  try {
@@ -3350,13 +3640,13 @@ function spawnPostUpdate() {
3350
3640
  }
3351
3641
  async function postUpdateFlow() {
3352
3642
  const { confirm: confirm4 } = await import("@inquirer/prompts");
3353
- console.log(chalk20.green(`
3643
+ console.log(chalk22.green(`
3354
3644
  md4ai v${CURRENT_VERSION} \u2014 you're on the latest version.
3355
3645
  `));
3356
3646
  const creds = await loadCredentials();
3357
3647
  const isLoggedIn = !!creds?.accessToken && Date.now() < creds.expiresAt;
3358
3648
  if (!isLoggedIn) {
3359
- console.log(chalk20.yellow(" You are not logged in.\n"));
3649
+ console.log(chalk22.yellow(" You are not logged in.\n"));
3360
3650
  const wantLogin = await confirm4({
3361
3651
  message: "Log in now?",
3362
3652
  default: true
@@ -3367,7 +3657,7 @@ async function postUpdateFlow() {
3367
3657
  await loginCommand2();
3368
3658
  console.log("");
3369
3659
  } else {
3370
- 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"));
3371
3661
  return;
3372
3662
  }
3373
3663
  }
@@ -3401,7 +3691,7 @@ async function postUpdateFlow() {
3401
3691
  const { mcpWatchCommand: mcpWatchCommand2 } = await Promise.resolve().then(() => (init_mcp_watch(), mcp_watch_exports));
3402
3692
  await mcpWatchCommand2();
3403
3693
  } else {
3404
- 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"));
3405
3695
  }
3406
3696
  }
3407
3697
  async function updateCommand(options) {
@@ -3409,19 +3699,19 @@ async function updateCommand(options) {
3409
3699
  await postUpdateFlow();
3410
3700
  return;
3411
3701
  }
3412
- console.log(chalk20.blue(`
3702
+ console.log(chalk22.blue(`
3413
3703
  Current version: v${CURRENT_VERSION}`));
3414
- console.log(chalk20.dim(" Checking for updates...\n"));
3704
+ console.log(chalk22.dim(" Checking for updates...\n"));
3415
3705
  const latest = await fetchLatestVersion();
3416
3706
  if (!latest) {
3417
- 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."));
3418
3708
  process.exit(1);
3419
3709
  }
3420
3710
  if (!isNewer2(latest, CURRENT_VERSION)) {
3421
3711
  await postUpdateFlow();
3422
3712
  return;
3423
3713
  }
3424
- 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}
3425
3715
  `));
3426
3716
  const { confirm: confirm4 } = await import("@inquirer/prompts");
3427
3717
  const wantUpdate = await confirm4({
@@ -3429,20 +3719,38 @@ async function updateCommand(options) {
3429
3719
  default: true
3430
3720
  });
3431
3721
  if (!wantUpdate) {
3432
- console.log(chalk20.dim("\nUpdate skipped.\n"));
3722
+ console.log(chalk22.dim("\nUpdate skipped.\n"));
3433
3723
  return;
3434
3724
  }
3435
- console.log(chalk20.blue("\n Installing...\n"));
3725
+ console.log(chalk22.blue("\n Installing...\n"));
3436
3726
  const ok = runNpmInstall();
3437
3727
  if (!ok) {
3438
- console.log(chalk20.red("\n npm install failed. Try running manually:"));
3439
- 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"));
3440
3730
  process.exit(1);
3441
3731
  }
3442
- console.log(chalk20.green("\n Updated successfully.\n"));
3732
+ console.log(chalk22.green("\n Updated successfully.\n"));
3443
3733
  spawnPostUpdate();
3444
3734
  }
3445
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
+
3446
3754
  // dist/index.js
3447
3755
  init_check_update();
3448
3756
  var program = new Command();
@@ -3465,6 +3773,8 @@ program.command("update").description("Check for updates, install, and optionall
3465
3773
  program.command("check-update").description("Check if a newer version of md4ai is available").action(checkForUpdate);
3466
3774
  program.command("mcp-watch").description("Monitor MCP server status on this device (runs until Ctrl+C)").action(mcpWatchCommand);
3467
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);
3468
3778
  var admin = program.command("admin").description("Admin commands for managing the tools registry");
3469
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);
3470
3780
  admin.command("list-tools").description("List all tools in the master registry").action(adminListToolsCommand);