@nifrajs/cli 3.5.0 → 4.0.2

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 (404) hide show
  1. package/README.md +42 -3
  2. package/dist/app-files.d.ts +11 -0
  3. package/dist/app-files.d.ts.map +1 -0
  4. package/dist/app-files.js +34 -0
  5. package/dist/app-files.js.map +1 -0
  6. package/dist/assure-hydration.d.ts +7 -1
  7. package/dist/assure-hydration.d.ts.map +1 -1
  8. package/dist/assure-hydration.js +128 -43
  9. package/dist/assure-hydration.js.map +1 -1
  10. package/dist/assure.d.ts.map +1 -1
  11. package/dist/assure.js +16 -2
  12. package/dist/assure.js.map +1 -1
  13. package/dist/capabilities-tool.d.ts +5 -1
  14. package/dist/capabilities-tool.d.ts.map +1 -1
  15. package/dist/capabilities-tool.js +32 -10
  16. package/dist/capabilities-tool.js.map +1 -1
  17. package/dist/cdn-check.d.ts +41 -0
  18. package/dist/cdn-check.d.ts.map +1 -0
  19. package/dist/cdn-check.js +207 -0
  20. package/dist/cdn-check.js.map +1 -0
  21. package/dist/check-diagnostics.d.ts +4 -0
  22. package/dist/check-diagnostics.d.ts.map +1 -1
  23. package/dist/check-diagnostics.js +13 -0
  24. package/dist/check-diagnostics.js.map +1 -1
  25. package/dist/check-scan.d.ts +49 -7
  26. package/dist/check-scan.d.ts.map +1 -1
  27. package/dist/check-scan.js +291 -62
  28. package/dist/check-scan.js.map +1 -1
  29. package/dist/check.d.ts.map +1 -1
  30. package/dist/check.js +99 -63
  31. package/dist/check.js.map +1 -1
  32. package/dist/cli.d.ts +42 -16
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +262 -82
  35. package/dist/cli.js.map +1 -1
  36. package/dist/command-catalog.d.ts +62 -5
  37. package/dist/command-catalog.d.ts.map +1 -1
  38. package/dist/command-catalog.js +275 -35
  39. package/dist/command-catalog.js.map +1 -1
  40. package/dist/config-target.d.ts +8 -0
  41. package/dist/config-target.d.ts.map +1 -0
  42. package/dist/config-target.js +45 -0
  43. package/dist/config-target.js.map +1 -0
  44. package/dist/contracts.d.ts.map +1 -1
  45. package/dist/contracts.js +52 -13
  46. package/dist/contracts.js.map +1 -1
  47. package/dist/db-audit.d.ts +37 -0
  48. package/dist/db-audit.d.ts.map +1 -0
  49. package/dist/db-audit.js +99 -0
  50. package/dist/db-audit.js.map +1 -0
  51. package/dist/db-child.d.ts +64 -0
  52. package/dist/db-child.d.ts.map +1 -0
  53. package/dist/db-child.js +213 -0
  54. package/dist/db-child.js.map +1 -0
  55. package/dist/db-config.d.ts +60 -0
  56. package/dist/db-config.d.ts.map +1 -0
  57. package/dist/db-config.js +176 -0
  58. package/dist/db-config.js.map +1 -0
  59. package/dist/db-tool.d.ts +84 -0
  60. package/dist/db-tool.d.ts.map +1 -0
  61. package/dist/db-tool.js +537 -0
  62. package/dist/db-tool.js.map +1 -0
  63. package/dist/dev-bun-config.d.ts +20 -13
  64. package/dist/dev-bun-config.d.ts.map +1 -1
  65. package/dist/dev-bun-config.js +98 -41
  66. package/dist/dev-bun-config.js.map +1 -1
  67. package/dist/dev-feed-tool.d.ts +106 -0
  68. package/dist/dev-feed-tool.d.ts.map +1 -0
  69. package/dist/dev-feed-tool.js +713 -0
  70. package/dist/dev-feed-tool.js.map +1 -0
  71. package/dist/dev-server-client.d.ts +67 -0
  72. package/dist/dev-server-client.d.ts.map +1 -0
  73. package/dist/dev-server-client.js +195 -0
  74. package/dist/dev-server-client.js.map +1 -0
  75. package/dist/diff-tool.d.ts +3 -3
  76. package/dist/diff-tool.d.ts.map +1 -1
  77. package/dist/diff-tool.js +6 -5
  78. package/dist/diff-tool.js.map +1 -1
  79. package/dist/docs-search.d.ts.map +1 -1
  80. package/dist/docs-search.js +12 -4
  81. package/dist/docs-search.js.map +1 -1
  82. package/dist/doctor.d.ts +17 -0
  83. package/dist/doctor.d.ts.map +1 -1
  84. package/dist/doctor.js +76 -15
  85. package/dist/doctor.js.map +1 -1
  86. package/dist/env-file.d.ts +15 -0
  87. package/dist/env-file.d.ts.map +1 -1
  88. package/dist/env-file.js +73 -1
  89. package/dist/env-file.js.map +1 -1
  90. package/dist/fix-recipes.d.ts.map +1 -1
  91. package/dist/fix-recipes.js +48 -22
  92. package/dist/fix-recipes.js.map +1 -1
  93. package/dist/frontend-guidance.js +5 -5
  94. package/dist/frontend-guidance.js.map +1 -1
  95. package/dist/i18n-check.d.ts +16 -0
  96. package/dist/i18n-check.d.ts.map +1 -0
  97. package/dist/i18n-check.js +154 -0
  98. package/dist/i18n-check.js.map +1 -0
  99. package/dist/init-agents.d.ts +49 -13
  100. package/dist/init-agents.d.ts.map +1 -1
  101. package/dist/init-agents.js +239 -34
  102. package/dist/init-agents.js.map +1 -1
  103. package/dist/internal/code-unit-order.d.ts +4 -0
  104. package/dist/internal/code-unit-order.d.ts.map +1 -0
  105. package/dist/internal/code-unit-order.js +6 -0
  106. package/dist/internal/code-unit-order.js.map +1 -0
  107. package/dist/internal/script-kind.d.ts +7 -0
  108. package/dist/internal/script-kind.d.ts.map +1 -0
  109. package/dist/internal/script-kind.js +12 -0
  110. package/dist/internal/script-kind.js.map +1 -0
  111. package/dist/internal/source-facts.d.ts +5 -0
  112. package/dist/internal/source-facts.d.ts.map +1 -1
  113. package/dist/internal/source-facts.js +61 -5
  114. package/dist/internal/source-facts.js.map +1 -1
  115. package/dist/internal/typescript-import.d.ts +49 -24
  116. package/dist/internal/typescript-import.d.ts.map +1 -1
  117. package/dist/internal/typescript-import.js +507 -67
  118. package/dist/internal/typescript-import.js.map +1 -1
  119. package/dist/introspect.d.ts +5 -1
  120. package/dist/introspect.d.ts.map +1 -1
  121. package/dist/introspect.js +92 -27
  122. package/dist/introspect.js.map +1 -1
  123. package/dist/learn.js +3 -3
  124. package/dist/learn.js.map +1 -1
  125. package/dist/levels-tool.d.ts +10 -0
  126. package/dist/levels-tool.d.ts.map +1 -1
  127. package/dist/levels-tool.js +33 -3
  128. package/dist/levels-tool.js.map +1 -1
  129. package/dist/load.d.ts +67 -17
  130. package/dist/load.d.ts.map +1 -1
  131. package/dist/load.js +78 -18
  132. package/dist/load.js.map +1 -1
  133. package/dist/manifest-tool.d.ts.map +1 -1
  134. package/dist/manifest-tool.js +3 -2
  135. package/dist/manifest-tool.js.map +1 -1
  136. package/dist/mcp-app-tool.d.ts.map +1 -1
  137. package/dist/mcp-app-tool.js +2 -0
  138. package/dist/mcp-app-tool.js.map +1 -1
  139. package/dist/mcp-context.js +1 -1
  140. package/dist/mcp-context.js.map +1 -1
  141. package/dist/mcp-delegate.d.ts +57 -0
  142. package/dist/mcp-delegate.d.ts.map +1 -0
  143. package/dist/mcp-delegate.js +130 -0
  144. package/dist/mcp-delegate.js.map +1 -0
  145. package/dist/mcp-docs-tools.d.ts.map +1 -1
  146. package/dist/mcp-docs-tools.js +23 -5
  147. package/dist/mcp-docs-tools.js.map +1 -1
  148. package/dist/mcp-exec.d.ts +2 -0
  149. package/dist/mcp-exec.d.ts.map +1 -1
  150. package/dist/mcp-exec.js +132 -108
  151. package/dist/mcp-exec.js.map +1 -1
  152. package/dist/mcp-http.d.ts.map +1 -1
  153. package/dist/mcp-http.js +17 -5
  154. package/dist/mcp-http.js.map +1 -1
  155. package/dist/mcp-io.d.ts +14 -0
  156. package/dist/mcp-io.d.ts.map +1 -1
  157. package/dist/mcp-io.js +45 -0
  158. package/dist/mcp-io.js.map +1 -1
  159. package/dist/mcp-isolate.d.ts +48 -0
  160. package/dist/mcp-isolate.d.ts.map +1 -0
  161. package/dist/mcp-isolate.js +222 -0
  162. package/dist/mcp-isolate.js.map +1 -0
  163. package/dist/mcp-project-child.d.ts +74 -0
  164. package/dist/mcp-project-child.d.ts.map +1 -0
  165. package/dist/mcp-project-child.js +130 -0
  166. package/dist/mcp-project-child.js.map +1 -0
  167. package/dist/mcp-render.js +2 -2
  168. package/dist/mcp-render.js.map +1 -1
  169. package/dist/mcp-root.d.ts +37 -9
  170. package/dist/mcp-root.d.ts.map +1 -1
  171. package/dist/mcp-root.js +110 -23
  172. package/dist/mcp-root.js.map +1 -1
  173. package/dist/mcp-run.d.ts +18 -1
  174. package/dist/mcp-run.d.ts.map +1 -1
  175. package/dist/mcp-run.js +82 -9
  176. package/dist/mcp-run.js.map +1 -1
  177. package/dist/mcp-ws.js +4 -1
  178. package/dist/mcp-ws.js.map +1 -1
  179. package/dist/mcp.d.ts +16 -3
  180. package/dist/mcp.d.ts.map +1 -1
  181. package/dist/mcp.js +81 -53
  182. package/dist/mcp.js.map +1 -1
  183. package/dist/migrate-layout.d.ts +44 -0
  184. package/dist/migrate-layout.d.ts.map +1 -0
  185. package/dist/migrate-layout.js +1155 -0
  186. package/dist/migrate-layout.js.map +1 -0
  187. package/dist/openapi-tool.d.ts +2 -2
  188. package/dist/openapi-tool.d.ts.map +1 -1
  189. package/dist/openapi-tool.js.map +1 -1
  190. package/dist/openapi-types.d.ts +2 -1
  191. package/dist/openapi-types.d.ts.map +1 -1
  192. package/dist/openapi-types.js +240 -12
  193. package/dist/openapi-types.js.map +1 -1
  194. package/dist/pipeline-report.d.ts +3 -2
  195. package/dist/pipeline-report.d.ts.map +1 -1
  196. package/dist/pipeline-report.js +19 -16
  197. package/dist/pipeline-report.js.map +1 -1
  198. package/dist/port.d.ts +5 -4
  199. package/dist/port.d.ts.map +1 -1
  200. package/dist/port.js +46 -29
  201. package/dist/port.js.map +1 -1
  202. package/dist/project-facts.d.ts +5 -0
  203. package/dist/project-facts.d.ts.map +1 -1
  204. package/dist/project-facts.js.map +1 -1
  205. package/dist/recipes/4.0.0.d.ts +9 -0
  206. package/dist/recipes/4.0.0.d.ts.map +1 -0
  207. package/dist/recipes/4.0.0.js +36 -0
  208. package/dist/recipes/4.0.0.js.map +1 -0
  209. package/dist/recipes/index.d.ts.map +1 -1
  210. package/dist/recipes/index.js +2 -0
  211. package/dist/recipes/index.js.map +1 -1
  212. package/dist/release-verification.d.ts +11 -1
  213. package/dist/release-verification.d.ts.map +1 -1
  214. package/dist/release-verification.js +47 -4
  215. package/dist/release-verification.js.map +1 -1
  216. package/dist/review.d.ts.map +1 -1
  217. package/dist/review.js +12 -8
  218. package/dist/review.js.map +1 -1
  219. package/dist/route-types.d.ts +28 -0
  220. package/dist/route-types.d.ts.map +1 -0
  221. package/dist/route-types.js +106 -0
  222. package/dist/route-types.js.map +1 -0
  223. package/dist/rules/codes.d.ts +9 -0
  224. package/dist/rules/codes.d.ts.map +1 -1
  225. package/dist/rules/codes.js +9 -0
  226. package/dist/rules/codes.js.map +1 -1
  227. package/dist/rules/data-guard.d.ts +12 -0
  228. package/dist/rules/data-guard.d.ts.map +1 -0
  229. package/dist/rules/data-guard.js +213 -0
  230. package/dist/rules/data-guard.js.map +1 -0
  231. package/dist/rules/index.d.ts +3 -0
  232. package/dist/rules/index.d.ts.map +1 -1
  233. package/dist/rules/index.js +17 -5
  234. package/dist/rules/index.js.map +1 -1
  235. package/dist/rules/islands.d.ts.map +1 -1
  236. package/dist/rules/islands.js +7 -4
  237. package/dist/rules/islands.js.map +1 -1
  238. package/dist/rules/legacy.d.ts.map +1 -1
  239. package/dist/rules/legacy.js +71 -38
  240. package/dist/rules/legacy.js.map +1 -1
  241. package/dist/rules/nano.d.ts.map +1 -1
  242. package/dist/rules/nano.js +15 -6
  243. package/dist/rules/nano.js.map +1 -1
  244. package/dist/rules/pages.d.ts +15 -0
  245. package/dist/rules/pages.d.ts.map +1 -0
  246. package/dist/rules/pages.js +88 -0
  247. package/dist/rules/pages.js.map +1 -0
  248. package/dist/rules/routes.d.ts +11 -0
  249. package/dist/rules/routes.d.ts.map +1 -1
  250. package/dist/rules/routes.js +180 -2
  251. package/dist/rules/routes.js.map +1 -1
  252. package/dist/rules/secrets.d.ts +10 -0
  253. package/dist/rules/secrets.d.ts.map +1 -0
  254. package/dist/rules/secrets.js +169 -0
  255. package/dist/rules/secrets.js.map +1 -0
  256. package/dist/rules/security.d.ts +7 -0
  257. package/dist/rules/security.d.ts.map +1 -1
  258. package/dist/rules/security.js +171 -65
  259. package/dist/rules/security.js.map +1 -1
  260. package/dist/rules/typescript.d.ts +16 -0
  261. package/dist/rules/typescript.d.ts.map +1 -0
  262. package/dist/rules/typescript.js +59 -0
  263. package/dist/rules/typescript.js.map +1 -0
  264. package/dist/rules/zones.d.ts +17 -0
  265. package/dist/rules/zones.d.ts.map +1 -0
  266. package/dist/rules/zones.js +174 -0
  267. package/dist/rules/zones.js.map +1 -0
  268. package/dist/scaffold.d.ts +25 -12
  269. package/dist/scaffold.d.ts.map +1 -1
  270. package/dist/scaffold.js +132 -87
  271. package/dist/scaffold.js.map +1 -1
  272. package/dist/sdk.d.ts +17 -3
  273. package/dist/sdk.d.ts.map +1 -1
  274. package/dist/sdk.js +941 -153
  275. package/dist/sdk.js.map +1 -1
  276. package/dist/search-terms.d.ts +2 -0
  277. package/dist/search-terms.d.ts.map +1 -1
  278. package/dist/search-terms.js +23 -8
  279. package/dist/search-terms.js.map +1 -1
  280. package/dist/smoke.d.ts +79 -0
  281. package/dist/smoke.d.ts.map +1 -0
  282. package/dist/smoke.js +494 -0
  283. package/dist/smoke.js.map +1 -0
  284. package/dist/sync-manifest.d.ts +10 -2
  285. package/dist/sync-manifest.d.ts.map +1 -1
  286. package/dist/sync-manifest.js +44 -19
  287. package/dist/sync-manifest.js.map +1 -1
  288. package/dist/sync-routes.d.ts +4 -3
  289. package/dist/sync-routes.d.ts.map +1 -1
  290. package/dist/sync-routes.js +4 -3
  291. package/dist/sync-routes.js.map +1 -1
  292. package/dist/test-tool.d.ts.map +1 -1
  293. package/dist/test-tool.js +30 -11
  294. package/dist/test-tool.js.map +1 -1
  295. package/dist/types-search.d.ts.map +1 -1
  296. package/dist/types-search.js +11 -5
  297. package/dist/types-search.js.map +1 -1
  298. package/dist/upgrade.d.ts +36 -6
  299. package/dist/upgrade.d.ts.map +1 -1
  300. package/dist/upgrade.js +167 -29
  301. package/dist/upgrade.js.map +1 -1
  302. package/dist/verification-plan.d.ts.map +1 -1
  303. package/dist/verification-plan.js +4 -0
  304. package/dist/verification-plan.js.map +1 -1
  305. package/dist/verification.d.ts.map +1 -1
  306. package/dist/verification.js +45 -12
  307. package/dist/verification.js.map +1 -1
  308. package/dist/web-app-options.d.ts +19 -0
  309. package/dist/web-app-options.d.ts.map +1 -0
  310. package/dist/web-app-options.js +49 -0
  311. package/dist/web-app-options.js.map +1 -0
  312. package/dist/work-graph.d.ts.map +1 -1
  313. package/dist/work-graph.js +29 -13
  314. package/dist/work-graph.js.map +1 -1
  315. package/docs/examples.json +185 -29
  316. package/docs/llms-full.txt +3807 -580
  317. package/docs/types.json +14229 -10939
  318. package/package.json +19 -12
  319. package/src/app-files.ts +40 -0
  320. package/src/assure-hydration.ts +139 -46
  321. package/src/assure.ts +17 -2
  322. package/src/capabilities-tool.ts +35 -9
  323. package/src/cdn-check.ts +268 -0
  324. package/src/check-diagnostics.ts +17 -0
  325. package/src/check-scan.ts +358 -63
  326. package/src/check.ts +97 -62
  327. package/src/cli.ts +296 -87
  328. package/src/command-catalog.ts +389 -48
  329. package/src/config-target.ts +47 -0
  330. package/src/contracts.ts +54 -12
  331. package/src/db-audit.ts +128 -0
  332. package/src/db-child.ts +285 -0
  333. package/src/db-config.ts +224 -0
  334. package/src/db-tool.ts +690 -0
  335. package/src/dev-bun-config.ts +116 -40
  336. package/src/dev-feed-tool.ts +912 -0
  337. package/src/dev-server-client.ts +267 -0
  338. package/src/diff-tool.ts +6 -5
  339. package/src/docs-search.ts +10 -3
  340. package/src/doctor.ts +98 -15
  341. package/src/env-file.ts +78 -1
  342. package/src/fix-recipes.ts +53 -32
  343. package/src/frontend-guidance.ts +5 -5
  344. package/src/i18n-check.ts +179 -0
  345. package/src/init-agents.ts +301 -38
  346. package/src/internal/code-unit-order.ts +5 -0
  347. package/src/internal/script-kind.ts +11 -0
  348. package/src/internal/source-facts.ts +96 -22
  349. package/src/internal/typescript-import.ts +663 -68
  350. package/src/introspect.ts +90 -27
  351. package/src/learn.ts +3 -3
  352. package/src/levels-tool.ts +47 -2
  353. package/src/load.ts +140 -33
  354. package/src/manifest-tool.ts +3 -2
  355. package/src/mcp-app-tool.ts +2 -0
  356. package/src/mcp-context.ts +1 -1
  357. package/src/mcp-delegate.ts +162 -0
  358. package/src/mcp-docs-tools.ts +23 -5
  359. package/src/mcp-exec.ts +160 -132
  360. package/src/mcp-http.ts +19 -5
  361. package/src/mcp-io.ts +57 -0
  362. package/src/mcp-isolate.ts +287 -0
  363. package/src/mcp-project-child.ts +182 -0
  364. package/src/mcp-render.ts +2 -2
  365. package/src/mcp-root.ts +117 -22
  366. package/src/mcp-run.ts +104 -10
  367. package/src/mcp-ws.ts +4 -1
  368. package/src/mcp.ts +106 -64
  369. package/src/migrate-layout.ts +1338 -0
  370. package/src/openapi-tool.ts +7 -3
  371. package/src/openapi-types.ts +315 -13
  372. package/src/pipeline-report.ts +22 -18
  373. package/src/port.ts +53 -30
  374. package/src/project-facts.ts +5 -0
  375. package/src/recipes/4.0.0.ts +37 -0
  376. package/src/recipes/index.ts +2 -0
  377. package/src/release-verification.ts +74 -5
  378. package/src/review.ts +13 -8
  379. package/src/route-types.ts +133 -0
  380. package/src/rules/codes.ts +9 -0
  381. package/src/rules/data-guard.ts +230 -0
  382. package/src/rules/index.ts +18 -5
  383. package/src/rules/islands.ts +12 -4
  384. package/src/rules/legacy.ts +71 -39
  385. package/src/rules/nano.ts +18 -6
  386. package/src/rules/pages.ts +99 -0
  387. package/src/rules/routes.ts +181 -2
  388. package/src/rules/secrets.ts +174 -0
  389. package/src/rules/security.ts +198 -70
  390. package/src/rules/typescript.ts +77 -0
  391. package/src/rules/zones.ts +195 -0
  392. package/src/scaffold.ts +147 -95
  393. package/src/sdk.ts +1215 -158
  394. package/src/search-terms.ts +24 -7
  395. package/src/smoke.ts +607 -0
  396. package/src/sync-manifest.ts +50 -19
  397. package/src/sync-routes.ts +4 -3
  398. package/src/test-tool.ts +32 -11
  399. package/src/types-search.ts +11 -4
  400. package/src/upgrade.ts +205 -25
  401. package/src/verification-plan.ts +13 -0
  402. package/src/verification.ts +50 -12
  403. package/src/web-app-options.ts +57 -0
  404. package/src/work-graph.ts +27 -13
package/src/mcp.ts CHANGED
@@ -20,19 +20,32 @@
20
20
  * - `nifra_levels` - the cumulative verification ladder (L0 contract → L4 invariants): what the
21
21
  * project actually proves, and why each level it misses does not hold.
22
22
  * - `nifra_doctor` - package.json dependency drift detector, with safe local-version auto-fix.
23
- * - `nifra_explain` - resolve an error (pasted, or the dev server's last) into a structured
23
+ * - `nifra_errors` - the running dev server's errors (SSR, loader, API, build, browser, hydration,
24
+ * crash), each a structured diagnostic tagged with its request; `since` cursors, stale flags.
25
+ * - `nifra_logs` - the dev server's console output, server and browser, tagged by request.
26
+ * - `nifra_db_schema` / `nifra_db_query` / `nifra_db_role` - the development database declared as
27
+ * `devDatabase` in nifra.config.ts: its schema, one read-only query (or plan) per call in a fresh
28
+ * subprocess killed at the deadline, and the SQL for a read-only role. Separate tools, so a client
29
+ * can allow reading the schema without allowing queries.
30
+ * - `nifra_explain` - resolve an error (pasted, or the dev server's latest) into a structured
24
31
  * diagnostic: stable code, a codeframe in the user's source, and the recognised cause + fix.
25
32
  * - `nifra_inspect` - read the running dev server's recent request traces (method/path/status/
26
- * duration/ISR) from the DevTools plugin: what your requests ACTUALLY did, not a guess.
33
+ * duration/ISR/errors): what your requests ACTUALLY did, not a guess.
27
34
  *
28
35
  * Wire it into a client (e.g. Claude Desktop / Cursor) as: command `nifra`, args `["mcp"]` (or
29
36
  * `["mcp", "<dir>"]` to pin the project directory explicitly). The server does NOT silently trust its
30
37
  * spawn directory: the root is resolved via `./mcp-root.ts` (marker walk-up, the client's MCP `roots`,
31
38
  * fail-closed tools when no nifra project is found) and announced in `initialize` + on every project
32
- * tool result. The protocol is hand-rolled (newline-delimited JSON-RPC 2.0 over stdio), including
39
+ * tool result. A project that installs its own `@nifrajs/cli` at a different version gets the session
40
+ * handed to that CLI; when that is impossible the version-sensitive tools refuse rather than answer
41
+ * for the wrong release (see `./mcp-delegate.ts`). The protocol is hand-rolled (newline-delimited JSON-RPC 2.0 over stdio), including
33
42
  * standard MCP progress notifications and request cancellation - no SDK dependency, the same
34
43
  * minimal-surface choice as the rest of nifra. The pure dispatch lives in `./mcp-protocol.ts`; this
35
44
  * module is the I/O shell (stdin loop, tool wiring, the run subprocess).
45
+ *
46
+ * This process runs no project code and holds none of the project's `.env`: the config, the backend
47
+ * and everything they import run in a fresh subprocess per call (`./mcp-isolate.ts`), and a session
48
+ * whose process Bun started with `.env` values moves to a copy that never loads them (`./cli.ts`).
36
49
  */
37
50
 
38
51
  import { stat } from "node:fs/promises"
@@ -40,7 +53,8 @@ import { resolve } from "node:path"
40
53
  import { loadDocsCorpus } from "./docs-search.ts"
41
54
  import { loadExamplesCorpus } from "./examples.ts"
42
55
  import type { LoadedApp } from "./load.ts"
43
- import { detectMonorepo, loadMonorepoApps } from "./load.ts"
56
+ import { loadMonorepoApps } from "./load.ts"
57
+ import { delegateToProjectCli, refuseVersionSensitive } from "./mcp-delegate.ts"
44
58
  import { docsTools } from "./mcp-docs-tools.ts"
45
59
  import {
46
60
  createMcpProtocolState,
@@ -56,11 +70,6 @@ import {
56
70
  type McpTool,
57
71
  rpcError,
58
72
  } from "./mcp-protocol.ts"
59
- import {
60
- extractBackendPrompts,
61
- extractBackendResources,
62
- extractBackendTools,
63
- } from "./mcp-reflect.ts"
64
73
  import { loadTypesCorpus } from "./types-search.ts"
65
74
 
66
75
  export {
@@ -99,7 +108,14 @@ export {
99
108
  } from "./mcp-io.ts"
100
109
  export { clientSupportsRoots, guardTools } from "./mcp-root.ts"
101
110
 
102
- import { createCachedAppLoader, projectTools } from "./mcp-exec.ts"
111
+ import { projectTools } from "./mcp-exec.ts"
112
+ import {
113
+ createAppSurface,
114
+ detectMonorepoIsolated,
115
+ type IsolatedSurface,
116
+ isolateResources,
117
+ isolateTools,
118
+ } from "./mcp-isolate.ts"
103
119
 
104
120
  export type { CachedAppLoaderOptions } from "./mcp-exec.ts"
105
121
  export {
@@ -115,7 +131,7 @@ export {
115
131
  wsHandler,
116
132
  } from "./mcp-exec.ts"
117
133
 
118
- import { namespaceForApp, projectFeatures } from "./mcp-context.ts"
134
+ import { namespaceForApp, projectPrompts, projectResources } from "./mcp-context.ts"
119
135
 
120
136
  export type { CommandMcpToolOptions } from "./mcp-context.ts"
121
137
  export {
@@ -127,35 +143,39 @@ export {
127
143
  resolveProjectDir,
128
144
  } from "./mcp-context.ts"
129
145
 
130
- async function backendFeatures(
131
- loader: () => Promise<LoadedApp>,
132
- base: McpServerFeatures,
133
- ): Promise<McpServerFeatures> {
134
- const resources = [...(base.resources ?? [])]
135
- const prompts = [...(base.prompts ?? [])]
136
- try {
137
- const app = await loader()
138
- resources.push(...extractBackendResources(app.backend))
139
- prompts.push(...extractBackendPrompts(app.backend))
140
- } catch {
141
- // Not loadable here (no web config, or the config itself throws). The server still serves.
142
- }
143
- return { resources, prompts }
144
- }
146
+ /**
147
+ * The loader the server builds its tool and resource lists with. It never loads: every handler that
148
+ * reaches the app runs in a project subprocess (`./mcp-isolate.ts`), so one that tried here fails.
149
+ */
150
+ const noAppInServer = (): Promise<LoadedApp> =>
151
+ Promise.reject(
152
+ new Error("[nifra] the MCP server does not load the app; project code runs in a subprocess"),
153
+ )
154
+
155
+ const isolatedToolsByDir = new Map<string, McpTool[]>()
145
156
 
146
157
  /**
147
- * The MCP tools the app itself declares via `app.tool(...)`, or none when the app cannot be loaded.
148
- *
149
- * Same reasoning as {@link backendFeatures}, on the hotter path: this ran on EVERY JSON-RPC message,
150
- * so an unloadable project failed `initialize` itself with a `-32603` and the session never opened.
151
- * An app's own tools are an extension of the built-in set, so their absence must not withdraw the
152
- * built-ins - a backend-only project still gets docs, examples, types, check, doctor, levels and test.
158
+ * The project's tools, each forwarded to a project subprocess unless it runs no project code. Built
159
+ * once per directory: `nifra_run`/`nifra_render` hold their warm worker in the tool, so a list rebuilt
160
+ * per message started a fresh worker for every `warm: true` call.
153
161
  */
154
- async function appDeclaredTools(loader: () => Promise<LoadedApp>): Promise<McpTool[]> {
155
- try {
156
- return extractBackendTools((await loader()).backend)
157
- } catch {
158
- return []
162
+ function isolatedProjectTools(cwd: string): McpTool[] {
163
+ let tools = isolatedToolsByDir.get(cwd)
164
+ if (tools === undefined) {
165
+ tools = isolateTools(cwd, projectTools(cwd, noAppInServer))
166
+ isolatedToolsByDir.set(cwd, tools)
167
+ }
168
+ return tools
169
+ }
170
+
171
+ /** The project's resources and prompts, with the ones the app declares. */
172
+ function appFeatures(cwd: string, surface: IsolatedSurface): McpServerFeatures {
173
+ return {
174
+ resources: [
175
+ ...isolateResources(cwd, projectResources(cwd, noAppInServer)),
176
+ ...surface.resources,
177
+ ],
178
+ prompts: [...projectPrompts(), ...surface.prompts],
159
179
  }
160
180
  }
161
181
 
@@ -163,42 +183,42 @@ const ROOTS_REQUEST_ID = "nifra:roots/list"
163
183
  const MAX_STDIO_MESSAGE_BYTES = 8 * 1024 * 1024
164
184
  const STDIO_ENCODER = new TextEncoder()
165
185
 
186
+ interface ProjectApp {
187
+ readonly name: string
188
+ readonly cwd: string
189
+ readonly surface: () => Promise<IsolatedSurface>
190
+ }
191
+
166
192
  /** Everything derived from the project root - rebuilt wholesale when the root changes (adoption of a
167
193
  * client workspace root), so no per-tool state can keep pointing at the old directory. */
168
194
  interface ProjectContext {
169
- readonly monorepo: Awaited<ReturnType<typeof detectMonorepo>>
195
+ /** A monorepo root's apps; `undefined` when the root is one app. */
196
+ readonly apps: readonly ProjectApp[] | undefined
170
197
  readonly features: McpServerFeatures
171
- readonly loadAppCached: () => Promise<LoadedApp>
198
+ readonly surface: () => Promise<IsolatedSurface>
172
199
  }
173
200
 
174
201
  async function createProjectContext(root: string): Promise<ProjectContext> {
175
- const monorepo = await detectMonorepo(root)
202
+ const monorepo = await detectMonorepoIsolated(root)
176
203
  if (monorepo) {
177
- const appEntries = await loadMonorepoApps(root, monorepo)
204
+ const apps = (await loadMonorepoApps(root, monorepo)).map(
205
+ ({ name, cwd }): ProjectApp => ({ name, cwd, surface: createAppSurface(cwd) }),
206
+ )
178
207
  const allResources: McpResource[] = []
179
208
  const allPrompts: McpPrompt[] = []
180
- for (const { name, cwd: appCwd } of appEntries) {
181
- const loader = createCachedAppLoader(appCwd)
182
- const ns = namespaceForApp(
183
- name,
184
- [],
185
- await backendFeatures(loader, projectFeatures(appCwd, loader)),
186
- )
209
+ for (const { name, cwd: appCwd, surface } of apps) {
210
+ const ns = namespaceForApp(name, [], appFeatures(appCwd, await surface()))
187
211
  allResources.push(...(ns.features.resources ?? []))
188
212
  allPrompts.push(...(ns.features.prompts ?? []))
189
213
  }
190
214
  return {
191
- monorepo,
215
+ apps,
192
216
  features: { resources: allResources, prompts: allPrompts },
193
- loadAppCached: createCachedAppLoader(root),
217
+ surface: createAppSurface(root),
194
218
  }
195
219
  }
196
- const loadAppCached = createCachedAppLoader(root)
197
- return {
198
- monorepo,
199
- features: await backendFeatures(loadAppCached, projectFeatures(root, loadAppCached)),
200
- loadAppCached,
201
- }
220
+ const surface = createAppSurface(root)
221
+ return { apps: undefined, features: appFeatures(root, await surface()), surface }
202
222
  }
203
223
 
204
224
  /**
@@ -217,6 +237,19 @@ export async function runMcpServer(
217
237
  throw new Error(`nifra mcp: directory not found: ${requested}`)
218
238
  }
219
239
  let rootState = await resolveRootState(requested, explicitDir !== undefined)
240
+ if (rootState.isProject) {
241
+ // Before anything reads stdin: a hand-off child inherits the descriptor and owns the session.
242
+ const exitCode = await delegateToProjectCli({
243
+ cwd,
244
+ root: rootState.root,
245
+ version,
246
+ args: explicitDir === undefined ? [] : [requested],
247
+ })
248
+ if (exitCode !== undefined) {
249
+ process.exitCode = exitCode
250
+ return
251
+ }
252
+ }
220
253
  let ctx = await createProjectContext(rootState.root)
221
254
  let drift: ToolingDrift | undefined = await detectToolingDrift(rootState.root, version)
222
255
  const serverInfo = { name: "nifra", version }
@@ -277,20 +310,27 @@ export async function runMcpServer(
277
310
  }
278
311
 
279
312
  let activeTools: McpTool[]
280
- if (ctx.monorepo) {
281
- const appEntries = await loadMonorepoApps(rootState.root, ctx.monorepo)
313
+ if (ctx.apps) {
282
314
  const allTools: McpTool[] = []
283
- for (const { name, cwd: appCwd } of appEntries) {
284
- const loader = createCachedAppLoader(appCwd)
285
- const tools = [...projectTools(appCwd, loader), ...(await appDeclaredTools(loader))]
315
+ for (const { name, cwd: appCwd, surface } of ctx.apps) {
316
+ const tools = [
317
+ ...refuseVersionSensitive(isolatedProjectTools(appCwd), drift),
318
+ ...(await surface()).tools,
319
+ ]
286
320
  const ns = namespaceForApp(name, tools, { resources: [], prompts: [] })
287
321
  allTools.push(...ns.tools)
288
322
  }
289
- activeTools = [...docsTools(loadDocsCorpus, loadExamplesCorpus, loadTypesCorpus), ...allTools]
323
+ activeTools = [
324
+ ...refuseVersionSensitive(
325
+ docsTools(loadDocsCorpus, loadExamplesCorpus, loadTypesCorpus),
326
+ drift,
327
+ ),
328
+ ...allTools,
329
+ ]
290
330
  } else {
291
331
  activeTools = [
292
- ...projectTools(rootState.root, ctx.loadAppCached),
293
- ...(await appDeclaredTools(ctx.loadAppCached)),
332
+ ...refuseVersionSensitive(isolatedProjectTools(rootState.root), drift),
333
+ ...(await ctx.surface()).tools,
294
334
  ]
295
335
  }
296
336
  const verdict = await rootVerdict(rootState)
@@ -308,6 +348,8 @@ export async function runMcpServer(
308
348
  const response = await handleRpc(message, activeTools, serverInfo, features, {
309
349
  state,
310
350
  sendNotification: send,
351
+ // The stdio caller is the developer's own agent: a failure it cannot read it cannot fix.
352
+ exposeToolErrors: true,
311
353
  })
312
354
  if (response) send(response)
313
355
  // Ask for the client's workspace roots once the handshake completes, and again whenever the