nexus-agents 8.8.0 → 8.8.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 (146) hide show
  1. package/dist/{ast-rule-runner-IESI3WWR.js → ast-rule-runner-WANOR3MX.js} +5 -5
  2. package/dist/{child-mcp-config-WQNW5YFO.js → child-mcp-config-N4HLLCEH.js} +5 -5
  3. package/dist/{chunk-TQT2BIZ7.js → chunk-276GRITW.js} +4 -4
  4. package/dist/{chunk-DEXENQXS.js → chunk-3CLKYFE3.js} +74 -7
  5. package/dist/chunk-3CLKYFE3.js.map +1 -0
  6. package/dist/{chunk-YUMKCIEL.js → chunk-3XQK4MEF.js} +2 -2
  7. package/dist/{chunk-6AGNJBHE.js → chunk-3YKFLM2B.js} +2 -2
  8. package/dist/{chunk-4HZCKQ25.js → chunk-64VMTY3M.js} +2 -2
  9. package/dist/{chunk-DY6DVTX2.js → chunk-6HETGJSB.js} +3 -3
  10. package/dist/{chunk-ODBLHHTP.js → chunk-6URMW3DB.js} +2 -2
  11. package/dist/{chunk-BNPXE7BD.js → chunk-7AU5WLAJ.js} +2 -2
  12. package/dist/{chunk-66GKBIY2.js → chunk-7J7UYJXC.js} +7 -7
  13. package/dist/{chunk-BIHRU7WA.js → chunk-7QQ5F7T5.js} +9 -9
  14. package/dist/{chunk-ITCKXMOH.js → chunk-7WJGV5DN.js} +2 -2
  15. package/dist/{chunk-ZN6BTFLD.js → chunk-AOS7F4QR.js} +2 -2
  16. package/dist/{chunk-4HXPNLZS.js → chunk-DKHBIJO4.js} +2 -2
  17. package/dist/chunk-DROYVWGE.js +7 -0
  18. package/dist/{chunk-37ONEK7R.js → chunk-EALWAQAQ.js} +12 -12
  19. package/dist/chunk-EALWAQAQ.js.map +1 -0
  20. package/dist/{chunk-HEV2C6VS.js → chunk-ERL3XHQO.js} +6 -6
  21. package/dist/{chunk-OLR2RIN2.js → chunk-F2GERNMF.js} +2 -2
  22. package/dist/{chunk-7BNB4YVO.js → chunk-FBWOWMEB.js} +3 -3
  23. package/dist/{chunk-W5VBFX65.js → chunk-FKKVZRSY.js} +2 -2
  24. package/dist/{chunk-HQ6K7OYH.js → chunk-FUMZUCP2.js} +10 -10
  25. package/dist/{chunk-3SO5DMHS.js → chunk-GXLI2E4U.js} +4 -4
  26. package/dist/{chunk-RWZJ2D2N.js → chunk-II5NJRKH.js} +2 -2
  27. package/dist/{chunk-MM223CVS.js → chunk-J7QCZRVK.js} +6 -1
  28. package/dist/{chunk-MM223CVS.js.map → chunk-J7QCZRVK.js.map} +1 -1
  29. package/dist/{chunk-F26X4N73.js → chunk-K2PVLCHR.js} +2 -2
  30. package/dist/{chunk-DZ5V47RU.js → chunk-MXQG3A5W.js} +6 -6
  31. package/dist/{chunk-4AE2QO7Q.js → chunk-N347WXTT.js} +2 -2
  32. package/dist/{chunk-ZQD67JES.js → chunk-NEJF4F6R.js} +4 -4
  33. package/dist/{chunk-CRKF5LUZ.js → chunk-NR62IQDC.js} +6 -6
  34. package/dist/{chunk-36AR3GCF.js → chunk-OCAMHBJ6.js} +10 -10
  35. package/dist/{chunk-REY2B5LO.js → chunk-Q5V3KTUL.js} +2 -2
  36. package/dist/{chunk-NSHGFRGF.js → chunk-QCSXKQTK.js} +2 -2
  37. package/dist/{chunk-SNEUKIBK.js → chunk-SNSPYZQT.js} +5 -5
  38. package/dist/{chunk-GUOMNRXZ.js → chunk-TZWJ7QLW.js} +2 -2
  39. package/dist/{chunk-PX422NHD.js → chunk-VADACTVB.js} +2 -2
  40. package/dist/{chunk-OPHI2RB6.js → chunk-VBC4JBRH.js} +5 -5
  41. package/dist/{chunk-W37HHXDL.js → chunk-WXU4Y5T3.js} +2 -2
  42. package/dist/{chunk-7BYI5CBK.js → chunk-Z55IEIBU.js} +113 -105
  43. package/dist/{chunk-7BYI5CBK.js.map → chunk-Z55IEIBU.js.map} +1 -1
  44. package/dist/{chunk-BBMGN7FS.js → chunk-ZGHJMFQ6.js} +3 -3
  45. package/dist/{cli-circuit-breaker-YAAGSJ5N.js → cli-circuit-breaker-AAHYLFXZ.js} +5 -5
  46. package/dist/cli.js +47 -47
  47. package/dist/{composite-router-TU6MVJBG.js → composite-router-VAVDLJGT.js} +4 -4
  48. package/dist/{consensus-vote-FJWLHMQL.js → consensus-vote-YYAWXULO.js} +21 -21
  49. package/dist/{context-retriever-A4BZMTWM.js → context-retriever-MYKBBDAN.js} +11 -11
  50. package/dist/doctor-deep-AKOPPCXB.js +13 -0
  51. package/dist/{doctor-live-G3EO5LQD.js → doctor-live-3PVIYQST.js} +12 -12
  52. package/dist/{expert-bridge-IKY5TTAH.js → expert-bridge-G3KN7QWL.js} +6 -6
  53. package/dist/factory-KVMLFEWJ.js +15 -0
  54. package/dist/factory-ROTP63IZ.js +25 -0
  55. package/dist/{improvement-review-JNHEZ2DJ.js → improvement-review-LEITEY7D.js} +8 -8
  56. package/dist/index.js +36 -36
  57. package/dist/{init-opencode-KENOM2HA.js → init-opencode-C63LASGX.js} +8 -8
  58. package/dist/issue-triage-PIE3YVCV.js +17 -0
  59. package/dist/{learning-persistence-7N5SZHNX.js → learning-persistence-JK2FELZL.js} +3 -3
  60. package/dist/{pr-reviewer-helpers-C72VNWXU.js → pr-reviewer-helpers-4VDGMGV5.js} +6 -6
  61. package/dist/{registry-command-53E7LRNR.js → registry-command-IRDP2L2R.js} +4 -4
  62. package/dist/{repo-security-plan-ZSXHLYMU.js → repo-security-plan-NS47HQEF.js} +5 -5
  63. package/dist/research-helpers-synthesize-5NBL637T.js +14 -0
  64. package/dist/{routing-memory-5RYZJE45.js → routing-memory-2IELT5GT.js} +4 -4
  65. package/dist/{session-memory-3GE74TXJ.js → session-memory-CH3CVLIT.js} +5 -5
  66. package/dist/setup-config-MKZZDUJP.js +11 -0
  67. package/dist/{setup-custom-api-5ZZQJ5WX.js → setup-custom-api-2I52S7XU.js} +6 -6
  68. package/dist/setup-data-dir-I6LJVAPB.js +29 -0
  69. package/dist/{tool-memory-ZW2J3PMR.js → tool-memory-STS3DWOK.js} +8 -8
  70. package/dist/{tool-memory-registry-adapters-PCOYMYRF.js → tool-memory-registry-adapters-MZ5CSCYW.js} +3 -3
  71. package/dist/unified-registry-NJS4ZWC3.js +26 -0
  72. package/dist/{weather-report-OQ2UA3DZ.js → weather-report-YG4T4Q23.js} +4 -4
  73. package/package.json +1 -1
  74. package/dist/chunk-37ONEK7R.js.map +0 -1
  75. package/dist/chunk-DEXENQXS.js.map +0 -1
  76. package/dist/chunk-SAKV5TSH.js +0 -7
  77. package/dist/doctor-deep-N4IFVZLP.js +0 -13
  78. package/dist/factory-EJ2ZMEQ7.js +0 -15
  79. package/dist/factory-VGRG7M62.js +0 -25
  80. package/dist/issue-triage-IWMHDNUY.js +0 -17
  81. package/dist/research-helpers-synthesize-X4O3S4SH.js +0 -14
  82. package/dist/setup-config-YGTQJTTC.js +0 -11
  83. package/dist/setup-data-dir-NKG6L7FO.js +0 -29
  84. package/dist/unified-registry-IH3UEZLZ.js +0 -26
  85. /package/dist/{ast-rule-runner-IESI3WWR.js.map → ast-rule-runner-WANOR3MX.js.map} +0 -0
  86. /package/dist/{child-mcp-config-WQNW5YFO.js.map → child-mcp-config-N4HLLCEH.js.map} +0 -0
  87. /package/dist/{chunk-TQT2BIZ7.js.map → chunk-276GRITW.js.map} +0 -0
  88. /package/dist/{chunk-YUMKCIEL.js.map → chunk-3XQK4MEF.js.map} +0 -0
  89. /package/dist/{chunk-6AGNJBHE.js.map → chunk-3YKFLM2B.js.map} +0 -0
  90. /package/dist/{chunk-4HZCKQ25.js.map → chunk-64VMTY3M.js.map} +0 -0
  91. /package/dist/{chunk-DY6DVTX2.js.map → chunk-6HETGJSB.js.map} +0 -0
  92. /package/dist/{chunk-ODBLHHTP.js.map → chunk-6URMW3DB.js.map} +0 -0
  93. /package/dist/{chunk-BNPXE7BD.js.map → chunk-7AU5WLAJ.js.map} +0 -0
  94. /package/dist/{chunk-66GKBIY2.js.map → chunk-7J7UYJXC.js.map} +0 -0
  95. /package/dist/{chunk-BIHRU7WA.js.map → chunk-7QQ5F7T5.js.map} +0 -0
  96. /package/dist/{chunk-ITCKXMOH.js.map → chunk-7WJGV5DN.js.map} +0 -0
  97. /package/dist/{chunk-ZN6BTFLD.js.map → chunk-AOS7F4QR.js.map} +0 -0
  98. /package/dist/{chunk-4HXPNLZS.js.map → chunk-DKHBIJO4.js.map} +0 -0
  99. /package/dist/{chunk-SAKV5TSH.js.map → chunk-DROYVWGE.js.map} +0 -0
  100. /package/dist/{chunk-HEV2C6VS.js.map → chunk-ERL3XHQO.js.map} +0 -0
  101. /package/dist/{chunk-OLR2RIN2.js.map → chunk-F2GERNMF.js.map} +0 -0
  102. /package/dist/{chunk-7BNB4YVO.js.map → chunk-FBWOWMEB.js.map} +0 -0
  103. /package/dist/{chunk-W5VBFX65.js.map → chunk-FKKVZRSY.js.map} +0 -0
  104. /package/dist/{chunk-HQ6K7OYH.js.map → chunk-FUMZUCP2.js.map} +0 -0
  105. /package/dist/{chunk-3SO5DMHS.js.map → chunk-GXLI2E4U.js.map} +0 -0
  106. /package/dist/{chunk-RWZJ2D2N.js.map → chunk-II5NJRKH.js.map} +0 -0
  107. /package/dist/{chunk-F26X4N73.js.map → chunk-K2PVLCHR.js.map} +0 -0
  108. /package/dist/{chunk-DZ5V47RU.js.map → chunk-MXQG3A5W.js.map} +0 -0
  109. /package/dist/{chunk-4AE2QO7Q.js.map → chunk-N347WXTT.js.map} +0 -0
  110. /package/dist/{chunk-ZQD67JES.js.map → chunk-NEJF4F6R.js.map} +0 -0
  111. /package/dist/{chunk-CRKF5LUZ.js.map → chunk-NR62IQDC.js.map} +0 -0
  112. /package/dist/{chunk-36AR3GCF.js.map → chunk-OCAMHBJ6.js.map} +0 -0
  113. /package/dist/{chunk-REY2B5LO.js.map → chunk-Q5V3KTUL.js.map} +0 -0
  114. /package/dist/{chunk-NSHGFRGF.js.map → chunk-QCSXKQTK.js.map} +0 -0
  115. /package/dist/{chunk-SNEUKIBK.js.map → chunk-SNSPYZQT.js.map} +0 -0
  116. /package/dist/{chunk-GUOMNRXZ.js.map → chunk-TZWJ7QLW.js.map} +0 -0
  117. /package/dist/{chunk-PX422NHD.js.map → chunk-VADACTVB.js.map} +0 -0
  118. /package/dist/{chunk-OPHI2RB6.js.map → chunk-VBC4JBRH.js.map} +0 -0
  119. /package/dist/{chunk-W37HHXDL.js.map → chunk-WXU4Y5T3.js.map} +0 -0
  120. /package/dist/{chunk-BBMGN7FS.js.map → chunk-ZGHJMFQ6.js.map} +0 -0
  121. /package/dist/{cli-circuit-breaker-YAAGSJ5N.js.map → cli-circuit-breaker-AAHYLFXZ.js.map} +0 -0
  122. /package/dist/{composite-router-TU6MVJBG.js.map → composite-router-VAVDLJGT.js.map} +0 -0
  123. /package/dist/{consensus-vote-FJWLHMQL.js.map → consensus-vote-YYAWXULO.js.map} +0 -0
  124. /package/dist/{context-retriever-A4BZMTWM.js.map → context-retriever-MYKBBDAN.js.map} +0 -0
  125. /package/dist/{doctor-deep-N4IFVZLP.js.map → doctor-deep-AKOPPCXB.js.map} +0 -0
  126. /package/dist/{doctor-live-G3EO5LQD.js.map → doctor-live-3PVIYQST.js.map} +0 -0
  127. /package/dist/{expert-bridge-IKY5TTAH.js.map → expert-bridge-G3KN7QWL.js.map} +0 -0
  128. /package/dist/{factory-EJ2ZMEQ7.js.map → factory-KVMLFEWJ.js.map} +0 -0
  129. /package/dist/{factory-VGRG7M62.js.map → factory-ROTP63IZ.js.map} +0 -0
  130. /package/dist/{improvement-review-JNHEZ2DJ.js.map → improvement-review-LEITEY7D.js.map} +0 -0
  131. /package/dist/{init-opencode-KENOM2HA.js.map → init-opencode-C63LASGX.js.map} +0 -0
  132. /package/dist/{issue-triage-IWMHDNUY.js.map → issue-triage-PIE3YVCV.js.map} +0 -0
  133. /package/dist/{learning-persistence-7N5SZHNX.js.map → learning-persistence-JK2FELZL.js.map} +0 -0
  134. /package/dist/{pr-reviewer-helpers-C72VNWXU.js.map → pr-reviewer-helpers-4VDGMGV5.js.map} +0 -0
  135. /package/dist/{registry-command-53E7LRNR.js.map → registry-command-IRDP2L2R.js.map} +0 -0
  136. /package/dist/{repo-security-plan-ZSXHLYMU.js.map → repo-security-plan-NS47HQEF.js.map} +0 -0
  137. /package/dist/{research-helpers-synthesize-X4O3S4SH.js.map → research-helpers-synthesize-5NBL637T.js.map} +0 -0
  138. /package/dist/{routing-memory-5RYZJE45.js.map → routing-memory-2IELT5GT.js.map} +0 -0
  139. /package/dist/{session-memory-3GE74TXJ.js.map → session-memory-CH3CVLIT.js.map} +0 -0
  140. /package/dist/{setup-config-YGTQJTTC.js.map → setup-config-MKZZDUJP.js.map} +0 -0
  141. /package/dist/{setup-custom-api-5ZZQJ5WX.js.map → setup-custom-api-2I52S7XU.js.map} +0 -0
  142. /package/dist/{setup-data-dir-NKG6L7FO.js.map → setup-data-dir-I6LJVAPB.js.map} +0 -0
  143. /package/dist/{tool-memory-ZW2J3PMR.js.map → tool-memory-STS3DWOK.js.map} +0 -0
  144. /package/dist/{tool-memory-registry-adapters-PCOYMYRF.js.map → tool-memory-registry-adapters-MZ5CSCYW.js.map} +0 -0
  145. /package/dist/{unified-registry-IH3UEZLZ.js.map → unified-registry-NJS4ZWC3.js.map} +0 -0
  146. /package/dist/{weather-report-OQ2UA3DZ.js.map → weather-report-YG4T4Q23.js.map} +0 -0
@@ -9,10 +9,10 @@ import {
9
9
  getBuiltInAstRulesPath,
10
10
  loadRules,
11
11
  runAstQaRules
12
- } from "./chunk-YUMKCIEL.js";
13
- import "./chunk-HEV2C6VS.js";
14
- import "./chunk-ITCKXMOH.js";
15
- import "./chunk-MM223CVS.js";
12
+ } from "./chunk-3XQK4MEF.js";
13
+ import "./chunk-ERL3XHQO.js";
14
+ import "./chunk-7WJGV5DN.js";
15
+ import "./chunk-J7QCZRVK.js";
16
16
  import "./chunk-ZPWHCABL.js";
17
17
  export {
18
18
  AST_RULE_LANGUAGES,
@@ -26,4 +26,4 @@ export {
26
26
  loadRules,
27
27
  runAstQaRules
28
28
  };
29
- //# sourceMappingURL=ast-rule-runner-IESI3WWR.js.map
29
+ //# sourceMappingURL=ast-rule-runner-WANOR3MX.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  nexusMkdtemp
3
- } from "./chunk-4HXPNLZS.js";
3
+ } from "./chunk-DKHBIJO4.js";
4
4
  import {
5
5
  createLogger
6
- } from "./chunk-HEV2C6VS.js";
7
- import "./chunk-ITCKXMOH.js";
8
- import "./chunk-MM223CVS.js";
6
+ } from "./chunk-ERL3XHQO.js";
7
+ import "./chunk-7WJGV5DN.js";
8
+ import "./chunk-J7QCZRVK.js";
9
9
  import "./chunk-ZPWHCABL.js";
10
10
 
11
11
  // src/cli-adapters/child-mcp-config.ts
@@ -62,4 +62,4 @@ export {
62
62
  generateMcpConfig,
63
63
  getDefaultAllowedTools
64
64
  };
65
- //# sourceMappingURL=child-mcp-config-WQNW5YFO.js.map
65
+ //# sourceMappingURL=child-mcp-config-N4HLLCEH.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createAutoAdapter
3
- } from "./chunk-BIHRU7WA.js";
3
+ } from "./chunk-7QQ5F7T5.js";
4
4
  import {
5
5
  getDefaultCliCircuitBreakerRegistry,
6
6
  mapModelErrorToCategory
7
- } from "./chunk-PX422NHD.js";
7
+ } from "./chunk-VADACTVB.js";
8
8
  import {
9
9
  CLI_NAMES,
10
10
  CircularBuffer,
@@ -24,7 +24,7 @@ import {
24
24
  recordRateLimitEvent,
25
25
  resolveModelIdentitySync,
26
26
  toRateLimitError
27
- } from "./chunk-HEV2C6VS.js";
27
+ } from "./chunk-ERL3XHQO.js";
28
28
 
29
29
  // src/config/model-equivalence.ts
30
30
  function canonicalModelKey(modelId) {
@@ -904,4 +904,4 @@ export {
904
904
  getGlobalRegistry,
905
905
  resetGlobalRegistry
906
906
  };
907
- //# sourceMappingURL=chunk-TQT2BIZ7.js.map
907
+ //# sourceMappingURL=chunk-276GRITW.js.map
@@ -1,15 +1,22 @@
1
1
  import {
2
2
  ParseError,
3
3
  SecurityError,
4
+ createLogger,
4
5
  err,
5
6
  getErrorMessage,
6
7
  ok,
7
8
  parseBoolEnv
8
- } from "./chunk-HEV2C6VS.js";
9
+ } from "./chunk-ERL3XHQO.js";
10
+ import {
11
+ findRepoRoot,
12
+ getActiveWorkspaceRoot,
13
+ isRepoRoot
14
+ } from "./chunk-J7QCZRVK.js";
9
15
 
10
16
  // src/cli/research-helpers-io.ts
11
17
  import * as fs2 from "node:fs/promises";
12
- import { join, resolve as resolve3 } from "node:path";
18
+ import { realpathSync, statSync } from "node:fs";
19
+ import { dirname as dirname2, join, resolve as resolve3 } from "node:path";
13
20
 
14
21
  // src/security/safe-path.ts
15
22
  import { resolve, sep } from "node:path";
@@ -84,8 +91,67 @@ async function ensureRegistryFile(rootDir, filename) {
84
91
  var REGISTRY_PATH = "docs/research/registry";
85
92
  var TECHNIQUES_FILE = "techniques.yaml";
86
93
  var PAPERS_FILE = "papers.yaml";
94
+ var logger = createLogger({ component: "research-registry-root" });
95
+ var registryRootByOrigin = /* @__PURE__ */ new Map();
96
+ var warnedCwdFallback = false;
97
+ function hasRegistryDir(dir) {
98
+ try {
99
+ return statSync(join(dir, REGISTRY_PATH)).isDirectory();
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+ function findRegistryAncestor(start) {
105
+ let current;
106
+ let startDev;
107
+ try {
108
+ current = realpathSync(start);
109
+ startDev = statSync(current).dev;
110
+ } catch {
111
+ return null;
112
+ }
113
+ for (let depth = 0; depth < 64; depth++) {
114
+ if (hasRegistryDir(current)) return current;
115
+ if (isRepoRoot(current)) return null;
116
+ const parent = dirname2(current);
117
+ if (parent === current) return null;
118
+ try {
119
+ if (statSync(parent).dev !== startDev) return null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ current = parent;
124
+ }
125
+ return null;
126
+ }
127
+ function resolveRegistryRoot(rootDir) {
128
+ if (rootDir !== void 0) return resolve3(rootDir);
129
+ const origin = getActiveWorkspaceRoot() ?? process.cwd();
130
+ const memoised = registryRootByOrigin.get(origin);
131
+ if (memoised !== void 0) return memoised;
132
+ const discovered = findRegistryAncestor(origin) ?? findRepoRoot(origin);
133
+ if (discovered !== null) {
134
+ registryRootByOrigin.set(origin, discovered);
135
+ return discovered;
136
+ }
137
+ let fallback;
138
+ try {
139
+ fallback = realpathSync(origin);
140
+ } catch {
141
+ fallback = resolve3(origin);
142
+ }
143
+ if (!warnedCwdFallback) {
144
+ warnedCwdFallback = true;
145
+ logger.warn(
146
+ "No research registry or git repo found above the working directory; using cwd as the research registry root",
147
+ { cwd: fallback, registryPath: REGISTRY_PATH }
148
+ );
149
+ }
150
+ registryRootByOrigin.set(origin, fallback);
151
+ return fallback;
152
+ }
87
153
  function getProjectRoot() {
88
- return process.cwd();
154
+ return resolveRegistryRoot();
89
155
  }
90
156
  function validatePath(constructedPath, allowedRoot) {
91
157
  const resolved = resolveInsideRoot(constructedPath, allowedRoot);
@@ -100,7 +166,7 @@ function validatePath(constructedPath, allowedRoot) {
100
166
  return { ok: true, value: resolved };
101
167
  }
102
168
  async function loadTechniquesRegistry(rootDir) {
103
- const root = rootDir ?? process.cwd();
169
+ const root = resolveRegistryRoot(rootDir);
104
170
  const filePath = join(root, REGISTRY_PATH, TECHNIQUES_FILE);
105
171
  const pathValidation = validatePath(filePath, root);
106
172
  if (!pathValidation.ok) {
@@ -116,7 +182,7 @@ async function loadTechniquesRegistry(rootDir) {
116
182
  }
117
183
  }
118
184
  async function loadPapersRegistry(rootDir) {
119
- const root = rootDir ?? process.cwd();
185
+ const root = resolveRegistryRoot(rootDir);
120
186
  const filePath = join(root, REGISTRY_PATH, PAPERS_FILE);
121
187
  const pathValidation = validatePath(filePath, root);
122
188
  if (!pathValidation.ok) {
@@ -132,7 +198,7 @@ async function loadPapersRegistry(rootDir) {
132
198
  }
133
199
  }
134
200
  async function savePapersRegistry(registry, rootDir) {
135
- const root = rootDir ?? process.cwd();
201
+ const root = resolveRegistryRoot(rootDir);
136
202
  const filePath = join(root, REGISTRY_PATH, PAPERS_FILE);
137
203
  const pathValidation = validatePath(filePath, root);
138
204
  if (!pathValidation.ok) {
@@ -151,9 +217,10 @@ async function savePapersRegistry(registry, rootDir) {
151
217
  export {
152
218
  resolveInsideRoot,
153
219
  REGISTRY_PATH,
220
+ resolveRegistryRoot,
154
221
  getProjectRoot,
155
222
  loadTechniquesRegistry,
156
223
  loadPapersRegistry,
157
224
  savePapersRegistry
158
225
  };
159
- //# sourceMappingURL=chunk-DEXENQXS.js.map
226
+ //# sourceMappingURL=chunk-3CLKYFE3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/research-helpers-io.ts","../src/security/safe-path.ts","../src/cli/research-scaffold.ts"],"sourcesContent":["/**\n * Research Registry I/O Operations\n *\n * File operations for loading and saving research registry YAML files.\n * Includes path traversal protection per Issue #353.\n *\n * @see docs/research/RESEARCH_INDEX.md\n * @see Issue #237 (Epic #225)\n * @see Issue #353 (Security - Path traversal fix)\n * @see Issue #5053 (Registry root resolution — workspace/repo root, not cwd)\n */\n\nimport * as fs from 'node:fs/promises';\nimport { realpathSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { resolveInsideRoot } from '../security/safe-path.js';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { Result } from '../core/index.js';\nimport { SecurityError, createLogger, getErrorMessage } from '../core/index.js';\nimport { getActiveWorkspaceRoot } from '../config/nexus-data-dir.js';\nimport { findRepoRoot, isRepoRoot } from '../config/repo-root-detection.js';\nimport { ParseError } from '../core/types/workflow.js';\nimport type { TechniquesRegistry, PapersRegistry } from './research-types.js';\nimport { ensureRegistryFile } from './research-scaffold.js';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\n/** Path to research registry directory relative to project root */\nexport const REGISTRY_PATH = 'docs/research/registry';\n\n/** Filename for techniques registry */\nexport const TECHNIQUES_FILE = 'techniques.yaml';\n\n/** Filename for papers registry */\nexport const PAPERS_FILE = 'papers.yaml';\n\n// =============================================================================\n// PROJECT ROOT\n// =============================================================================\n\nconst logger = createLogger({ component: 'research-registry-root' });\n\n/**\n * Per-process memo of discovered registry roots (#5053), keyed on the origin\n * the walk started from (the active workspace root, else cwd). The MCP server\n * fetches its client's `roots` asynchronously after `initialized`, so a\n * `research_*` call can land before — or a `roots/list_changed` after — the\n * workspace root is known; keying on the origin means a changed origin\n * resolves afresh instead of inheriting the first caller's cwd-derived root.\n */\nconst registryRootByOrigin = new Map<string, string>();\n/** The cwd-fallback warning fires once per process overall, not per origin. */\nlet warnedCwdFallback = false;\n\n// @export-no-consumer-yet — see #5053. Test-only reset hook for the per-origin\n// memo and the one-shot cwd warning, the same shape as the data-dir module's\n// `_reset*ForTests` helpers; no production consumer is intended.\n/** Test helper — clears the registry-root memo and the one-shot warning. */\nexport function _resetRegistryRootForTests(): void {\n registryRootByOrigin.clear();\n warnedCwdFallback = false;\n}\n\n/** True iff `<dir>/docs/research/registry` exists as a directory. */\nfunction hasRegistryDir(dir: string): boolean {\n try {\n return statSync(join(dir, REGISTRY_PATH)).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Walks upward from `start` looking for the nearest ancestor (inclusive) that\n * already contains `docs/research/registry`, searching only within the\n * enclosing git repo: the walk ends at the first directory that `isRepoRoot`\n * (after checking that directory itself), so a nested checkout never adopts —\n * or writes into — an outer repo's registry. Mirrors `findRepoRoot`'s other\n * defenses: bounded depth, stops at the filesystem root, and refuses to cross\n * a mount point so a sandboxed workdir cannot pick up a host-side registry.\n */\nfunction findRegistryAncestor(start: string): string | null {\n let current: string;\n let startDev: number;\n try {\n current = realpathSync(start);\n startDev = statSync(current).dev;\n } catch {\n return null;\n }\n for (let depth = 0; depth < 64; depth++) {\n if (hasRegistryDir(current)) return current;\n if (isRepoRoot(current)) return null;\n const parent = dirname(current);\n if (parent === current) return null;\n try {\n if (statSync(parent).dev !== startDev) return null;\n } catch {\n return null;\n }\n current = parent;\n }\n return null;\n}\n\n/**\n * Resolves the directory that owns `docs/research/registry` (#5053).\n *\n * Resolution order:\n * 1. An explicit `rootDir` argument wins outright (resolved, never memoised).\n * 2. The nearest ancestor of the active workspace root (MCP `roots`, #3991)\n * — or of `process.cwd()` when none is set — that already contains\n * `docs/research/registry`, without crossing a repo boundary: the search\n * stops at the enclosing repo root, so a nested checkout never resolves\n * to an outer repo's registry.\n * 3. Otherwise the enclosing git repo root via `findRepoRoot`, so the\n * first-run scaffold (#2470) lands at the repo root rather than below it.\n * 4. Otherwise cwd, with a warning emitted once per process: nothing above\n * cwd identifies a project, so cwd is a guess rather than a discovery.\n *\n * Steps 2–4 are memoised per origin (workspace root or cwd), so a workspace\n * root that arrives after the first call — MCP roots are fetched after\n * `initialized` — is honoured rather than shadowed by an earlier cwd-derived\n * result. Before this resolver every helper defaulted to cwd, so a server\n * started inside `packages/nexus-agents` read (and scaffolded) a shadow\n * registry there instead of the repo's.\n */\nexport function resolveRegistryRoot(rootDir?: string): string {\n if (rootDir !== undefined) return resolve(rootDir);\n\n const origin = getActiveWorkspaceRoot() ?? process.cwd();\n const memoised = registryRootByOrigin.get(origin);\n if (memoised !== undefined) return memoised;\n\n const discovered = findRegistryAncestor(origin) ?? findRepoRoot(origin);\n if (discovered !== null) {\n registryRootByOrigin.set(origin, discovered);\n return discovered;\n }\n\n let fallback: string;\n try {\n fallback = realpathSync(origin);\n } catch {\n fallback = resolve(origin);\n }\n if (!warnedCwdFallback) {\n warnedCwdFallback = true;\n logger.warn(\n 'No research registry or git repo found above the working directory; using cwd as the research registry root',\n { cwd: fallback, registryPath: REGISTRY_PATH }\n );\n }\n registryRootByOrigin.set(origin, fallback);\n return fallback;\n}\n\n/**\n * Get the project root directory that owns the research registry.\n * Delegates to {@link resolveRegistryRoot} — no longer bare `process.cwd()`.\n */\nexport function getProjectRoot(): string {\n return resolveRegistryRoot();\n}\n\n// =============================================================================\n// PATH VALIDATION\n// =============================================================================\n\n/**\n * Validates that a constructed path stays within the allowed root directory.\n * Prevents path traversal attacks (e.g., ../../../etc/passwd).\n *\n * @param constructedPath - The full path to validate\n * @param allowedRoot - The root directory that paths must be within\n * @returns Result with validated absolute path or SecurityError\n */\nfunction validatePath(constructedPath: string, allowedRoot: string): Result<string, SecurityError> {\n const resolved = resolveInsideRoot(constructedPath, allowedRoot);\n if (resolved === null) {\n return {\n ok: false,\n error: new SecurityError('Path traversal detected: path escapes allowed root directory', {\n context: { constructedPath, allowedRoot: resolve(allowedRoot) },\n }),\n };\n }\n return { ok: true, value: resolved };\n}\n\n// =============================================================================\n// LOAD OPERATIONS\n// =============================================================================\n\n/**\n * Load techniques registry from YAML file.\n * Validates path to prevent directory traversal attacks.\n *\n * @param rootDir - Project root directory (defaults to the resolved registry root, see {@link resolveRegistryRoot})\n * @returns Result with TechniquesRegistry or SecurityError/ParseError\n */\nexport async function loadTechniquesRegistry(\n rootDir?: string\n): Promise<Result<TechniquesRegistry, SecurityError | ParseError>> {\n const root = resolveRegistryRoot(rootDir);\n const filePath = join(root, REGISTRY_PATH, TECHNIQUES_FILE);\n\n const pathValidation = validatePath(filePath, root);\n if (!pathValidation.ok) {\n return pathValidation;\n }\n\n // #2470: try to scaffold an empty techniques.yaml on first run so research_*\n // workflows don't error on a fresh install. No-op when file exists.\n // When scaffold refuses (e.g. <rootDir>/docs/ doesn't exist, or\n // NEXUS_NO_SCAFFOLD=1), fall through and let readFile produce the original\n // ENOENT error — that's the existing contract.\n await ensureRegistryFile(root, TECHNIQUES_FILE);\n\n try {\n const content = await fs.readFile(pathValidation.value, 'utf-8');\n return { ok: true, value: parseYaml(content) as TechniquesRegistry };\n } catch (error) {\n const message = getErrorMessage(error);\n return { ok: false, error: new ParseError(`Failed to load techniques registry: ${message}`) };\n }\n}\n\n/**\n * Load papers registry from YAML file.\n * Validates path to prevent directory traversal attacks.\n *\n * @param rootDir - Project root directory (defaults to the resolved registry root, see {@link resolveRegistryRoot})\n * @returns Result with PapersRegistry or SecurityError/ParseError\n */\nexport async function loadPapersRegistry(\n rootDir?: string\n): Promise<Result<PapersRegistry, SecurityError | ParseError>> {\n const root = resolveRegistryRoot(rootDir);\n const filePath = join(root, REGISTRY_PATH, PAPERS_FILE);\n\n const pathValidation = validatePath(filePath, root);\n if (!pathValidation.ok) {\n return pathValidation;\n }\n\n // #2470: try to scaffold an empty papers.yaml on first run so research_*\n // workflows don't error on a fresh install. No-op when file exists; falls\n // through silently when scaffold refuses (no docs/ root, or NEXUS_NO_SCAFFOLD).\n await ensureRegistryFile(root, PAPERS_FILE);\n\n try {\n const content = await fs.readFile(pathValidation.value, 'utf-8');\n return { ok: true, value: parseYaml(content) as PapersRegistry };\n } catch (error) {\n const message = getErrorMessage(error);\n return { ok: false, error: new ParseError(`Failed to load papers registry: ${message}`) };\n }\n}\n\n// =============================================================================\n// SAVE OPERATIONS\n// =============================================================================\n\n/**\n * Save techniques registry to YAML file.\n * Validates path to prevent directory traversal attacks.\n *\n * @param registry - The techniques registry to save\n * @param rootDir - Project root directory (defaults to the resolved registry root, see {@link resolveRegistryRoot})\n * @returns Result with void on success or SecurityError/ParseError on failure\n */\nexport async function saveTechniquesRegistry(\n registry: TechniquesRegistry,\n rootDir?: string\n): Promise<Result<void, SecurityError | ParseError>> {\n const root = resolveRegistryRoot(rootDir);\n const filePath = join(root, REGISTRY_PATH, TECHNIQUES_FILE);\n\n const pathValidation = validatePath(filePath, root);\n if (!pathValidation.ok) {\n return pathValidation;\n }\n\n try {\n const content = stringifyYaml(registry, { lineWidth: 100 });\n await fs.writeFile(pathValidation.value, content, 'utf-8');\n return { ok: true, value: undefined };\n } catch (error) {\n const message = getErrorMessage(error);\n return { ok: false, error: new ParseError(`Failed to save techniques registry: ${message}`) };\n }\n}\n\n/**\n * Save papers registry to YAML file.\n * Validates path to prevent directory traversal attacks.\n *\n * @param registry - The papers registry to save\n * @param rootDir - Project root directory (defaults to the resolved registry root, see {@link resolveRegistryRoot})\n * @returns Result with void on success or SecurityError/ParseError on failure\n */\nexport async function savePapersRegistry(\n registry: PapersRegistry,\n rootDir?: string\n): Promise<Result<void, SecurityError | ParseError>> {\n const root = resolveRegistryRoot(rootDir);\n const filePath = join(root, REGISTRY_PATH, PAPERS_FILE);\n\n const pathValidation = validatePath(filePath, root);\n if (!pathValidation.ok) {\n return pathValidation;\n }\n\n try {\n const content = stringifyYaml(registry, { lineWidth: 100 });\n await fs.writeFile(pathValidation.value, content, 'utf-8');\n return { ok: true, value: undefined };\n } catch (error) {\n const message = getErrorMessage(error);\n return { ok: false, error: new ParseError(`Failed to save papers registry: ${message}`) };\n }\n}\n","/**\n * Safe Path — Path traversal guards for scanner-controlled inputs (#1813, #1814)\n *\n * When reading files whose path comes from untrusted input (e.g., SARIF\n * scanner output), we must verify the resolved path stays within the\n * workspace root. Without this, a malicious scanner can cause arbitrary\n * file reads whose contents are exfiltrated via downstream LLM prompts\n * (CWE-22).\n *\n * Shared helper to prevent the finding-triage/fix-generator drift that\n * required two separate fixes.\n *\n * @module security/safe-path\n */\n\nimport { resolve, sep } from 'node:path';\n\n/**\n * Resolve a scanner-supplied path against the workspace root, returning null\n * if it escapes. Null means the caller must fall back to a safe alternative\n * (e.g., scanner-provided snippet) rather than reading the file.\n *\n * @param filePath - Scanner-supplied path (may be relative or absolute)\n * @param root - Workspace root (defaults to process.cwd())\n * @returns Resolved absolute path if inside root, null otherwise\n */\nexport function resolveInsideRoot(filePath: string, root: string = process.cwd()): string | null {\n const resolvedRoot = resolve(root);\n const resolved = resolve(resolvedRoot, filePath);\n if (resolved === resolvedRoot) return resolved;\n if (resolved.startsWith(resolvedRoot + sep)) return resolved;\n return null;\n}\n","/**\n * research-scaffold — auto-create empty research registry files when missing.\n *\n * On a fresh install or in a sandboxed environment where the project doesn't\n * have `docs/research/registry/`, the research workflows previously errored\n * with `Failed to load papers registry: ENOENT`. The user has no idea what\n * to do.\n *\n * This module provides `ensureResearchRegistry()`: idempotent scaffolding\n * that creates empty YAML registries on ENOENT, announces what it did to\n * stderr (so the operator knows), and returns the path that's now safe to\n * read.\n *\n * Source: Issue #2470 (epic #2467 child).\n */\n\nimport * as fs from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { stringify as stringifyYaml } from 'yaml';\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, getErrorMessage } from '../core/index.js';\nimport { ParseError } from '../core/types/workflow.js';\n\nimport {\n REGISTRY_PATH,\n PAPERS_FILE,\n TECHNIQUES_FILE,\n resolveRegistryRoot,\n} from './research-helpers-io.js';\nimport { parseBoolEnv } from '../config/defaults-env.js';\n\n/**\n * Names of the registry files this scaffolder knows how to create.\n * `sources.yaml` and `alignments.yaml` live alongside but are populated by\n * different code paths; this scaffolder only owns the two YAML registries\n * that block `research_query` / `research_synthesize` on a fresh install.\n */\ntype ScaffoldedFile = typeof PAPERS_FILE | typeof TECHNIQUES_FILE;\n\nconst SCHEMA_VERSION = '1.0';\n\nconst ANNOUNCED = new Set<string>();\n\nfunction announceOnce(path: string, kind: ScaffoldedFile): void {\n // De-dupe so that running multiple research commands in one session\n // produces one announcement per scaffolded file, not per call.\n if (ANNOUNCED.has(path)) return;\n ANNOUNCED.add(path);\n process.stderr.write(\n `[scaffold] Created empty ${kind} at ${path}; add entries via 'nexus-agents research add' or research_add MCP tool.\\n`\n );\n}\n\n/**\n * Operator opt-out. When set, scaffolding errors loudly with an actionable\n * message instead of silently writing files. Useful for CI and for\n * environments that want strict \"fail when state is wrong\" semantics.\n */\nfunction scaffoldDisabled(): boolean {\n return parseBoolEnv('NEXUS_NO_SCAFFOLD', false);\n}\n\nfunction emptyPapersYaml(): string {\n return stringifyYaml({ schema_version: SCHEMA_VERSION, papers: {} });\n}\n\nfunction emptyTechniquesYaml(): string {\n return stringifyYaml({ schema_version: SCHEMA_VERSION, techniques: {} });\n}\n\n/**\n * Create the registry directory + an empty YAML file if either is missing.\n * Idempotent: existing files are left untouched.\n *\n * Scaffolds only when `<rootDir>/docs/` already exists. That guard keeps\n * tests, vitest's package cwd, and random scratch directories from getting\n * a `docs/research/` subtree implicitly. Operators on a real project root\n * (which always has `docs/`, either from cloning the repo or running\n * `nexus-agents init`) still get the auto-create behavior the issue asks for.\n *\n * Returns the resolved file path on success, or a ParseError when scaffolding\n * is disabled / refused (so the caller can pass the error through unchanged\n * for backwards compat with existing call sites).\n */\nexport async function ensureRegistryFile(\n rootDir: string,\n filename: ScaffoldedFile\n): Promise<Result<string, ParseError>> {\n const filePath = resolve(rootDir, REGISTRY_PATH, filename);\n\n if (existsSync(filePath)) return ok(filePath);\n\n if (scaffoldDisabled()) {\n return err(\n new ParseError(\n `Registry file missing: ${filePath}. Scaffolding disabled (NEXUS_NO_SCAFFOLD=1). Create the file manually or unset the env var.`\n )\n );\n }\n\n // Refuse to create a docs/ subtree in directories that aren't already\n // documented project roots. The presence of `<rootDir>/docs/` is the\n // strong signal we use.\n if (!existsSync(resolve(rootDir, 'docs'))) {\n return err(\n new ParseError(\n `Registry file missing: ${filePath}. ${rootDir}/docs/ does not exist; ` +\n `refusing to create a Nexus subtree in a non-documented root. ` +\n `Run 'nexus-agents init' first, or create ${rootDir}/docs/ manually.`\n )\n );\n }\n\n try {\n await fs.mkdir(dirname(filePath), { recursive: true });\n const content = filename === PAPERS_FILE ? emptyPapersYaml() : emptyTechniquesYaml();\n await fs.writeFile(filePath, content, 'utf-8');\n announceOnce(filePath, filename);\n return ok(filePath);\n } catch (e: unknown) {\n return err(\n new ParseError(\n `Failed to scaffold ${filename} at ${filePath}: ${getErrorMessage(e)}. Set NEXUS_NO_SCAFFOLD=1 and create manually if scaffolding is unwanted.`\n )\n );\n }\n}\n\n/**\n * Convenience: scaffold both papers.yaml and techniques.yaml. Used by the\n * research workflow startup paths that need both registries available.\n * Without an explicit `rootDir` the files land at the resolved registry root\n * (#5053), never at a bare cwd below it.\n */\nexport async function ensureResearchRegistry(rootDir?: string): Promise<Result<void, ParseError>> {\n const root = resolveRegistryRoot(rootDir);\n const papers = await ensureRegistryFile(root, PAPERS_FILE);\n if (!papers.ok) return papers;\n const techniques = await ensureRegistryFile(root, TECHNIQUES_FILE);\n if (!techniques.ok) return techniques;\n return ok(undefined);\n}\n\n/**\n * Test-only reset of the announcement de-dupe set. Production code never\n * needs this; tests do because Vitest reuses process state across files.\n */\nexport function _resetAnnouncedForTests(): void {\n ANNOUNCED.clear();\n}\n"],"mappings":"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","names":["fs","dirname","resolve","stringifyYaml","resolve","resolve","dirname","resolve","stringifyYaml"]}
@@ -3,7 +3,7 @@ import {
3
3
  ValidationError,
4
4
  createLogger,
5
5
  formatZodError
6
- } from "./chunk-HEV2C6VS.js";
6
+ } from "./chunk-ERL3XHQO.js";
7
7
 
8
8
  // src/security/ast-rule-runner.ts
9
9
  import { existsSync } from "node:fs";
@@ -241,4 +241,4 @@ export {
241
241
  collectAstQaFindings,
242
242
  runAstQaRules
243
243
  };
244
- //# sourceMappingURL=chunk-YUMKCIEL.js.map
244
+ //# sourceMappingURL=chunk-3XQK4MEF.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getTimeProvider
3
- } from "./chunk-HEV2C6VS.js";
3
+ } from "./chunk-ERL3XHQO.js";
4
4
 
5
5
  // src/security/audit-trail.ts
6
6
  var MAX_EVENTS = 1e4;
@@ -205,4 +205,4 @@ export {
205
205
  createGraphAuditBridge,
206
206
  createAuditTrail
207
207
  };
208
- //# sourceMappingURL=chunk-6AGNJBHE.js.map
208
+ //# sourceMappingURL=chunk-3YKFLM2B.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getErrorMessage
3
- } from "./chunk-HEV2C6VS.js";
3
+ } from "./chunk-ERL3XHQO.js";
4
4
 
5
5
  // src/cli/setup-config.ts
6
6
  import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
@@ -77,4 +77,4 @@ function ensureBackup(outputPath) {
77
77
  export {
78
78
  runConfigInitSync
79
79
  };
80
- //# sourceMappingURL=chunk-4HZCKQ25.js.map
80
+ //# sourceMappingURL=chunk-64VMTY3M.js.map
@@ -3,10 +3,10 @@ import {
3
3
  createLogger,
4
4
  getDefaultRegistry,
5
5
  roundToMicroUsd
6
- } from "./chunk-HEV2C6VS.js";
6
+ } from "./chunk-ERL3XHQO.js";
7
7
  import {
8
8
  getNexusDataDir
9
- } from "./chunk-MM223CVS.js";
9
+ } from "./chunk-J7QCZRVK.js";
10
10
 
11
11
  // src/learning/usage-log.ts
12
12
  import { z } from "zod";
@@ -176,4 +176,4 @@ export {
176
176
  loadUsageEvents,
177
177
  rollupByModel
178
178
  };
179
- //# sourceMappingURL=chunk-DY6DVTX2.js.map
179
+ //# sourceMappingURL=chunk-6HETGJSB.js.map
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-YYJFZ7TO.js";
6
6
  import {
7
7
  nexusDataPath
8
- } from "./chunk-MM223CVS.js";
8
+ } from "./chunk-J7QCZRVK.js";
9
9
 
10
10
  // src/mcp/tools/tool-memory-registry-adapters.ts
11
11
  var DEFAULT_SEARCH_LIMIT = 10;
@@ -93,4 +93,4 @@ export {
93
93
  StatsOnlyAdapter,
94
94
  ensureSharedMemoryRegistry
95
95
  };
96
- //# sourceMappingURL=chunk-ODBLHHTP.js.map
96
+ //# sourceMappingURL=chunk-6URMW3DB.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createLogger,
6
6
  getTimeProvider
7
- } from "./chunk-HEV2C6VS.js";
7
+ } from "./chunk-ERL3XHQO.js";
8
8
 
9
9
  // src/mcp/tools/scanner-registry-fetcher.ts
10
10
  import { z } from "zod";
@@ -721,4 +721,4 @@ export {
721
721
  generateSecurityPlan,
722
722
  buildPlanFromAnalysis
723
723
  };
724
- //# sourceMappingURL=chunk-BNPXE7BD.js.map
724
+ //# sourceMappingURL=chunk-7AU5WLAJ.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  resolveCliSlot
3
- } from "./chunk-GUOMNRXZ.js";
3
+ } from "./chunk-TZWJ7QLW.js";
4
4
  import {
5
5
  createLogger,
6
6
  getTimeProvider,
7
7
  isRateLimitText
8
- } from "./chunk-HEV2C6VS.js";
8
+ } from "./chunk-ERL3XHQO.js";
9
9
 
10
10
  // src/pipeline/expert-bridge.ts
11
11
  function resolveCliFromModelString(model) {
@@ -21,7 +21,7 @@ async function getMcpConfigPath() {
21
21
  if (cachedMcpConfigPath !== null) return cachedMcpConfigPath;
22
22
  mcpConfigInitPromise ??= (async () => {
23
23
  try {
24
- const { generateMcpConfig } = await import("./child-mcp-config-WQNW5YFO.js");
24
+ const { generateMcpConfig } = await import("./child-mcp-config-N4HLLCEH.js");
25
25
  const config = await generateMcpConfig();
26
26
  cachedMcpConfigPath = config.configPath;
27
27
  cachedMcpConfigCleanup = config.cleanup;
@@ -93,8 +93,8 @@ var routerInitPromise = null;
93
93
  async function getRouter() {
94
94
  if (cachedRouter !== null) return cachedRouter;
95
95
  routerInitPromise ??= (async () => {
96
- const { createAllAdapters } = await import("./factory-VGRG7M62.js");
97
- const { createCompositeRouter } = await import("./composite-router-TU6MVJBG.js");
96
+ const { createAllAdapters } = await import("./factory-ROTP63IZ.js");
97
+ const { createCompositeRouter } = await import("./composite-router-VAVDLJGT.js");
98
98
  const adapters = createAllAdapters();
99
99
  if (adapters.size === 0) {
100
100
  routerInitPromise = null;
@@ -102,7 +102,7 @@ async function getRouter() {
102
102
  }
103
103
  cachedRouter = adaptCompositeRouter(createCompositeRouter(adapters));
104
104
  try {
105
- const { createCliCircuitBreakerIntegration } = await import("./cli-circuit-breaker-YAAGSJ5N.js");
105
+ const { createCliCircuitBreakerIntegration } = await import("./cli-circuit-breaker-AAHYLFXZ.js");
106
106
  cachedCircuitBreaker = createCliCircuitBreakerIntegration([...adapters.values()]);
107
107
  } catch (error) {
108
108
  const msg = error instanceof Error ? error.message : String(error);
@@ -213,4 +213,4 @@ export {
213
213
  tokenSplitFromUsage,
214
214
  executeExpert
215
215
  };
216
- //# sourceMappingURL=chunk-66GKBIY2.js.map
216
+ //# sourceMappingURL=chunk-7J7UYJXC.js.map
@@ -5,34 +5,34 @@ import {
5
5
  planOptionalParams,
6
6
  requireApiKey,
7
7
  validateApiKeyPresence
8
- } from "./chunk-W5VBFX65.js";
8
+ } from "./chunk-FKKVZRSY.js";
9
9
  import {
10
10
  CUSTOM_API_DEFAULT_MODEL
11
- } from "./chunk-NSHGFRGF.js";
11
+ } from "./chunk-QCSXKQTK.js";
12
12
  import {
13
13
  CUSTOM_API_BASE_URL_ENV,
14
14
  PROVIDER_ENV_KEYS,
15
15
  assertCustomApiHostResolvesPublic,
16
16
  validateCustomApiBaseUrl
17
- } from "./chunk-OLR2RIN2.js";
17
+ } from "./chunk-F2GERNMF.js";
18
18
  import {
19
19
  nexusMkdtempSync
20
- } from "./chunk-4HXPNLZS.js";
20
+ } from "./chunk-DKHBIJO4.js";
21
21
  import {
22
22
  getAvailabilityCache
23
- } from "./chunk-GUOMNRXZ.js";
23
+ } from "./chunk-TZWJ7QLW.js";
24
24
  import {
25
25
  extractSystemPrompt
26
26
  } from "./chunk-QZUG667L.js";
27
27
  import {
28
28
  generateHyphenId
29
- } from "./chunk-W37HHXDL.js";
29
+ } from "./chunk-WXU4Y5T3.js";
30
30
  import {
31
31
  CliCircuitBreaker,
32
32
  DEFAULT_CIRCUIT_BREAKER_CONFIG,
33
33
  getCliCircuitBreakerSnapshot,
34
34
  getDefaultCliCircuitBreakerRegistry
35
- } from "./chunk-PX422NHD.js";
35
+ } from "./chunk-VADACTVB.js";
36
36
  import {
37
37
  CLI_SUBPROCESS_TIMEOUTS,
38
38
  CLI_TIMEOUTS,
@@ -72,7 +72,7 @@ import {
72
72
  resolveCliAlias,
73
73
  resolveRetryAfterMs,
74
74
  retryAfterMsFromContext
75
- } from "./chunk-HEV2C6VS.js";
75
+ } from "./chunk-ERL3XHQO.js";
76
76
 
77
77
  // src/cli-adapters/cli-to-model-adapter.ts
78
78
  var CliToModelAdapter = class {
@@ -4946,4 +4946,4 @@ export {
4946
4946
  SdkAdapter,
4947
4947
  createAutoAdapter
4948
4948
  };
4949
- //# sourceMappingURL=chunk-BIHRU7WA.js.map
4949
+ //# sourceMappingURL=chunk-7QQ5F7T5.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getNexusDataDir
3
- } from "./chunk-MM223CVS.js";
3
+ } from "./chunk-J7QCZRVK.js";
4
4
 
5
5
  // src/config/learning-persistence.ts
6
6
  import { mkdirSync } from "node:fs";
@@ -45,4 +45,4 @@ export {
45
45
  isPersistenceEnabled,
46
46
  ensureLearningDir
47
47
  };
48
- //# sourceMappingURL=chunk-ITCKXMOH.js.map
48
+ //# sourceMappingURL=chunk-7WJGV5DN.js.map
@@ -2,7 +2,7 @@ import {
2
2
  TASK_CATEGORIES,
3
3
  getAdaptiveBonus,
4
4
  getOutcomeStore
5
- } from "./chunk-HEV2C6VS.js";
5
+ } from "./chunk-ERL3XHQO.js";
6
6
 
7
7
  // src/cli/doctor-deep.ts
8
8
  var CLI_NAMES = ["claude", "gemini", "codex", "opencode"];
@@ -106,4 +106,4 @@ export {
106
106
  runDeepDiagnostics,
107
107
  formatDeepDiagnostics
108
108
  };
109
- //# sourceMappingURL=chunk-ZN6BTFLD.js.map
109
+ //# sourceMappingURL=chunk-AOS7F4QR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  nexusDataPathEnsure
3
- } from "./chunk-MM223CVS.js";
3
+ } from "./chunk-J7QCZRVK.js";
4
4
 
5
5
  // src/config/nexus-tmp-dir.ts
6
6
  import { mkdirSync, mkdtempSync } from "node:fs";
@@ -36,4 +36,4 @@ export {
36
36
  nexusMkdtempSync,
37
37
  nexusMkdtemp
38
38
  };
39
- //# sourceMappingURL=chunk-4HXPNLZS.js.map
39
+ //# sourceMappingURL=chunk-DKHBIJO4.js.map
@@ -0,0 +1,7 @@
1
+ // src/version.ts
2
+ var VERSION = true ? "8.8.1" : "dev";
3
+
4
+ export {
5
+ VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-DROYVWGE.js.map
@@ -5,13 +5,13 @@ import {
5
5
  loadTechniquesRegistry,
6
6
  resolveInsideRoot,
7
7
  savePapersRegistry
8
- } from "./chunk-DEXENQXS.js";
8
+ } from "./chunk-3CLKYFE3.js";
9
9
  import {
10
10
  resolveToken
11
- } from "./chunk-RWZJ2D2N.js";
11
+ } from "./chunk-II5NJRKH.js";
12
12
  import {
13
13
  getToolMemory
14
- } from "./chunk-CRKF5LUZ.js";
14
+ } from "./chunk-NR62IQDC.js";
15
15
  import {
16
16
  capitalizeWords,
17
17
  tokenizeFiltered,
@@ -40,14 +40,14 @@ import {
40
40
  ok,
41
41
  registerPersistentDistillerFactory,
42
42
  sigmoidConfidence
43
- } from "./chunk-HEV2C6VS.js";
43
+ } from "./chunk-ERL3XHQO.js";
44
44
  import {
45
45
  ensureLearningDir,
46
46
  getRulesFile
47
- } from "./chunk-ITCKXMOH.js";
47
+ } from "./chunk-7WJGV5DN.js";
48
48
  import {
49
49
  nexusDataPath
50
- } from "./chunk-MM223CVS.js";
50
+ } from "./chunk-J7QCZRVK.js";
51
51
 
52
52
  // src/learning/strategy-distiller-persistence.ts
53
53
  import { writeFileSync, readFileSync, renameSync, unlinkSync, existsSync } from "node:fs";
@@ -2234,10 +2234,10 @@ ${topicLines}
2234
2234
 
2235
2235
  // src/cli/research-helpers-index-ops.ts
2236
2236
  function getRegistryPath() {
2237
- return path3.resolve(process.cwd(), "docs/research/registry");
2237
+ return path3.join(getProjectRoot(), REGISTRY_PATH);
2238
2238
  }
2239
2239
  function getIndexPath() {
2240
- return path3.resolve(process.cwd(), "docs/research/RESEARCH_INDEX.md");
2240
+ return path3.join(getProjectRoot(), "docs/research/RESEARCH_INDEX.md");
2241
2241
  }
2242
2242
  async function handleStatsCommand(options) {
2243
2243
  const registryPath = getRegistryPath();
@@ -3441,7 +3441,7 @@ function buildIndex(files) {
3441
3441
 
3442
3442
  // src/context/repo-map-callsites.ts
3443
3443
  import { readFileSync as readFileSync3 } from "node:fs";
3444
- import { resolve as resolve4 } from "node:path";
3444
+ import { resolve as resolve3 } from "node:path";
3445
3445
 
3446
3446
  // src/indexer/usage-ast.ts
3447
3447
  import { extname } from "node:path";
@@ -3557,7 +3557,7 @@ var CALLSITE_TOP_N_MODULES = 25;
3557
3557
  var MAX_PROBE_SYMBOLS = 60;
3558
3558
  var MAX_CALLSITE_FILES = 4e3;
3559
3559
  function defaultSourceRoot() {
3560
- return resolve4(process.cwd(), "src");
3560
+ return resolve3(process.cwd(), "src");
3561
3561
  }
3562
3562
  function addModuleExports(mod, name, owner) {
3563
3563
  for (const file of mod.files) {
@@ -3585,7 +3585,7 @@ function collectFiles(index, sourceRoot, maxFiles) {
3585
3585
  for (const file of mod.files) {
3586
3586
  if (seen.has(file.path)) continue;
3587
3587
  seen.add(file.path);
3588
- refs.push({ abs: resolve4(sourceRoot, file.path), rel: file.path });
3588
+ refs.push({ abs: resolve3(sourceRoot, file.path), rel: file.path });
3589
3589
  if (refs.length >= maxFiles) return refs;
3590
3590
  }
3591
3591
  }
@@ -4165,4 +4165,4 @@ export {
4165
4165
  DEFAULT_CONTEXT_BUDGET_TOKENS,
4166
4166
  getContextPromptPrefix
4167
4167
  };
4168
- //# sourceMappingURL=chunk-37ONEK7R.js.map
4168
+ //# sourceMappingURL=chunk-EALWAQAQ.js.map