@askexenow/exe-os 0.9.48 → 0.9.49

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/bin/cli.js +297 -94
  2. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -1886,8 +1886,8 @@ function deriveMachineKey() {
1886
1886
  }
1887
1887
  function readMachineId() {
1888
1888
  try {
1889
- const { readFileSync: readFileSync30 } = __require("fs");
1890
- return readFileSync30("/etc/machine-id", "utf-8").trim();
1889
+ const { readFileSync: readFileSync31 } = __require("fs");
1890
+ return readFileSync31("/etc/machine-id", "utf-8").trim();
1891
1891
  } catch {
1892
1892
  return "";
1893
1893
  }
@@ -9140,9 +9140,9 @@ Unclassified: ${unclassified}
9140
9140
  }
9141
9141
  async function exportBatches(options) {
9142
9142
  const fs8 = await import("fs");
9143
- const path49 = await import("path");
9143
+ const path50 = await import("path");
9144
9144
  const client = getClient();
9145
- const outDir = path49.join(process.cwd(), "exe/output/classifications/input");
9145
+ const outDir = path50.join(process.cwd(), "exe/output/classifications/input");
9146
9146
  fs8.mkdirSync(outDir, { recursive: true });
9147
9147
  const countResult = await client.execute({
9148
9148
  sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
@@ -9166,7 +9166,7 @@ async function exportBatches(options) {
9166
9166
  const text = String(row.text || "").replace(/\n/g, " ");
9167
9167
  return JSON.stringify({ id: row.id, text });
9168
9168
  });
9169
- const batchFile = path49.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
9169
+ const batchFile = path50.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
9170
9170
  fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
9171
9171
  exported += batch.rows.length;
9172
9172
  offset += options.batchSize;
@@ -9182,7 +9182,7 @@ async function exportBatches(options) {
9182
9182
  }
9183
9183
  async function importClassifications(importDir) {
9184
9184
  const fs8 = await import("fs");
9185
- const path49 = await import("path");
9185
+ const path50 = await import("path");
9186
9186
  const client = getClient();
9187
9187
  const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
9188
9188
  process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
@@ -9190,7 +9190,7 @@ async function importClassifications(importDir) {
9190
9190
  let imported = 0;
9191
9191
  let invalid = 0;
9192
9192
  for (const file of files) {
9193
- const lines = fs8.readFileSync(path49.join(importDir, file), "utf-8").split("\n").filter(Boolean);
9193
+ const lines = fs8.readFileSync(path50.join(importDir, file), "utf-8").split("\n").filter(Boolean);
9194
9194
  for (const line of lines) {
9195
9195
  try {
9196
9196
  const rec = JSON.parse(line);
@@ -11828,10 +11828,10 @@ async function disposeEmbedder() {
11828
11828
  async function embedDirect(text) {
11829
11829
  const llamaCpp = await import("node-llama-cpp");
11830
11830
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
11831
- const { existsSync: existsSync35 } = await import("fs");
11832
- const path49 = await import("path");
11833
- const modelPath = path49.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
11834
- if (!existsSync35(modelPath)) {
11831
+ const { existsSync: existsSync36 } = await import("fs");
11832
+ const path50 = await import("path");
11833
+ const modelPath = path50.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
11834
+ if (!existsSync36(modelPath)) {
11835
11835
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
11836
11836
  }
11837
11837
  const llama = await llamaCpp.getLlama();
@@ -21308,8 +21308,8 @@ var init_ErrorOverview = __esm({
21308
21308
  "use strict";
21309
21309
  init_Box();
21310
21310
  init_Text();
21311
- cleanupPath = (path49) => {
21312
- return path49?.replace(`file://${cwd()}/`, "");
21311
+ cleanupPath = (path50) => {
21312
+ return path50?.replace(`file://${cwd()}/`, "");
21313
21313
  };
21314
21314
  stackUtils = new StackUtils({
21315
21315
  cwd: cwd(),
@@ -23717,11 +23717,11 @@ function Footer() {
23717
23717
  } catch {
23718
23718
  }
23719
23719
  try {
23720
- const { existsSync: existsSync35 } = await import("fs");
23720
+ const { existsSync: existsSync36 } = await import("fs");
23721
23721
  const { join } = await import("path");
23722
23722
  const home = process.env.HOME ?? "";
23723
23723
  const pidPath = join(home, ".exe-os", "exed.pid");
23724
- setDaemon(existsSync35(pidPath) ? "running" : "stopped");
23724
+ setDaemon(existsSync36(pidPath) ? "running" : "stopped");
23725
23725
  } catch {
23726
23726
  setDaemon("unknown");
23727
23727
  }
@@ -26505,15 +26505,15 @@ function CommandCenterView({
26505
26505
  const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
26506
26506
  const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
26507
26507
  const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
26508
- const { readFileSync: readFileSync30, existsSync: existsSync35 } = await import("fs");
26508
+ const { readFileSync: readFileSync31, existsSync: existsSync36 } = await import("fs");
26509
26509
  const { join } = await import("path");
26510
26510
  const { homedir: homedir8 } = await import("os");
26511
26511
  const configPath = join(homedir8(), ".exe-os", "config.json");
26512
26512
  let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
26513
26513
  let providerConfigs = {};
26514
- if (existsSync35(configPath)) {
26514
+ if (existsSync36(configPath)) {
26515
26515
  try {
26516
- const raw = JSON.parse(readFileSync30(configPath, "utf8"));
26516
+ const raw = JSON.parse(readFileSync31(configPath, "utf8"));
26517
26517
  if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
26518
26518
  if (raw.providers && typeof raw.providers === "object") {
26519
26519
  providerConfigs = raw.providers;
@@ -26574,7 +26574,7 @@ function CommandCenterView({
26574
26574
  const markerDir = join(homedir8(), ".exe-os", "session-cache");
26575
26575
  const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
26576
26576
  for (const f of agentFiles) {
26577
- const data = JSON.parse(readFileSync30(join(markerDir, f), "utf8"));
26577
+ const data = JSON.parse(readFileSync31(join(markerDir, f), "utf8"));
26578
26578
  if (data.agentRole) {
26579
26579
  agentRole = data.agentRole;
26580
26580
  break;
@@ -26757,7 +26757,7 @@ function CommandCenterView({
26757
26757
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
26758
26758
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
26759
26759
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
26760
- const { existsSync: existsSync35 } = await import("fs");
26760
+ const { existsSync: existsSync36 } = await import("fs");
26761
26761
  const { join } = await import("path");
26762
26762
  const client = getClient2();
26763
26763
  if (!client) {
@@ -26828,7 +26828,7 @@ function CommandCenterView({
26828
26828
  }
26829
26829
  const memoryCount = memoryCounts.get(name) ?? 0;
26830
26830
  const openTaskCount = openTaskCounts.get(name) ?? 0;
26831
- const hasGit = projectDir ? existsSync35(join(projectDir, ".git")) : false;
26831
+ const hasGit = projectDir ? existsSync36(join(projectDir, ".git")) : false;
26832
26832
  const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
26833
26833
  projectList.push({
26834
26834
  projectName: name,
@@ -26853,7 +26853,7 @@ function CommandCenterView({
26853
26853
  setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
26854
26854
  try {
26855
26855
  const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
26856
- setHealth((h) => ({ ...h, daemon: existsSync35(pidPath) ? "running" : "stopped" }));
26856
+ setHealth((h) => ({ ...h, daemon: existsSync36(pidPath) ? "running" : "stopped" }));
26857
26857
  } catch {
26858
26858
  }
26859
26859
  const activityResult = await client.execute(
@@ -28976,12 +28976,12 @@ async function loadGatewayConfig() {
28976
28976
  state.running = false;
28977
28977
  }
28978
28978
  try {
28979
- const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
28979
+ const { existsSync: existsSync36, readFileSync: readFileSync31 } = await import("fs");
28980
28980
  const { join } = await import("path");
28981
28981
  const home = process.env.HOME ?? "";
28982
28982
  const configPath = join(home, ".exe-os", "gateway.json");
28983
- if (existsSync35(configPath)) {
28984
- const raw = JSON.parse(readFileSync30(configPath, "utf8"));
28983
+ if (existsSync36(configPath)) {
28984
+ const raw = JSON.parse(readFileSync31(configPath, "utf8"));
28985
28985
  state.port = raw.port ?? 3100;
28986
28986
  state.gatewayUrl = raw.gatewayUrl ?? "";
28987
28987
  if (raw.adapters) {
@@ -29579,12 +29579,12 @@ function TeamView({ onBack, onViewSessions }) {
29579
29579
  setMembers(teamData);
29580
29580
  setDbError(null);
29581
29581
  try {
29582
- const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
29582
+ const { existsSync: existsSync36, readFileSync: readFileSync31 } = await import("fs");
29583
29583
  const { join } = await import("path");
29584
29584
  const home = process.env.HOME ?? "";
29585
29585
  const gatewayConfig = join(home, ".exe-os", "gateway.json");
29586
- if (existsSync35(gatewayConfig)) {
29587
- const raw = JSON.parse(readFileSync30(gatewayConfig, "utf8"));
29586
+ if (existsSync36(gatewayConfig)) {
29587
+ const raw = JSON.parse(readFileSync31(gatewayConfig, "utf8"));
29588
29588
  if (raw.agents && raw.agents.length > 0) {
29589
29589
  setExternals(raw.agents.map((a) => ({
29590
29590
  name: a.name,
@@ -29764,8 +29764,8 @@ __export(wiki_client_exports, {
29764
29764
  listDocuments: () => listDocuments,
29765
29765
  listWorkspaces: () => listWorkspaces
29766
29766
  });
29767
- async function wikiFetch(config, path49, method = "GET", body) {
29768
- const url = `${config.baseUrl}/api/v1${path49}`;
29767
+ async function wikiFetch(config, path50, method = "GET", body) {
29768
+ const url = `${config.baseUrl}/api/v1${path50}`;
29769
29769
  const headers = {
29770
29770
  Authorization: `Bearer ${config.apiKey}`,
29771
29771
  "Content-Type": "application/json"
@@ -29798,7 +29798,7 @@ async function wikiFetch(config, path49, method = "GET", body) {
29798
29798
  }
29799
29799
  }
29800
29800
  if (!response.ok) {
29801
- throw new Error(`Wiki API ${method} ${path49}: ${response.status} ${response.statusText}`);
29801
+ throw new Error(`Wiki API ${method} ${path50}: ${response.status} ${response.statusText}`);
29802
29802
  }
29803
29803
  return response.json();
29804
29804
  } finally {
@@ -30392,12 +30392,12 @@ function SettingsView({ onBack }) {
30392
30392
  }
30393
30393
  setProviders(providerList);
30394
30394
  try {
30395
- const { existsSync: existsSync35 } = await import("fs");
30395
+ const { existsSync: existsSync36 } = await import("fs");
30396
30396
  const { join } = await import("path");
30397
30397
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
30398
30398
  const cfg = await loadConfig2();
30399
30399
  const home = process.env.HOME ?? "";
30400
- const hasKey = existsSync35(join(home, ".exe-os", "master.key"));
30400
+ const hasKey = existsSync36(join(home, ".exe-os", "master.key"));
30401
30401
  if (cfg.cloud) {
30402
30402
  setCloud({
30403
30403
  configured: true,
@@ -30410,22 +30410,22 @@ function SettingsView({ onBack }) {
30410
30410
  const pidPath = join(home, ".exe-os", "exed.pid");
30411
30411
  let daemon = "unknown";
30412
30412
  try {
30413
- daemon = existsSync35(pidPath) ? "running" : "stopped";
30413
+ daemon = existsSync36(pidPath) ? "running" : "stopped";
30414
30414
  } catch {
30415
30415
  }
30416
30416
  let version = "unknown";
30417
30417
  try {
30418
- const { readFileSync: readFileSync30 } = await import("fs");
30418
+ const { readFileSync: readFileSync31 } = await import("fs");
30419
30419
  const { createRequire: createRequire3 } = await import("module");
30420
30420
  const require2 = createRequire3(import.meta.url);
30421
30421
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
30422
- const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
30422
+ const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
30423
30423
  version = pkg.version;
30424
30424
  } catch {
30425
30425
  try {
30426
- const { readFileSync: readFileSync30 } = await import("fs");
30426
+ const { readFileSync: readFileSync31 } = await import("fs");
30427
30427
  const { join: joinPath } = await import("path");
30428
- const pkg = JSON.parse(readFileSync30(joinPath(process.cwd(), "package.json"), "utf8"));
30428
+ const pkg = JSON.parse(readFileSync31(joinPath(process.cwd(), "package.json"), "utf8"));
30429
30429
  version = pkg.version;
30430
30430
  } catch {
30431
30431
  }
@@ -31624,15 +31624,199 @@ var init_installer3 = __esm({
31624
31624
  }
31625
31625
  });
31626
31626
 
31627
- // src/bin/cli.ts
31628
- import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
31627
+ // src/adapters/claude/mcp-diagnostics.ts
31628
+ var mcp_diagnostics_exports = {};
31629
+ __export(mcp_diagnostics_exports, {
31630
+ diagnoseClaudeMcpConfig: () => diagnoseClaudeMcpConfig,
31631
+ formatMcpDiagnosticReport: () => formatMcpDiagnosticReport
31632
+ });
31633
+ import { existsSync as existsSync34, readFileSync as readFileSync29 } from "fs";
31629
31634
  import path48 from "path";
31630
31635
  import os21 from "os";
31636
+ function readJson(filePath) {
31637
+ if (!existsSync34(filePath)) return null;
31638
+ try {
31639
+ return JSON.parse(readFileSync29(filePath, "utf8"));
31640
+ } catch {
31641
+ return null;
31642
+ }
31643
+ }
31644
+ function pathApplies2(projectPath, cwd2) {
31645
+ const project = path48.resolve(projectPath);
31646
+ const current = path48.resolve(cwd2);
31647
+ return current === project || current.startsWith(project + path48.sep);
31648
+ }
31649
+ function findAncestorMcpJsons2(cwd2, homeDir) {
31650
+ const files = [];
31651
+ let dir = path48.resolve(cwd2);
31652
+ const root = path48.parse(dir).root;
31653
+ const stop = path48.resolve(homeDir);
31654
+ while (dir !== root) {
31655
+ const candidate = path48.join(dir, ".mcp.json");
31656
+ if (existsSync34(candidate)) files.push(candidate);
31657
+ if (dir === stop) break;
31658
+ dir = path48.dirname(dir);
31659
+ }
31660
+ return files;
31661
+ }
31662
+ function extractPermissionPrefixes(settings) {
31663
+ const allow = settings?.permissions?.allow ?? [];
31664
+ const prefixes = /* @__PURE__ */ new Set();
31665
+ for (const entry of allow) {
31666
+ const match = entry.match(/^mcp__(.+?)__/);
31667
+ if (match?.[1]) prefixes.add(match[1]);
31668
+ }
31669
+ return [...prefixes].sort();
31670
+ }
31671
+ function serverAllowedByPermissions(serverName, prefixes) {
31672
+ return prefixes.has(serverName) || prefixes.has(serverName.replace(/-/g, "_"));
31673
+ }
31674
+ function diagnoseClaudeMcpConfig(homeDir = os21.homedir(), cwd2 = process.cwd(), env = process.env) {
31675
+ const claudeJsonPath = path48.join(homeDir, ".claude.json");
31676
+ const settingsPath = path48.join(homeDir, ".claude", "settings.json");
31677
+ const claudeJson = readJson(claudeJsonPath);
31678
+ const settings = readJson(settingsPath);
31679
+ const issues = [];
31680
+ if (!claudeJson) {
31681
+ issues.push({
31682
+ severity: "error",
31683
+ code: "claude-json-missing-or-invalid",
31684
+ message: "~/.claude.json is missing or invalid, so Claude MCP server config cannot be audited.",
31685
+ fix: "Run exe-os claude install, then re-open Claude Code."
31686
+ });
31687
+ }
31688
+ if (!settings) {
31689
+ issues.push({
31690
+ severity: "warn",
31691
+ code: "settings-json-missing-or-invalid",
31692
+ message: "~/.claude/settings.json is missing or invalid; MCP permissions cannot be audited.",
31693
+ fix: "Run exe-os claude install, then re-open Claude Code."
31694
+ });
31695
+ }
31696
+ const globalServers = Object.keys(claudeJson?.mcpServers ?? {}).sort();
31697
+ const projectServers = [];
31698
+ const applicableProjects = [];
31699
+ for (const [projectPath, projectConfig] of Object.entries(claudeJson?.projects ?? {})) {
31700
+ if (!pathApplies2(projectPath, cwd2)) continue;
31701
+ applicableProjects.push([projectPath, projectConfig]);
31702
+ for (const name of Object.keys(projectConfig.mcpServers ?? {})) {
31703
+ projectServers.push({ name, source: "project", path: projectPath });
31704
+ }
31705
+ if (Array.isArray(projectConfig.enabledMcpServers)) {
31706
+ issues.push({
31707
+ severity: projectConfig.enabledMcpServers.length === 0 ? "warn" : "info",
31708
+ code: "enabled-mcp-servers-present",
31709
+ message: `Project ${projectPath} contains enabledMcpServers=${JSON.stringify(projectConfig.enabledMcpServers)}. This key is not the same as enabledMcpjsonServers and has caused confusing /mcp debugging reports.`,
31710
+ fix: "If MCP tools are hidden, back up ~/.claude.json, remove only this key, restart Claude Code, and compare /mcp + ToolSearch."
31711
+ });
31712
+ }
31713
+ }
31714
+ const mcpJsonServers = [];
31715
+ for (const mcpJsonPath of findAncestorMcpJsons2(cwd2, homeDir)) {
31716
+ const mcpJson = readJson(mcpJsonPath);
31717
+ for (const name of Object.keys(mcpJson?.mcpServers ?? {})) {
31718
+ mcpJsonServers.push({ name, source: "mcp.json", path: mcpJsonPath });
31719
+ }
31720
+ }
31721
+ const permissionPrefixes = extractPermissionPrefixes(settings);
31722
+ const permissionPrefixSet = new Set(permissionPrefixes);
31723
+ const allConfiguredServerNames = [...globalServers, ...projectServers.map((s) => s.name), ...mcpJsonServers.map((s) => s.name)];
31724
+ const configuredServers = [...new Set(allConfiguredServerNames)].sort();
31725
+ const nonExeServers = configuredServers.filter((s) => s !== "exe-os" && s !== "exe-mem");
31726
+ const nonExeWithoutPermissions = nonExeServers.filter((s) => !serverAllowedByPermissions(s, permissionPrefixSet));
31727
+ if (nonExeWithoutPermissions.length > 0 && permissionPrefixes.some((p) => p === "exe-os" || p === "exe-mem")) {
31728
+ issues.push({
31729
+ severity: "warn",
31730
+ code: "non-exe-mcp-not-permission-allowlisted",
31731
+ message: `Interactive Claude Code may only surface/auto-run permission-allowed MCP tools. exe-os is allowlisted, but these configured servers are not: ${nonExeWithoutPermissions.join(", ")}.`,
31732
+ fix: "In Claude Code, run /permissions and allow the needed MCP server tools (for example mcp__figma__*), then restart. As a diagnostic only, test ENABLE_TOOL_SEARCH=false claude to compare tool injection."
31733
+ });
31734
+ }
31735
+ const duplicateNames = allConfiguredServerNames.filter((name, index, arr) => arr.indexOf(name) !== index);
31736
+ if (duplicateNames.length > 0) {
31737
+ issues.push({
31738
+ severity: "warn",
31739
+ code: "duplicate-mcp-server-names",
31740
+ message: `Duplicate MCP server names exist across global/project/.mcp.json scopes: ${[...new Set(duplicateNames)].join(", ")}. Claude Code can show Connected while dropping tools when scopes collide.`,
31741
+ fix: "Use one source of truth per MCP server name; remove or rename duplicates."
31742
+ });
31743
+ }
31744
+ for (const server of mcpJsonServers) {
31745
+ for (const [projectPath, projectConfig] of applicableProjects) {
31746
+ if (!projectConfig.mcpServers?.[server.name]) continue;
31747
+ const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
31748
+ if (!enabled.has(server.name)) {
31749
+ issues.push({
31750
+ severity: "error",
31751
+ code: "disabled-mcp-json-shadows-project-server",
31752
+ message: `${server.name} appears in ${server.path} and project ${projectPath}, but is not enabled in enabledMcpjsonServers. This can shadow the project server and yield Connected/0 tools.`,
31753
+ fix: "Remove/rename the duplicate .mcp.json entry or enable that .mcp.json server explicitly."
31754
+ });
31755
+ }
31756
+ }
31757
+ }
31758
+ if (env.ENABLE_TOOL_SEARCH && env.ENABLE_TOOL_SEARCH !== "auto") {
31759
+ issues.push({
31760
+ severity: "info",
31761
+ code: "enable-tool-search-env-set",
31762
+ message: `ENABLE_TOOL_SEARCH is set to ${env.ENABLE_TOOL_SEARCH}. This changes Claude Code's deferred tool injection path.`,
31763
+ fix: "For debugging, compare interactive launches with ENABLE_TOOL_SEARCH=false, ENABLE_TOOL_SEARCH=auto, and unset."
31764
+ });
31765
+ }
31766
+ if (configuredServers.length > 8) {
31767
+ issues.push({
31768
+ severity: "info",
31769
+ code: "large-mcp-surface",
31770
+ message: `${configuredServers.length} MCP servers are configured for this project. If pipe mode sees tools but interactive mode does not, reduce scope temporarily or enable only needed servers while debugging Claude Code tool injection.`,
31771
+ fix: "Keep exe-os global, move project/design tools into the active project only, and use /permissions for the servers needed in that session."
31772
+ });
31773
+ }
31774
+ return {
31775
+ cwd: path48.resolve(cwd2),
31776
+ globalServers,
31777
+ projectServers: projectServers.sort((a, b) => a.name.localeCompare(b.name)),
31778
+ mcpJsonServers: mcpJsonServers.sort((a, b) => a.name.localeCompare(b.name)),
31779
+ permissionPrefixes,
31780
+ issues
31781
+ };
31782
+ }
31783
+ function formatMcpDiagnosticReport(report) {
31784
+ const lines = [];
31785
+ lines.push("Claude MCP Diagnostic");
31786
+ lines.push(`cwd: ${report.cwd}`);
31787
+ lines.push("");
31788
+ lines.push(`global servers: ${report.globalServers.length ? report.globalServers.join(", ") : "none"}`);
31789
+ lines.push(`project servers: ${report.projectServers.length ? report.projectServers.map((s) => `${s.name} (${s.path})`).join(", ") : "none"}`);
31790
+ lines.push(`.mcp.json servers: ${report.mcpJsonServers.length ? report.mcpJsonServers.map((s) => `${s.name} (${s.path})`).join(", ") : "none"}`);
31791
+ lines.push(`permission MCP prefixes: ${report.permissionPrefixes.length ? report.permissionPrefixes.join(", ") : "none"}`);
31792
+ lines.push("");
31793
+ if (report.issues.length === 0) {
31794
+ lines.push("\u2713 No obvious MCP config hazards detected.");
31795
+ return lines.join("\n");
31796
+ }
31797
+ lines.push("Issues / clues:");
31798
+ for (const issue of report.issues) {
31799
+ const icon = issue.severity === "error" ? "\u2717" : issue.severity === "warn" ? "!" : "i";
31800
+ lines.push(`${icon} [${issue.code}] ${issue.message}`);
31801
+ if (issue.fix) lines.push(` Fix/test: ${issue.fix}`);
31802
+ }
31803
+ return lines.join("\n");
31804
+ }
31805
+ var init_mcp_diagnostics = __esm({
31806
+ "src/adapters/claude/mcp-diagnostics.ts"() {
31807
+ "use strict";
31808
+ }
31809
+ });
31810
+
31811
+ // src/bin/cli.ts
31812
+ import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
31813
+ import path49 from "path";
31814
+ import os22 from "os";
31631
31815
  var args = process.argv.slice(2);
31632
31816
  if (args.includes("--version") || args.includes("-v")) {
31633
31817
  try {
31634
- const pkgPath = path48.join(path48.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
31635
- const pkg = JSON.parse(readFileSync29(pkgPath, "utf8"));
31818
+ const pkgPath = path49.join(path49.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
31819
+ const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
31636
31820
  console.log(pkg.version);
31637
31821
  } catch {
31638
31822
  console.log("unknown");
@@ -31666,6 +31850,17 @@ if (args.includes("--global")) {
31666
31850
  case "check":
31667
31851
  await runClaudeCheck();
31668
31852
  break;
31853
+ case "mcp-doctor":
31854
+ await runClaudeMcpDoctor();
31855
+ break;
31856
+ case "mcp":
31857
+ if (args[2] === "doctor") {
31858
+ await runClaudeMcpDoctor();
31859
+ } else {
31860
+ console.error("Usage: exe-os claude mcp-doctor");
31861
+ process.exit(1);
31862
+ }
31863
+ break;
31669
31864
  case "uninstall":
31670
31865
  await runClaudeUninstall(args.slice(2));
31671
31866
  break;
@@ -31799,11 +31994,11 @@ ID: ${result.id}`);
31799
31994
  });
31800
31995
  await init_App2().then(() => App_exports);
31801
31996
  } else {
31802
- const claudeDir = path48.join(os21.homedir(), ".claude");
31803
- const settingsPath = path48.join(claudeDir, "settings.json");
31804
- const hasClaudeCode = existsSync34(settingsPath) && (() => {
31997
+ const claudeDir = path49.join(os22.homedir(), ".claude");
31998
+ const settingsPath = path49.join(claudeDir, "settings.json");
31999
+ const hasClaudeCode = existsSync35(settingsPath) && (() => {
31805
32000
  try {
31806
- const raw = readFileSync29(settingsPath, "utf8");
32001
+ const raw = readFileSync30(settingsPath, "utf8");
31807
32002
  return raw.includes("exe-os") || raw.includes("exe-mem");
31808
32003
  } catch {
31809
32004
  return false;
@@ -31813,9 +32008,9 @@ ID: ${result.id}`);
31813
32008
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
31814
32009
  let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
31815
32010
  try {
31816
- const rosterPath = path48.join(os21.homedir(), ".exe-os", "exe-employees.json");
31817
- if (existsSync34(rosterPath)) {
31818
- const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
32011
+ const rosterPath = path49.join(os22.homedir(), ".exe-os", "exe-employees.json");
32012
+ if (existsSync35(rosterPath)) {
32013
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
31819
32014
  const coo = roster.find((e) => e.role === "COO");
31820
32015
  if (coo) cooName = coo.name;
31821
32016
  }
@@ -31833,6 +32028,7 @@ cd into a project folder and run \x1B[1m${cooName}1\x1B[0m to boot your COO.
31833
32028
  exe-os setup Re-run setup wizard
31834
32029
  exe-os cloud sync Sync all data with Exe Cloud
31835
32030
  exe-os claude check Verify Claude Code integration
32031
+ exe-os claude mcp-doctor Diagnose Claude MCP tool visibility
31836
32032
  exe-os tui Launch TUI dashboard (Mode 2)
31837
32033
  exe-os --help Show all commands
31838
32034
  `);
@@ -31880,14 +32076,14 @@ async function runCodexInstall() {
31880
32076
  }
31881
32077
  async function runClaudeCheck() {
31882
32078
  const { detectMcpNameCollisions: detectMcpNameCollisions2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
31883
- const claudeDir = path48.join(os21.homedir(), ".claude");
31884
- const settingsPath = path48.join(claudeDir, "settings.json");
31885
- const claudeJsonPath = path48.join(os21.homedir(), ".claude.json");
32079
+ const claudeDir = path49.join(os22.homedir(), ".claude");
32080
+ const settingsPath = path49.join(claudeDir, "settings.json");
32081
+ const claudeJsonPath = path49.join(os22.homedir(), ".claude.json");
31886
32082
  let ok = true;
31887
- if (existsSync34(settingsPath)) {
32083
+ if (existsSync35(settingsPath)) {
31888
32084
  let settings;
31889
32085
  try {
31890
- settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
32086
+ settings = JSON.parse(readFileSync30(settingsPath, "utf8"));
31891
32087
  } catch {
31892
32088
  console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
31893
32089
  ok = false;
@@ -31913,10 +32109,10 @@ async function runClaudeCheck() {
31913
32109
  console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
31914
32110
  ok = false;
31915
32111
  }
31916
- if (existsSync34(claudeJsonPath)) {
32112
+ if (existsSync35(claudeJsonPath)) {
31917
32113
  let claudeJson;
31918
32114
  try {
31919
- claudeJson = JSON.parse(readFileSync29(claudeJsonPath, "utf8"));
32115
+ claudeJson = JSON.parse(readFileSync30(claudeJsonPath, "utf8"));
31920
32116
  } catch {
31921
32117
  console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
31922
32118
  ok = false;
@@ -31935,7 +32131,7 @@ async function runClaudeCheck() {
31935
32131
  console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
31936
32132
  ok = false;
31937
32133
  }
31938
- const collisions = detectMcpNameCollisions2(os21.homedir(), process.cwd());
32134
+ const collisions = detectMcpNameCollisions2(os22.homedir(), process.cwd());
31939
32135
  const disabledCollisions = collisions.filter((c) => c.disabledInMcpJson);
31940
32136
  if (disabledCollisions.length > 0) {
31941
32137
  console.log("\x1B[31m\u2717\x1B[0m MCP name collision: disabled .mcp.json entries shadow project MCP servers");
@@ -31949,8 +32145,8 @@ async function runClaudeCheck() {
31949
32145
  } else {
31950
32146
  console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
31951
32147
  }
31952
- const skillsDir = path48.join(claudeDir, "skills");
31953
- if (existsSync34(skillsDir)) {
32148
+ const skillsDir = path49.join(claudeDir, "skills");
32149
+ if (existsSync35(skillsDir)) {
31954
32150
  console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
31955
32151
  } else {
31956
32152
  console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
@@ -31963,20 +32159,27 @@ async function runClaudeCheck() {
31963
32159
  console.log("\nAll checks passed.");
31964
32160
  }
31965
32161
  }
32162
+ async function runClaudeMcpDoctor() {
32163
+ const { diagnoseClaudeMcpConfig: diagnoseClaudeMcpConfig2, formatMcpDiagnosticReport: formatMcpDiagnosticReport2 } = await Promise.resolve().then(() => (init_mcp_diagnostics(), mcp_diagnostics_exports));
32164
+ const report = diagnoseClaudeMcpConfig2(os22.homedir(), process.cwd(), process.env);
32165
+ console.log(formatMcpDiagnosticReport2(report));
32166
+ const hasError = report.issues.some((issue) => issue.severity === "error");
32167
+ process.exit(hasError ? 1 : 0);
32168
+ }
31966
32169
  async function runClaudeUninstall(flags = []) {
31967
32170
  const dryRun = flags.includes("--dry-run");
31968
32171
  const purge = flags.includes("--purge");
31969
- const homeDir = os21.homedir();
31970
- const claudeDir = path48.join(homeDir, ".claude");
31971
- const settingsPath = path48.join(claudeDir, "settings.json");
31972
- const claudeJsonPath = path48.join(homeDir, ".claude.json");
31973
- const exeOsDir = path48.join(homeDir, ".exe-os");
32172
+ const homeDir = os22.homedir();
32173
+ const claudeDir = path49.join(homeDir, ".claude");
32174
+ const settingsPath = path49.join(claudeDir, "settings.json");
32175
+ const claudeJsonPath = path49.join(homeDir, ".claude.json");
32176
+ const exeOsDir = path49.join(homeDir, ".exe-os");
31974
32177
  let removed = 0;
31975
32178
  const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
31976
32179
  let settings = {};
31977
- if (existsSync34(settingsPath)) {
32180
+ if (existsSync35(settingsPath)) {
31978
32181
  try {
31979
- settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
32182
+ settings = JSON.parse(readFileSync30(settingsPath, "utf8"));
31980
32183
  } catch {
31981
32184
  console.error("Your ~/.claude/settings.json appears malformed.");
31982
32185
  if (purge) {
@@ -32021,8 +32224,8 @@ async function runClaudeUninstall(flags = []) {
32021
32224
  removed++;
32022
32225
  }
32023
32226
  }
32024
- if (existsSync34(claudeJsonPath)) {
32025
- const raw = readFileSync29(claudeJsonPath, "utf8");
32227
+ if (existsSync35(claudeJsonPath)) {
32228
+ const raw = readFileSync30(claudeJsonPath, "utf8");
32026
32229
  if (raw.length > 1e6) {
32027
32230
  console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
32028
32231
  } else {
@@ -32051,14 +32254,14 @@ async function runClaudeUninstall(flags = []) {
32051
32254
  }
32052
32255
  }
32053
32256
  }
32054
- const skillsDir = path48.join(claudeDir, "skills");
32055
- if (existsSync34(skillsDir)) {
32257
+ const skillsDir = path49.join(claudeDir, "skills");
32258
+ if (existsSync35(skillsDir)) {
32056
32259
  let skillCount = 0;
32057
32260
  try {
32058
32261
  const entries = readdirSync10(skillsDir);
32059
32262
  for (const entry of entries) {
32060
32263
  if (entry.startsWith("exe")) {
32061
- const fullPath = path48.join(skillsDir, entry);
32264
+ const fullPath = path49.join(skillsDir, entry);
32062
32265
  if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
32063
32266
  skillCount++;
32064
32267
  }
@@ -32070,9 +32273,9 @@ async function runClaudeUninstall(flags = []) {
32070
32273
  removed++;
32071
32274
  }
32072
32275
  }
32073
- const claudeMdPath = path48.join(claudeDir, "CLAUDE.md");
32074
- if (existsSync34(claudeMdPath)) {
32075
- const content = readFileSync29(claudeMdPath, "utf8");
32276
+ const claudeMdPath = path49.join(claudeDir, "CLAUDE.md");
32277
+ if (existsSync35(claudeMdPath)) {
32278
+ const content = readFileSync30(claudeMdPath, "utf8");
32076
32279
  const startMarker = "<!-- exe-os:orchestration-start -->";
32077
32280
  const endMarker = "<!-- exe-os:orchestration-end -->";
32078
32281
  const startIdx = content.indexOf(startMarker);
@@ -32084,16 +32287,16 @@ async function runClaudeUninstall(flags = []) {
32084
32287
  removed++;
32085
32288
  }
32086
32289
  }
32087
- const agentsDir = path48.join(claudeDir, "agents");
32088
- if (existsSync34(agentsDir)) {
32290
+ const agentsDir = path49.join(claudeDir, "agents");
32291
+ if (existsSync35(agentsDir)) {
32089
32292
  let agentCount = 0;
32090
32293
  try {
32091
32294
  const entries = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
32092
32295
  let knownNames = /* @__PURE__ */ new Set();
32093
- const rosterPath = path48.join(exeOsDir, "exe-employees.json");
32094
- if (existsSync34(rosterPath)) {
32296
+ const rosterPath = path49.join(exeOsDir, "exe-employees.json");
32297
+ if (existsSync35(rosterPath)) {
32095
32298
  try {
32096
- const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
32299
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
32097
32300
  knownNames = new Set(roster.map((e) => e.name));
32098
32301
  } catch {
32099
32302
  }
@@ -32101,7 +32304,7 @@ async function runClaudeUninstall(flags = []) {
32101
32304
  for (const entry of entries) {
32102
32305
  const name = entry.replace(/\.md$/, "");
32103
32306
  if (knownNames.has(name)) {
32104
- if (!dryRun) rmSync(path48.join(agentsDir, entry), { force: true });
32307
+ if (!dryRun) rmSync(path49.join(agentsDir, entry), { force: true });
32105
32308
  agentCount++;
32106
32309
  }
32107
32310
  }
@@ -32112,16 +32315,16 @@ async function runClaudeUninstall(flags = []) {
32112
32315
  removed++;
32113
32316
  }
32114
32317
  }
32115
- const projectsDir = path48.join(claudeDir, "projects");
32116
- if (existsSync34(projectsDir)) {
32318
+ const projectsDir = path49.join(claudeDir, "projects");
32319
+ if (existsSync35(projectsDir)) {
32117
32320
  let projectCount = 0;
32118
32321
  try {
32119
32322
  const projects = readdirSync10(projectsDir);
32120
32323
  for (const proj of projects) {
32121
- const projSettings = path48.join(projectsDir, proj, "settings.json");
32122
- if (!existsSync34(projSettings)) continue;
32324
+ const projSettings = path49.join(projectsDir, proj, "settings.json");
32325
+ if (!existsSync35(projSettings)) continue;
32123
32326
  try {
32124
- const pSettings = JSON.parse(readFileSync29(projSettings, "utf8"));
32327
+ const pSettings = JSON.parse(readFileSync30(projSettings, "utf8"));
32125
32328
  let changed = false;
32126
32329
  if (Array.isArray(pSettings.permissions?.allow)) {
32127
32330
  const before = pSettings.permissions.allow.length;
@@ -32155,18 +32358,18 @@ async function runClaudeUninstall(flags = []) {
32155
32358
  };
32156
32359
  const exeBinPath = findExeBin3();
32157
32360
  if (!exeBinPath) throw new Error("exe-os not found in PATH");
32158
- const binDir = path48.dirname(exeBinPath);
32361
+ const binDir = path49.dirname(exeBinPath);
32159
32362
  let symlinkCount = 0;
32160
- const rosterPath = path48.join(exeOsDir, "exe-employees.json");
32161
- if (existsSync34(rosterPath)) {
32162
- const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
32363
+ const rosterPath = path49.join(exeOsDir, "exe-employees.json");
32364
+ if (existsSync35(rosterPath)) {
32365
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
32163
32366
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
32164
32367
  const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
32165
32368
  for (const emp of roster) {
32166
32369
  if (emp.name === coordinatorName) continue;
32167
32370
  for (const suffix of ["", "-opencode"]) {
32168
- const linkPath = path48.join(binDir, `${emp.name}${suffix}`);
32169
- if (existsSync34(linkPath)) {
32371
+ const linkPath = path49.join(binDir, `${emp.name}${suffix}`);
32372
+ if (existsSync35(linkPath)) {
32170
32373
  if (!dryRun) rmSync(linkPath, { force: true });
32171
32374
  symlinkCount++;
32172
32375
  }
@@ -32179,7 +32382,7 @@ async function runClaudeUninstall(flags = []) {
32179
32382
  }
32180
32383
  } catch {
32181
32384
  }
32182
- if (purge && existsSync34(exeOsDir)) {
32385
+ if (purge && existsSync35(exeOsDir)) {
32183
32386
  if (!dryRun) {
32184
32387
  process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
32185
32388
  process.stdout.write(" Removing ~/.exe-os...\n");
@@ -32204,7 +32407,7 @@ async function checkForUpdateOnBoot() {
32204
32407
  const config = await loadConfig2();
32205
32408
  if (!config.autoUpdate.checkOnBoot) return;
32206
32409
  const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
32207
- const packageRoot = path48.resolve(
32410
+ const packageRoot = path49.resolve(
32208
32411
  new URL("../..", import.meta.url).pathname
32209
32412
  );
32210
32413
  const result = checkForUpdate2(packageRoot);
@@ -32265,7 +32468,7 @@ async function runActivate(key) {
32265
32468
  const idTemplate = getIdentityTemplate(identityKey);
32266
32469
  if (idTemplate) {
32267
32470
  const idPath = identityPath2(name);
32268
- const dir = path48.dirname(idPath);
32471
+ const dir = path49.dirname(idPath);
32269
32472
  if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
32270
32473
  fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
32271
32474
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",