@nifrajs/cli 3.3.0 → 4.0.0

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 (425) 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 +7 -15
  22. package/dist/check-diagnostics.d.ts.map +1 -1
  23. package/dist/check-diagnostics.js +162 -713
  24. package/dist/check-diagnostics.js.map +1 -1
  25. package/dist/check-scan.d.ts +58 -7
  26. package/dist/check-scan.d.ts.map +1 -1
  27. package/dist/check-scan.js +323 -62
  28. package/dist/check-scan.js.map +1 -1
  29. package/dist/check.d.ts +1 -0
  30. package/dist/check.d.ts.map +1 -1
  31. package/dist/check.js +145 -54
  32. package/dist/check.js.map +1 -1
  33. package/dist/cli.d.ts +42 -16
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +306 -87
  36. package/dist/cli.js.map +1 -1
  37. package/dist/command-catalog.d.ts +60 -5
  38. package/dist/command-catalog.d.ts.map +1 -1
  39. package/dist/command-catalog.js +298 -33
  40. package/dist/command-catalog.js.map +1 -1
  41. package/dist/config-target.d.ts +8 -0
  42. package/dist/config-target.d.ts.map +1 -0
  43. package/dist/config-target.js +45 -0
  44. package/dist/config-target.js.map +1 -0
  45. package/dist/contracts.d.ts +3 -2
  46. package/dist/contracts.d.ts.map +1 -1
  47. package/dist/contracts.js +56 -16
  48. package/dist/contracts.js.map +1 -1
  49. package/dist/db-audit.d.ts +37 -0
  50. package/dist/db-audit.d.ts.map +1 -0
  51. package/dist/db-audit.js +99 -0
  52. package/dist/db-audit.js.map +1 -0
  53. package/dist/db-child.d.ts +64 -0
  54. package/dist/db-child.d.ts.map +1 -0
  55. package/dist/db-child.js +213 -0
  56. package/dist/db-child.js.map +1 -0
  57. package/dist/db-config.d.ts +60 -0
  58. package/dist/db-config.d.ts.map +1 -0
  59. package/dist/db-config.js +176 -0
  60. package/dist/db-config.js.map +1 -0
  61. package/dist/db-tool.d.ts +84 -0
  62. package/dist/db-tool.d.ts.map +1 -0
  63. package/dist/db-tool.js +537 -0
  64. package/dist/db-tool.js.map +1 -0
  65. package/dist/dev-bun-config.d.ts +20 -13
  66. package/dist/dev-bun-config.d.ts.map +1 -1
  67. package/dist/dev-bun-config.js +98 -41
  68. package/dist/dev-bun-config.js.map +1 -1
  69. package/dist/dev-feed-tool.d.ts +106 -0
  70. package/dist/dev-feed-tool.d.ts.map +1 -0
  71. package/dist/dev-feed-tool.js +713 -0
  72. package/dist/dev-feed-tool.js.map +1 -0
  73. package/dist/dev-server-client.d.ts +67 -0
  74. package/dist/dev-server-client.d.ts.map +1 -0
  75. package/dist/dev-server-client.js +195 -0
  76. package/dist/dev-server-client.js.map +1 -0
  77. package/dist/diagnostics.d.ts +86 -0
  78. package/dist/diagnostics.d.ts.map +1 -1
  79. package/dist/diagnostics.js +85 -0
  80. package/dist/diagnostics.js.map +1 -1
  81. package/dist/diff-tool.d.ts +3 -3
  82. package/dist/diff-tool.d.ts.map +1 -1
  83. package/dist/diff-tool.js +6 -5
  84. package/dist/diff-tool.js.map +1 -1
  85. package/dist/docs-search.d.ts.map +1 -1
  86. package/dist/docs-search.js +12 -4
  87. package/dist/docs-search.js.map +1 -1
  88. package/dist/doctor.d.ts +17 -0
  89. package/dist/doctor.d.ts.map +1 -1
  90. package/dist/doctor.js +76 -15
  91. package/dist/doctor.js.map +1 -1
  92. package/dist/env-file.d.ts +15 -0
  93. package/dist/env-file.d.ts.map +1 -1
  94. package/dist/env-file.js +73 -1
  95. package/dist/env-file.js.map +1 -1
  96. package/dist/fix-recipes.d.ts.map +1 -1
  97. package/dist/fix-recipes.js +41 -21
  98. package/dist/fix-recipes.js.map +1 -1
  99. package/dist/frontend-guidance.js +5 -5
  100. package/dist/frontend-guidance.js.map +1 -1
  101. package/dist/i18n-check.d.ts +16 -0
  102. package/dist/i18n-check.d.ts.map +1 -0
  103. package/dist/i18n-check.js +154 -0
  104. package/dist/i18n-check.js.map +1 -0
  105. package/dist/init-agents.d.ts +49 -13
  106. package/dist/init-agents.d.ts.map +1 -1
  107. package/dist/init-agents.js +239 -34
  108. package/dist/init-agents.js.map +1 -1
  109. package/dist/internal/code-unit-order.d.ts +4 -0
  110. package/dist/internal/code-unit-order.d.ts.map +1 -0
  111. package/dist/internal/code-unit-order.js +6 -0
  112. package/dist/internal/code-unit-order.js.map +1 -0
  113. package/dist/internal/script-kind.d.ts +7 -0
  114. package/dist/internal/script-kind.d.ts.map +1 -0
  115. package/dist/internal/script-kind.js +12 -0
  116. package/dist/internal/script-kind.js.map +1 -0
  117. package/dist/internal/source-facts.d.ts +5 -0
  118. package/dist/internal/source-facts.d.ts.map +1 -1
  119. package/dist/internal/source-facts.js +61 -5
  120. package/dist/internal/source-facts.js.map +1 -1
  121. package/dist/internal/typescript-import.d.ts +49 -24
  122. package/dist/internal/typescript-import.d.ts.map +1 -1
  123. package/dist/internal/typescript-import.js +507 -67
  124. package/dist/internal/typescript-import.js.map +1 -1
  125. package/dist/introspect.d.ts +6 -4
  126. package/dist/introspect.d.ts.map +1 -1
  127. package/dist/introspect.js +93 -74
  128. package/dist/introspect.js.map +1 -1
  129. package/dist/learn.js +3 -3
  130. package/dist/learn.js.map +1 -1
  131. package/dist/levels-tool.d.ts +10 -0
  132. package/dist/levels-tool.d.ts.map +1 -1
  133. package/dist/levels-tool.js +33 -3
  134. package/dist/levels-tool.js.map +1 -1
  135. package/dist/load.d.ts +71 -16
  136. package/dist/load.d.ts.map +1 -1
  137. package/dist/load.js +78 -18
  138. package/dist/load.js.map +1 -1
  139. package/dist/manifest-tool.d.ts.map +1 -1
  140. package/dist/manifest-tool.js +3 -2
  141. package/dist/manifest-tool.js.map +1 -1
  142. package/dist/mcp-app-tool.d.ts.map +1 -1
  143. package/dist/mcp-app-tool.js +2 -0
  144. package/dist/mcp-app-tool.js.map +1 -1
  145. package/dist/mcp-context.d.ts.map +1 -1
  146. package/dist/mcp-context.js +5 -6
  147. package/dist/mcp-context.js.map +1 -1
  148. package/dist/mcp-delegate.d.ts +57 -0
  149. package/dist/mcp-delegate.d.ts.map +1 -0
  150. package/dist/mcp-delegate.js +130 -0
  151. package/dist/mcp-delegate.js.map +1 -0
  152. package/dist/mcp-docs-tools.d.ts.map +1 -1
  153. package/dist/mcp-docs-tools.js +23 -5
  154. package/dist/mcp-docs-tools.js.map +1 -1
  155. package/dist/mcp-exec.d.ts +3 -0
  156. package/dist/mcp-exec.d.ts.map +1 -1
  157. package/dist/mcp-exec.js +263 -131
  158. package/dist/mcp-exec.js.map +1 -1
  159. package/dist/mcp-http.d.ts.map +1 -1
  160. package/dist/mcp-http.js +20 -5
  161. package/dist/mcp-http.js.map +1 -1
  162. package/dist/mcp-io.d.ts +26 -0
  163. package/dist/mcp-io.d.ts.map +1 -1
  164. package/dist/mcp-io.js +113 -0
  165. package/dist/mcp-io.js.map +1 -1
  166. package/dist/mcp-isolate.d.ts +48 -0
  167. package/dist/mcp-isolate.d.ts.map +1 -0
  168. package/dist/mcp-isolate.js +222 -0
  169. package/dist/mcp-isolate.js.map +1 -0
  170. package/dist/mcp-path.d.ts +10 -0
  171. package/dist/mcp-path.d.ts.map +1 -0
  172. package/dist/mcp-path.js +51 -0
  173. package/dist/mcp-path.js.map +1 -0
  174. package/dist/mcp-project-child.d.ts +74 -0
  175. package/dist/mcp-project-child.d.ts.map +1 -0
  176. package/dist/mcp-project-child.js +130 -0
  177. package/dist/mcp-project-child.js.map +1 -0
  178. package/dist/mcp-reflect.d.ts.map +1 -1
  179. package/dist/mcp-reflect.js +2 -2
  180. package/dist/mcp-reflect.js.map +1 -1
  181. package/dist/mcp-render.d.ts.map +1 -1
  182. package/dist/mcp-render.js +64 -45
  183. package/dist/mcp-render.js.map +1 -1
  184. package/dist/mcp-root.d.ts +37 -9
  185. package/dist/mcp-root.d.ts.map +1 -1
  186. package/dist/mcp-root.js +124 -25
  187. package/dist/mcp-root.js.map +1 -1
  188. package/dist/mcp-run.d.ts +18 -1
  189. package/dist/mcp-run.d.ts.map +1 -1
  190. package/dist/mcp-run.js +159 -55
  191. package/dist/mcp-run.js.map +1 -1
  192. package/dist/mcp-ws.js +4 -1
  193. package/dist/mcp-ws.js.map +1 -1
  194. package/dist/mcp.d.ts +18 -5
  195. package/dist/mcp.d.ts.map +1 -1
  196. package/dist/mcp.js +165 -82
  197. package/dist/mcp.js.map +1 -1
  198. package/dist/migrate-layout.d.ts +44 -0
  199. package/dist/migrate-layout.d.ts.map +1 -0
  200. package/dist/migrate-layout.js +982 -0
  201. package/dist/migrate-layout.js.map +1 -0
  202. package/dist/openapi-tool.d.ts +2 -2
  203. package/dist/openapi-tool.d.ts.map +1 -1
  204. package/dist/openapi-tool.js.map +1 -1
  205. package/dist/openapi-types.d.ts +2 -1
  206. package/dist/openapi-types.d.ts.map +1 -1
  207. package/dist/openapi-types.js +240 -12
  208. package/dist/openapi-types.js.map +1 -1
  209. package/dist/pipeline-report.d.ts +3 -2
  210. package/dist/pipeline-report.d.ts.map +1 -1
  211. package/dist/pipeline-report.js +19 -16
  212. package/dist/pipeline-report.js.map +1 -1
  213. package/dist/port.d.ts +5 -4
  214. package/dist/port.d.ts.map +1 -1
  215. package/dist/port.js +46 -29
  216. package/dist/port.js.map +1 -1
  217. package/dist/project-facts.d.ts +28 -4
  218. package/dist/project-facts.d.ts.map +1 -1
  219. package/dist/project-facts.js +12 -2
  220. package/dist/project-facts.js.map +1 -1
  221. package/dist/recipes/4.0.0.d.ts +9 -0
  222. package/dist/recipes/4.0.0.d.ts.map +1 -0
  223. package/dist/recipes/4.0.0.js +34 -0
  224. package/dist/recipes/4.0.0.js.map +1 -0
  225. package/dist/recipes/index.d.ts.map +1 -1
  226. package/dist/recipes/index.js +2 -0
  227. package/dist/recipes/index.js.map +1 -1
  228. package/dist/release-verification.d.ts +11 -1
  229. package/dist/release-verification.d.ts.map +1 -1
  230. package/dist/release-verification.js +47 -4
  231. package/dist/release-verification.js.map +1 -1
  232. package/dist/review.d.ts +21 -0
  233. package/dist/review.d.ts.map +1 -0
  234. package/dist/review.js +823 -0
  235. package/dist/review.js.map +1 -0
  236. package/dist/route-types.d.ts +28 -0
  237. package/dist/route-types.d.ts.map +1 -0
  238. package/dist/route-types.js +106 -0
  239. package/dist/route-types.js.map +1 -0
  240. package/dist/rules/codes.d.ts +9 -0
  241. package/dist/rules/codes.d.ts.map +1 -1
  242. package/dist/rules/codes.js +9 -0
  243. package/dist/rules/codes.js.map +1 -1
  244. package/dist/rules/data-guard.d.ts +12 -0
  245. package/dist/rules/data-guard.d.ts.map +1 -0
  246. package/dist/rules/data-guard.js +213 -0
  247. package/dist/rules/data-guard.js.map +1 -0
  248. package/dist/rules/index.d.ts +3 -0
  249. package/dist/rules/index.d.ts.map +1 -1
  250. package/dist/rules/index.js +17 -5
  251. package/dist/rules/index.js.map +1 -1
  252. package/dist/rules/islands.d.ts.map +1 -1
  253. package/dist/rules/islands.js +7 -4
  254. package/dist/rules/islands.js.map +1 -1
  255. package/dist/rules/legacy.d.ts +3 -1
  256. package/dist/rules/legacy.d.ts.map +1 -1
  257. package/dist/rules/legacy.js +829 -34
  258. package/dist/rules/legacy.js.map +1 -1
  259. package/dist/rules/nano.d.ts.map +1 -1
  260. package/dist/rules/nano.js +15 -6
  261. package/dist/rules/nano.js.map +1 -1
  262. package/dist/rules/pages.d.ts +15 -0
  263. package/dist/rules/pages.d.ts.map +1 -0
  264. package/dist/rules/pages.js +88 -0
  265. package/dist/rules/pages.js.map +1 -0
  266. package/dist/rules/routes.d.ts +11 -0
  267. package/dist/rules/routes.d.ts.map +1 -1
  268. package/dist/rules/routes.js +180 -2
  269. package/dist/rules/routes.js.map +1 -1
  270. package/dist/rules/secrets.d.ts +10 -0
  271. package/dist/rules/secrets.d.ts.map +1 -0
  272. package/dist/rules/secrets.js +169 -0
  273. package/dist/rules/secrets.js.map +1 -0
  274. package/dist/rules/security.d.ts +7 -0
  275. package/dist/rules/security.d.ts.map +1 -1
  276. package/dist/rules/security.js +171 -65
  277. package/dist/rules/security.js.map +1 -1
  278. package/dist/rules/typescript.d.ts +16 -0
  279. package/dist/rules/typescript.d.ts.map +1 -0
  280. package/dist/rules/typescript.js +59 -0
  281. package/dist/rules/typescript.js.map +1 -0
  282. package/dist/rules/zones.d.ts +17 -0
  283. package/dist/rules/zones.d.ts.map +1 -0
  284. package/dist/rules/zones.js +174 -0
  285. package/dist/rules/zones.js.map +1 -0
  286. package/dist/scaffold.d.ts +25 -12
  287. package/dist/scaffold.d.ts.map +1 -1
  288. package/dist/scaffold.js +154 -90
  289. package/dist/scaffold.js.map +1 -1
  290. package/dist/sdk.d.ts +17 -3
  291. package/dist/sdk.d.ts.map +1 -1
  292. package/dist/sdk.js +941 -153
  293. package/dist/sdk.js.map +1 -1
  294. package/dist/search-terms.d.ts +2 -0
  295. package/dist/search-terms.d.ts.map +1 -1
  296. package/dist/search-terms.js +23 -8
  297. package/dist/search-terms.js.map +1 -1
  298. package/dist/smoke.d.ts +79 -0
  299. package/dist/smoke.d.ts.map +1 -0
  300. package/dist/smoke.js +494 -0
  301. package/dist/smoke.js.map +1 -0
  302. package/dist/sync-manifest.d.ts.map +1 -1
  303. package/dist/sync-manifest.js +2 -1
  304. package/dist/sync-manifest.js.map +1 -1
  305. package/dist/sync-routes.d.ts +4 -3
  306. package/dist/sync-routes.d.ts.map +1 -1
  307. package/dist/sync-routes.js +4 -3
  308. package/dist/sync-routes.js.map +1 -1
  309. package/dist/test-tool.d.ts.map +1 -1
  310. package/dist/test-tool.js +30 -11
  311. package/dist/test-tool.js.map +1 -1
  312. package/dist/types-search.d.ts.map +1 -1
  313. package/dist/types-search.js +11 -5
  314. package/dist/types-search.js.map +1 -1
  315. package/dist/upgrade.d.ts +36 -6
  316. package/dist/upgrade.d.ts.map +1 -1
  317. package/dist/upgrade.js +167 -29
  318. package/dist/upgrade.js.map +1 -1
  319. package/dist/verification-plan.d.ts.map +1 -1
  320. package/dist/verification-plan.js +8 -0
  321. package/dist/verification-plan.js.map +1 -1
  322. package/dist/verification.d.ts.map +1 -1
  323. package/dist/verification.js +45 -12
  324. package/dist/verification.js.map +1 -1
  325. package/dist/web-app-options.d.ts +19 -0
  326. package/dist/web-app-options.d.ts.map +1 -0
  327. package/dist/web-app-options.js +49 -0
  328. package/dist/web-app-options.js.map +1 -0
  329. package/dist/work-graph.d.ts +3 -0
  330. package/dist/work-graph.d.ts.map +1 -1
  331. package/dist/work-graph.js +33 -16
  332. package/dist/work-graph.js.map +1 -1
  333. package/docs/examples.json +185 -29
  334. package/docs/llms-full.txt +4123 -548
  335. package/docs/types.json +14391 -10060
  336. package/package.json +19 -11
  337. package/src/app-files.ts +40 -0
  338. package/src/assure-hydration.ts +139 -46
  339. package/src/assure.ts +17 -2
  340. package/src/capabilities-tool.ts +35 -9
  341. package/src/cdn-check.ts +268 -0
  342. package/src/check-diagnostics.ts +199 -797
  343. package/src/check-scan.ts +394 -63
  344. package/src/check.ts +146 -58
  345. package/src/cli.ts +347 -92
  346. package/src/command-catalog.ts +407 -45
  347. package/src/config-target.ts +47 -0
  348. package/src/contracts.ts +66 -15
  349. package/src/db-audit.ts +128 -0
  350. package/src/db-child.ts +285 -0
  351. package/src/db-config.ts +224 -0
  352. package/src/db-tool.ts +690 -0
  353. package/src/dev-bun-config.ts +116 -40
  354. package/src/dev-feed-tool.ts +912 -0
  355. package/src/dev-server-client.ts +267 -0
  356. package/src/diagnostics.ts +179 -0
  357. package/src/diff-tool.ts +6 -5
  358. package/src/docs-search.ts +10 -3
  359. package/src/doctor.ts +98 -15
  360. package/src/env-file.ts +78 -1
  361. package/src/fix-recipes.ts +44 -31
  362. package/src/frontend-guidance.ts +5 -5
  363. package/src/i18n-check.ts +179 -0
  364. package/src/init-agents.ts +301 -38
  365. package/src/internal/code-unit-order.ts +5 -0
  366. package/src/internal/script-kind.ts +11 -0
  367. package/src/internal/source-facts.ts +96 -22
  368. package/src/internal/typescript-import.ts +663 -68
  369. package/src/introspect.ts +93 -82
  370. package/src/learn.ts +3 -3
  371. package/src/levels-tool.ts +47 -2
  372. package/src/load.ts +144 -32
  373. package/src/manifest-tool.ts +3 -2
  374. package/src/mcp-app-tool.ts +2 -0
  375. package/src/mcp-context.ts +5 -7
  376. package/src/mcp-delegate.ts +162 -0
  377. package/src/mcp-docs-tools.ts +23 -5
  378. package/src/mcp-exec.ts +301 -161
  379. package/src/mcp-http.ts +22 -5
  380. package/src/mcp-io.ts +130 -0
  381. package/src/mcp-isolate.ts +287 -0
  382. package/src/mcp-path.ts +50 -0
  383. package/src/mcp-project-child.ts +182 -0
  384. package/src/mcp-reflect.ts +3 -2
  385. package/src/mcp-render.ts +72 -42
  386. package/src/mcp-root.ts +130 -24
  387. package/src/mcp-run.ts +190 -56
  388. package/src/mcp-ws.ts +4 -1
  389. package/src/mcp.ts +193 -91
  390. package/src/migrate-layout.ts +1121 -0
  391. package/src/openapi-tool.ts +7 -3
  392. package/src/openapi-types.ts +315 -13
  393. package/src/pipeline-report.ts +22 -18
  394. package/src/port.ts +53 -30
  395. package/src/project-facts.ts +42 -8
  396. package/src/recipes/4.0.0.ts +35 -0
  397. package/src/recipes/index.ts +2 -0
  398. package/src/release-verification.ts +74 -5
  399. package/src/review.ts +988 -0
  400. package/src/route-types.ts +133 -0
  401. package/src/rules/codes.ts +9 -0
  402. package/src/rules/data-guard.ts +230 -0
  403. package/src/rules/index.ts +18 -5
  404. package/src/rules/islands.ts +12 -4
  405. package/src/rules/legacy.ts +947 -35
  406. package/src/rules/nano.ts +18 -6
  407. package/src/rules/pages.ts +99 -0
  408. package/src/rules/routes.ts +181 -2
  409. package/src/rules/secrets.ts +174 -0
  410. package/src/rules/security.ts +198 -70
  411. package/src/rules/typescript.ts +77 -0
  412. package/src/rules/zones.ts +195 -0
  413. package/src/scaffold.ts +175 -98
  414. package/src/sdk.ts +1215 -158
  415. package/src/search-terms.ts +24 -7
  416. package/src/smoke.ts +607 -0
  417. package/src/sync-manifest.ts +2 -1
  418. package/src/sync-routes.ts +4 -3
  419. package/src/test-tool.ts +32 -11
  420. package/src/types-search.ts +11 -4
  421. package/src/upgrade.ts +205 -25
  422. package/src/verification-plan.ts +27 -0
  423. package/src/verification.ts +50 -12
  424. package/src/web-app-options.ts +57 -0
  425. package/src/work-graph.ts +40 -16
@@ -7,8 +7,14 @@
7
7
  */
8
8
  import { resolve } from "node:path";
9
9
  import { formatAssuranceReport } from "./assure.js";
10
+ import { cdnCheckSpec } from "./cdn-check.js";
10
11
  import { renderCheckReport } from "./check.js";
12
+ import { dbAuditSpec, dbQuerySpec, dbRoleSpec, dbSchemaSpec } from "./db-tool.js";
13
+ import { errorsSpec, logsSpec } from "./dev-feed-tool.js";
14
+ import { diagnostic, normalizeSeverity, toSarifLog } from "./diagnostics.js";
15
+ import { i18nCheckPassed, renderI18nCheck } from "./i18n-check.js";
11
16
  import { collectPortResult, renderReport } from "./port.js";
17
+ import { reviewSpec } from "./review.js";
12
18
  import { collectProjectWorkGraph, renderWorkGraphText, } from "./work-graph.js";
13
19
  function freezeJson(value) {
14
20
  if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
@@ -27,6 +33,17 @@ export function toCommandCatalogEntry(spec) {
27
33
  outputVersion: spec.output.version,
28
34
  transports: Object.freeze([...spec.transports]),
29
35
  stability: spec.stability,
36
+ cliOnlyFields: Object.freeze([...(spec.cliOnlyFields ?? [])]),
37
+ });
38
+ }
39
+ /** The input schema a command's MCP tool advertises: its catalog schema without CLI-only fields. */
40
+ export function commandMcpInputSchema(entry) {
41
+ const properties = entry.inputSchema.properties;
42
+ if (entry.cliOnlyFields.length === 0 || typeof properties !== "object" || properties === null)
43
+ return entry.inputSchema;
44
+ return freezeJson({
45
+ ...entry.inputSchema,
46
+ properties: Object.fromEntries(Object.entries(properties).filter(([key]) => !entry.cliOnlyFields.includes(key))),
30
47
  });
31
48
  }
32
49
  function record(value) {
@@ -143,6 +160,7 @@ function parseBooleanFlags(raw, fields) {
143
160
  const CHECK_SCHEMA = input(objectSchema({
144
161
  lintsOnly: { type: "boolean" },
145
162
  json: { type: "boolean" },
163
+ sarif: { type: "boolean" },
146
164
  maxDiagnostics: { type: "integer", minimum: 1 },
147
165
  dir: { type: "string" },
148
166
  }), (value) => {
@@ -151,7 +169,7 @@ const CHECK_SCHEMA = input(objectSchema({
151
169
  if (maxDiagnostics !== undefined && (!Number.isInteger(maxDiagnostics) || maxDiagnostics < 1))
152
170
  throw new TypeError("maxDiagnostics must be a positive integer");
153
171
  return {
154
- ...parseBooleanFlags(raw, ["lintsOnly", "json"]),
172
+ ...parseBooleanFlags(raw, ["lintsOnly", "json", "sarif"]),
155
173
  ...(maxDiagnostics === undefined ? {} : { maxDiagnostics }),
156
174
  ...(raw.dir === undefined ? {} : { dir: raw.dir }),
157
175
  };
@@ -311,23 +329,45 @@ const FIX_SCHEMA = input(objectSchema({ code: { type: "string" }, json: { type:
311
329
  };
312
330
  });
313
331
  const MIGRATE_SCHEMA = input(objectSchema({
332
+ kind: { type: "string", enum: ["layout"] },
314
333
  from: { type: "string", enum: ["tailwind"] },
315
334
  to: { type: "string", enum: ["stylex"] },
316
335
  write: { type: "boolean" },
317
336
  json: { type: "boolean" },
318
337
  dir: { type: "string" },
319
- }, ["from", "to"]), (value) => {
338
+ }), (value) => {
320
339
  const raw = withDir(record(value));
340
+ const flags = {
341
+ ...parseBooleanFlags(raw, ["write", "json"]),
342
+ ...(raw.dir === undefined ? {} : { dir: raw.dir }),
343
+ };
344
+ const kind = optionalString(raw.kind, "kind");
345
+ if (kind !== undefined) {
346
+ if (kind !== "layout")
347
+ throw new TypeError("the migration must be `layout` (or --from tailwind --to stylex)");
348
+ return { kind, ...flags };
349
+ }
321
350
  const from = optionalString(raw.from, "from");
322
351
  const to = optionalString(raw.to, "to");
323
352
  if (from !== "tailwind")
324
353
  throw new TypeError("from must be tailwind");
325
354
  if (to !== "stylex")
326
355
  throw new TypeError("to must be stylex");
356
+ return { from, to, ...flags };
357
+ });
358
+ const TARGET_SCHEMA = input(objectSchema({ target: { type: "string" }, json: { type: "boolean" }, dir: { type: "string" } }), (value) => {
359
+ const raw = withDir(record(value));
360
+ const target = optionalString(raw.target, "target");
327
361
  return {
328
- from,
329
- to,
330
- ...parseBooleanFlags(raw, ["write", "json"]),
362
+ ...parseBooleanFlags(raw, ["json"]),
363
+ ...(raw.dir === undefined ? {} : { dir: raw.dir }),
364
+ ...(target === undefined ? {} : { target }),
365
+ };
366
+ });
367
+ const TYPES_SCHEMA = input(objectSchema({ check: { type: "boolean" }, json: { type: "boolean" }, dir: { type: "string" } }), (value) => {
368
+ const raw = withDir(record(value));
369
+ return {
370
+ ...parseBooleanFlags(raw, ["check", "json"]),
331
371
  ...(raw.dir === undefined ? {} : { dir: raw.dir }),
332
372
  };
333
373
  });
@@ -418,15 +458,62 @@ const PORT_SCHEMA = input(objectSchema({
418
458
  ...(raw.dir === undefined ? {} : { dir: raw.dir }),
419
459
  };
420
460
  });
461
+ const SMOKE_SCHEMA = input(objectSchema({
462
+ fixture: { type: "string" },
463
+ inProcess: { type: "boolean" },
464
+ json: { type: "boolean" },
465
+ dir: { type: "string" },
466
+ }), (value) => {
467
+ const raw = withDir(record(value));
468
+ return {
469
+ fixture: optionalString(raw.fixture, "fixture"),
470
+ ...parseBooleanFlags(raw, ["inProcess", "json"]),
471
+ ...(raw.dir === undefined ? {} : { dir: raw.dir }),
472
+ };
473
+ });
474
+ const I18N_SCHEMA = input(objectSchema({
475
+ action: { type: "string", enum: ["check"] },
476
+ entry: { type: "string" },
477
+ json: { type: "boolean" },
478
+ strict: { type: "boolean" },
479
+ dir: { type: "string" },
480
+ }, ["action"]), (value) => {
481
+ const raw = withDir(record(value));
482
+ if (raw.action !== "check") {
483
+ throw new TypeError(raw.action === undefined
484
+ ? "i18n needs an action: nifra i18n check [entry]"
485
+ : `unknown i18n action: ${String(raw.action)} (the one action is check)`);
486
+ }
487
+ return {
488
+ action: "check",
489
+ entry: optionalString(raw.entry, "entry"),
490
+ ...parseBooleanFlags(raw, ["json", "strict"]),
491
+ ...(raw.dir === undefined ? {} : { dir: raw.dir }),
492
+ };
493
+ });
421
494
  function loadAppFor(ctx) {
422
495
  if (ctx.loadApp !== undefined)
423
496
  return ctx.loadApp();
424
497
  return import("./load.js").then(({ loadApp }) => loadApp(ctx.cwd));
425
498
  }
426
- function checkJson(out) {
427
- return out.structuredDiagnostics === undefined
428
- ? out
429
- : { ...out, diagnostics: out.structuredDiagnostics };
499
+ function structuredDiagnostics(out) {
500
+ if (out.structuredDiagnostics !== undefined)
501
+ return out.structuredDiagnostics;
502
+ return out.diagnostics.map((value) => diagnostic({
503
+ code: value.code ?? value.rule,
504
+ severity: normalizeSeverity(value.severity),
505
+ message: value.message,
506
+ ...(value.file === undefined ? {} : { file: value.file }),
507
+ ...(value.line === undefined ? {} : { line: value.line }),
508
+ ...(value.evidence === undefined ? {} : { evidence: value.evidence }),
509
+ ...(value.fix === undefined ? {} : { fix: { recipe: value.fix } }),
510
+ ...(value.verify === undefined ? {} : { verify: value.verify }),
511
+ }));
512
+ }
513
+ function checkJson(out, input) {
514
+ if (input?.sarif === true)
515
+ return toSarifLog(structuredDiagnostics(out));
516
+ return { ...out, diagnostics: structuredDiagnostics(out) };
430
517
  }
431
518
  const checkSpec = {
432
519
  name: "check",
@@ -443,6 +530,7 @@ const checkSpec = {
443
530
  flags: [
444
531
  { name: "lints-only", field: "lintsOnly", type: "boolean" },
445
532
  { name: "json", field: "json", type: "boolean" },
533
+ { name: "sarif", field: "sarif", type: "boolean" },
446
534
  ],
447
535
  },
448
536
  async run(value, ctx) {
@@ -453,9 +541,11 @@ const checkSpec = {
453
541
  ...(value.maxDiagnostics === undefined ? {} : { maxDiagnostics: value.maxDiagnostics }),
454
542
  })).check();
455
543
  },
456
- render: (out) => renderCheckReport(out),
544
+ render: (out, input) => input?.sarif === true
545
+ ? JSON.stringify(checkJson(out, input), null, 2).split("\n")
546
+ : renderCheckReport(out),
457
547
  success: (out) => out.ok,
458
- json: (out) => checkJson(out),
548
+ json: (out, input) => checkJson(out, input),
459
549
  };
460
550
  const assureSpec = {
461
551
  name: "assure",
@@ -580,6 +670,8 @@ const manifestSpec = {
580
670
  output: output({ type: "object" }),
581
671
  transports: ["cli", "mcp"],
582
672
  stability: "stable",
673
+ // The signer is the operator's KMS/HSM callback; an agent signing its own manifest defeats it.
674
+ cliOnlyFields: ["sign"],
583
675
  argv: {
584
676
  positionals: ["action", "before", "after"],
585
677
  flags: [
@@ -704,7 +796,9 @@ const openApiSpec = {
704
796
  ],
705
797
  },
706
798
  async run(value, ctx) {
707
- const app = await loadAppFor(ctx);
799
+ const app = ctx.loadApp !== undefined
800
+ ? await ctx.loadApp()
801
+ : await import("./load.js").then(({ loadBackendApp }) => loadBackendApp(ctx.cwd));
708
802
  const { renderOpenApiWithTypes } = await import("./openapi-tool.js");
709
803
  const format = value.format ?? "json";
710
804
  return {
@@ -818,12 +912,13 @@ const fixSpec = {
818
912
  };
819
913
  const migrateSpec = {
820
914
  name: "migrate",
821
- summary: "Migrate safe static Tailwind className utilities to native StyleX props.",
915
+ summary: "Move an app onto the frontend/backend split (`migrate layout`), or migrate static Tailwind utilities to StyleX.",
822
916
  input: MIGRATE_SCHEMA,
823
917
  output: output({
824
918
  type: "object",
825
919
  properties: {
826
920
  ok: { type: "boolean" },
921
+ kind: { type: "string" },
827
922
  from: { type: "string" },
828
923
  to: { type: "string" },
829
924
  write: { type: "boolean" },
@@ -832,12 +927,16 @@ const migrateSpec = {
832
927
  written: { type: "array" },
833
928
  issues: { type: "array" },
834
929
  files: { type: "array" },
930
+ splits: { type: "array" },
931
+ moves: { type: "array" },
932
+ rewritten: { type: "array" },
835
933
  },
836
- required: ["ok", "from", "to", "write", "scanned", "changed", "written", "issues", "files"],
934
+ required: ["ok", "write", "issues"],
837
935
  }),
838
936
  transports: ["cli"],
839
937
  stability: "stable",
840
938
  argv: {
939
+ positionals: ["kind"],
841
940
  flags: [
842
941
  { name: "from", field: "from", type: "string" },
843
942
  { name: "to", field: "to", type: "string" },
@@ -847,27 +946,134 @@ const migrateSpec = {
847
946
  ],
848
947
  },
849
948
  async run(value, ctx) {
949
+ const dir = resolve(ctx.cwd, value.dir ?? ".");
950
+ const write = value.write === undefined ? {} : { write: value.write };
951
+ if (value.kind === "layout") {
952
+ const { migrateLayout } = await import("./migrate-layout.js");
953
+ return { kind: "layout", ...(await migrateLayout(dir, write)) };
954
+ }
850
955
  const { migrateTailwindToStylex } = await import("./stylex-migrate.js");
851
- return migrateTailwindToStylex(resolve(ctx.cwd, value.dir ?? "."), {
852
- ...(value.write === undefined ? {} : { write: value.write }),
853
- });
956
+ return migrateTailwindToStylex(dir, write);
854
957
  },
855
- render: (out) => [
856
- out.ok
857
- ? `✓ migrated ${out.changed.length} file${out.changed.length === 1 ? "" : "s"}`
858
- : `⚠ migrated ${out.changed.length} safe file${out.changed.length === 1 ? "" : "s"}; ${out.issues.length} manual issue${out.issues.length === 1 ? "" : "s"} remain`,
859
- ` scanned ${out.scanned} source file${out.scanned === 1 ? "" : "s"}`,
860
- ...(out.write
861
- ? [` wrote ${out.written.length} file${out.written.length === 1 ? "" : "s"}`]
862
- : out.changed.length > 0
863
- ? [" dry run: pass --write to apply the safe changes"]
958
+ render: (out) => {
959
+ if (out.kind === "layout") {
960
+ return [
961
+ out.ok
962
+ ? `✓ ${out.splits.length} route file${out.splits.length === 1 ? "" : "s"} split, ${out.moves.length} file${out.moves.length === 1 ? "" : "s"} moved`
963
+ : `⚠ ${out.issues.length} issue${out.issues.length === 1 ? "" : "s"} need a decision; everything else is planned`,
964
+ ...out.splits.map((split) => ` split ${split.file} → ${split.backend} (${split.moved.join(", ")})`),
965
+ ...out.moves.map((move) => ` move ${move.from} → ${move.to}`),
966
+ ...out.rewritten.map((file) => ` edit ${file} (imports)`),
967
+ ...out.issues.map((issue) => ` ⚠ ${issue.file}: ${issue.reason}`),
968
+ ...(out.write ? [] : [" dry run: pass --write to apply"]),
969
+ ];
970
+ }
971
+ return [
972
+ out.ok
973
+ ? `✓ migrated ${out.changed.length} file${out.changed.length === 1 ? "" : "s"}`
974
+ : `⚠ migrated ${out.changed.length} safe file${out.changed.length === 1 ? "" : "s"}; ${out.issues.length} manual issue${out.issues.length === 1 ? "" : "s"} remain`,
975
+ ` scanned ${out.scanned} source file${out.scanned === 1 ? "" : "s"}`,
976
+ ...(out.write
977
+ ? [` wrote ${out.written.length} file${out.written.length === 1 ? "" : "s"}`]
978
+ : out.changed.length > 0
979
+ ? [" dry run: pass --write to apply the safe changes"]
980
+ : []),
981
+ ...out.issues
982
+ .slice(0, 30)
983
+ .map((issue) => ` ${issue.file}:${issue.line} ${issue.token} — ${issue.reason}`),
984
+ ...(out.issues.length > 30
985
+ ? [` … ${out.issues.length - 30} more issue${out.issues.length - 30 === 1 ? "" : "s"}`]
864
986
  : []),
865
- ...out.issues
866
- .slice(0, 30)
867
- .map((issue) => ` ${issue.file}:${issue.line} ${issue.token} — ${issue.reason}`),
868
- ...(out.issues.length > 30
869
- ? [` … ${out.issues.length - 30} more issue${out.issues.length - 30 === 1 ? "" : "s"}`]
870
- : []),
987
+ ];
988
+ },
989
+ success: (out) => out.ok,
990
+ };
991
+ const targetSpec = {
992
+ name: "target",
993
+ summary: "Show the app's deploy target, or switch it (`nifra target cloudflare`): the `target` `nifra build` emits, kept in nifra.config.ts.",
994
+ input: TARGET_SCHEMA,
995
+ output: output({
996
+ type: "object",
997
+ properties: {
998
+ ok: { type: "boolean" },
999
+ target: { type: "string" },
1000
+ previous: { type: "string" },
1001
+ changed: { type: "boolean" },
1002
+ run: { type: "string" },
1003
+ },
1004
+ required: ["ok", "target", "changed", "run"],
1005
+ }),
1006
+ transports: ["cli"],
1007
+ stability: "stable",
1008
+ argv: {
1009
+ positionals: ["target"],
1010
+ flags: [
1011
+ { name: "json", field: "json", type: "boolean" },
1012
+ { name: "dir", field: "dir", type: "string" },
1013
+ ],
1014
+ },
1015
+ async run(value, ctx) {
1016
+ const dir = resolve(ctx.cwd, value.dir ?? ".");
1017
+ const { parseBuildTarget, planBuildTarget } = await import("@nifrajs/web/build");
1018
+ const { readConfigTarget, writeConfigTarget } = await import("./config-target.js");
1019
+ const current = readConfigTarget(dir);
1020
+ const target = parseBuildTarget(value.target ?? current ?? "bun", value.target === undefined ? "target in nifra.config.ts" : "target");
1021
+ const changed = value.target !== undefined && value.target !== current;
1022
+ if (changed)
1023
+ writeConfigTarget(dir, target);
1024
+ return {
1025
+ ok: true,
1026
+ target,
1027
+ ...(changed && current !== undefined ? { previous: current } : {}),
1028
+ changed,
1029
+ run: planBuildTarget(target, "dist").run,
1030
+ };
1031
+ },
1032
+ render: (out) => [
1033
+ out.changed
1034
+ ? `✓ target: ${out.target}${out.previous === undefined ? "" : ` (was ${out.previous})`}`
1035
+ : `target: ${out.target}`,
1036
+ ` nifra build → ${out.run}`,
1037
+ ],
1038
+ success: (out) => out.ok,
1039
+ };
1040
+ const typesSpec = {
1041
+ name: "types",
1042
+ summary: "Generate each route's `./+types` module (params, schema-typed data, typed `api`); `--check` fails when one is stale.",
1043
+ input: TYPES_SCHEMA,
1044
+ output: output({
1045
+ type: "object",
1046
+ properties: {
1047
+ ok: { type: "boolean" },
1048
+ check: { type: "boolean" },
1049
+ written: { type: "array" },
1050
+ removed: { type: "array" },
1051
+ stale: { type: "array" },
1052
+ tsconfig: { type: "string" },
1053
+ },
1054
+ required: ["ok", "check", "written", "removed", "stale"],
1055
+ }),
1056
+ transports: ["cli"],
1057
+ stability: "stable",
1058
+ argv: {
1059
+ flags: [
1060
+ { name: "check", field: "check", type: "boolean" },
1061
+ { name: "json", field: "json", type: "boolean" },
1062
+ { name: "dir", field: "dir", type: "string" },
1063
+ ],
1064
+ },
1065
+ async run(value, ctx) {
1066
+ const { routeTypes } = await import("./route-types.js");
1067
+ return routeTypes(resolve(ctx.cwd, value.dir ?? "."), { check: value.check === true });
1068
+ },
1069
+ render: (out) => [
1070
+ out.check
1071
+ ? out.ok
1072
+ ? "✓ route types are up to date"
1073
+ : `✖ ${out.stale.length} route type file${out.stale.length === 1 ? " is" : "s are"} stale - run \`nifra types\``
1074
+ : `✓ route types: ${out.written.length} written, ${out.removed.length} removed`,
1075
+ ...out.stale.map((file) => ` stale ${file}`),
1076
+ ...(out.tsconfig === undefined ? [] : [` ⚠ ${out.tsconfig}`]),
871
1077
  ],
872
1078
  success: (out) => out.ok,
873
1079
  };
@@ -1048,6 +1254,28 @@ const replaySpec = {
1048
1254
  render: (out) => [JSON.stringify(out, null, 2)],
1049
1255
  success: (out) => out.ok,
1050
1256
  };
1257
+ const smokeSpec = {
1258
+ name: "smoke",
1259
+ summary: "Run the declared production SSR, mounted-API, 404, header, auth, contract, and hydration smoke checks.",
1260
+ input: SMOKE_SCHEMA,
1261
+ output: output({ type: "object" }),
1262
+ transports: ["cli", "mcp"],
1263
+ stability: "stable",
1264
+ argv: {
1265
+ flags: [
1266
+ { name: "fixture", field: "fixture", type: "string" },
1267
+ { name: "in-process", field: "inProcess", type: "boolean" },
1268
+ { name: "json", field: "json", type: "boolean" },
1269
+ ],
1270
+ },
1271
+ async run(value, ctx) {
1272
+ const { loadSmokeFixture, runSmoke } = await import("./smoke.js");
1273
+ const fixture = await loadSmokeFixture(ctx.cwd, value.fixture);
1274
+ return runSmoke(fixture, { inProcess: value.inProcess === true });
1275
+ },
1276
+ render: (out) => out.checks.map((check) => `${check.status === "pass" ? "✓" : check.status === "skip" ? "•" : "✖"} ${check.id}${check.message === undefined ? "" : ` - ${check.message}`}`),
1277
+ success: (out) => out.ok,
1278
+ };
1051
1279
  const portSpec = {
1052
1280
  name: "port",
1053
1281
  summary: "Print a feature by deploy-target portability matrix with file:line evidence and gate against an unsupported target.",
@@ -1076,7 +1304,7 @@ const portSpec = {
1076
1304
  return [
1077
1305
  report,
1078
1306
  "",
1079
- "[nifra] --ci needs a deploy target to gate against, and none was detected. Pass --target <bun|node|deno|cf-pages|vercel>.",
1307
+ "[nifra] --ci needs a deploy target to gate against, and none was detected. Pass --target <bun|node|deno|cloudflare|vercel>.",
1080
1308
  ];
1081
1309
  return [report];
1082
1310
  },
@@ -1090,8 +1318,34 @@ const portSpec = {
1090
1318
  },
1091
1319
  json: (out) => out.json,
1092
1320
  };
1321
+ // CLI only: it imports app code (the catalogs), which a long-lived MCP server process would cache.
1322
+ const i18nSpec = {
1323
+ name: "i18n",
1324
+ summary: "Check i18n catalogs (imports the module exporting `locales` and `catalogs`): coverage, missing and unused keys, ICU syntax, placeholder and tag parity, plural cases, script purity, untranslated messages.",
1325
+ input: I18N_SCHEMA,
1326
+ output: output({ type: "object" }),
1327
+ transports: ["cli"],
1328
+ stability: "stable",
1329
+ argv: {
1330
+ positionals: ["action", "entry"],
1331
+ flags: [
1332
+ { name: "json", field: "json", type: "boolean" },
1333
+ { name: "strict", field: "strict", type: "boolean" },
1334
+ ],
1335
+ },
1336
+ async run(value, ctx) {
1337
+ const { runI18nCheck } = await import("./i18n-check.js");
1338
+ return runI18nCheck(value.dir === undefined ? ctx.cwd : resolve(ctx.cwd, value.dir), {
1339
+ entry: value.entry,
1340
+ });
1341
+ },
1342
+ render: (out) => renderI18nCheck(out),
1343
+ success: (out, input) => i18nCheckPassed(out, input.strict === true),
1344
+ json: (out) => out,
1345
+ };
1093
1346
  export const commandSpecs = Object.freeze([
1094
1347
  checkSpec,
1348
+ reviewSpec,
1095
1349
  assureSpec,
1096
1350
  levelsSpec,
1097
1351
  capabilitiesSpec,
@@ -1102,6 +1356,8 @@ export const commandSpecs = Object.freeze([
1102
1356
  doctorSpec,
1103
1357
  fixSpec,
1104
1358
  migrateSpec,
1359
+ targetSpec,
1360
+ typesSpec,
1105
1361
  snapshotSpec,
1106
1362
  diffSpec,
1107
1363
  contractsSpec,
@@ -1109,7 +1365,16 @@ export const commandSpecs = Object.freeze([
1109
1365
  syncRoutesSpec,
1110
1366
  proveSpec,
1111
1367
  replaySpec,
1368
+ smokeSpec,
1112
1369
  portSpec,
1370
+ i18nSpec,
1371
+ errorsSpec,
1372
+ logsSpec,
1373
+ cdnCheckSpec,
1374
+ dbSchemaSpec,
1375
+ dbQuerySpec,
1376
+ dbRoleSpec,
1377
+ dbAuditSpec,
1113
1378
  ]);
1114
1379
  const commandByName = new Map(commandSpecs.map((spec) => [spec.name, spec]));
1115
1380
  export function findCommandSpec(name) {