nexus-agents 2.71.0 → 2.72.1

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 (125) hide show
  1. package/dist/{adaptive-memory-MKSYEBST.js → adaptive-memory-UPE76IP6.js} +5 -5
  2. package/dist/{chunk-DWLATKBK.js → child-mcp-config-5HRJGLCR.js} +6 -4
  3. package/dist/child-mcp-config-5HRJGLCR.js.map +1 -0
  4. package/dist/{chunk-ZPPX2K57.js → chunk-2KB63QGE.js} +2 -2
  5. package/dist/{chunk-L2LQ3TSV.js → chunk-2MD5MWCK.js} +2 -2
  6. package/dist/{chunk-ANC3HU6F.js → chunk-345KMHWH.js} +6 -6
  7. package/dist/chunk-345KMHWH.js.map +1 -0
  8. package/dist/{chunk-NER7H3RJ.js → chunk-3FIDMWFC.js} +2 -2
  9. package/dist/{chunk-POQQ7A5E.js → chunk-53K3KEKT.js} +51 -707
  10. package/dist/chunk-53K3KEKT.js.map +1 -0
  11. package/dist/chunk-5MHIWRKB.js +691 -0
  12. package/dist/chunk-5MHIWRKB.js.map +1 -0
  13. package/dist/{chunk-VGZJIR22.js → chunk-5WQ3SRSE.js} +2 -2
  14. package/dist/{chunk-TOYPY5XA.js → chunk-A35XORXU.js} +73 -10
  15. package/dist/chunk-A35XORXU.js.map +1 -0
  16. package/dist/chunk-BVETPIOQ.js +556 -0
  17. package/dist/chunk-BVETPIOQ.js.map +1 -0
  18. package/dist/{chunk-7LHQBMBM.js → chunk-C3JGKBL2.js} +25 -12
  19. package/dist/{chunk-7LHQBMBM.js.map → chunk-C3JGKBL2.js.map} +1 -1
  20. package/dist/{chunk-OF7CYMMA.js → chunk-DA5UDQYW.js} +2 -2
  21. package/dist/{chunk-XATH462F.js → chunk-ES6GFP35.js} +186 -34
  22. package/dist/chunk-ES6GFP35.js.map +1 -0
  23. package/dist/chunk-GOT7OAL5.js +59 -0
  24. package/dist/chunk-GOT7OAL5.js.map +1 -0
  25. package/dist/{chunk-LJT65EA7.js → chunk-I7ORMAO7.js} +2 -2
  26. package/dist/{chunk-AGVLFRN7.js → chunk-J4VR2WNI.js} +2998 -7188
  27. package/dist/chunk-J4VR2WNI.js.map +1 -0
  28. package/dist/{chunk-LMRKHQG5.js → chunk-L6N2S3UB.js} +2 -2
  29. package/dist/{chunk-7OBFO4GF.js → chunk-O4KUCF5S.js} +125 -40
  30. package/dist/chunk-O4KUCF5S.js.map +1 -0
  31. package/dist/chunk-P5OFZWDW.js +303 -0
  32. package/dist/chunk-P5OFZWDW.js.map +1 -0
  33. package/dist/{chunk-MJHOSM5U.js → chunk-QECRZ3YA.js} +2 -2
  34. package/dist/{chunk-WYSHXPKK.js → chunk-QL4HCYRD.js} +4 -44
  35. package/dist/chunk-QL4HCYRD.js.map +1 -0
  36. package/dist/{chunk-E66KFRSJ.js → chunk-TF3GROMO.js} +2 -2
  37. package/dist/{chunk-U3HZQTUF.js → chunk-TQFRPFMG.js} +2 -2
  38. package/dist/{chunk-KJCSRP34.js → chunk-V7ATY4BG.js} +3 -3
  39. package/dist/{chunk-32RIOULO.js → chunk-VPC3YNFR.js} +2 -2
  40. package/dist/{chunk-3BKVYSY6.js → chunk-VTVKC4FS.js} +4 -4
  41. package/dist/{chunk-U6BK5DQU.js → chunk-YOREAPF6.js} +315 -31
  42. package/dist/chunk-YOREAPF6.js.map +1 -0
  43. package/dist/cli-circuit-breaker-GFF2RLBZ.js +14 -0
  44. package/dist/cli.d.ts +3 -1
  45. package/dist/cli.js +1038 -1581
  46. package/dist/cli.js.map +1 -1
  47. package/dist/{composite-router-AYVJPIOS.js → composite-router-33F3F74I.js} +4 -4
  48. package/dist/{consensus-vote-EXWACBMR.js → consensus-vote-5V4KVHBE.js} +12 -11
  49. package/dist/doctor-deep-AHDTNURD.js +13 -0
  50. package/dist/expert-bridge-DMDHHDEU.js +11 -0
  51. package/dist/factory-FVD7PZ6S.js +15 -0
  52. package/dist/{factory-KMBWFIX2.js → factory-VQS3HJ7V.js} +6 -6
  53. package/dist/index.d.ts +997 -3517
  54. package/dist/index.js +74 -807
  55. package/dist/index.js.map +1 -1
  56. package/dist/init-opencode-EIOIPVWL.js +158 -0
  57. package/dist/init-opencode-EIOIPVWL.js.map +1 -0
  58. package/dist/issue-triage-HJUJWGAD.js +16 -0
  59. package/dist/{learning-persistence-FILWP3IR.js → learning-persistence-N6ILD2HX.js} +3 -3
  60. package/dist/{mobimem-77W5ED4Z.js → mobimem-BOJFXQ7B.js} +4 -4
  61. package/dist/{nexus-data-dir-M6DYKIHJ.js → nexus-data-dir-77UO7N6J.js} +2 -2
  62. package/dist/{registry-command-BBLIXULQ.js → registry-command-NCWUJKAF.js} +4 -4
  63. package/dist/{repo-security-plan-7SNM7JQN.js → repo-security-plan-3J45VAD6.js} +5 -5
  64. package/dist/research-helpers-synthesize-UGQHZZJN.js +12 -0
  65. package/dist/{routing-memory-DCIZEEVC.js → routing-memory-NO7QEH7T.js} +4 -4
  66. package/dist/{session-memory-5TSAASQW.js → session-memory-DOXLEWEU.js} +5 -5
  67. package/dist/{setup-command-5VGIQETA.js → setup-command-BWUFMZ7U.js} +10 -10
  68. package/dist/setup-config-E3JZYSLR.js +11 -0
  69. package/dist/{setup-custom-api-IQX3GD2D.js → setup-custom-api-DHJ5DRH2.js} +6 -6
  70. package/dist/{weather-report-NETGWTJX.js → weather-report-FNN4OX3N.js} +4 -4
  71. package/package.json +1 -1
  72. package/dist/chunk-7OBFO4GF.js.map +0 -1
  73. package/dist/chunk-AGVLFRN7.js.map +0 -1
  74. package/dist/chunk-ANC3HU6F.js.map +0 -1
  75. package/dist/chunk-DWLATKBK.js.map +0 -1
  76. package/dist/chunk-FDNWRZNJ.js +0 -22
  77. package/dist/chunk-FDNWRZNJ.js.map +0 -1
  78. package/dist/chunk-POQQ7A5E.js.map +0 -1
  79. package/dist/chunk-TOYPY5XA.js.map +0 -1
  80. package/dist/chunk-U6BK5DQU.js.map +0 -1
  81. package/dist/chunk-WYSHXPKK.js.map +0 -1
  82. package/dist/chunk-XATH462F.js.map +0 -1
  83. package/dist/cli-circuit-breaker-2CJ6NV52.js +0 -14
  84. package/dist/doctor-deep-BJFDBGPO.js +0 -13
  85. package/dist/expert-bridge-75WNNWI4.js +0 -11
  86. package/dist/factory-H5BYL4V5.js +0 -15
  87. package/dist/issue-triage-4SEP4WID.js +0 -16
  88. package/dist/mcp-config-OCWIXE2Y.js +0 -13
  89. package/dist/research-helpers-synthesize-7CI2FJE5.js +0 -12
  90. package/dist/setup-config-EA5RDIO2.js +0 -11
  91. package/dist/weather-report-NETGWTJX.js.map +0 -1
  92. /package/dist/{adaptive-memory-MKSYEBST.js.map → adaptive-memory-UPE76IP6.js.map} +0 -0
  93. /package/dist/{chunk-ZPPX2K57.js.map → chunk-2KB63QGE.js.map} +0 -0
  94. /package/dist/{chunk-L2LQ3TSV.js.map → chunk-2MD5MWCK.js.map} +0 -0
  95. /package/dist/{chunk-NER7H3RJ.js.map → chunk-3FIDMWFC.js.map} +0 -0
  96. /package/dist/{chunk-VGZJIR22.js.map → chunk-5WQ3SRSE.js.map} +0 -0
  97. /package/dist/{chunk-OF7CYMMA.js.map → chunk-DA5UDQYW.js.map} +0 -0
  98. /package/dist/{chunk-LJT65EA7.js.map → chunk-I7ORMAO7.js.map} +0 -0
  99. /package/dist/{chunk-LMRKHQG5.js.map → chunk-L6N2S3UB.js.map} +0 -0
  100. /package/dist/{chunk-MJHOSM5U.js.map → chunk-QECRZ3YA.js.map} +0 -0
  101. /package/dist/{chunk-E66KFRSJ.js.map → chunk-TF3GROMO.js.map} +0 -0
  102. /package/dist/{chunk-U3HZQTUF.js.map → chunk-TQFRPFMG.js.map} +0 -0
  103. /package/dist/{chunk-KJCSRP34.js.map → chunk-V7ATY4BG.js.map} +0 -0
  104. /package/dist/{chunk-32RIOULO.js.map → chunk-VPC3YNFR.js.map} +0 -0
  105. /package/dist/{chunk-3BKVYSY6.js.map → chunk-VTVKC4FS.js.map} +0 -0
  106. /package/dist/{cli-circuit-breaker-2CJ6NV52.js.map → cli-circuit-breaker-GFF2RLBZ.js.map} +0 -0
  107. /package/dist/{composite-router-AYVJPIOS.js.map → composite-router-33F3F74I.js.map} +0 -0
  108. /package/dist/{consensus-vote-EXWACBMR.js.map → consensus-vote-5V4KVHBE.js.map} +0 -0
  109. /package/dist/{doctor-deep-BJFDBGPO.js.map → doctor-deep-AHDTNURD.js.map} +0 -0
  110. /package/dist/{expert-bridge-75WNNWI4.js.map → expert-bridge-DMDHHDEU.js.map} +0 -0
  111. /package/dist/{factory-H5BYL4V5.js.map → factory-FVD7PZ6S.js.map} +0 -0
  112. /package/dist/{factory-KMBWFIX2.js.map → factory-VQS3HJ7V.js.map} +0 -0
  113. /package/dist/{issue-triage-4SEP4WID.js.map → issue-triage-HJUJWGAD.js.map} +0 -0
  114. /package/dist/{learning-persistence-FILWP3IR.js.map → learning-persistence-N6ILD2HX.js.map} +0 -0
  115. /package/dist/{mcp-config-OCWIXE2Y.js.map → mobimem-BOJFXQ7B.js.map} +0 -0
  116. /package/dist/{mobimem-77W5ED4Z.js.map → nexus-data-dir-77UO7N6J.js.map} +0 -0
  117. /package/dist/{registry-command-BBLIXULQ.js.map → registry-command-NCWUJKAF.js.map} +0 -0
  118. /package/dist/{nexus-data-dir-M6DYKIHJ.js.map → repo-security-plan-3J45VAD6.js.map} +0 -0
  119. /package/dist/{repo-security-plan-7SNM7JQN.js.map → research-helpers-synthesize-UGQHZZJN.js.map} +0 -0
  120. /package/dist/{research-helpers-synthesize-7CI2FJE5.js.map → routing-memory-NO7QEH7T.js.map} +0 -0
  121. /package/dist/{routing-memory-DCIZEEVC.js.map → session-memory-DOXLEWEU.js.map} +0 -0
  122. /package/dist/{session-memory-5TSAASQW.js.map → setup-command-BWUFMZ7U.js.map} +0 -0
  123. /package/dist/{setup-command-5VGIQETA.js.map → setup-config-E3JZYSLR.js.map} +0 -0
  124. /package/dist/{setup-custom-api-IQX3GD2D.js.map → setup-custom-api-DHJ5DRH2.js.map} +0 -0
  125. /package/dist/{setup-config-EA5RDIO2.js.map → weather-report-FNN4OX3N.js.map} +0 -0
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  DEFAULTS
3
- } from "./chunk-L2LQ3TSV.js";
3
+ } from "./chunk-2MD5MWCK.js";
4
4
  import {
5
5
  resolveInsideRoot
6
6
  } from "./chunk-NUBSJGQZ.js";
7
7
  import {
8
8
  createAllAdapters
9
- } from "./chunk-XATH462F.js";
9
+ } from "./chunk-ES6GFP35.js";
10
10
  import {
11
11
  capitalize
12
12
  } from "./chunk-633WH2ML.js";
13
13
  import {
14
+ CLI_SUBPROCESS_TIMEOUTS,
14
15
  CliNameSchema,
15
16
  DEFAULT_CAPABILITIES,
16
17
  DEFAULT_COMPOSITE_CONFIG,
@@ -26,19 +27,20 @@ import {
26
27
  ok,
27
28
  symbols,
28
29
  writeLine
29
- } from "./chunk-7OBFO4GF.js";
30
+ } from "./chunk-O4KUCF5S.js";
30
31
  import {
31
32
  getLearningDir,
32
33
  getOutcomesFile,
33
34
  getRulesFile,
34
35
  isPersistenceEnabled
35
- } from "./chunk-LJT65EA7.js";
36
+ } from "./chunk-I7ORMAO7.js";
36
37
  import {
38
+ detectSandbox,
37
39
  getNexusDataDir
38
- } from "./chunk-FDNWRZNJ.js";
40
+ } from "./chunk-GOT7OAL5.js";
39
41
 
40
42
  // src/version.ts
41
- var VERSION = true ? "2.71.0" : "dev";
43
+ var VERSION = true ? "2.72.1" : "dev";
42
44
 
43
45
  // src/config/schemas-core.ts
44
46
  import { z } from "zod";
@@ -1295,12 +1297,18 @@ function getConfig(options) {
1295
1297
  }
1296
1298
 
1297
1299
  // src/cli/setup-data-dir.ts
1298
- import { mkdirSync, existsSync as existsSync3 } from "fs";
1299
- import { join as join3 } from "path";
1300
+ import { mkdirSync, existsSync as existsSync4 } from "fs";
1301
+ import { join as join4 } from "path";
1300
1302
 
1301
1303
  // src/cli/doctor.ts
1302
- import { existsSync as existsSync2, readFileSync as readFileSync2, accessSync, constants as fsConstants } from "fs";
1303
- import { join as join2 } from "path";
1304
+ import {
1305
+ existsSync as existsSync3,
1306
+ readFileSync as readFileSync3,
1307
+ readdirSync,
1308
+ accessSync,
1309
+ constants as fsConstants
1310
+ } from "fs";
1311
+ import { join as join3 } from "path";
1304
1312
 
1305
1313
  // src/mcp/server.ts
1306
1314
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -1600,7 +1608,12 @@ function printRegistryAdvisory(advisory) {
1600
1608
  writeLine(
1601
1609
  `${colors.yellow}${symbols.warn}${colors.reset} Model registry is ${ageText} \u2014 may have stale model data`
1602
1610
  );
1603
- writeLine(` ${colors.dim}Run: npx tsx scripts/probe-models.ts${colors.reset}`);
1611
+ writeLine(
1612
+ ` ${colors.dim}Update with 'npm update -g nexus-agents' to pick up the latest registry,${colors.reset}`
1613
+ );
1614
+ writeLine(
1615
+ ` ${colors.dim}or run 'nexus-agents registry refresh' to probe currently-installed models.${colors.reset}`
1616
+ );
1604
1617
  } else {
1605
1618
  writeLine(`${formatStatus(true)} Model registry: ${ageText}`);
1606
1619
  }
@@ -1670,6 +1683,42 @@ function printDataDirectory(check) {
1670
1683
  writeLine(` ${colors.dim}Run: nexus-agents setup${colors.reset}`);
1671
1684
  }
1672
1685
  }
1686
+ function printSandboxHeader(check) {
1687
+ if (check.active) {
1688
+ writeLine(`${formatStatus(true)} Sandbox flavor: ${check.flavor ?? "(unknown)"}`);
1689
+ writeLine(` ${colors.dim}NEXUS_SANDBOX_ROOT: ${check.root ?? "(unset)"}${colors.reset}`);
1690
+ return;
1691
+ }
1692
+ writeLine(
1693
+ `${formatStatus(false, true)} Container detected (${check.heuristicMatch ?? "unknown"}) but ${colors.yellow}NEXUS_SANDBOX is unset${colors.reset}`
1694
+ );
1695
+ writeLine(
1696
+ ` ${colors.dim}Set NEXUS_SANDBOX=<flavor> in the image to opt into sandbox-aware behaviour.${colors.reset}`
1697
+ );
1698
+ }
1699
+ function printSandboxWarnings(check) {
1700
+ if (check.mismatch && check.active) {
1701
+ writeLine(
1702
+ ` ${colors.yellow}Mismatch: NEXUS_SANDBOX is set but no container heuristic matched (heuristic=${String(check.heuristicMatch)}).${colors.reset}`
1703
+ );
1704
+ }
1705
+ if (check.dataDirInsideRepo) {
1706
+ writeLine(
1707
+ ` ${colors.yellow}NEXUS_DATA_DIR resolves inside a single repo subfolder of NEXUS_SANDBOX_ROOT \u2014 state will be lost when switching repos. Set NEXUS_DATA_DIR at the multi-repo root.${colors.reset}`
1708
+ );
1709
+ }
1710
+ }
1711
+ function printSandbox(check) {
1712
+ const heuristicSaysContainer = check.heuristicMatch === "docker" || check.heuristicMatch === "podman";
1713
+ if (!check.active && !heuristicSaysContainer) {
1714
+ return;
1715
+ }
1716
+ writeLine(`${colors.cyan}Checking sandbox awareness...${colors.reset}`);
1717
+ writeLine("");
1718
+ printSandboxHeader(check);
1719
+ printSandboxWarnings(check);
1720
+ writeLine("");
1721
+ }
1673
1722
  function printDoctorSummary(result) {
1674
1723
  const unhealthyCount = result.clis.filter((c) => !c.installed || !c.authenticated).length;
1675
1724
  const nodeIssue = result.nodeVersion.supported ? 0 : 1;
@@ -1720,9 +1769,197 @@ function printDoctorResults(result) {
1720
1769
  printSqliteCheck(result.sqliteCheck);
1721
1770
  printDataDirectory(result.dataDirectory);
1722
1771
  writeLine("");
1772
+ printSandbox(result.sandbox);
1723
1773
  printDoctorSummary(result);
1724
1774
  }
1725
1775
 
1776
+ // src/cli/cli-auth-probe.ts
1777
+ import { execFile } from "child_process";
1778
+ import { promisify } from "util";
1779
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1780
+ import { homedir } from "os";
1781
+ import { join as join2 } from "path";
1782
+ var execFileAsync = promisify(execFile);
1783
+ var HOME = homedir();
1784
+ function claudeNeedsLogin(reason) {
1785
+ return {
1786
+ cli: "claude",
1787
+ state: "needs-login",
1788
+ reason,
1789
+ fixCommand: "claude /login",
1790
+ envFallback: "ANTHROPIC_API_KEY",
1791
+ fixUrl: "https://console.anthropic.com/account/keys"
1792
+ };
1793
+ }
1794
+ function probeClaude() {
1795
+ if (process.env["ANTHROPIC_API_KEY"] !== void 0 && process.env["ANTHROPIC_API_KEY"] !== "") {
1796
+ return { cli: "claude", state: "authenticated", via: "env-var" };
1797
+ }
1798
+ const credPath = join2(HOME, ".claude", ".credentials.json");
1799
+ if (!existsSync2(credPath)) {
1800
+ return claudeNeedsLogin(
1801
+ "No credentials at ~/.claude/.credentials.json and ANTHROPIC_API_KEY is not set"
1802
+ );
1803
+ }
1804
+ try {
1805
+ const parsed = JSON.parse(readFileSync2(credPath, "utf-8"));
1806
+ if (!isClaudeCredsShape(parsed)) {
1807
+ return claudeNeedsLogin(
1808
+ "Credentials file present but not in expected shape (missing claudeAiOauth.accessToken)"
1809
+ );
1810
+ }
1811
+ const expiresAt = parsed.claudeAiOauth.expiresAt;
1812
+ if (typeof expiresAt === "number" && expiresAt < Date.now()) {
1813
+ return claudeNeedsLogin(`OAuth token expired ${new Date(expiresAt).toISOString()}`);
1814
+ }
1815
+ return {
1816
+ cli: "claude",
1817
+ state: "authenticated",
1818
+ via: "cli-credentials",
1819
+ ...typeof expiresAt === "number" ? { meta: { expiresAt } } : {}
1820
+ };
1821
+ } catch (e) {
1822
+ return {
1823
+ cli: "claude",
1824
+ state: "error",
1825
+ reason: `Failed to read claude credentials: ${e instanceof Error ? e.message : String(e)}`
1826
+ };
1827
+ }
1828
+ }
1829
+ function codexNeedsLogin(reason) {
1830
+ return {
1831
+ cli: "codex",
1832
+ state: "needs-login",
1833
+ reason,
1834
+ fixCommand: "codex login",
1835
+ envFallback: "OPENAI_API_KEY",
1836
+ fixUrl: "https://platform.openai.com/api-keys"
1837
+ };
1838
+ }
1839
+ function classifyCodexStdout(stdout) {
1840
+ if (/not logged/i.test(stdout) || /no.*token/i.test(stdout)) {
1841
+ return codexNeedsLogin(stdout.trim().split("\n")[0] ?? "Not logged in");
1842
+ }
1843
+ return { cli: "codex", state: "authenticated", via: "cli-credentials" };
1844
+ }
1845
+ async function probeCodex() {
1846
+ if (process.env["OPENAI_API_KEY"] !== void 0 && process.env["OPENAI_API_KEY"] !== "") {
1847
+ return { cli: "codex", state: "authenticated", via: "env-var" };
1848
+ }
1849
+ try {
1850
+ const { stdout } = await execFileAsync("codex", ["login", "status"], {
1851
+ timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs
1852
+ });
1853
+ return classifyCodexStdout(stdout);
1854
+ } catch (e) {
1855
+ const msg = e instanceof Error ? e.message : String(e);
1856
+ if (/ENOENT|not found/i.test(msg)) {
1857
+ return { cli: "codex", state: "not-installed", reason: "codex binary not on PATH" };
1858
+ }
1859
+ return codexNeedsLogin("Not logged in (codex login status returned non-zero)");
1860
+ }
1861
+ }
1862
+ function geminiNeedsLogin(reason) {
1863
+ return {
1864
+ cli: "gemini",
1865
+ state: "needs-login",
1866
+ reason,
1867
+ fixCommand: "gemini",
1868
+ envFallback: "GOOGLE_AI_API_KEY",
1869
+ fixUrl: "https://aistudio.google.com/apikey"
1870
+ };
1871
+ }
1872
+ function classifyGeminiCreds(parsed) {
1873
+ if (!isGeminiCredsShape(parsed)) {
1874
+ return geminiNeedsLogin("OAuth credentials file present but not in expected shape");
1875
+ }
1876
+ if (typeof parsed.expiry_date === "number" && parsed.expiry_date < Date.now()) {
1877
+ return geminiNeedsLogin(
1878
+ `OAuth access token expired ${new Date(parsed.expiry_date).toISOString()} (refresh may still work)`
1879
+ );
1880
+ }
1881
+ return {
1882
+ cli: "gemini",
1883
+ state: "authenticated",
1884
+ via: "cli-credentials",
1885
+ ...typeof parsed.expiry_date === "number" ? { meta: { expiresAt: parsed.expiry_date } } : {}
1886
+ };
1887
+ }
1888
+ function probeGemini() {
1889
+ const env = process.env["GOOGLE_AI_API_KEY"] ?? process.env["GEMINI_API_KEY"];
1890
+ if (env !== void 0 && env !== "") {
1891
+ return { cli: "gemini", state: "authenticated", via: "env-var" };
1892
+ }
1893
+ const credPath = join2(HOME, ".gemini", "oauth_creds.json");
1894
+ if (!existsSync2(credPath)) {
1895
+ return geminiNeedsLogin(
1896
+ "No OAuth credentials at ~/.gemini/oauth_creds.json and GOOGLE_AI_API_KEY/GEMINI_API_KEY are not set"
1897
+ );
1898
+ }
1899
+ try {
1900
+ return classifyGeminiCreds(JSON.parse(readFileSync2(credPath, "utf-8")));
1901
+ } catch (e) {
1902
+ return {
1903
+ cli: "gemini",
1904
+ state: "error",
1905
+ reason: `Failed to read gemini credentials: ${e instanceof Error ? e.message : String(e)}`
1906
+ };
1907
+ }
1908
+ }
1909
+ async function probeOpencode() {
1910
+ try {
1911
+ const { stdout } = await execFileAsync("opencode", ["auth", "list"], {
1912
+ timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs
1913
+ });
1914
+ if (/0 credentials/i.test(stdout)) {
1915
+ return {
1916
+ cli: "opencode",
1917
+ state: "needs-login",
1918
+ reason: "No providers configured in opencode",
1919
+ fixCommand: "opencode auth login",
1920
+ fixUrl: "https://opencode.ai/docs/config"
1921
+ };
1922
+ }
1923
+ return { cli: "opencode", state: "authenticated", via: "cli-credentials" };
1924
+ } catch (e) {
1925
+ const msg = e instanceof Error ? e.message : String(e);
1926
+ if (/ENOENT|not found/i.test(msg)) {
1927
+ return { cli: "opencode", state: "not-installed", reason: "opencode binary not on PATH" };
1928
+ }
1929
+ return {
1930
+ cli: "opencode",
1931
+ state: "error",
1932
+ reason: msg.split("\n")[0] ?? "opencode auth list failed"
1933
+ };
1934
+ }
1935
+ }
1936
+ function isClaudeCredsShape(v) {
1937
+ if (typeof v !== "object" || v === null) return false;
1938
+ const oauth = v.claudeAiOauth;
1939
+ if (typeof oauth !== "object" || oauth === null) return false;
1940
+ return typeof oauth.accessToken === "string";
1941
+ }
1942
+ function isGeminiCredsShape(v) {
1943
+ if (typeof v !== "object" || v === null) return false;
1944
+ return typeof v.access_token === "string";
1945
+ }
1946
+ async function probeCli(cli) {
1947
+ switch (cli) {
1948
+ case "claude":
1949
+ return Promise.resolve(probeClaude());
1950
+ case "codex":
1951
+ return probeCodex();
1952
+ case "gemini":
1953
+ return Promise.resolve(probeGemini());
1954
+ case "opencode":
1955
+ return probeOpencode();
1956
+ }
1957
+ }
1958
+ async function probeAllClis() {
1959
+ const clis = ["claude", "gemini", "codex", "opencode"];
1960
+ return Promise.all(clis.map((c) => probeCli(c)));
1961
+ }
1962
+
1726
1963
  // src/cli/doctor.ts
1727
1964
  var REQUIRED_NODE_MAJOR2 = 22;
1728
1965
  var API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_AI_API_KEY"];
@@ -1783,8 +2020,9 @@ function detectAuthMethod(name) {
1783
2020
  };
1784
2021
  return authMethods[name];
1785
2022
  }
1786
- function createHealthyResult(name, health, capacity) {
1787
- const authenticated = health.healthy;
2023
+ function createHealthyResult(name, health, authProbe, capacity) {
2024
+ const versionOk = health.healthy;
2025
+ const authenticated = versionOk && authProbe.state === "authenticated";
1788
2026
  const result = {
1789
2027
  name,
1790
2028
  installed: true,
@@ -1797,6 +2035,13 @@ function createHealthyResult(name, health, capacity) {
1797
2035
  if (health.message !== void 0 && health.message !== "") {
1798
2036
  return { ...result, error: health.message };
1799
2037
  }
2038
+ if (!authenticated && authProbe.state === "needs-login") {
2039
+ return {
2040
+ ...result,
2041
+ error: authProbe.reason,
2042
+ fix: authProbe.fixCommand
2043
+ };
2044
+ }
1800
2045
  if (!authenticated) {
1801
2046
  return { ...result, fix: getFixCommand(name, "auth") };
1802
2047
  }
@@ -1812,14 +2057,14 @@ async function checkCli(name) {
1812
2057
  return createNotFoundResult(name, "Adapter not available");
1813
2058
  }
1814
2059
  try {
1815
- const health = await adapter.healthCheck();
2060
+ const [health, authProbe] = await Promise.all([adapter.healthCheck(), probeCli(name)]);
1816
2061
  let capacity;
1817
2062
  try {
1818
2063
  capacity = await adapter.getCapacity();
1819
2064
  } catch (capErr) {
1820
2065
  void capErr;
1821
2066
  }
1822
- return createHealthyResult(name, health, capacity);
2067
+ return createHealthyResult(name, health, authProbe, capacity);
1823
2068
  } catch (error) {
1824
2069
  const message = getErrorMessage(error);
1825
2070
  const isNotFound = message.includes("ENOENT") || message.includes("not found");
@@ -1843,7 +2088,7 @@ function checkApiKeys() {
1843
2088
  }
1844
2089
  function checkConfigFile() {
1845
2090
  for (const configPath of CONFIG_FILE_PATHS) {
1846
- if (existsSync2(configPath)) {
2091
+ if (existsSync3(configPath)) {
1847
2092
  return { found: true, path: configPath };
1848
2093
  }
1849
2094
  }
@@ -1865,27 +2110,45 @@ function buildRegistryAdvisory(cliResults) {
1865
2110
  const reason = available ? `${cliName} CLI is installed` : `${cliName} CLI is not installed`;
1866
2111
  return { modelId: m.id, displayName: m.displayName, cliName, available, reason };
1867
2112
  });
1868
- const STALE_THRESHOLD_DAYS = 30;
2113
+ const STALE_THRESHOLD_DAYS = 90;
1869
2114
  const updatedAt = new Date(DEFAULT_MODEL_CAPABILITIES.updatedAt);
1870
2115
  const nowMs = getTimeProvider().now();
1871
2116
  const ageDays = Math.floor((nowMs - updatedAt.getTime()) / (1e3 * 60 * 60 * 24));
2117
+ const isFreshInstall = !hasPriorUsage();
2118
+ const exceedsThreshold = ageDays > STALE_THRESHOLD_DAYS;
1872
2119
  return {
1873
2120
  totalModels: models.length,
1874
2121
  availableModels: models.filter((m) => m.available).length,
1875
2122
  unavailableModels: models.filter((m) => !m.available).length,
1876
2123
  models,
1877
2124
  registryAgeDays: ageDays,
1878
- registryStale: ageDays > STALE_THRESHOLD_DAYS
2125
+ registryStale: exceedsThreshold && !isFreshInstall
1879
2126
  };
1880
2127
  }
2128
+ function hasPriorUsage() {
2129
+ try {
2130
+ const root = getNexusDataDir();
2131
+ if (!existsSync3(root)) return false;
2132
+ for (const sub of ["audit", "learning", "sessions", "voting"]) {
2133
+ const p = `${root}/${sub}`;
2134
+ try {
2135
+ if (existsSync3(p) && readdirSync(p).length > 0) return true;
2136
+ } catch {
2137
+ }
2138
+ }
2139
+ return false;
2140
+ } catch {
2141
+ return false;
2142
+ }
2143
+ }
1881
2144
  function countJsonlLines(filePath) {
1882
- if (!existsSync2(filePath)) return 0;
1883
- return readFileSync2(filePath, "utf-8").split("\n").filter((l) => l.trim().length > 0).length;
2145
+ if (!existsSync3(filePath)) return 0;
2146
+ return readFileSync3(filePath, "utf-8").split("\n").filter((l) => l.trim().length > 0).length;
1884
2147
  }
1885
2148
  function readRulesMetadata(filePath) {
1886
- if (!existsSync2(filePath)) return { count: 0, savedAt: null };
2149
+ if (!existsSync3(filePath)) return { count: 0, savedAt: null };
1887
2150
  try {
1888
- const raw = JSON.parse(readFileSync2(filePath, "utf-8"));
2151
+ const raw = JSON.parse(readFileSync3(filePath, "utf-8"));
1889
2152
  const rules = raw["rules"];
1890
2153
  const saved = raw["savedAt"];
1891
2154
  return {
@@ -1897,7 +2160,7 @@ function readRulesMetadata(filePath) {
1897
2160
  }
1898
2161
  }
1899
2162
  function checkDirAccess(dir) {
1900
- const exists = existsSync2(dir);
2163
+ const exists = existsSync3(dir);
1901
2164
  if (!exists) return { exists: false, writable: false };
1902
2165
  try {
1903
2166
  accessSync(dir, fsConstants.W_OK);
@@ -1957,10 +2220,10 @@ async function checkSqlite() {
1957
2220
  }
1958
2221
  function checkDataDirectory() {
1959
2222
  const rootPath = getNexusDataDir();
1960
- const rootExists = existsSync2(rootPath);
2223
+ const rootExists = existsSync3(rootPath);
1961
2224
  const subdirectories = DATA_SUBDIRECTORIES.map((name) => {
1962
- const fullPath = join2(rootPath, name);
1963
- const exists = existsSync2(fullPath);
2225
+ const fullPath = join3(rootPath, name);
2226
+ const exists = existsSync3(fullPath);
1964
2227
  return { name, path: fullPath, exists, writable: exists && isWritable(fullPath) };
1965
2228
  });
1966
2229
  return { rootExists, rootPath, subdirectories };
@@ -1973,6 +2236,24 @@ function isWritable(dirPath) {
1973
2236
  return false;
1974
2237
  }
1975
2238
  }
2239
+ function checkSandbox() {
2240
+ const info = detectSandbox();
2241
+ const dataDir = getNexusDataDir();
2242
+ const root = info.root;
2243
+ const dataDirInsideRepo = info.active && root !== void 0 && dataDir.startsWith(`${root.replace(/\/$/, "")}/`) && // path segments BETWEEN root and `.nexus-agents/` indicate the data dir
2244
+ // lives in a subfolder rather than at the multi-repo root.
2245
+ dataDir.replace(`${root.replace(/\/$/, "")}/`, "").split("/").length > 1;
2246
+ const heuristicSaysContainer = info.heuristicMatch === "docker" || info.heuristicMatch === "podman";
2247
+ const mismatch = info.active && info.heuristicMatch === "unknown" || !info.active && heuristicSaysContainer;
2248
+ return {
2249
+ active: info.active,
2250
+ flavor: info.flavor,
2251
+ root: info.root,
2252
+ heuristicMatch: info.heuristicMatch,
2253
+ mismatch,
2254
+ dataDirInsideRepo
2255
+ };
2256
+ }
1976
2257
  async function runDoctor() {
1977
2258
  const clis = await Promise.all([
1978
2259
  checkCli("claude"),
@@ -1990,6 +2271,7 @@ async function runDoctor() {
1990
2271
  const learningPersistence = checkLearningPersistence();
1991
2272
  const sqliteCheck = await checkSqlite();
1992
2273
  const dataDirectory = checkDataDirectory();
2274
+ const sandbox = checkSandbox();
1993
2275
  const hasAuthMethod = apiKeys.some((k) => k.configured) || clis.some((c) => c.installed && c.authenticated);
1994
2276
  const allHealthy = nodeVersion.supported && hasAuthMethod && mcpServerReady && clis.every((c) => c.installed && c.authenticated && c.versionStatus !== "unsupported");
1995
2277
  return {
@@ -2003,6 +2285,7 @@ async function runDoctor() {
2003
2285
  learningPersistence,
2004
2286
  sqliteCheck,
2005
2287
  dataDirectory,
2288
+ sandbox,
2006
2289
  allHealthy,
2007
2290
  timestamp: new Date(getTimeProvider().now())
2008
2291
  };
@@ -2024,7 +2307,7 @@ async function runDoctorFix(result) {
2024
2307
  writeLine2("\u2500".repeat(40));
2025
2308
  let fixCount = 0;
2026
2309
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
2027
- const { runSetup } = await import("./setup-command-5VGIQETA.js");
2310
+ const { runSetup } = await import("./setup-command-BWUFMZ7U.js");
2028
2311
  const setupResult = runSetup({
2029
2312
  skipMcp: true,
2030
2313
  skipRules: true,
@@ -2038,7 +2321,7 @@ async function runDoctorFix(result) {
2038
2321
  }
2039
2322
  }
2040
2323
  if (!result.configFile.found) {
2041
- const { runConfigInitSync } = await import("./setup-config-EA5RDIO2.js");
2324
+ const { runConfigInitSync } = await import("./setup-config-E3JZYSLR.js");
2042
2325
  const configResult = runConfigInitSync(process.cwd(), false, false);
2043
2326
  if (configResult.success && configResult.created) {
2044
2327
  writeLine2(`\u2713 Generated config: ${configResult.path}`);
@@ -2071,7 +2354,7 @@ function initDataDirectories(dryRun = false) {
2071
2354
  ensureDir(NEXUS_DATA_DIR, dryRun, created, alreadyExisted);
2072
2355
  for (const subdir of DATA_SUBDIRECTORIES) {
2073
2356
  const mode = RESTRICTED_DIRS.has(subdir) ? 448 : void 0;
2074
- ensureDir(join3(NEXUS_DATA_DIR, subdir), dryRun, created, alreadyExisted, mode);
2357
+ ensureDir(join4(NEXUS_DATA_DIR, subdir), dryRun, created, alreadyExisted, mode);
2075
2358
  }
2076
2359
  return { success: true, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: null };
2077
2360
  } catch (error) {
@@ -2080,7 +2363,7 @@ function initDataDirectories(dryRun = false) {
2080
2363
  }
2081
2364
  }
2082
2365
  function ensureDir(dirPath, dryRun, created, alreadyExisted, mode) {
2083
- if (existsSync3(dirPath)) {
2366
+ if (existsSync4(dirPath)) {
2084
2367
  alreadyExisted.push(dirPath);
2085
2368
  return;
2086
2369
  }
@@ -2122,6 +2405,7 @@ export {
2122
2405
  filterAvailableModels,
2123
2406
  DEFAULT_TASK_TTL_MS,
2124
2407
  clampTaskTtl,
2408
+ probeAllClis,
2125
2409
  DATA_SUBDIRECTORIES,
2126
2410
  checkApiKeys,
2127
2411
  checkSqlite,
@@ -2134,4 +2418,4 @@ export {
2134
2418
  startStdioServer,
2135
2419
  closeServer
2136
2420
  };
2137
- //# sourceMappingURL=chunk-U6BK5DQU.js.map
2421
+ //# sourceMappingURL=chunk-YOREAPF6.js.map