@askexenow/exe-os 0.9.48 → 0.9.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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();
@@ -16607,7 +16607,20 @@ async function runUpdate(cliArgs) {
16607
16607
  } catch {
16608
16608
  }
16609
16609
  }
16610
- console.log(" Hooks re-wired, daemon restarted automatically.");
16610
+ console.log("\u{1F527} Re-registering MCP, hooks, wrappers, and daemon...");
16611
+ try {
16612
+ execSync13("exe-os-install --global", {
16613
+ stdio: ["pipe", "inherit", "inherit"],
16614
+ timeout: 3e5
16615
+ });
16616
+ } catch (err) {
16617
+ console.error("\n\u26A0\uFE0F Package updated, but post-update install failed.");
16618
+ console.error(" Run manually: exe-os-install --global");
16619
+ if (err instanceof Error && err.message) {
16620
+ console.error(` Error: ${err.message.split("\n")[0]}`);
16621
+ }
16622
+ }
16623
+ console.log(" MCP, hooks, and daemon refreshed.");
16611
16624
  console.log("");
16612
16625
  console.log(" \x1B[33m\u26A1 Run /mcp in each active Claude Code session to pick up new tools.\x1B[0m");
16613
16626
  console.log(" \x1B[2m(MCP servers can't hot-reload \u2014 Claude Code needs to reconnect them.)\x1B[0m");
@@ -21308,8 +21321,8 @@ var init_ErrorOverview = __esm({
21308
21321
  "use strict";
21309
21322
  init_Box();
21310
21323
  init_Text();
21311
- cleanupPath = (path49) => {
21312
- return path49?.replace(`file://${cwd()}/`, "");
21324
+ cleanupPath = (path50) => {
21325
+ return path50?.replace(`file://${cwd()}/`, "");
21313
21326
  };
21314
21327
  stackUtils = new StackUtils({
21315
21328
  cwd: cwd(),
@@ -23717,11 +23730,11 @@ function Footer() {
23717
23730
  } catch {
23718
23731
  }
23719
23732
  try {
23720
- const { existsSync: existsSync35 } = await import("fs");
23733
+ const { existsSync: existsSync36 } = await import("fs");
23721
23734
  const { join } = await import("path");
23722
23735
  const home = process.env.HOME ?? "";
23723
23736
  const pidPath = join(home, ".exe-os", "exed.pid");
23724
- setDaemon(existsSync35(pidPath) ? "running" : "stopped");
23737
+ setDaemon(existsSync36(pidPath) ? "running" : "stopped");
23725
23738
  } catch {
23726
23739
  setDaemon("unknown");
23727
23740
  }
@@ -26505,15 +26518,15 @@ function CommandCenterView({
26505
26518
  const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
26506
26519
  const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
26507
26520
  const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
26508
- const { readFileSync: readFileSync30, existsSync: existsSync35 } = await import("fs");
26521
+ const { readFileSync: readFileSync31, existsSync: existsSync36 } = await import("fs");
26509
26522
  const { join } = await import("path");
26510
26523
  const { homedir: homedir8 } = await import("os");
26511
26524
  const configPath = join(homedir8(), ".exe-os", "config.json");
26512
26525
  let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
26513
26526
  let providerConfigs = {};
26514
- if (existsSync35(configPath)) {
26527
+ if (existsSync36(configPath)) {
26515
26528
  try {
26516
- const raw = JSON.parse(readFileSync30(configPath, "utf8"));
26529
+ const raw = JSON.parse(readFileSync31(configPath, "utf8"));
26517
26530
  if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
26518
26531
  if (raw.providers && typeof raw.providers === "object") {
26519
26532
  providerConfigs = raw.providers;
@@ -26574,7 +26587,7 @@ function CommandCenterView({
26574
26587
  const markerDir = join(homedir8(), ".exe-os", "session-cache");
26575
26588
  const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
26576
26589
  for (const f of agentFiles) {
26577
- const data = JSON.parse(readFileSync30(join(markerDir, f), "utf8"));
26590
+ const data = JSON.parse(readFileSync31(join(markerDir, f), "utf8"));
26578
26591
  if (data.agentRole) {
26579
26592
  agentRole = data.agentRole;
26580
26593
  break;
@@ -26757,7 +26770,7 @@ function CommandCenterView({
26757
26770
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
26758
26771
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
26759
26772
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
26760
- const { existsSync: existsSync35 } = await import("fs");
26773
+ const { existsSync: existsSync36 } = await import("fs");
26761
26774
  const { join } = await import("path");
26762
26775
  const client = getClient2();
26763
26776
  if (!client) {
@@ -26828,7 +26841,7 @@ function CommandCenterView({
26828
26841
  }
26829
26842
  const memoryCount = memoryCounts.get(name) ?? 0;
26830
26843
  const openTaskCount = openTaskCounts.get(name) ?? 0;
26831
- const hasGit = projectDir ? existsSync35(join(projectDir, ".git")) : false;
26844
+ const hasGit = projectDir ? existsSync36(join(projectDir, ".git")) : false;
26832
26845
  const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
26833
26846
  projectList.push({
26834
26847
  projectName: name,
@@ -26853,7 +26866,7 @@ function CommandCenterView({
26853
26866
  setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
26854
26867
  try {
26855
26868
  const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
26856
- setHealth((h) => ({ ...h, daemon: existsSync35(pidPath) ? "running" : "stopped" }));
26869
+ setHealth((h) => ({ ...h, daemon: existsSync36(pidPath) ? "running" : "stopped" }));
26857
26870
  } catch {
26858
26871
  }
26859
26872
  const activityResult = await client.execute(
@@ -28976,12 +28989,12 @@ async function loadGatewayConfig() {
28976
28989
  state.running = false;
28977
28990
  }
28978
28991
  try {
28979
- const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
28992
+ const { existsSync: existsSync36, readFileSync: readFileSync31 } = await import("fs");
28980
28993
  const { join } = await import("path");
28981
28994
  const home = process.env.HOME ?? "";
28982
28995
  const configPath = join(home, ".exe-os", "gateway.json");
28983
- if (existsSync35(configPath)) {
28984
- const raw = JSON.parse(readFileSync30(configPath, "utf8"));
28996
+ if (existsSync36(configPath)) {
28997
+ const raw = JSON.parse(readFileSync31(configPath, "utf8"));
28985
28998
  state.port = raw.port ?? 3100;
28986
28999
  state.gatewayUrl = raw.gatewayUrl ?? "";
28987
29000
  if (raw.adapters) {
@@ -29579,12 +29592,12 @@ function TeamView({ onBack, onViewSessions }) {
29579
29592
  setMembers(teamData);
29580
29593
  setDbError(null);
29581
29594
  try {
29582
- const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
29595
+ const { existsSync: existsSync36, readFileSync: readFileSync31 } = await import("fs");
29583
29596
  const { join } = await import("path");
29584
29597
  const home = process.env.HOME ?? "";
29585
29598
  const gatewayConfig = join(home, ".exe-os", "gateway.json");
29586
- if (existsSync35(gatewayConfig)) {
29587
- const raw = JSON.parse(readFileSync30(gatewayConfig, "utf8"));
29599
+ if (existsSync36(gatewayConfig)) {
29600
+ const raw = JSON.parse(readFileSync31(gatewayConfig, "utf8"));
29588
29601
  if (raw.agents && raw.agents.length > 0) {
29589
29602
  setExternals(raw.agents.map((a) => ({
29590
29603
  name: a.name,
@@ -29764,8 +29777,8 @@ __export(wiki_client_exports, {
29764
29777
  listDocuments: () => listDocuments,
29765
29778
  listWorkspaces: () => listWorkspaces
29766
29779
  });
29767
- async function wikiFetch(config, path49, method = "GET", body) {
29768
- const url = `${config.baseUrl}/api/v1${path49}`;
29780
+ async function wikiFetch(config, path50, method = "GET", body) {
29781
+ const url = `${config.baseUrl}/api/v1${path50}`;
29769
29782
  const headers = {
29770
29783
  Authorization: `Bearer ${config.apiKey}`,
29771
29784
  "Content-Type": "application/json"
@@ -29798,7 +29811,7 @@ async function wikiFetch(config, path49, method = "GET", body) {
29798
29811
  }
29799
29812
  }
29800
29813
  if (!response.ok) {
29801
- throw new Error(`Wiki API ${method} ${path49}: ${response.status} ${response.statusText}`);
29814
+ throw new Error(`Wiki API ${method} ${path50}: ${response.status} ${response.statusText}`);
29802
29815
  }
29803
29816
  return response.json();
29804
29817
  } finally {
@@ -30392,12 +30405,12 @@ function SettingsView({ onBack }) {
30392
30405
  }
30393
30406
  setProviders(providerList);
30394
30407
  try {
30395
- const { existsSync: existsSync35 } = await import("fs");
30408
+ const { existsSync: existsSync36 } = await import("fs");
30396
30409
  const { join } = await import("path");
30397
30410
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
30398
30411
  const cfg = await loadConfig2();
30399
30412
  const home = process.env.HOME ?? "";
30400
- const hasKey = existsSync35(join(home, ".exe-os", "master.key"));
30413
+ const hasKey = existsSync36(join(home, ".exe-os", "master.key"));
30401
30414
  if (cfg.cloud) {
30402
30415
  setCloud({
30403
30416
  configured: true,
@@ -30410,22 +30423,22 @@ function SettingsView({ onBack }) {
30410
30423
  const pidPath = join(home, ".exe-os", "exed.pid");
30411
30424
  let daemon = "unknown";
30412
30425
  try {
30413
- daemon = existsSync35(pidPath) ? "running" : "stopped";
30426
+ daemon = existsSync36(pidPath) ? "running" : "stopped";
30414
30427
  } catch {
30415
30428
  }
30416
30429
  let version = "unknown";
30417
30430
  try {
30418
- const { readFileSync: readFileSync30 } = await import("fs");
30431
+ const { readFileSync: readFileSync31 } = await import("fs");
30419
30432
  const { createRequire: createRequire3 } = await import("module");
30420
30433
  const require2 = createRequire3(import.meta.url);
30421
30434
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
30422
- const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
30435
+ const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
30423
30436
  version = pkg.version;
30424
30437
  } catch {
30425
30438
  try {
30426
- const { readFileSync: readFileSync30 } = await import("fs");
30439
+ const { readFileSync: readFileSync31 } = await import("fs");
30427
30440
  const { join: joinPath } = await import("path");
30428
- const pkg = JSON.parse(readFileSync30(joinPath(process.cwd(), "package.json"), "utf8"));
30441
+ const pkg = JSON.parse(readFileSync31(joinPath(process.cwd(), "package.json"), "utf8"));
30429
30442
  version = pkg.version;
30430
30443
  } catch {
30431
30444
  }
@@ -31624,15 +31637,199 @@ var init_installer3 = __esm({
31624
31637
  }
31625
31638
  });
31626
31639
 
31627
- // src/bin/cli.ts
31628
- import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
31640
+ // src/adapters/claude/mcp-diagnostics.ts
31641
+ var mcp_diagnostics_exports = {};
31642
+ __export(mcp_diagnostics_exports, {
31643
+ diagnoseClaudeMcpConfig: () => diagnoseClaudeMcpConfig,
31644
+ formatMcpDiagnosticReport: () => formatMcpDiagnosticReport
31645
+ });
31646
+ import { existsSync as existsSync34, readFileSync as readFileSync29 } from "fs";
31629
31647
  import path48 from "path";
31630
31648
  import os21 from "os";
31649
+ function readJson(filePath) {
31650
+ if (!existsSync34(filePath)) return null;
31651
+ try {
31652
+ return JSON.parse(readFileSync29(filePath, "utf8"));
31653
+ } catch {
31654
+ return null;
31655
+ }
31656
+ }
31657
+ function pathApplies2(projectPath, cwd2) {
31658
+ const project = path48.resolve(projectPath);
31659
+ const current = path48.resolve(cwd2);
31660
+ return current === project || current.startsWith(project + path48.sep);
31661
+ }
31662
+ function findAncestorMcpJsons2(cwd2, homeDir) {
31663
+ const files = [];
31664
+ let dir = path48.resolve(cwd2);
31665
+ const root = path48.parse(dir).root;
31666
+ const stop = path48.resolve(homeDir);
31667
+ while (dir !== root) {
31668
+ const candidate = path48.join(dir, ".mcp.json");
31669
+ if (existsSync34(candidate)) files.push(candidate);
31670
+ if (dir === stop) break;
31671
+ dir = path48.dirname(dir);
31672
+ }
31673
+ return files;
31674
+ }
31675
+ function extractPermissionPrefixes(settings) {
31676
+ const allow = settings?.permissions?.allow ?? [];
31677
+ const prefixes = /* @__PURE__ */ new Set();
31678
+ for (const entry of allow) {
31679
+ const match = entry.match(/^mcp__(.+?)__/);
31680
+ if (match?.[1]) prefixes.add(match[1]);
31681
+ }
31682
+ return [...prefixes].sort();
31683
+ }
31684
+ function serverAllowedByPermissions(serverName, prefixes) {
31685
+ return prefixes.has(serverName) || prefixes.has(serverName.replace(/-/g, "_"));
31686
+ }
31687
+ function diagnoseClaudeMcpConfig(homeDir = os21.homedir(), cwd2 = process.cwd(), env = process.env) {
31688
+ const claudeJsonPath = path48.join(homeDir, ".claude.json");
31689
+ const settingsPath = path48.join(homeDir, ".claude", "settings.json");
31690
+ const claudeJson = readJson(claudeJsonPath);
31691
+ const settings = readJson(settingsPath);
31692
+ const issues = [];
31693
+ if (!claudeJson) {
31694
+ issues.push({
31695
+ severity: "error",
31696
+ code: "claude-json-missing-or-invalid",
31697
+ message: "~/.claude.json is missing or invalid, so Claude MCP server config cannot be audited.",
31698
+ fix: "Run exe-os claude install, then re-open Claude Code."
31699
+ });
31700
+ }
31701
+ if (!settings) {
31702
+ issues.push({
31703
+ severity: "warn",
31704
+ code: "settings-json-missing-or-invalid",
31705
+ message: "~/.claude/settings.json is missing or invalid; MCP permissions cannot be audited.",
31706
+ fix: "Run exe-os claude install, then re-open Claude Code."
31707
+ });
31708
+ }
31709
+ const globalServers = Object.keys(claudeJson?.mcpServers ?? {}).sort();
31710
+ const projectServers = [];
31711
+ const applicableProjects = [];
31712
+ for (const [projectPath, projectConfig] of Object.entries(claudeJson?.projects ?? {})) {
31713
+ if (!pathApplies2(projectPath, cwd2)) continue;
31714
+ applicableProjects.push([projectPath, projectConfig]);
31715
+ for (const name of Object.keys(projectConfig.mcpServers ?? {})) {
31716
+ projectServers.push({ name, source: "project", path: projectPath });
31717
+ }
31718
+ if (Array.isArray(projectConfig.enabledMcpServers)) {
31719
+ issues.push({
31720
+ severity: projectConfig.enabledMcpServers.length === 0 ? "warn" : "info",
31721
+ code: "enabled-mcp-servers-present",
31722
+ message: `Project ${projectPath} contains enabledMcpServers=${JSON.stringify(projectConfig.enabledMcpServers)}. This key is not the same as enabledMcpjsonServers and has caused confusing /mcp debugging reports.`,
31723
+ fix: "If MCP tools are hidden, back up ~/.claude.json, remove only this key, restart Claude Code, and compare /mcp + ToolSearch."
31724
+ });
31725
+ }
31726
+ }
31727
+ const mcpJsonServers = [];
31728
+ for (const mcpJsonPath of findAncestorMcpJsons2(cwd2, homeDir)) {
31729
+ const mcpJson = readJson(mcpJsonPath);
31730
+ for (const name of Object.keys(mcpJson?.mcpServers ?? {})) {
31731
+ mcpJsonServers.push({ name, source: "mcp.json", path: mcpJsonPath });
31732
+ }
31733
+ }
31734
+ const permissionPrefixes = extractPermissionPrefixes(settings);
31735
+ const permissionPrefixSet = new Set(permissionPrefixes);
31736
+ const allConfiguredServerNames = [...globalServers, ...projectServers.map((s) => s.name), ...mcpJsonServers.map((s) => s.name)];
31737
+ const configuredServers = [...new Set(allConfiguredServerNames)].sort();
31738
+ const nonExeServers = configuredServers.filter((s) => s !== "exe-os" && s !== "exe-mem");
31739
+ const nonExeWithoutPermissions = nonExeServers.filter((s) => !serverAllowedByPermissions(s, permissionPrefixSet));
31740
+ if (nonExeWithoutPermissions.length > 0 && permissionPrefixes.some((p) => p === "exe-os" || p === "exe-mem")) {
31741
+ issues.push({
31742
+ severity: "warn",
31743
+ code: "non-exe-mcp-not-permission-allowlisted",
31744
+ 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(", ")}.`,
31745
+ 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."
31746
+ });
31747
+ }
31748
+ const duplicateNames = allConfiguredServerNames.filter((name, index, arr) => arr.indexOf(name) !== index);
31749
+ if (duplicateNames.length > 0) {
31750
+ issues.push({
31751
+ severity: "warn",
31752
+ code: "duplicate-mcp-server-names",
31753
+ 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.`,
31754
+ fix: "Use one source of truth per MCP server name; remove or rename duplicates."
31755
+ });
31756
+ }
31757
+ for (const server of mcpJsonServers) {
31758
+ for (const [projectPath, projectConfig] of applicableProjects) {
31759
+ if (!projectConfig.mcpServers?.[server.name]) continue;
31760
+ const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
31761
+ if (!enabled.has(server.name)) {
31762
+ issues.push({
31763
+ severity: "error",
31764
+ code: "disabled-mcp-json-shadows-project-server",
31765
+ 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.`,
31766
+ fix: "Remove/rename the duplicate .mcp.json entry or enable that .mcp.json server explicitly."
31767
+ });
31768
+ }
31769
+ }
31770
+ }
31771
+ if (env.ENABLE_TOOL_SEARCH && env.ENABLE_TOOL_SEARCH !== "auto") {
31772
+ issues.push({
31773
+ severity: "info",
31774
+ code: "enable-tool-search-env-set",
31775
+ message: `ENABLE_TOOL_SEARCH is set to ${env.ENABLE_TOOL_SEARCH}. This changes Claude Code's deferred tool injection path.`,
31776
+ fix: "For debugging, compare interactive launches with ENABLE_TOOL_SEARCH=false, ENABLE_TOOL_SEARCH=auto, and unset."
31777
+ });
31778
+ }
31779
+ if (configuredServers.length > 8) {
31780
+ issues.push({
31781
+ severity: "info",
31782
+ code: "large-mcp-surface",
31783
+ 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.`,
31784
+ fix: "Keep exe-os global, move project/design tools into the active project only, and use /permissions for the servers needed in that session."
31785
+ });
31786
+ }
31787
+ return {
31788
+ cwd: path48.resolve(cwd2),
31789
+ globalServers,
31790
+ projectServers: projectServers.sort((a, b) => a.name.localeCompare(b.name)),
31791
+ mcpJsonServers: mcpJsonServers.sort((a, b) => a.name.localeCompare(b.name)),
31792
+ permissionPrefixes,
31793
+ issues
31794
+ };
31795
+ }
31796
+ function formatMcpDiagnosticReport(report) {
31797
+ const lines = [];
31798
+ lines.push("Claude MCP Diagnostic");
31799
+ lines.push(`cwd: ${report.cwd}`);
31800
+ lines.push("");
31801
+ lines.push(`global servers: ${report.globalServers.length ? report.globalServers.join(", ") : "none"}`);
31802
+ lines.push(`project servers: ${report.projectServers.length ? report.projectServers.map((s) => `${s.name} (${s.path})`).join(", ") : "none"}`);
31803
+ lines.push(`.mcp.json servers: ${report.mcpJsonServers.length ? report.mcpJsonServers.map((s) => `${s.name} (${s.path})`).join(", ") : "none"}`);
31804
+ lines.push(`permission MCP prefixes: ${report.permissionPrefixes.length ? report.permissionPrefixes.join(", ") : "none"}`);
31805
+ lines.push("");
31806
+ if (report.issues.length === 0) {
31807
+ lines.push("\u2713 No obvious MCP config hazards detected.");
31808
+ return lines.join("\n");
31809
+ }
31810
+ lines.push("Issues / clues:");
31811
+ for (const issue of report.issues) {
31812
+ const icon = issue.severity === "error" ? "\u2717" : issue.severity === "warn" ? "!" : "i";
31813
+ lines.push(`${icon} [${issue.code}] ${issue.message}`);
31814
+ if (issue.fix) lines.push(` Fix/test: ${issue.fix}`);
31815
+ }
31816
+ return lines.join("\n");
31817
+ }
31818
+ var init_mcp_diagnostics = __esm({
31819
+ "src/adapters/claude/mcp-diagnostics.ts"() {
31820
+ "use strict";
31821
+ }
31822
+ });
31823
+
31824
+ // src/bin/cli.ts
31825
+ import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
31826
+ import path49 from "path";
31827
+ import os22 from "os";
31631
31828
  var args = process.argv.slice(2);
31632
31829
  if (args.includes("--version") || args.includes("-v")) {
31633
31830
  try {
31634
- const pkgPath = path48.join(path48.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
31635
- const pkg = JSON.parse(readFileSync29(pkgPath, "utf8"));
31831
+ const pkgPath = path49.join(path49.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
31832
+ const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
31636
31833
  console.log(pkg.version);
31637
31834
  } catch {
31638
31835
  console.log("unknown");
@@ -31666,6 +31863,17 @@ if (args.includes("--global")) {
31666
31863
  case "check":
31667
31864
  await runClaudeCheck();
31668
31865
  break;
31866
+ case "mcp-doctor":
31867
+ await runClaudeMcpDoctor();
31868
+ break;
31869
+ case "mcp":
31870
+ if (args[2] === "doctor") {
31871
+ await runClaudeMcpDoctor();
31872
+ } else {
31873
+ console.error("Usage: exe-os claude mcp-doctor");
31874
+ process.exit(1);
31875
+ }
31876
+ break;
31669
31877
  case "uninstall":
31670
31878
  await runClaudeUninstall(args.slice(2));
31671
31879
  break;
@@ -31799,11 +32007,11 @@ ID: ${result.id}`);
31799
32007
  });
31800
32008
  await init_App2().then(() => App_exports);
31801
32009
  } else {
31802
- const claudeDir = path48.join(os21.homedir(), ".claude");
31803
- const settingsPath = path48.join(claudeDir, "settings.json");
31804
- const hasClaudeCode = existsSync34(settingsPath) && (() => {
32010
+ const claudeDir = path49.join(os22.homedir(), ".claude");
32011
+ const settingsPath = path49.join(claudeDir, "settings.json");
32012
+ const hasClaudeCode = existsSync35(settingsPath) && (() => {
31805
32013
  try {
31806
- const raw = readFileSync29(settingsPath, "utf8");
32014
+ const raw = readFileSync30(settingsPath, "utf8");
31807
32015
  return raw.includes("exe-os") || raw.includes("exe-mem");
31808
32016
  } catch {
31809
32017
  return false;
@@ -31813,9 +32021,9 @@ ID: ${result.id}`);
31813
32021
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
31814
32022
  let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
31815
32023
  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"));
32024
+ const rosterPath = path49.join(os22.homedir(), ".exe-os", "exe-employees.json");
32025
+ if (existsSync35(rosterPath)) {
32026
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
31819
32027
  const coo = roster.find((e) => e.role === "COO");
31820
32028
  if (coo) cooName = coo.name;
31821
32029
  }
@@ -31833,6 +32041,7 @@ cd into a project folder and run \x1B[1m${cooName}1\x1B[0m to boot your COO.
31833
32041
  exe-os setup Re-run setup wizard
31834
32042
  exe-os cloud sync Sync all data with Exe Cloud
31835
32043
  exe-os claude check Verify Claude Code integration
32044
+ exe-os claude mcp-doctor Diagnose Claude MCP tool visibility
31836
32045
  exe-os tui Launch TUI dashboard (Mode 2)
31837
32046
  exe-os --help Show all commands
31838
32047
  `);
@@ -31880,14 +32089,14 @@ async function runCodexInstall() {
31880
32089
  }
31881
32090
  async function runClaudeCheck() {
31882
32091
  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");
32092
+ const claudeDir = path49.join(os22.homedir(), ".claude");
32093
+ const settingsPath = path49.join(claudeDir, "settings.json");
32094
+ const claudeJsonPath = path49.join(os22.homedir(), ".claude.json");
31886
32095
  let ok = true;
31887
- if (existsSync34(settingsPath)) {
32096
+ if (existsSync35(settingsPath)) {
31888
32097
  let settings;
31889
32098
  try {
31890
- settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
32099
+ settings = JSON.parse(readFileSync30(settingsPath, "utf8"));
31891
32100
  } catch {
31892
32101
  console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
31893
32102
  ok = false;
@@ -31913,10 +32122,10 @@ async function runClaudeCheck() {
31913
32122
  console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
31914
32123
  ok = false;
31915
32124
  }
31916
- if (existsSync34(claudeJsonPath)) {
32125
+ if (existsSync35(claudeJsonPath)) {
31917
32126
  let claudeJson;
31918
32127
  try {
31919
- claudeJson = JSON.parse(readFileSync29(claudeJsonPath, "utf8"));
32128
+ claudeJson = JSON.parse(readFileSync30(claudeJsonPath, "utf8"));
31920
32129
  } catch {
31921
32130
  console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
31922
32131
  ok = false;
@@ -31935,7 +32144,7 @@ async function runClaudeCheck() {
31935
32144
  console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
31936
32145
  ok = false;
31937
32146
  }
31938
- const collisions = detectMcpNameCollisions2(os21.homedir(), process.cwd());
32147
+ const collisions = detectMcpNameCollisions2(os22.homedir(), process.cwd());
31939
32148
  const disabledCollisions = collisions.filter((c) => c.disabledInMcpJson);
31940
32149
  if (disabledCollisions.length > 0) {
31941
32150
  console.log("\x1B[31m\u2717\x1B[0m MCP name collision: disabled .mcp.json entries shadow project MCP servers");
@@ -31949,8 +32158,8 @@ async function runClaudeCheck() {
31949
32158
  } else {
31950
32159
  console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
31951
32160
  }
31952
- const skillsDir = path48.join(claudeDir, "skills");
31953
- if (existsSync34(skillsDir)) {
32161
+ const skillsDir = path49.join(claudeDir, "skills");
32162
+ if (existsSync35(skillsDir)) {
31954
32163
  console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
31955
32164
  } else {
31956
32165
  console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
@@ -31963,20 +32172,27 @@ async function runClaudeCheck() {
31963
32172
  console.log("\nAll checks passed.");
31964
32173
  }
31965
32174
  }
32175
+ async function runClaudeMcpDoctor() {
32176
+ const { diagnoseClaudeMcpConfig: diagnoseClaudeMcpConfig2, formatMcpDiagnosticReport: formatMcpDiagnosticReport2 } = await Promise.resolve().then(() => (init_mcp_diagnostics(), mcp_diagnostics_exports));
32177
+ const report = diagnoseClaudeMcpConfig2(os22.homedir(), process.cwd(), process.env);
32178
+ console.log(formatMcpDiagnosticReport2(report));
32179
+ const hasError = report.issues.some((issue) => issue.severity === "error");
32180
+ process.exit(hasError ? 1 : 0);
32181
+ }
31966
32182
  async function runClaudeUninstall(flags = []) {
31967
32183
  const dryRun = flags.includes("--dry-run");
31968
32184
  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");
32185
+ const homeDir = os22.homedir();
32186
+ const claudeDir = path49.join(homeDir, ".claude");
32187
+ const settingsPath = path49.join(claudeDir, "settings.json");
32188
+ const claudeJsonPath = path49.join(homeDir, ".claude.json");
32189
+ const exeOsDir = path49.join(homeDir, ".exe-os");
31974
32190
  let removed = 0;
31975
32191
  const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
31976
32192
  let settings = {};
31977
- if (existsSync34(settingsPath)) {
32193
+ if (existsSync35(settingsPath)) {
31978
32194
  try {
31979
- settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
32195
+ settings = JSON.parse(readFileSync30(settingsPath, "utf8"));
31980
32196
  } catch {
31981
32197
  console.error("Your ~/.claude/settings.json appears malformed.");
31982
32198
  if (purge) {
@@ -32021,8 +32237,8 @@ async function runClaudeUninstall(flags = []) {
32021
32237
  removed++;
32022
32238
  }
32023
32239
  }
32024
- if (existsSync34(claudeJsonPath)) {
32025
- const raw = readFileSync29(claudeJsonPath, "utf8");
32240
+ if (existsSync35(claudeJsonPath)) {
32241
+ const raw = readFileSync30(claudeJsonPath, "utf8");
32026
32242
  if (raw.length > 1e6) {
32027
32243
  console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
32028
32244
  } else {
@@ -32051,14 +32267,14 @@ async function runClaudeUninstall(flags = []) {
32051
32267
  }
32052
32268
  }
32053
32269
  }
32054
- const skillsDir = path48.join(claudeDir, "skills");
32055
- if (existsSync34(skillsDir)) {
32270
+ const skillsDir = path49.join(claudeDir, "skills");
32271
+ if (existsSync35(skillsDir)) {
32056
32272
  let skillCount = 0;
32057
32273
  try {
32058
32274
  const entries = readdirSync10(skillsDir);
32059
32275
  for (const entry of entries) {
32060
32276
  if (entry.startsWith("exe")) {
32061
- const fullPath = path48.join(skillsDir, entry);
32277
+ const fullPath = path49.join(skillsDir, entry);
32062
32278
  if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
32063
32279
  skillCount++;
32064
32280
  }
@@ -32070,9 +32286,9 @@ async function runClaudeUninstall(flags = []) {
32070
32286
  removed++;
32071
32287
  }
32072
32288
  }
32073
- const claudeMdPath = path48.join(claudeDir, "CLAUDE.md");
32074
- if (existsSync34(claudeMdPath)) {
32075
- const content = readFileSync29(claudeMdPath, "utf8");
32289
+ const claudeMdPath = path49.join(claudeDir, "CLAUDE.md");
32290
+ if (existsSync35(claudeMdPath)) {
32291
+ const content = readFileSync30(claudeMdPath, "utf8");
32076
32292
  const startMarker = "<!-- exe-os:orchestration-start -->";
32077
32293
  const endMarker = "<!-- exe-os:orchestration-end -->";
32078
32294
  const startIdx = content.indexOf(startMarker);
@@ -32084,16 +32300,16 @@ async function runClaudeUninstall(flags = []) {
32084
32300
  removed++;
32085
32301
  }
32086
32302
  }
32087
- const agentsDir = path48.join(claudeDir, "agents");
32088
- if (existsSync34(agentsDir)) {
32303
+ const agentsDir = path49.join(claudeDir, "agents");
32304
+ if (existsSync35(agentsDir)) {
32089
32305
  let agentCount = 0;
32090
32306
  try {
32091
32307
  const entries = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
32092
32308
  let knownNames = /* @__PURE__ */ new Set();
32093
- const rosterPath = path48.join(exeOsDir, "exe-employees.json");
32094
- if (existsSync34(rosterPath)) {
32309
+ const rosterPath = path49.join(exeOsDir, "exe-employees.json");
32310
+ if (existsSync35(rosterPath)) {
32095
32311
  try {
32096
- const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
32312
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
32097
32313
  knownNames = new Set(roster.map((e) => e.name));
32098
32314
  } catch {
32099
32315
  }
@@ -32101,7 +32317,7 @@ async function runClaudeUninstall(flags = []) {
32101
32317
  for (const entry of entries) {
32102
32318
  const name = entry.replace(/\.md$/, "");
32103
32319
  if (knownNames.has(name)) {
32104
- if (!dryRun) rmSync(path48.join(agentsDir, entry), { force: true });
32320
+ if (!dryRun) rmSync(path49.join(agentsDir, entry), { force: true });
32105
32321
  agentCount++;
32106
32322
  }
32107
32323
  }
@@ -32112,16 +32328,16 @@ async function runClaudeUninstall(flags = []) {
32112
32328
  removed++;
32113
32329
  }
32114
32330
  }
32115
- const projectsDir = path48.join(claudeDir, "projects");
32116
- if (existsSync34(projectsDir)) {
32331
+ const projectsDir = path49.join(claudeDir, "projects");
32332
+ if (existsSync35(projectsDir)) {
32117
32333
  let projectCount = 0;
32118
32334
  try {
32119
32335
  const projects = readdirSync10(projectsDir);
32120
32336
  for (const proj of projects) {
32121
- const projSettings = path48.join(projectsDir, proj, "settings.json");
32122
- if (!existsSync34(projSettings)) continue;
32337
+ const projSettings = path49.join(projectsDir, proj, "settings.json");
32338
+ if (!existsSync35(projSettings)) continue;
32123
32339
  try {
32124
- const pSettings = JSON.parse(readFileSync29(projSettings, "utf8"));
32340
+ const pSettings = JSON.parse(readFileSync30(projSettings, "utf8"));
32125
32341
  let changed = false;
32126
32342
  if (Array.isArray(pSettings.permissions?.allow)) {
32127
32343
  const before = pSettings.permissions.allow.length;
@@ -32155,18 +32371,18 @@ async function runClaudeUninstall(flags = []) {
32155
32371
  };
32156
32372
  const exeBinPath = findExeBin3();
32157
32373
  if (!exeBinPath) throw new Error("exe-os not found in PATH");
32158
- const binDir = path48.dirname(exeBinPath);
32374
+ const binDir = path49.dirname(exeBinPath);
32159
32375
  let symlinkCount = 0;
32160
- const rosterPath = path48.join(exeOsDir, "exe-employees.json");
32161
- if (existsSync34(rosterPath)) {
32162
- const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
32376
+ const rosterPath = path49.join(exeOsDir, "exe-employees.json");
32377
+ if (existsSync35(rosterPath)) {
32378
+ const roster = JSON.parse(readFileSync30(rosterPath, "utf8"));
32163
32379
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
32164
32380
  const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
32165
32381
  for (const emp of roster) {
32166
32382
  if (emp.name === coordinatorName) continue;
32167
32383
  for (const suffix of ["", "-opencode"]) {
32168
- const linkPath = path48.join(binDir, `${emp.name}${suffix}`);
32169
- if (existsSync34(linkPath)) {
32384
+ const linkPath = path49.join(binDir, `${emp.name}${suffix}`);
32385
+ if (existsSync35(linkPath)) {
32170
32386
  if (!dryRun) rmSync(linkPath, { force: true });
32171
32387
  symlinkCount++;
32172
32388
  }
@@ -32179,7 +32395,7 @@ async function runClaudeUninstall(flags = []) {
32179
32395
  }
32180
32396
  } catch {
32181
32397
  }
32182
- if (purge && existsSync34(exeOsDir)) {
32398
+ if (purge && existsSync35(exeOsDir)) {
32183
32399
  if (!dryRun) {
32184
32400
  process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
32185
32401
  process.stdout.write(" Removing ~/.exe-os...\n");
@@ -32204,13 +32420,13 @@ async function checkForUpdateOnBoot() {
32204
32420
  const config = await loadConfig2();
32205
32421
  if (!config.autoUpdate.checkOnBoot) return;
32206
32422
  const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
32207
- const packageRoot = path48.resolve(
32423
+ const packageRoot = path49.resolve(
32208
32424
  new URL("../..", import.meta.url).pathname
32209
32425
  );
32210
32426
  const result = checkForUpdate2(packageRoot);
32211
32427
  if (result.updateAvailable) {
32212
32428
  process.stderr.write(
32213
- `\x1B[33m[update]\x1B[0m exe-os v${result.remoteVersion} available (current: v${result.localVersion}). Run \x1B[36mnpm update -g @askexenow/exe-os\x1B[0m to upgrade.
32429
+ `\x1B[33m[update]\x1B[0m exe-os v${result.remoteVersion} available (current: v${result.localVersion}). Run \x1B[36mexe-os update\x1B[0m to upgrade.
32214
32430
  `
32215
32431
  );
32216
32432
  }
@@ -32265,7 +32481,7 @@ async function runActivate(key) {
32265
32481
  const idTemplate = getIdentityTemplate(identityKey);
32266
32482
  if (idTemplate) {
32267
32483
  const idPath = identityPath2(name);
32268
- const dir = path48.dirname(idPath);
32484
+ const dir = path49.dirname(idPath);
32269
32485
  if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
32270
32486
  fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
32271
32487
  }
@@ -901,7 +901,20 @@ async function runUpdate(cliArgs) {
901
901
  } catch {
902
902
  }
903
903
  }
904
- console.log(" Hooks re-wired, daemon restarted automatically.");
904
+ console.log("\u{1F527} Re-registering MCP, hooks, wrappers, and daemon...");
905
+ try {
906
+ execSync2("exe-os-install --global", {
907
+ stdio: ["pipe", "inherit", "inherit"],
908
+ timeout: 3e5
909
+ });
910
+ } catch (err) {
911
+ console.error("\n\u26A0\uFE0F Package updated, but post-update install failed.");
912
+ console.error(" Run manually: exe-os-install --global");
913
+ if (err instanceof Error && err.message) {
914
+ console.error(` Error: ${err.message.split("\n")[0]}`);
915
+ }
916
+ }
917
+ console.log(" MCP, hooks, and daemon refreshed.");
905
918
  console.log("");
906
919
  console.log(" \x1B[33m\u26A1 Run /mcp in each active Claude Code session to pick up new tools.\x1B[0m");
907
920
  console.log(" \x1B[2m(MCP servers can't hot-reload \u2014 Claude Code needs to reconnect them.)\x1B[0m");
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.51",
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",