@warlock.js/core 5.34.3 → 5.36.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 (281) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/esm/cli/cli-commands.manager.mjs +1 -1
  3. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  4. package/esm/cli/commands/add.command.mjs +1 -0
  5. package/esm/cli/commands/add.command.mjs.map +1 -1
  6. package/esm/cli/commands/build.command.mjs +30 -3
  7. package/esm/cli/commands/build.command.mjs.map +1 -1
  8. package/esm/cli/commands/check/check-command.action.mjs +1 -0
  9. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  10. package/esm/cli/commands/check.command.mjs +5 -0
  11. package/esm/cli/commands/check.command.mjs.map +1 -1
  12. package/esm/cli/commands/explain/explain-command.action.mjs +21 -0
  13. package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -1
  14. package/esm/cli/commands/explain.command.mjs +14 -4
  15. package/esm/cli/commands/explain.command.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generate.command.mjs +19 -1
  17. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/endpoint.generator.mjs +1 -1
  19. package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generators/web.generator.mjs +169 -0
  21. package/esm/cli/commands/generate/generators/web.generator.mjs.map +1 -0
  22. package/esm/cli/commands/generate/templates/web-stubs.mjs +231 -0
  23. package/esm/cli/commands/generate/templates/web-stubs.mjs.map +1 -0
  24. package/esm/cli/commands/inspect/inspect-command.action.mjs +4 -3
  25. package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -1
  26. package/esm/cli/commands/inspect.command.mjs +13 -8
  27. package/esm/cli/commands/inspect.command.mjs.map +1 -1
  28. package/esm/cli/commands/mcp/mcp-command.action.mjs +1 -1
  29. package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -1
  30. package/esm/config/config-loader.d.mts.map +1 -1
  31. package/esm/config/config-loader.mjs +2 -0
  32. package/esm/config/config-loader.mjs.map +1 -1
  33. package/esm/config/load-config-files.mjs +4 -0
  34. package/esm/config/load-config-files.mjs.map +1 -1
  35. package/esm/dev-server/file-event-handler.mjs +11 -1
  36. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  37. package/esm/dev-server/file-manager.d.mts +1 -1
  38. package/esm/dev-server/file-manager.d.mts.map +1 -1
  39. package/esm/dev-server/file-manager.mjs.map +1 -1
  40. package/esm/dev-server/files-orchestrator.mjs +16 -1
  41. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  42. package/esm/dev-server/files-watcher.mjs +13 -4
  43. package/esm/dev-server/files-watcher.mjs.map +1 -1
  44. package/esm/dev-server/loader/loader-port-acks.mjs +21 -9
  45. package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
  46. package/esm/dev-server/loader/register-loader.mjs +31 -12
  47. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  48. package/esm/dev-server/module-loader.mjs +15 -1
  49. package/esm/dev-server/module-loader.mjs.map +1 -1
  50. package/esm/dev-server/parse-imports.mjs +1 -1
  51. package/esm/dev-server/special-file-patterns.mjs +4 -1
  52. package/esm/dev-server/special-file-patterns.mjs.map +1 -1
  53. package/esm/dev-server/type-generator.mjs +1 -1
  54. package/esm/e2e/cdp.d.mts.map +1 -1
  55. package/esm/e2e/cdp.mjs +9 -2
  56. package/esm/e2e/cdp.mjs.map +1 -1
  57. package/esm/e2e/index.d.mts +2 -2
  58. package/esm/e2e/page.d.mts +14 -1
  59. package/esm/e2e/page.d.mts.map +1 -1
  60. package/esm/e2e/page.mjs +81 -8
  61. package/esm/e2e/page.mjs.map +1 -1
  62. package/esm/e2e/types.d.mts +49 -0
  63. package/esm/e2e/types.d.mts.map +1 -1
  64. package/esm/e2e/types.mjs.map +1 -1
  65. package/esm/errors/error-codes.d.mts +72 -0
  66. package/esm/errors/error-codes.d.mts.map +1 -1
  67. package/esm/errors/error-codes.mjs +72 -0
  68. package/esm/errors/error-codes.mjs.map +1 -1
  69. package/esm/errors/index.d.mts +2 -2
  70. package/esm/errors/index.mjs +2 -2
  71. package/esm/generations/add-command.action.mjs +2 -0
  72. package/esm/generations/add-command.action.mjs.map +1 -1
  73. package/esm/generations/design-system-payload.mjs +53 -0
  74. package/esm/generations/design-system-payload.mjs.map +1 -0
  75. package/esm/generations/features/design-system.feature.mjs +38 -0
  76. package/esm/generations/features/design-system.feature.mjs.map +1 -0
  77. package/esm/generations/features/index.mjs +3 -1
  78. package/esm/generations/features/index.mjs.map +1 -1
  79. package/esm/generations/features/notifications.feature.mjs +13 -2
  80. package/esm/generations/features/notifications.feature.mjs.map +1 -1
  81. package/esm/generations/features/test.feature.mjs +48 -13
  82. package/esm/generations/features/test.feature.mjs.map +1 -1
  83. package/esm/generations/features/web.feature.mjs +3 -3
  84. package/esm/generations/features/web.feature.mjs.map +1 -1
  85. package/esm/generations/stubs.mjs +9 -5
  86. package/esm/generations/stubs.mjs.map +1 -1
  87. package/esm/http/error-mapper.d.mts +1 -1
  88. package/esm/http/error-mapper.mjs +3 -3
  89. package/esm/http/error-mapper.mjs.map +1 -1
  90. package/esm/http/error-response.d.mts +3 -0
  91. package/esm/http/error-response.d.mts.map +1 -1
  92. package/esm/http/error-response.mjs +8 -2
  93. package/esm/http/error-response.mjs.map +1 -1
  94. package/esm/http/errors/errors.d.mts +8 -0
  95. package/esm/http/errors/errors.d.mts.map +1 -1
  96. package/esm/http/errors/errors.mjs +13 -1
  97. package/esm/http/errors/errors.mjs.map +1 -1
  98. package/esm/http/index.d.mts +3 -2
  99. package/esm/http/index.mjs +3 -2
  100. package/esm/http/list-endpoint.d.mts +35 -0
  101. package/esm/http/list-endpoint.d.mts.map +1 -0
  102. package/esm/http/list-endpoint.mjs +28 -0
  103. package/esm/http/list-endpoint.mjs.map +1 -0
  104. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  105. package/esm/http/middleware/inject-request-context.mjs +12 -2
  106. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  107. package/esm/http/server.mjs +1 -1
  108. package/esm/index.d.mts +10 -6
  109. package/esm/index.mjs +5 -2
  110. package/esm/localization/index.mjs +3 -1
  111. package/esm/localization/translation-usage-graph.d.mts +49 -0
  112. package/esm/localization/translation-usage-graph.d.mts.map +1 -0
  113. package/esm/localization/translation-usage-graph.mjs +128 -0
  114. package/esm/localization/translation-usage-graph.mjs.map +1 -0
  115. package/esm/localization/translation-usage.d.mts +85 -0
  116. package/esm/localization/translation-usage.d.mts.map +1 -0
  117. package/esm/localization/translation-usage.mjs +307 -0
  118. package/esm/localization/translation-usage.mjs.map +1 -0
  119. package/esm/openapi/security.mjs +12 -4
  120. package/esm/openapi/security.mjs.map +1 -1
  121. package/esm/openapi/types.d.mts +2 -0
  122. package/esm/openapi/types.d.mts.map +1 -1
  123. package/esm/production/build-budgets.d.mts +64 -0
  124. package/esm/production/build-budgets.d.mts.map +1 -0
  125. package/esm/production/build-budgets.mjs +109 -0
  126. package/esm/production/build-budgets.mjs.map +1 -0
  127. package/esm/production/production-builder.mjs +13 -1
  128. package/esm/production/production-builder.mjs.map +1 -1
  129. package/esm/production/resolve-build-config.d.mts +1 -1
  130. package/esm/production/resolve-build-config.d.mts.map +1 -1
  131. package/esm/production/resolve-build-config.mjs.map +1 -1
  132. package/esm/production/route-registration-snapshot.mjs +5 -3
  133. package/esm/production/route-registration-snapshot.mjs.map +1 -1
  134. package/esm/restful/restful.d.mts +1 -1
  135. package/esm/router/router.d.mts +2 -0
  136. package/esm/router/router.d.mts.map +1 -1
  137. package/esm/router/router.mjs +4 -1
  138. package/esm/router/router.mjs.map +1 -1
  139. package/esm/router/types.d.mts +19 -0
  140. package/esm/router/types.d.mts.map +1 -1
  141. package/esm/tests/fakes/storage-fake.d.mts +3 -1
  142. package/esm/tests/fakes/storage-fake.d.mts.map +1 -1
  143. package/esm/tests/fakes/storage-fake.mjs +3 -3
  144. package/esm/tests/fakes/storage-fake.mjs.map +1 -1
  145. package/esm/tests/fakes/test-fakes.d.mts +3 -1
  146. package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
  147. package/esm/tests/fakes/test-fakes.mjs +3 -2
  148. package/esm/tests/fakes/test-fakes.mjs.map +1 -1
  149. package/esm/tests/index.d.mts +4 -2
  150. package/esm/tests/index.mjs +3 -1
  151. package/esm/tests/load-unit-config.mjs +24 -0
  152. package/esm/tests/load-unit-config.mjs.map +1 -0
  153. package/esm/tests/response-contract.d.mts +32 -0
  154. package/esm/tests/response-contract.d.mts.map +1 -0
  155. package/esm/tests/response-contract.mjs +116 -0
  156. package/esm/tests/response-contract.mjs.map +1 -0
  157. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  158. package/esm/tests/start-http-development-server.mjs +10 -2
  159. package/esm/tests/start-http-development-server.mjs.map +1 -1
  160. package/esm/tests/test-lifecycle-state.mjs.map +1 -1
  161. package/esm/tests/test-locales.mjs +33 -0
  162. package/esm/tests/test-locales.mjs.map +1 -0
  163. package/esm/tests/test-runtime-scope.d.mts +15 -0
  164. package/esm/tests/test-runtime-scope.d.mts.map +1 -0
  165. package/esm/tests/test-runtime-scope.mjs +80 -0
  166. package/esm/tests/test-runtime-scope.mjs.map +1 -0
  167. package/esm/tests/vitest-setup.d.mts +13 -3
  168. package/esm/tests/vitest-setup.d.mts.map +1 -1
  169. package/esm/tests/vitest-setup.mjs +146 -18
  170. package/esm/tests/vitest-setup.mjs.map +1 -1
  171. package/esm/tooling/check/affected-files.d.mts +5 -0
  172. package/esm/tooling/check/affected-files.d.mts.map +1 -0
  173. package/esm/tooling/check/affected-files.mjs +56 -0
  174. package/esm/tooling/check/affected-files.mjs.map +1 -0
  175. package/esm/tooling/check/api-route-references.d.mts +16 -0
  176. package/esm/tooling/check/api-route-references.d.mts.map +1 -0
  177. package/esm/tooling/check/api-route-references.mjs +146 -0
  178. package/esm/tooling/check/api-route-references.mjs.map +1 -0
  179. package/esm/tooling/check/changed-files.d.mts +2 -0
  180. package/esm/tooling/check/changed-files.d.mts.map +1 -1
  181. package/esm/tooling/check/changed-files.mjs +2 -2
  182. package/esm/tooling/check/changed-files.mjs.map +1 -1
  183. package/esm/tooling/check/conventions-step.d.mts +7 -3
  184. package/esm/tooling/check/conventions-step.d.mts.map +1 -1
  185. package/esm/tooling/check/conventions-step.mjs +59 -8
  186. package/esm/tooling/check/conventions-step.mjs.map +1 -1
  187. package/esm/tooling/check/fixes.d.mts +8 -0
  188. package/esm/tooling/check/fixes.d.mts.map +1 -0
  189. package/esm/tooling/check/fixes.mjs +31 -0
  190. package/esm/tooling/check/fixes.mjs.map +1 -0
  191. package/esm/tooling/check/index.d.mts +12 -0
  192. package/esm/tooling/check/index.mjs +3 -2
  193. package/esm/tooling/check/run-check.d.mts +2 -0
  194. package/esm/tooling/check/run-check.d.mts.map +1 -1
  195. package/esm/tooling/check/run-check.mjs +23 -5
  196. package/esm/tooling/check/run-check.mjs.map +1 -1
  197. package/esm/tooling/check/run-tests.mjs +1 -1
  198. package/esm/tooling/check/source-ast.d.mts +1 -0
  199. package/esm/tooling/check/translation-conventions.mjs +32 -0
  200. package/esm/tooling/check/translation-conventions.mjs.map +1 -0
  201. package/esm/tooling/check/web-conventions.d.mts +10 -0
  202. package/esm/tooling/check/web-conventions.d.mts.map +1 -0
  203. package/esm/tooling/check/web-conventions.mjs +126 -0
  204. package/esm/tooling/check/web-conventions.mjs.map +1 -0
  205. package/esm/tooling/index.d.mts +7 -2
  206. package/esm/tooling/index.mjs +4 -2
  207. package/esm/tooling/inspect/collect-files.mjs +172 -0
  208. package/esm/tooling/inspect/collect-files.mjs.map +1 -0
  209. package/esm/tooling/inspect/collect-pages.mjs +3 -2
  210. package/esm/tooling/inspect/collect-pages.mjs.map +1 -1
  211. package/esm/tooling/inspect/collect-routes.mjs +1 -1
  212. package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
  213. package/esm/tooling/inspect/explain-file.d.mts +12 -0
  214. package/esm/tooling/inspect/explain-file.d.mts.map +1 -0
  215. package/esm/tooling/inspect/explain-file.mjs +45 -0
  216. package/esm/tooling/inspect/explain-file.mjs.map +1 -0
  217. package/esm/tooling/inspect/index.d.mts +3 -2
  218. package/esm/tooling/inspect/index.mjs +2 -1
  219. package/esm/tooling/inspect/inspect-app.d.mts.map +1 -1
  220. package/esm/tooling/inspect/inspect-app.mjs +9 -3
  221. package/esm/tooling/inspect/inspect-app.mjs.map +1 -1
  222. package/esm/tooling/inspect/inspect-report.schema.mjs +34 -1
  223. package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -1
  224. package/esm/tooling/inspect/inspect-report.types.d.mts +17 -1
  225. package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -1
  226. package/esm/tooling/inspect/inspect-report.types.mjs +2 -1
  227. package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -1
  228. package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -1
  229. package/esm/tooling/mcp/mcp-tools.mjs +8 -2
  230. package/esm/tooling/mcp/mcp-tools.mjs.map +1 -1
  231. package/esm/tooling/problem.d.mts +4 -0
  232. package/esm/tooling/problem.d.mts.map +1 -1
  233. package/esm/tooling/problem.mjs +1 -0
  234. package/esm/tooling/problem.mjs.map +1 -1
  235. package/esm/tooling/syntax/ast.d.mts +2 -0
  236. package/esm/tooling/typecheck/run-typecheck.mjs +5 -1
  237. package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -1
  238. package/esm/upgrade/codemods/index.mjs +2 -0
  239. package/esm/upgrade/codemods/index.mjs.map +1 -1
  240. package/esm/upgrade/codemods/vite-vitest.mjs +170 -0
  241. package/esm/upgrade/codemods/vite-vitest.mjs.map +1 -0
  242. package/esm/upgrade/notes.mjs +4 -0
  243. package/esm/upgrade/notes.mjs.map +1 -1
  244. package/esm/vite/index.d.mts +3 -1
  245. package/esm/vite/index.mjs +3 -1
  246. package/esm/vite/test-setup-reporter.d.mts +16 -0
  247. package/esm/vite/test-setup-reporter.d.mts.map +1 -0
  248. package/esm/vite/test-setup-reporter.mjs +55 -0
  249. package/esm/vite/test-setup-reporter.mjs.map +1 -0
  250. package/esm/vite/test-workers.d.mts +5 -0
  251. package/esm/vite/test-workers.d.mts.map +1 -0
  252. package/esm/vite/test-workers.mjs +38 -0
  253. package/esm/vite/test-workers.mjs.map +1 -0
  254. package/esm/warlock-config/index.d.mts +2 -1
  255. package/esm/warlock-config/types.d.mts +3 -0
  256. package/esm/warlock-config/types.d.mts.map +1 -1
  257. package/llms-full.txt +874 -9
  258. package/llms.txt +2 -0
  259. package/package.json +15 -14
  260. package/schemas/inspect-report.schema.json +47 -1
  261. package/skills/build-budgets/SKILL.md +51 -0
  262. package/skills/build-restful/SKILL.md +56 -0
  263. package/skills/configure-app/SKILL.md +7 -0
  264. package/skills/create-module/SKILL.md +9 -0
  265. package/skills/generate-web-ui/SKILL.md +71 -0
  266. package/skills/index.md +2 -2
  267. package/skills/register-route/SKILL.md +13 -0
  268. package/skills/run-app/SKILL.md +1 -1
  269. package/skills/send-mail/SKILL.md +25 -0
  270. package/skills/send-response/SKILL.md +50 -0
  271. package/skills/test-browser/SKILL.md +53 -0
  272. package/skills/test-http/SKILL.md +91 -0
  273. package/skills/test-service/SKILL.md +161 -3
  274. package/skills/upgrade-from-5-29/SKILL.md +109 -0
  275. package/skills/use-localization/SKILL.md +25 -0
  276. package/skills/warlock-check/SKILL.md +75 -1
  277. package/skills/warlock-inspect/SKILL.md +45 -2
  278. package/skills/warlock-mcp/SKILL.md +4 -0
  279. package/skills/write-cli-command/SKILL.md +18 -0
  280. package/esm/production/build-app-production.mjs +0 -14
  281. package/esm/production/build-app-production.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/mcp/mcp-tools.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n explainErrorCode,\n isWarlockErrorCode,\n} from \"../../errors/error-codes\";\nimport { WarlockError } from \"../../errors/warlock-error\";\nimport { CHECK_STEPS, type CheckReport, type CheckStepName } from \"../check/check-report.types\";\nimport type { TestRunReport } from \"../check/run-tests\";\nimport type {\n DevtoolsClient,\n DevtoolsRequestRecord,\n DevtoolsRequestSummaryRecord,\n} from \"../devtools/devtools-client\";\nimport { type GeneratorSpec, isMakeGenerator, MAKE_GENERATORS } from \"../generate\";\nimport {\n INSPECT_SECTIONS,\n type InspectReport,\n type InspectRoute,\n type InspectSection,\n} from \"../inspect/inspect-report.types\";\nimport { buildDbSchema } from \"./db-schema\";\nimport { type DocsCorpus, readDocsFile, searchDocs } from \"./docs-search\";\nimport { inputSchema, type McpInputSchema, type McpInputProperty } from \"./tool-input\";\n\n/**\n * What the `warlock mcp` tools run against. The CLI fills it from a read-only\n * boot of the app; `warlock dev --mcp` fills it from the live dev process;\n * tests pass fakes.\n */\nexport type WarlockMcpContext = {\n /** Project root. */\n cwd: string;\n /** Whether tools may write project files (`--allow-writes`). */\n allowWrites: boolean;\n /** Describe the app (see `inspectApp`). */\n inspect(sections: readonly InspectSection[]): Promise<InspectReport>;\n /** Run `warlock check`. */\n check(options: {\n changed?: boolean;\n base?: string;\n skip?: readonly CheckStepName[];\n }): Promise<CheckReport>;\n /** Run tests for files, the changed files, or the suite. */\n tests(options: {\n files?: readonly string[];\n changed?: boolean;\n base?: string;\n }): Promise<TestRunReport>;\n /** Connect to the running dev server's devtools API (throws WL8008 / WL8009). */\n devtools(): Promise<DevtoolsClient>;\n /** Preview (and, with `allowWrites`, apply) a `make` / `add` generator. */\n generation: {\n plan(spec: GeneratorSpec): Promise<unknown>;\n apply(spec: GeneratorSpec): Promise<unknown>;\n };\n /** The installed docs corpus (cached by the caller). */\n docs(): Promise<DocsCorpus>;\n /** The `warlock add` feature names, advertised as the `generate_add` enum. */\n features?: readonly string[];\n};\n\n/** One tool the server exposes. */\nexport type WarlockMcpTool = {\n /** Underscored, never dotted: model APIs refuse dots in tool names (`^[a-zA-Z0-9_-]{1,128}$`). */\n name: string;\n description: string;\n input: McpInputSchema;\n /** Whether a call can change project files. Only exposed with `allowWrites`. */\n writes: boolean;\n /** A check run before validation; return a message to refuse the call. */\n precheck?: (input: Record<string, unknown>) => string | null;\n run(input: Record<string, unknown>): Promise<unknown>;\n};\n\n/** The `make` generators the `generate_make` tool can run (`warlock generate.<name>`). */\nexport const MCP_MAKE_GENERATORS = MAKE_GENERATORS;\n\nfunction stringArg(input: Record<string, unknown>, key: string): string | undefined {\n const value = input[key];\n\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction booleanArg(input: Record<string, unknown>, key: string): boolean {\n return input[key] === true;\n}\n\nfunction integerArg(input: Record<string, unknown>, key: string, fallback: number): number {\n const value = input[key];\n\n return typeof value === \"number\" && Number.isInteger(value) ? value : fallback;\n}\n\nfunction stringsArg(input: Record<string, unknown>, key: string): string[] | undefined {\n const value = input[key];\n\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : undefined;\n}\n\nfunction includesText(values: readonly (string | null | undefined)[], needle: string): boolean {\n const lowered = needle.toLowerCase();\n\n return values.some((value) => typeof value === \"string\" && value.toLowerCase().includes(lowered));\n}\n\nconst MUTATING = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\nfunction filterRoutes(\n routes: readonly InspectRoute[],\n input: Record<string, unknown>,\n): InspectRoute[] {\n const filter = stringArg(input, \"filter\");\n const method = stringArg(input, \"method\")?.toUpperCase();\n const unguarded = booleanArg(input, \"unguardedWrites\");\n\n return routes.filter(\n (route) =>\n (!filter ||\n includesText([route.path, route.name, route.handler, route.sourceFile], filter)) &&\n (!method || route.method === method) &&\n (!unguarded || (MUTATING.has(route.method) && !route.auth.required && !route.isPage)),\n );\n}\n\n/** The `filter` match of one entry of a section, by the fields an agent would search. */\nfunction sectionEntryMatches(section: InspectSection, entry: unknown, filter: string): boolean {\n if (typeof entry !== \"object\" || entry === null) return false;\n\n const fields: Record<InspectSection, readonly string[]> = {\n routes: [\"path\", \"name\", \"handler\", \"sourceFile\"],\n models: [\"name\", \"table\", \"sourceFile\"],\n resources: [\"name\"],\n config: [\"key\", \"file\"],\n events: [\"event\"],\n pages: [\"path\", \"name\", \"file\"],\n jobs: [\"name\", \"queue\"],\n schedules: [\"name\"],\n packages: [\"name\"],\n };\n\n return includesText(\n fields[section].map((field) => {\n const value = Reflect.get(entry, field);\n return typeof value === \"string\" ? value : null;\n }),\n filter,\n );\n}\n\nconst SECTION_DESCRIPTIONS: Record<InspectSection, string> = {\n routes:\n \"Registered HTTP routes from the booted router: method, path, name, source file, handler, middleware chain in run order, auth guard, CSRF, rate limit, logging policy, request JSON Schemas and declared responses ($refs resolve against `components`).\",\n models:\n \"Cascade model classes: table, primary key, data source, columns (from the model's Seal schema), relations, resource, delete strategy and timestamp columns.\",\n resources:\n \"Resources reached from route responses or models, with their output shape as JSON Schema and the models that use them.\",\n config:\n \"Every loaded config key as a dotted path with its TYPE (never its value) and the src/config file it came from.\",\n events:\n \"Event listeners app modules registered while loading, grouped by event, with the registering file.\",\n pages:\n \"Pages @warlock.js/web installed: path, name, file, layout chain, loaders, actions and guard.\",\n jobs: \"Queue jobs defined with defineJob() from @warlock.js/queue: name, queue, attempts, backoff, retention.\",\n schedules:\n \"Scheduled tasks from @warlock.js/scheduler: name, cron expression, intervals, next run.\",\n packages: \"Installed @warlock.js/* packages and their versions.\",\n};\n\nfunction inspectSectionTool(context: WarlockMcpContext, section: InspectSection): WarlockMcpTool {\n const properties: Record<string, McpInputProperty> = {\n filter: {\n type: \"string\",\n description:\n \"Case-insensitive substring to keep only matching entries (names, paths, keys, files).\",\n },\n };\n\n if (section === \"routes\") {\n properties.method = {\n type: \"string\",\n description: \"Only routes with this HTTP method (GET, POST, …).\",\n };\n properties.unguardedWrites = {\n type: \"boolean\",\n description: \"Only POST/PUT/PATCH/DELETE API routes that have no auth guard.\",\n };\n }\n\n return {\n name: `inspect_${section}`,\n description: `${SECTION_DESCRIPTIONS[section]} Read from the app's registries (the same data as \\`warlock inspect --json --section ${section}\\`), not from source text. Read-only.`,\n input: inputSchema(properties),\n writes: false,\n async run(input) {\n const report = await context.inspect([section]);\n const entries: unknown[] = report[section] ?? [];\n const filter = stringArg(input, \"filter\");\n const kept =\n section === \"routes\" && report.routes\n ? filterRoutes(report.routes, input)\n : filter\n ? entries.filter((entry) => sectionEntryMatches(section, entry, filter))\n : entries;\n const withSchemas = section === \"routes\" || section === \"models\" || section === \"resources\";\n\n return {\n schemaVersion: report.schemaVersion,\n [section]: kept,\n total: entries.length,\n ...(withSchemas ? { components: report.components } : {}),\n warnings: report.warnings,\n };\n },\n };\n}\n\nfunction summarizeRequest(summary: DevtoolsRequestSummaryRecord, record?: DevtoolsRequestRecord) {\n if (!record) return summary;\n\n const nPlusOneShapes = new Set(\n record.warnings\n .filter((warning) => warning.type === \"n+1\" && typeof warning.shape === \"string\")\n .map((warning) => warning.shape),\n );\n\n return {\n ...summary,\n error: record.error ?? null,\n phases: record.phases,\n queries: record.queries.map((query) => ({\n ...query,\n nPlusOne: typeof query.shape === \"string\" && nPlusOneShapes.has(query.shape),\n })),\n warnings: record.warnings,\n logs: record.logs,\n cache: record.cache,\n mails: record.mails,\n timings: {\n totalMs: record.durationMs ?? null,\n phasesMs: record.phases.reduce<Record<string, number>>((totals, phase) => {\n totals[phase.name] = (totals[phase.name] ?? 0) + phase.durationMs;\n return totals;\n }, {}),\n queriesMs: record.queries.reduce(\n (total, query) => total + (typeof query.durationMs === \"number\" ? query.durationMs : 0),\n 0,\n ),\n },\n };\n}\n\nfunction writeProperty(): Record<string, McpInputProperty> {\n return {\n write: {\n type: \"boolean\",\n description:\n \"Write the files into the project. Default false: only return the diff (the generator runs in a throw-away copy of the project).\",\n },\n };\n}\n\nfunction refuseWrite(context: WarlockMcpContext) {\n return (input: Record<string, unknown>): string | null =>\n input.write === true && !context.allowWrites\n ? `WL7005: this MCP server is read-only; restart it with --allow-writes to let generate tools write. Without it they return the diff only.`\n : null;\n}\n\nconst MAKE_FLAGS: Record<string, McpInputProperty> = {\n withValidation: {\n type: \"boolean\",\n description: \"controller: also generate a validation schema and bind it (--with-validation).\",\n },\n withResource: {\n type: \"boolean\",\n description: \"model: also generate a resource (--with-resource).\",\n },\n table: { type: \"string\", description: \"model: the table name (--table).\" },\n minimal: {\n type: \"boolean\",\n description: \"module: a minimal skeleton instead of the full CRUD scaffold (--minimal).\",\n },\n noTimestamps: {\n type: \"boolean\",\n description: \"model / migration: no timestamp columns (--no-timestamps).\",\n },\n add: {\n type: \"string\",\n description: \"migration: columns to add, DSL name:type:modifier,… (--add).\",\n },\n drop: { type: \"string\", description: \"migration: comma-separated columns to drop (--drop).\" },\n rename: { type: \"string\", description: \"migration: columns to rename, old:new,… (--rename).\" },\n force: { type: \"boolean\", description: \"Overwrite files that already exist (--force).\" },\n overwrite: {\n type: \"boolean\",\n description:\n \"module / model: with force, also replace files that differ from the output (--overwrite).\",\n },\n};\n\nconst FLAG_NAMES: Record<string, string> = {\n withValidation: \"with-validation\",\n withResource: \"with-resource\",\n table: \"table\",\n minimal: \"minimal\",\n noTimestamps: \"no-timestamps\",\n add: \"add\",\n drop: \"drop\",\n rename: \"rename\",\n force: \"force\",\n overwrite: \"overwrite\",\n};\n\n/** The generator spec a `generate_make` call describes. */\nexport function makeSpecFromInput(input: Record<string, unknown>): GeneratorSpec {\n const generator = stringArg(input, \"generator\");\n const name = stringArg(input, \"name\");\n\n if (!generator || !isMakeGenerator(generator) || !name) {\n throw new Error(\n `generate_make needs a generator (${MCP_MAKE_GENERATORS.join(\", \")}) and a name.`,\n );\n }\n\n const flags: Record<string, string | boolean> = {};\n\n for (const [key, flag] of Object.entries(FLAG_NAMES)) {\n const value = input[key];\n\n if (value === true) flags[flag] = true;\n if (typeof value === \"string\" && value.length > 0) flags[flag] = value;\n }\n\n return {\n type: \"make\",\n generator,\n name,\n flags,\n };\n}\n\n/**\n * The tools `warlock mcp` serves. Read-only unless `context.allowWrites`:\n * generate tools then accept `write: true`; without it they only return the\n * diff a generator would produce.\n */\nexport function createWarlockMcpTools(context: WarlockMcpContext): WarlockMcpTool[] {\n const tools: WarlockMcpTool[] = [\n {\n name: \"inspect_app\",\n description:\n \"Summary of the app: name, version, Node, environment, how many routes/models/resources/config keys/events/pages/jobs/schedules/packages it has, and inspect warnings (WL codes for modules that failed to load). Start here, then call the inspect_<section> tool you need. Read-only.\",\n input: inputSchema({}),\n writes: false,\n async run() {\n const report = await context.inspect(INSPECT_SECTIONS);\n const counts = Object.fromEntries(\n INSPECT_SECTIONS.map((section) => [section, report[section]?.length ?? 0]),\n );\n\n return {\n schemaVersion: report.schemaVersion,\n app: report.app,\n counts,\n warnings: report.warnings,\n };\n },\n },\n ...INSPECT_SECTIONS.map((section) => inspectSectionTool(context, section)),\n {\n name: \"db_schema\",\n description:\n \"The database schema the app's models declare: one entry per table with columns (type, required, primary key, foreign key of belongsTo relations), relations, timestamp columns and delete strategy. Built from the models' Seal schemas — no database connection is opened, so migration-only columns are not listed. Read-only.\",\n input: inputSchema({\n model: { type: \"string\", description: \"Only this model (class name) or table.\" },\n }),\n writes: false,\n async run(input) {\n const report = await context.inspect([\"models\"]);\n\n return buildDbSchema(report.models ?? [], stringArg(input, \"model\"));\n },\n },\n {\n name: \"docs_search\",\n description:\n \"Search the documentation of the @warlock.js packages INSTALLED in this app (their skills, README and llms files under node_modules) — offline, so answers match the installed versions. Returns ranked sections with the file, heading, line and an excerpt; read a whole skill with docs_read. Read-only.\",\n input: inputSchema(\n {\n query: {\n type: \"string\",\n description: \"Words to look for, e.g. 'validation schema file upload'.\",\n },\n limit: {\n type: \"integer\",\n description: \"Maximum hits (default 8).\",\n minimum: 1,\n maximum: 25,\n },\n package: {\n type: \"string\",\n description: \"Only this package, e.g. '@warlock.js/cascade'.\",\n },\n },\n [\"query\"],\n ),\n writes: false,\n async run(input) {\n const corpus = await context.docs();\n const query = stringArg(input, \"query\") ?? \"\";\n\n return {\n query,\n files: corpus.files.length,\n hits: searchDocs(corpus, query, {\n limit: integerArg(input, \"limit\", 8),\n package: stringArg(input, \"package\"),\n }),\n };\n },\n },\n {\n name: \"docs_read\",\n description:\n \"Read one file returned by docs_search (a skill, README or llms file of an installed @warlock.js package), whole or one section by its heading. Only files of that corpus can be read. Read-only.\",\n input: inputSchema(\n {\n file: { type: \"string\", description: \"The `file` of a docs_search hit.\" },\n heading: { type: \"string\", description: \"Only the section with this exact heading.\" },\n },\n [\"file\"],\n ),\n writes: false,\n async run(input) {\n const corpus = await context.docs();\n const file = stringArg(input, \"file\") ?? \"\";\n const found = readDocsFile(corpus, context.cwd, file, {\n heading: stringArg(input, \"heading\"),\n });\n\n if (!found)\n throw new Error(\n `\"${file}\" is not an installed docs file (or has no such heading). Use docs_search first.`,\n );\n\n return found;\n },\n },\n {\n name: \"explain\",\n description:\n \"Explain a stable Warlock error code (WL3001, WL7301, WL8003, …): what it means, whether it is your code or this machine, the one-line fix, the skill that covers it and its docs link. Without a code, lists every code. Read-only.\",\n input: inputSchema({ code: { type: \"string\", description: \"The code, e.g. WL3001.\" } }),\n writes: false,\n async run(input) {\n const code = stringArg(input, \"code\");\n\n if (!code) {\n return Object.values(ERROR_CODES).map((definition) => ({\n code: definition.code,\n title: definition.title,\n category: definition.category,\n }));\n }\n\n const definition = explainErrorCode(code);\n\n if (!definition)\n throw new Error(\n `Unknown error code \"${code}\". Call explain without a code to list them.`,\n );\n\n return {\n ...definition,\n docs: isWarlockErrorCode(definition.code) ? errorCodeDocsUrl(definition.code) : null,\n };\n },\n },\n {\n name: \"check_run\",\n description:\n \"Run `warlock check`: typecheck, lint, the affected tests and the framework convention checks, as one report with one problem per entry (stable WL code, file:line, fix hint, skill). exitCode 1 means your code, 2 means this machine (missing service, Node version, crashed tool) — do not edit code for a 2. Use changed: true while iterating. Does not write project files.\",\n input: inputSchema({\n changed: {\n type: \"boolean\",\n description: \"Only the files changed against HEAD (plus untracked) and what they affect.\",\n },\n base: {\n type: \"string\",\n description: \"Compare with the merge base of this git ref instead (implies changed).\",\n },\n skip: {\n type: \"array\",\n description: \"Steps to skip.\",\n items: { type: \"string\", enum: CHECK_STEPS },\n },\n }),\n writes: false,\n async run(input) {\n const skip = (stringsArg(input, \"skip\") ?? []).filter((step): step is CheckStepName =>\n (CHECK_STEPS as readonly string[]).includes(step),\n );\n\n return context.check({\n changed: booleanArg(input, \"changed\"),\n base: stringArg(input, \"base\"),\n skip,\n });\n },\n },\n {\n name: \"test_run\",\n description:\n \"Run the app's vitest tests: given test files run as they are; given source files, the tests that import them run (vitest related); changed: true runs the tests related to the files changed in git; neither runs the whole suite. Returns totals and one problem per failure (WL7301 failed test, WL7302 file failed to load, WL8xxx this machine). Does not write project files.\",\n input: inputSchema({\n files: {\n type: \"array\",\n description: \"Project-relative files (tests or sources).\",\n items: { type: \"string\" },\n },\n changed: {\n type: \"boolean\",\n description: \"The tests related to the files changed against HEAD.\",\n },\n base: {\n type: \"string\",\n description: \"With changed: compare with the merge base of this ref.\",\n },\n }),\n writes: false,\n async run(input) {\n return context.tests({\n files: stringsArg(input, \"files\"),\n changed: booleanArg(input, \"changed\"),\n base: stringArg(input, \"base\"),\n });\n },\n },\n {\n name: \"requests_recent\",\n description:\n \"The most recent HTTP requests the running dev server handled, from @warlock.js/devtools (needs `warlock dev` with devtools installed): method, path, matched route, status, duration, and — with details — the phase timeline (route match, each middleware, validation, handler), every query with its duration and an nPlusOne flag, N+1 warnings, logs, cache operations and captured mails. Request and response bodies are not recorded by devtools; use `warlock trace` for those. Read-only.\",\n input: inputSchema({\n limit: {\n type: \"integer\",\n description: \"How many requests (default 10, newest first).\",\n minimum: 1,\n maximum: 50,\n },\n method: { type: \"string\", description: \"Only this HTTP method.\" },\n path: { type: \"string\", description: \"Only paths containing this text.\" },\n status: {\n type: \"integer\",\n description: \"Only this response status.\",\n minimum: 100,\n maximum: 599,\n },\n details: {\n type: \"boolean\",\n description: \"Include the full timeline of each request (default true).\",\n },\n }),\n writes: false,\n async run(input) {\n const devtools = await context.devtools();\n const method = stringArg(input, \"method\")?.toUpperCase();\n const pathFilter = stringArg(input, \"path\");\n const status = typeof input.status === \"number\" ? input.status : undefined;\n const details = input.details !== false;\n const summaries = (await devtools.listRequests())\n .filter(\n (summary) =>\n (!method || summary.method === method) &&\n (!pathFilter || summary.path.includes(pathFilter)) &&\n (status === undefined || summary.status === status),\n )\n .sort((first, second) => second.startedAt - first.startedAt)\n .slice(0, integerArg(input, \"limit\", 10));\n const requests = [];\n\n for (const summary of summaries) {\n requests.push(\n summarizeRequest(summary, details ? await devtools.getRequest(summary.id) : undefined),\n );\n }\n\n return { devtools: { url: devtools.url, path: devtools.devtoolsPath }, requests };\n },\n },\n {\n name: \"generate_make\",\n description: `Run a \\`warlock generate.<generator>\\` scaffold (${MCP_MAKE_GENERATORS.join(\", \")}) and return the unified diff of every file it would create or change. By default nothing in the project is written: the generator runs in a throw-away copy.${context.allowWrites ? \" Pass write: true to write the files into the project.\" : \" This server is read-only (no --allow-writes).\"} Names are module/name for everything but module (e.g. posts/create-post) and the model path for migration.`,\n input: inputSchema(\n {\n generator: { type: \"string\", description: \"Which generator.\", enum: MCP_MAKE_GENERATORS },\n name: {\n type: \"string\",\n description:\n \"The generator's argument: `posts` for module, `posts/create-post` for the others, the model path for migration.\",\n },\n ...MAKE_FLAGS,\n ...(context.allowWrites ? writeProperty() : {}),\n },\n [\"generator\", \"name\"],\n ),\n writes: context.allowWrites,\n precheck: refuseWrite(context),\n async run(input) {\n const spec = makeSpecFromInput(input);\n\n return input.write === true\n ? context.generation.apply(spec)\n : context.generation.plan(spec);\n },\n },\n {\n name: \"generate_add\",\n description: `Run \\`warlock add <features…>\\` (an installable framework feature: a database driver, queue, web, devtools, …) and return the unified diff of every file it would create or change, package.json included. Dependencies are recorded, never installed, in the preview.${context.allowWrites ? \" Pass write: true to apply it to the project (this installs the dependencies).\" : \" This server is read-only (no --allow-writes).\"}`,\n input: inputSchema(\n {\n features: {\n type: \"array\",\n description: \"Feature names, e.g. ['devtools'].\",\n items: context.features?.length\n ? { type: \"string\", enum: context.features }\n : { type: \"string\" },\n },\n ...(context.allowWrites ? writeProperty() : {}),\n },\n [\"features\"],\n ),\n writes: context.allowWrites,\n precheck: refuseWrite(context),\n async run(input) {\n const features = stringsArg(input, \"features\") ?? [];\n\n if (features.length === 0) throw new Error(\"generate_add needs at least one feature name.\");\n\n const spec: GeneratorSpec = { type: \"add\", features };\n\n return input.write === true\n ? context.generation.apply(spec)\n : context.generation.plan(spec);\n },\n },\n ];\n\n return tools;\n}\n\n/** The message a tool failure reaches the client with: code, message and the fix. */\nexport function describeToolError(error: unknown): string {\n if (error instanceof WarlockError) {\n return `${error.code}: ${error.message} Fix: ${ERROR_CODES[error.code].fix}`;\n }\n\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-tools.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/mcp/mcp-tools.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n explainErrorCode,\n isWarlockErrorCode,\n} from \"../../errors/error-codes\";\nimport { WarlockError } from \"../../errors/warlock-error\";\nimport { CHECK_STEPS, type CheckReport, type CheckStepName } from \"../check/check-report.types\";\nimport type { TestRunReport } from \"../check/run-tests\";\nimport type {\n DevtoolsClient,\n DevtoolsRequestRecord,\n DevtoolsRequestSummaryRecord,\n} from \"../devtools/devtools-client\";\nimport { type GeneratorSpec, isMakeGenerator, MAKE_GENERATORS } from \"../generate\";\nimport {\n INSPECT_SECTIONS,\n type InspectReport,\n type InspectRoute,\n type InspectSection,\n} from \"../inspect/inspect-report.types\";\nimport { buildDbSchema } from \"./db-schema\";\nimport { type DocsCorpus, readDocsFile, searchDocs } from \"./docs-search\";\nimport { inputSchema, type McpInputSchema, type McpInputProperty } from \"./tool-input\";\n\n/**\n * What the `warlock mcp` tools run against. The CLI fills it from a read-only\n * boot of the app; `warlock dev --mcp` fills it from the live dev process;\n * tests pass fakes.\n */\nexport type WarlockMcpContext = {\n /** Project root. */\n cwd: string;\n /** Whether tools may write project files (`--allow-writes`). */\n allowWrites: boolean;\n /** Describe the app (see `inspectApp`). */\n inspect(sections: readonly InspectSection[]): Promise<InspectReport>;\n /** Run `warlock check`. */\n check(options: {\n changed?: boolean;\n base?: string;\n skip?: readonly CheckStepName[];\n }): Promise<CheckReport>;\n /** Run tests for files, the changed files, or the suite. */\n tests(options: {\n files?: readonly string[];\n changed?: boolean;\n base?: string;\n }): Promise<TestRunReport>;\n /** Connect to the running dev server's devtools API (throws WL8008 / WL8009). */\n devtools(): Promise<DevtoolsClient>;\n /** Preview (and, with `allowWrites`, apply) a `make` / `add` generator. */\n generation: {\n plan(spec: GeneratorSpec): Promise<unknown>;\n apply(spec: GeneratorSpec): Promise<unknown>;\n };\n /** The installed docs corpus (cached by the caller). */\n docs(): Promise<DocsCorpus>;\n /** The `warlock add` feature names, advertised as the `generate_add` enum. */\n features?: readonly string[];\n};\n\n/** One tool the server exposes. */\nexport type WarlockMcpTool = {\n /** Underscored, never dotted: model APIs refuse dots in tool names (`^[a-zA-Z0-9_-]{1,128}$`). */\n name: string;\n description: string;\n input: McpInputSchema;\n /** Whether a call can change project files. Only exposed with `allowWrites`. */\n writes: boolean;\n /** A check run before validation; return a message to refuse the call. */\n precheck?: (input: Record<string, unknown>) => string | null;\n run(input: Record<string, unknown>): Promise<unknown>;\n};\n\n/** The `make` generators the `generate_make` tool can run (`warlock generate.<name>`). */\nexport const MCP_MAKE_GENERATORS = MAKE_GENERATORS;\n\nfunction stringArg(input: Record<string, unknown>, key: string): string | undefined {\n const value = input[key];\n\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction booleanArg(input: Record<string, unknown>, key: string): boolean {\n return input[key] === true;\n}\n\nfunction integerArg(input: Record<string, unknown>, key: string, fallback: number): number {\n const value = input[key];\n\n return typeof value === \"number\" && Number.isInteger(value) ? value : fallback;\n}\n\nfunction stringsArg(input: Record<string, unknown>, key: string): string[] | undefined {\n const value = input[key];\n\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : undefined;\n}\n\nfunction includesText(values: readonly (string | null | undefined)[], needle: string): boolean {\n const lowered = needle.toLowerCase();\n\n return values.some((value) => typeof value === \"string\" && value.toLowerCase().includes(lowered));\n}\n\nconst MUTATING = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\nfunction filterRoutes(\n routes: readonly InspectRoute[],\n input: Record<string, unknown>,\n): InspectRoute[] {\n const filter = stringArg(input, \"filter\");\n const method = stringArg(input, \"method\")?.toUpperCase();\n const unguarded = booleanArg(input, \"unguardedWrites\");\n\n return routes.filter(\n (route) =>\n (!filter ||\n includesText([route.path, route.name, route.handler, route.sourceFile], filter)) &&\n (!method || route.method === method) &&\n (!unguarded || (MUTATING.has(route.method) && !route.auth.required && !route.isPage)),\n );\n}\n\n/** The `filter` match of one entry of a section, by the fields an agent would search. */\nfunction sectionEntryMatches(section: InspectSection, entry: unknown, filter: string): boolean {\n if (typeof entry !== \"object\" || entry === null) return false;\n\n const fields: Record<InspectSection, readonly string[]> = {\n routes: [\"path\", \"name\", \"handler\", \"sourceFile\"],\n models: [\"name\", \"table\", \"sourceFile\"],\n resources: [\"name\"],\n config: [\"key\", \"file\"],\n events: [\"event\"],\n pages: [\"path\", \"name\", \"file\"],\n jobs: [\"name\", \"queue\"],\n schedules: [\"name\"],\n packages: [\"name\"],\n files: [\"file\", \"module\", \"role\"],\n };\n\n return includesText(\n fields[section].map((field) => {\n const value = Reflect.get(entry, field);\n return typeof value === \"string\" ? value : null;\n }),\n filter,\n );\n}\n\nconst SECTION_DESCRIPTIONS: Record<InspectSection, string> = {\n routes:\n \"Registered HTTP routes from the booted router: method, path, name, source file, handler, middleware chain in run order, auth guard, CSRF, rate limit, logging policy, request JSON Schemas and declared responses ($refs resolve against `components`).\",\n models:\n \"Cascade model classes: table, primary key, data source, columns (from the model's Seal schema), relations, resource, delete strategy and timestamp columns.\",\n resources:\n \"Resources reached from route responses or models, with their output shape as JSON Schema and the models that use them.\",\n config:\n \"Every loaded config key as a dotted path with its TYPE (never its value) and the src/config file it came from.\",\n events:\n \"Event listeners app modules registered while loading, grouped by event, with the registering file.\",\n pages:\n \"Pages @warlock.js/web installed: path, name, file, layout chain, loaders, actions and guard.\",\n jobs: \"Queue jobs defined with defineJob() from @warlock.js/queue: name, queue, attempts, backoff, retention.\",\n schedules:\n \"Scheduled tasks from @warlock.js/scheduler: name, cron expression, intervals, next run.\",\n files:\n \"Source files, convention roles, exports and resolved imports/importedBy (static analysis, no execution).\",\n packages: \"Installed @warlock.js/* packages and their versions.\",\n};\n\nfunction inspectSectionTool(context: WarlockMcpContext, section: InspectSection): WarlockMcpTool {\n const properties: Record<string, McpInputProperty> = {\n filter: {\n type: \"string\",\n description:\n \"Case-insensitive substring to keep only matching entries (names, paths, keys, files).\",\n },\n };\n\n if (section === \"routes\") {\n properties.method = {\n type: \"string\",\n description: \"Only routes with this HTTP method (GET, POST, …).\",\n };\n properties.unguardedWrites = {\n type: \"boolean\",\n description: \"Only POST/PUT/PATCH/DELETE API routes that have no auth guard.\",\n };\n }\n\n return {\n name: `inspect_${section}`,\n description: `${SECTION_DESCRIPTIONS[section]} The same data as \\`warlock inspect --json --section ${section}\\`. Read-only.`,\n input: inputSchema(properties),\n writes: false,\n async run(input) {\n const report = await context.inspect([section]);\n const entries: unknown[] = report[section] ?? [];\n const filter = stringArg(input, \"filter\");\n const kept =\n section === \"routes\" && report.routes\n ? filterRoutes(report.routes, input)\n : filter\n ? entries.filter((entry) => sectionEntryMatches(section, entry, filter))\n : entries;\n const withSchemas = section === \"routes\" || section === \"models\" || section === \"resources\";\n\n return {\n schemaVersion: report.schemaVersion,\n [section]: kept,\n total: entries.length,\n ...(withSchemas ? { components: report.components } : {}),\n warnings: report.warnings,\n };\n },\n };\n}\n\nfunction summarizeRequest(summary: DevtoolsRequestSummaryRecord, record?: DevtoolsRequestRecord) {\n if (!record) return summary;\n\n const nPlusOneShapes = new Set(\n record.warnings\n .filter((warning) => warning.type === \"n+1\" && typeof warning.shape === \"string\")\n .map((warning) => warning.shape),\n );\n\n return {\n ...summary,\n error: record.error ?? null,\n phases: record.phases,\n queries: record.queries.map((query) => ({\n ...query,\n nPlusOne: typeof query.shape === \"string\" && nPlusOneShapes.has(query.shape),\n })),\n warnings: record.warnings,\n logs: record.logs,\n cache: record.cache,\n mails: record.mails,\n timings: {\n totalMs: record.durationMs ?? null,\n phasesMs: record.phases.reduce<Record<string, number>>((totals, phase) => {\n totals[phase.name] = (totals[phase.name] ?? 0) + phase.durationMs;\n return totals;\n }, {}),\n queriesMs: record.queries.reduce(\n (total, query) => total + (typeof query.durationMs === \"number\" ? query.durationMs : 0),\n 0,\n ),\n },\n };\n}\n\nfunction writeProperty(): Record<string, McpInputProperty> {\n return {\n write: {\n type: \"boolean\",\n description:\n \"Write the files into the project. Default false: only return the diff (the generator runs in a throw-away copy of the project).\",\n },\n };\n}\n\nfunction refuseWrite(context: WarlockMcpContext) {\n return (input: Record<string, unknown>): string | null =>\n input.write === true && !context.allowWrites\n ? `WL7005: this MCP server is read-only; restart it with --allow-writes to let generate tools write. Without it they return the diff only.`\n : null;\n}\n\nconst MAKE_FLAGS: Record<string, McpInputProperty> = {\n withValidation: {\n type: \"boolean\",\n description: \"controller: also generate a validation schema and bind it (--with-validation).\",\n },\n withResource: {\n type: \"boolean\",\n description: \"model: also generate a resource (--with-resource).\",\n },\n table: { type: \"string\", description: \"model: the table name (--table).\" },\n minimal: {\n type: \"boolean\",\n description: \"module: a minimal skeleton instead of the full CRUD scaffold (--minimal).\",\n },\n noTimestamps: {\n type: \"boolean\",\n description: \"model / migration: no timestamp columns (--no-timestamps).\",\n },\n add: {\n type: \"string\",\n description: \"migration: columns to add, DSL name:type:modifier,… (--add).\",\n },\n drop: { type: \"string\", description: \"migration: comma-separated columns to drop (--drop).\" },\n rename: { type: \"string\", description: \"migration: columns to rename, old:new,… (--rename).\" },\n force: { type: \"boolean\", description: \"Overwrite files that already exist (--force).\" },\n overwrite: {\n type: \"boolean\",\n description:\n \"module / model: with force, also replace files that differ from the output (--overwrite).\",\n },\n};\n\nconst FLAG_NAMES: Record<string, string> = {\n withValidation: \"with-validation\",\n withResource: \"with-resource\",\n table: \"table\",\n minimal: \"minimal\",\n noTimestamps: \"no-timestamps\",\n add: \"add\",\n drop: \"drop\",\n rename: \"rename\",\n force: \"force\",\n overwrite: \"overwrite\",\n};\n\n/** The generator spec a `generate_make` call describes. */\nexport function makeSpecFromInput(input: Record<string, unknown>): GeneratorSpec {\n const generator = stringArg(input, \"generator\");\n const name = stringArg(input, \"name\");\n\n if (!generator || !isMakeGenerator(generator) || !name) {\n throw new Error(\n `generate_make needs a generator (${MCP_MAKE_GENERATORS.join(\", \")}) and a name.`,\n );\n }\n\n const flags: Record<string, string | boolean> = {};\n\n for (const [key, flag] of Object.entries(FLAG_NAMES)) {\n const value = input[key];\n\n if (value === true) flags[flag] = true;\n if (typeof value === \"string\" && value.length > 0) flags[flag] = value;\n }\n\n return {\n type: \"make\",\n generator,\n name,\n flags,\n };\n}\n\n/**\n * The tools `warlock mcp` serves. Read-only unless `context.allowWrites`:\n * generate tools then accept `write: true`; without it they only return the\n * diff a generator would produce.\n */\nexport function createWarlockMcpTools(context: WarlockMcpContext): WarlockMcpTool[] {\n const tools: WarlockMcpTool[] = [\n {\n name: \"inspect_app\",\n description:\n \"Summary of the app: name, version, Node, environment, how many routes/models/resources/config keys/events/pages/jobs/schedules/packages it has, and inspect warnings (WL codes for modules that failed to load). Start here, then call the inspect_<section> tool you need. Read-only.\",\n input: inputSchema({}),\n writes: false,\n async run() {\n const report = await context.inspect(INSPECT_SECTIONS);\n const counts = Object.fromEntries(\n INSPECT_SECTIONS.map((section) => [section, report[section]?.length ?? 0]),\n );\n\n return {\n schemaVersion: report.schemaVersion,\n app: report.app,\n counts,\n warnings: report.warnings,\n };\n },\n },\n ...INSPECT_SECTIONS.map((section) => inspectSectionTool(context, section)),\n {\n name: \"db_schema\",\n description:\n \"The database schema the app's models declare: one entry per table with columns (type, required, primary key, foreign key of belongsTo relations), relations, timestamp columns and delete strategy. Built from the models' Seal schemas — no database connection is opened, so migration-only columns are not listed. Read-only.\",\n input: inputSchema({\n model: { type: \"string\", description: \"Only this model (class name) or table.\" },\n }),\n writes: false,\n async run(input) {\n const report = await context.inspect([\"models\"]);\n\n return buildDbSchema(report.models ?? [], stringArg(input, \"model\"));\n },\n },\n {\n name: \"docs_search\",\n description:\n \"Search the documentation of the @warlock.js packages INSTALLED in this app (their skills, README and llms files under node_modules) — offline, so answers match the installed versions. Returns ranked sections with the file, heading, line and an excerpt; read a whole skill with docs_read. Read-only.\",\n input: inputSchema(\n {\n query: {\n type: \"string\",\n description: \"Words to look for, e.g. 'validation schema file upload'.\",\n },\n limit: {\n type: \"integer\",\n description: \"Maximum hits (default 8).\",\n minimum: 1,\n maximum: 25,\n },\n package: {\n type: \"string\",\n description: \"Only this package, e.g. '@warlock.js/cascade'.\",\n },\n },\n [\"query\"],\n ),\n writes: false,\n async run(input) {\n const corpus = await context.docs();\n const query = stringArg(input, \"query\") ?? \"\";\n\n return {\n query,\n files: corpus.files.length,\n hits: searchDocs(corpus, query, {\n limit: integerArg(input, \"limit\", 8),\n package: stringArg(input, \"package\"),\n }),\n };\n },\n },\n {\n name: \"docs_read\",\n description:\n \"Read one file returned by docs_search (a skill, README or llms file of an installed @warlock.js package), whole or one section by its heading. Only files of that corpus can be read. Read-only.\",\n input: inputSchema(\n {\n file: { type: \"string\", description: \"The `file` of a docs_search hit.\" },\n heading: { type: \"string\", description: \"Only the section with this exact heading.\" },\n },\n [\"file\"],\n ),\n writes: false,\n async run(input) {\n const corpus = await context.docs();\n const file = stringArg(input, \"file\") ?? \"\";\n const found = readDocsFile(corpus, context.cwd, file, {\n heading: stringArg(input, \"heading\"),\n });\n\n if (!found)\n throw new Error(\n `\"${file}\" is not an installed docs file (or has no such heading). Use docs_search first.`,\n );\n\n return found;\n },\n },\n {\n name: \"explain\",\n description:\n \"Explain a stable Warlock error code (WL3001, WL7301, WL8003, …): what it means, whether it is your code or this machine, the one-line fix, the skill that covers it and its docs link. Without a code, lists every code. Read-only.\",\n input: inputSchema({ code: { type: \"string\", description: \"The code, e.g. WL3001.\" } }),\n writes: false,\n async run(input) {\n const code = stringArg(input, \"code\");\n\n if (!code) {\n return Object.values(ERROR_CODES).map((definition) => ({\n code: definition.code,\n title: definition.title,\n category: definition.category,\n }));\n }\n\n const definition = explainErrorCode(code);\n\n if (!definition)\n throw new Error(\n `Unknown error code \"${code}\". Call explain without a code to list them.`,\n );\n\n return {\n ...definition,\n docs: isWarlockErrorCode(definition.code) ? errorCodeDocsUrl(definition.code) : null,\n };\n },\n },\n {\n name: \"check_run\",\n description:\n \"Run `warlock check`: typecheck, lint, the affected tests and the framework convention checks, as one report with one problem per entry (stable WL code, file:line, fix hint, skill). exitCode 1 means your code, 2 means this machine (missing service, Node version, crashed tool) — do not edit code for a 2. Use changed: true while iterating. Does not write project files.\",\n input: inputSchema({\n changed: {\n type: \"boolean\",\n description: \"Only the files changed against HEAD (plus untracked) and what they affect.\",\n },\n base: {\n type: \"string\",\n description: \"Compare with the merge base of this git ref instead (implies changed).\",\n },\n skip: {\n type: \"array\",\n description: \"Steps to skip.\",\n items: { type: \"string\", enum: CHECK_STEPS },\n },\n }),\n writes: false,\n async run(input) {\n const skip = (stringsArg(input, \"skip\") ?? []).filter((step): step is CheckStepName =>\n (CHECK_STEPS as readonly string[]).includes(step),\n );\n\n return context.check({\n changed: booleanArg(input, \"changed\"),\n base: stringArg(input, \"base\"),\n skip,\n });\n },\n },\n {\n name: \"test_run\",\n description:\n \"Run the app's vitest tests: given test files run as they are; given source files, the tests that import them run (vitest related); changed: true runs the tests related to the files changed in git; neither runs the whole suite. Returns totals and one problem per failure (WL7301 failed test, WL7302 file failed to load, WL8xxx this machine). Does not write project files.\",\n input: inputSchema({\n files: {\n type: \"array\",\n description: \"Project-relative files (tests or sources).\",\n items: { type: \"string\" },\n },\n changed: {\n type: \"boolean\",\n description: \"The tests related to the files changed against HEAD.\",\n },\n base: {\n type: \"string\",\n description: \"With changed: compare with the merge base of this ref.\",\n },\n }),\n writes: false,\n async run(input) {\n return context.tests({\n files: stringsArg(input, \"files\"),\n changed: booleanArg(input, \"changed\"),\n base: stringArg(input, \"base\"),\n });\n },\n },\n {\n name: \"requests_recent\",\n description:\n \"The most recent HTTP requests the running dev server handled, from @warlock.js/devtools (needs `warlock dev` with devtools installed): method, path, matched route, status, duration, and — with details — the phase timeline (route match, each middleware, validation, handler), every query with its duration and an nPlusOne flag, N+1 warnings, logs, cache operations and captured mails. Request and response bodies are not recorded by devtools; use `warlock trace` for those. Read-only.\",\n input: inputSchema({\n limit: {\n type: \"integer\",\n description: \"How many requests (default 10, newest first).\",\n minimum: 1,\n maximum: 50,\n },\n method: { type: \"string\", description: \"Only this HTTP method.\" },\n path: { type: \"string\", description: \"Only paths containing this text.\" },\n status: {\n type: \"integer\",\n description: \"Only this response status.\",\n minimum: 100,\n maximum: 599,\n },\n details: {\n type: \"boolean\",\n description: \"Include the full timeline of each request (default true).\",\n },\n }),\n writes: false,\n async run(input) {\n const devtools = await context.devtools();\n const method = stringArg(input, \"method\")?.toUpperCase();\n const pathFilter = stringArg(input, \"path\");\n const status = typeof input.status === \"number\" ? input.status : undefined;\n const details = input.details !== false;\n const summaries = (await devtools.listRequests())\n .filter(\n (summary) =>\n (!method || summary.method === method) &&\n (!pathFilter || summary.path.includes(pathFilter)) &&\n (status === undefined || summary.status === status),\n )\n .sort((first, second) => second.startedAt - first.startedAt)\n .slice(0, integerArg(input, \"limit\", 10));\n const requests = [];\n\n for (const summary of summaries) {\n requests.push(\n summarizeRequest(summary, details ? await devtools.getRequest(summary.id) : undefined),\n );\n }\n\n return { devtools: { url: devtools.url, path: devtools.devtoolsPath }, requests };\n },\n },\n {\n name: \"generate_make\",\n description: `Run a \\`warlock generate.<generator>\\` scaffold (${MCP_MAKE_GENERATORS.join(\", \")}) and return the unified diff of every file it would create or change. By default nothing in the project is written: the generator runs in a throw-away copy.${context.allowWrites ? \" Pass write: true to write the files into the project.\" : \" This server is read-only (no --allow-writes).\"} Names are module/name for everything but module (e.g. posts/create-post) and the model path for migration.`,\n input: inputSchema(\n {\n generator: { type: \"string\", description: \"Which generator.\", enum: MCP_MAKE_GENERATORS },\n name: {\n type: \"string\",\n description:\n \"The generator's argument: `posts` for module, `posts/create-post` for the others, the model path for migration.\",\n },\n ...MAKE_FLAGS,\n ...(context.allowWrites ? writeProperty() : {}),\n },\n [\"generator\", \"name\"],\n ),\n writes: context.allowWrites,\n precheck: refuseWrite(context),\n async run(input) {\n const spec = makeSpecFromInput(input);\n\n return input.write === true\n ? context.generation.apply(spec)\n : context.generation.plan(spec);\n },\n },\n {\n name: \"generate_add\",\n description: `Run \\`warlock add <features…>\\` (an installable framework feature: a database driver, queue, web, devtools, …) and return the unified diff of every file it would create or change, package.json included. Dependencies are recorded, never installed, in the preview.${context.allowWrites ? \" Pass write: true to apply it to the project (this installs the dependencies).\" : \" This server is read-only (no --allow-writes).\"}`,\n input: inputSchema(\n {\n features: {\n type: \"array\",\n description: \"Feature names, e.g. ['devtools'].\",\n items: context.features?.length\n ? { type: \"string\", enum: context.features }\n : { type: \"string\" },\n },\n ...(context.allowWrites ? writeProperty() : {}),\n },\n [\"features\"],\n ),\n writes: context.allowWrites,\n precheck: refuseWrite(context),\n async run(input) {\n const features = stringsArg(input, \"features\") ?? [];\n\n if (features.length === 0) throw new Error(\"generate_add needs at least one feature name.\");\n\n const spec: GeneratorSpec = { type: \"add\", features };\n\n return input.write === true\n ? context.generation.apply(spec)\n : context.generation.plan(spec);\n },\n },\n ];\n\n return tools;\n}\n\n/** The message a tool failure reaches the client with: code, message and the fix. */\nexport function describeToolError(error: unknown): string {\n if (error instanceof WarlockError) {\n return `${error.code}: ${error.message} Fix: ${ERROR_CODES[error.code].fix}`;\n }\n\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { ErrorCodeCategory, WarlockErrorCode } from "../errors/error-codes.mjs";
2
+ import { ProblemFix } from "./check/fixes.mjs";
2
3
  //#region ../../packages/core/src/tooling/problem.d.ts
3
4
  /** The tool that found a problem. */
4
5
  export type ProblemSource = "typescript" | "oxlint" | "vitest" | "convention" | "environment" | "doctor";
@@ -10,6 +11,8 @@ export type ProblemSource = "typescript" | "oxlint" | "vitest" | "convention" |
10
11
  * `file`, `line` and `column` are `null` when the problem has no position.
11
12
  */
12
13
  export type ToolingProblem = {
14
+ /** Optional safe edit, using zero-based UTF-16 source offsets. */
15
+ fix?: ProblemFix;
13
16
  /** Stable Warlock error code; `warlock explain <code>` prints the fix. */
14
17
  code: WarlockErrorCode;
15
18
  /** `code` = fix the project; `environment` = fix this machine. */
@@ -37,6 +40,7 @@ export type ToolingProblem = {
37
40
  };
38
41
  /** The fields a reporter supplies; the rest come from the registry. */
39
42
  export type ProblemInput = {
43
+ fix?: ProblemFix;
40
44
  code: WarlockErrorCode;
41
45
  source: ProblemSource;
42
46
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"problem.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"mappings":";;;YAQY;;;;;;;;YAeA;;EAEV,MAAM;;EAEN,UAAU;;EAEV;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;YAIU;EACV,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;;;;;;wBAOc,cAAc,OAAO,eAAe"}
1
+ {"version":3,"file":"problem.d.mts","names":["_$__check_fixes0.ProblemFix"],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"mappings":";;;;YAQY;;;;;;;;YAeA;;EAEV,MAFUA;;EAIV,MAAM;;EAEN,UAAU;;EAEV;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;YAIU;EACV,MADUA;EAEV,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;;;;;;wBAOc,cAAc,OAAO,eAAe"}
@@ -8,6 +8,7 @@ import { ERROR_CODES, errorCodeDocsUrl } from "../errors/error-codes.mjs";
8
8
  function createProblem(input) {
9
9
  const definition = ERROR_CODES[input.code];
10
10
  return {
11
+ ...input.fix ? { fix: input.fix } : {},
11
12
  code: input.code,
12
13
  category: definition.category,
13
14
  severity: input.severity ?? "error",
@@ -1 +1 @@
1
- {"version":3,"file":"problem.mjs","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n type ErrorCodeCategory,\n type WarlockErrorCode,\n} from \"../errors/error-codes\";\n\n/** The tool that found a problem. */\nexport type ProblemSource =\n | \"typescript\"\n | \"oxlint\"\n | \"vitest\"\n | \"convention\"\n | \"environment\"\n | \"doctor\";\n\n/**\n * One problem, in the shape every machine-readable command prints\n * (`warlock check --json`, `warlock typecheck --json`, `warlock doctor --json`).\n *\n * Every key is always present, so a consumer never has to test for one:\n * `file`, `line` and `column` are `null` when the problem has no position.\n */\nexport type ToolingProblem = {\n /** Stable Warlock error code; `warlock explain <code>` prints the fix. */\n code: WarlockErrorCode;\n /** `code` = fix the project; `environment` = fix this machine. */\n category: ErrorCodeCategory;\n /** `error` fails the run; `warning` is reported and does not. */\n severity: \"error\" | \"warning\";\n /** The tool that reported it. */\n source: ProblemSource;\n /** Project-relative POSIX path, or `null`. */\n file: string | null;\n /** 1-based line, or `null`. */\n line: number | null;\n /** 1-based column, or `null`. */\n column: number | null;\n /** What is wrong, in the reporting tool's words. */\n message: string;\n /** One line on what to do, from the code registry. */\n hint: string;\n /** Slug of the skill that explains the fix. */\n skill: string;\n /** Docs URL of the code. */\n docs: string;\n /** The tool's own rule or code (`TS2345`, `no-unused-vars`, a test name), or `null`. */\n rule: string | null;\n};\n\n/** The fields a reporter supplies; the rest come from the registry. */\nexport type ProblemInput = {\n code: WarlockErrorCode;\n source: ProblemSource;\n message: string;\n severity?: \"error\" | \"warning\";\n file?: string | null;\n line?: number | null;\n column?: number | null;\n rule?: string | null;\n};\n\n/**\n * Build a {@link ToolingProblem}, filling `category`, `hint`, `skill` and `docs`\n * from the code registry so every reporter describes a code the same way.\n */\nexport function createProblem(input: ProblemInput): ToolingProblem {\n const definition = ERROR_CODES[input.code];\n\n return {\n code: input.code,\n category: definition.category,\n severity: input.severity ?? \"error\",\n source: input.source,\n file: input.file ?? null,\n line: input.line ?? null,\n column: input.column ?? null,\n message: input.message,\n hint: definition.fix,\n skill: definition.skill,\n docs: errorCodeDocsUrl(input.code),\n rule: input.rule ?? null,\n };\n}\n"],"mappings":";;;;;;;AAkEA,SAAgB,cAAc,OAAqC;CACjE,MAAM,aAAa,YAAY,MAAM;CAErC,OAAO;EACL,MAAM,MAAM;EACZ,UAAU,WAAW;EACrB,UAAU,MAAM,YAAY;EAC5B,QAAQ,MAAM;EACd,MAAM,MAAM,QAAQ;EACpB,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM,UAAU;EACxB,SAAS,MAAM;EACf,MAAM,WAAW;EACjB,OAAO,WAAW;EAClB,MAAM,iBAAiB,MAAM,IAAI;EACjC,MAAM,MAAM,QAAQ;CACtB;AACF"}
1
+ {"version":3,"file":"problem.mjs","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n type ErrorCodeCategory,\n type WarlockErrorCode,\n} from \"../errors/error-codes\";\n\n/** The tool that found a problem. */\nexport type ProblemSource =\n | \"typescript\"\n | \"oxlint\"\n | \"vitest\"\n | \"convention\"\n | \"environment\"\n | \"doctor\";\n\n/**\n * One problem, in the shape every machine-readable command prints\n * (`warlock check --json`, `warlock typecheck --json`, `warlock doctor --json`).\n *\n * Every key is always present, so a consumer never has to test for one:\n * `file`, `line` and `column` are `null` when the problem has no position.\n */\nexport type ToolingProblem = {\n /** Optional safe edit, using zero-based UTF-16 source offsets. */\n fix?: import(\"./check/fixes\").ProblemFix;\n /** Stable Warlock error code; `warlock explain <code>` prints the fix. */\n code: WarlockErrorCode;\n /** `code` = fix the project; `environment` = fix this machine. */\n category: ErrorCodeCategory;\n /** `error` fails the run; `warning` is reported and does not. */\n severity: \"error\" | \"warning\";\n /** The tool that reported it. */\n source: ProblemSource;\n /** Project-relative POSIX path, or `null`. */\n file: string | null;\n /** 1-based line, or `null`. */\n line: number | null;\n /** 1-based column, or `null`. */\n column: number | null;\n /** What is wrong, in the reporting tool's words. */\n message: string;\n /** One line on what to do, from the code registry. */\n hint: string;\n /** Slug of the skill that explains the fix. */\n skill: string;\n /** Docs URL of the code. */\n docs: string;\n /** The tool's own rule or code (`TS2345`, `no-unused-vars`, a test name), or `null`. */\n rule: string | null;\n};\n\n/** The fields a reporter supplies; the rest come from the registry. */\nexport type ProblemInput = {\n fix?: import(\"./check/fixes\").ProblemFix;\n code: WarlockErrorCode;\n source: ProblemSource;\n message: string;\n severity?: \"error\" | \"warning\";\n file?: string | null;\n line?: number | null;\n column?: number | null;\n rule?: string | null;\n};\n\n/**\n * Build a {@link ToolingProblem}, filling `category`, `hint`, `skill` and `docs`\n * from the code registry so every reporter describes a code the same way.\n */\nexport function createProblem(input: ProblemInput): ToolingProblem {\n const definition = ERROR_CODES[input.code];\n\n return {\n ...(input.fix ? { fix: input.fix } : {}),\n code: input.code,\n category: definition.category,\n severity: input.severity ?? \"error\",\n source: input.source,\n file: input.file ?? null,\n line: input.line ?? null,\n column: input.column ?? null,\n message: input.message,\n hint: definition.fix,\n skill: definition.skill,\n docs: errorCodeDocsUrl(input.code),\n rule: input.rule ?? null,\n };\n}\n"],"mappings":";;;;;;;AAqEA,SAAgB,cAAc,OAAqC;CACjE,MAAM,aAAa,YAAY,MAAM;CAErC,OAAO;EACL,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,IAAI,IAAI,CAAC;EACtC,MAAM,MAAM;EACZ,UAAU,WAAW;EACrB,UAAU,MAAM,YAAY;EAC5B,QAAQ,MAAM;EACd,MAAM,MAAM,QAAQ;EACpB,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM,UAAU;EACxB,SAAS,MAAM;EACf,MAAM,WAAW;EACjB,OAAO,WAAW;EAClB,MAAM,iBAAiB,MAAM,IAAI;EACjC,MAAM,MAAM,QAAQ;CACtB;AACF"}
@@ -0,0 +1,2 @@
1
+ import "oxc-parser";
2
+ export type * from "oxc-parser";
@@ -71,7 +71,11 @@ function runTypecheck(options = {}) {
71
71
  if (wanted) {
72
72
  const session = new CompilerSession(cwd, configPath);
73
73
  try {
74
- for (const file of directImporters(session, wanted, cwd)) wanted.add(file);
74
+ for (;;) {
75
+ const importers = directImporters(session, wanted, cwd);
76
+ if (!importers.size) break;
77
+ for (const file of importers) wanted.add(file);
78
+ }
75
79
  } finally {
76
80
  session.close();
77
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"run-typecheck.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/typecheck/run-typecheck.ts"],"sourcesContent":["import { existsSync, mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\nimport { CompilerSession, runCompiler, type CompilerDiagnostic } from \"../typescript/compiler\";\n\nexport type TypecheckOptions = {\n cwd?: string;\n project?: string;\n files?: readonly string[];\n buildInfoFile?: string;\n};\nexport type TypecheckResult = {\n ok: boolean;\n mode: \"program\" | \"files\";\n files: string[] | null;\n checkedFiles: number;\n problems: ToolingProblem[];\n durationMs: number;\n buildInfoFile: string | null;\n};\nexport const DEFAULT_TYPECHECK_BUILD_INFO = path.join(\".warlock\", \"cache\", \"typecheck.tsbuildinfo\");\nconst toPosix = (file: string) => file.split(path.sep).join(\"/\");\nfunction isProjectFile(file: string, root: string, declarations = true): boolean {\n return (\n file.startsWith(root + path.sep) &&\n !file.split(path.sep).includes(\"node_modules\") &&\n (declarations || !file.endsWith(\".d.ts\"))\n );\n}\nexport function diagnosticToProblem(\n diagnostic: CompilerDiagnostic,\n cwd: string,\n code: \"WL7101\" | \"WL7102\" = \"WL7101\",\n): ToolingProblem {\n return createProblem({\n code,\n source: \"typescript\",\n severity: diagnostic.severity,\n message: diagnostic.message,\n file: diagnostic.fileName ? toPosix(path.relative(cwd, diagnostic.fileName)) : null,\n line: diagnostic.fileName ? diagnostic.line : null,\n column: diagnostic.fileName ? diagnostic.column : null,\n rule: `TS${diagnostic.code}`,\n });\n}\n\n/** Resolve direct importers with the native checker, including package exports and aliases. */\nexport function directImporters(\n session: CompilerSession,\n targets: ReadonlySet<string>,\n root: string,\n): Set<string> {\n const importers = new Set<string>();\n for (const file of session.fileNames) {\n if (!isProjectFile(file, root, false) || targets.has(file)) continue;\n if (session.importFiles(file).some((imported) => targets.has(path.resolve(imported))))\n importers.add(file);\n }\n return importers;\n}\n\n/** TypeScript 7's native incremental compiler owns build state; changed mode filters its diagnostics. */\nexport function runTypecheck(options: TypecheckOptions = {}): TypecheckResult {\n const startedAt = Date.now();\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const configPath = path.resolve(cwd, options.project ?? \"tsconfig.json\");\n const result = (\n problems: ToolingProblem[],\n rest: Partial<TypecheckResult> = {},\n ): TypecheckResult => ({\n ok: !problems.some((problem) => problem.severity === \"error\"),\n mode: options.files ? \"files\" : \"program\",\n files: null,\n checkedFiles: 0,\n problems,\n durationMs: Date.now() - startedAt,\n buildInfoFile: null,\n ...rest,\n });\n if (!existsSync(configPath))\n return result([\n createProblem({\n code: \"WL7102\",\n source: \"typescript\",\n message: `No TypeScript config at ${toPosix(path.relative(cwd, configPath)) || configPath}.`,\n file: toPosix(path.relative(cwd, configPath)),\n }),\n ]);\n const buildInfoFile = path.resolve(cwd, options.buildInfoFile ?? DEFAULT_TYPECHECK_BUILD_INFO);\n mkdirSync(path.dirname(buildInfoFile), { recursive: true });\n const compiled = runCompiler(cwd, [\n \"-p\",\n configPath,\n \"--noEmit\",\n \"--incremental\",\n \"--tsBuildInfoFile\",\n buildInfoFile,\n \"--composite\",\n \"false\",\n \"--emitDeclarationOnly\",\n \"false\",\n \"--listFiles\",\n ]);\n const sources = compiled.output\n .split(/\\r?\\n/)\n .map((line) => (path.isAbsolute(line) ? line : \"\"))\n .filter((file) => isProjectFile(file, cwd));\n const wanted = options.files\n ? new Set(options.files.map((file) => path.resolve(cwd, file)))\n : null;\n if (wanted) {\n const session = new CompilerSession(cwd, configPath);\n try {\n for (const file of directImporters(session, wanted, cwd)) wanted.add(file);\n } finally {\n session.close();\n }\n }\n const checked = sources.filter((file) => !wanted || wanted.has(file));\n const configCodes = new Set([18002, 18003]);\n const problems = [...compiled.diagnostics]\n .sort((a, b) => Number(b.fileName === configPath) - Number(a.fileName === configPath))\n .filter(\n (diagnostic) =>\n !diagnostic.fileName ||\n diagnostic.fileName === configPath ||\n (isProjectFile(diagnostic.fileName, cwd) && (!wanted || wanted.has(diagnostic.fileName))),\n )\n .map((diagnostic) =>\n diagnosticToProblem(\n diagnostic,\n cwd,\n !diagnostic.fileName ||\n diagnostic.fileName === configPath ||\n configCodes.has(diagnostic.code)\n ? \"WL7102\"\n : \"WL7101\",\n ),\n );\n return result(problems, {\n files: wanted ? checked.map((file) => toPosix(path.relative(cwd, file))).sort() : null,\n checkedFiles: checked.length,\n buildInfoFile: existsSync(buildInfoFile) ? buildInfoFile : null,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-typecheck.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/typecheck/run-typecheck.ts"],"sourcesContent":["import { existsSync, mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\nimport { CompilerSession, runCompiler, type CompilerDiagnostic } from \"../typescript/compiler\";\n\nexport type TypecheckOptions = {\n cwd?: string;\n project?: string;\n files?: readonly string[];\n buildInfoFile?: string;\n};\nexport type TypecheckResult = {\n ok: boolean;\n mode: \"program\" | \"files\";\n files: string[] | null;\n checkedFiles: number;\n problems: ToolingProblem[];\n durationMs: number;\n buildInfoFile: string | null;\n};\nexport const DEFAULT_TYPECHECK_BUILD_INFO = path.join(\".warlock\", \"cache\", \"typecheck.tsbuildinfo\");\nconst toPosix = (file: string) => file.split(path.sep).join(\"/\");\nfunction isProjectFile(file: string, root: string, declarations = true): boolean {\n return (\n file.startsWith(root + path.sep) &&\n !file.split(path.sep).includes(\"node_modules\") &&\n (declarations || !file.endsWith(\".d.ts\"))\n );\n}\nexport function diagnosticToProblem(\n diagnostic: CompilerDiagnostic,\n cwd: string,\n code: \"WL7101\" | \"WL7102\" = \"WL7101\",\n): ToolingProblem {\n return createProblem({\n code,\n source: \"typescript\",\n severity: diagnostic.severity,\n message: diagnostic.message,\n file: diagnostic.fileName ? toPosix(path.relative(cwd, diagnostic.fileName)) : null,\n line: diagnostic.fileName ? diagnostic.line : null,\n column: diagnostic.fileName ? diagnostic.column : null,\n rule: `TS${diagnostic.code}`,\n });\n}\n\n/** Resolve direct importers with the native checker, including package exports and aliases. */\nexport function directImporters(\n session: CompilerSession,\n targets: ReadonlySet<string>,\n root: string,\n): Set<string> {\n const importers = new Set<string>();\n for (const file of session.fileNames) {\n if (!isProjectFile(file, root, false) || targets.has(file)) continue;\n if (session.importFiles(file).some((imported) => targets.has(path.resolve(imported))))\n importers.add(file);\n }\n return importers;\n}\n\n/** TypeScript 7's native incremental compiler owns build state; changed mode filters its diagnostics. */\nexport function runTypecheck(options: TypecheckOptions = {}): TypecheckResult {\n const startedAt = Date.now();\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const configPath = path.resolve(cwd, options.project ?? \"tsconfig.json\");\n const result = (\n problems: ToolingProblem[],\n rest: Partial<TypecheckResult> = {},\n ): TypecheckResult => ({\n ok: !problems.some((problem) => problem.severity === \"error\"),\n mode: options.files ? \"files\" : \"program\",\n files: null,\n checkedFiles: 0,\n problems,\n durationMs: Date.now() - startedAt,\n buildInfoFile: null,\n ...rest,\n });\n if (!existsSync(configPath))\n return result([\n createProblem({\n code: \"WL7102\",\n source: \"typescript\",\n message: `No TypeScript config at ${toPosix(path.relative(cwd, configPath)) || configPath}.`,\n file: toPosix(path.relative(cwd, configPath)),\n }),\n ]);\n const buildInfoFile = path.resolve(cwd, options.buildInfoFile ?? DEFAULT_TYPECHECK_BUILD_INFO);\n mkdirSync(path.dirname(buildInfoFile), { recursive: true });\n const compiled = runCompiler(cwd, [\n \"-p\",\n configPath,\n \"--noEmit\",\n \"--incremental\",\n \"--tsBuildInfoFile\",\n buildInfoFile,\n \"--composite\",\n \"false\",\n \"--emitDeclarationOnly\",\n \"false\",\n \"--listFiles\",\n ]);\n const sources = compiled.output\n .split(/\\r?\\n/)\n .map((line) => (path.isAbsolute(line) ? line : \"\"))\n .filter((file) => isProjectFile(file, cwd));\n const wanted = options.files\n ? new Set(options.files.map((file) => path.resolve(cwd, file)))\n : null;\n if (wanted) {\n const session = new CompilerSession(cwd, configPath);\n try {\n for (;;) {\n const importers = directImporters(session, wanted, cwd);\n if (!importers.size) break;\n for (const file of importers) wanted.add(file);\n }\n } finally {\n session.close();\n }\n }\n const checked = sources.filter((file) => !wanted || wanted.has(file));\n const configCodes = new Set([18002, 18003]);\n const problems = [...compiled.diagnostics]\n .sort((a, b) => Number(b.fileName === configPath) - Number(a.fileName === configPath))\n .filter(\n (diagnostic) =>\n !diagnostic.fileName ||\n diagnostic.fileName === configPath ||\n (isProjectFile(diagnostic.fileName, cwd) && (!wanted || wanted.has(diagnostic.fileName))),\n )\n .map((diagnostic) =>\n diagnosticToProblem(\n diagnostic,\n cwd,\n !diagnostic.fileName ||\n diagnostic.fileName === configPath ||\n configCodes.has(diagnostic.code)\n ? \"WL7102\"\n : \"WL7101\",\n ),\n );\n return result(problems, {\n files: wanted ? checked.map((file) => toPosix(path.relative(cwd, file))).sort() : null,\n checkedFiles: checked.length,\n buildInfoFile: existsSync(buildInfoFile) ? buildInfoFile : null,\n });\n}\n"],"mappings":"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"}
@@ -4,6 +4,7 @@ import { loggerSyncCalls } from "./logger-sync-calls.mjs";
4
4
  import { node2215, warlockVersions } from "./package-json.mjs";
5
5
  import { oxcToolchain, oxcToolchainRemoval } from "./oxc-toolchain.mjs";
6
6
  import { requestLogSnapshot } from "./request-log-snapshot.mjs";
7
+ import { viteVitest } from "./vite-vitest.mjs";
7
8
  import { aiAnthropicBody, aiCompactionOptIn, aiFinishReason, aiToolDispatch, rateLimitDefaults, sealContainerCatch, sealRefineOutput, sealUnionOutput, sealValidateResult } from "./review-sites.mjs";
8
9
 
9
10
  //#region ../../packages/core/src/upgrade/codemods/index.ts
@@ -29,6 +30,7 @@ const CODEMODS = [
29
30
  aiCompactionOptIn,
30
31
  oxcToolchain,
31
32
  oxcToolchainRemoval,
33
+ viteVitest,
32
34
  warlockVersions
33
35
  ];
34
36
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/index.ts"],"sourcesContent":["import type { Codemod } from \"../types\";\nimport { headViewport } from \"./head-viewport\";\nimport { loggerSyncCalls } from \"./logger-sync-calls\";\nimport { node2215, warlockVersions } from \"./package-json\";\nimport { oxcToolchain, oxcToolchainRemoval } from \"./oxc-toolchain\";\nimport { requestLogSnapshot } from \"./request-log-snapshot\";\nimport {\n aiAnthropicBody,\n aiCompactionOptIn,\n aiFinishReason,\n aiToolDispatch,\n rateLimitDefaults,\n sealContainerCatch,\n sealRefineOutput,\n sealUnionOutput,\n sealValidateResult,\n} from \"./review-sites\";\n\n/**\n * Every codemod, oldest release first. A codemod runs when the app upgrades\n * across its `version` (`from < version <= to`); `\"target\"` ones run on every\n * upgrade. Add one here for each breaking change, and name its id next to the\n * `**BREAKING**` entry in the package changelog.\n */\nexport const CODEMODS: readonly Codemod[] = [\n loggerSyncCalls,\n requestLogSnapshot,\n headViewport,\n sealValidateResult,\n aiToolDispatch,\n aiAnthropicBody,\n aiFinishReason,\n node2215,\n rateLimitDefaults,\n sealRefineOutput,\n sealContainerCatch,\n sealUnionOutput,\n aiCompactionOptIn,\n oxcToolchain,\n oxcToolchainRemoval,\n warlockVersions,\n];\n\nexport * from \"./helpers\";\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAa,WAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/index.ts"],"sourcesContent":["import type { Codemod } from \"../types\";\nimport { headViewport } from \"./head-viewport\";\nimport { loggerSyncCalls } from \"./logger-sync-calls\";\nimport { node2215, warlockVersions } from \"./package-json\";\nimport { oxcToolchain, oxcToolchainRemoval } from \"./oxc-toolchain\";\nimport { requestLogSnapshot } from \"./request-log-snapshot\";\nimport { viteVitest } from \"./vite-vitest\";\nimport {\n aiAnthropicBody,\n aiCompactionOptIn,\n aiFinishReason,\n aiToolDispatch,\n rateLimitDefaults,\n sealContainerCatch,\n sealRefineOutput,\n sealUnionOutput,\n sealValidateResult,\n} from \"./review-sites\";\n\n/**\n * Every codemod, oldest release first. A codemod runs when the app upgrades\n * across its `version` (`from < version <= to`); `\"target\"` ones run on every\n * upgrade. Add one here for each breaking change, and name its id next to the\n * `**BREAKING**` entry in the package changelog.\n */\nexport const CODEMODS: readonly Codemod[] = [\n loggerSyncCalls,\n requestLogSnapshot,\n headViewport,\n sealValidateResult,\n aiToolDispatch,\n aiAnthropicBody,\n aiFinishReason,\n node2215,\n rateLimitDefaults,\n sealRefineOutput,\n sealContainerCatch,\n sealUnionOutput,\n aiCompactionOptIn,\n oxcToolchain,\n oxcToolchainRemoval,\n viteVitest,\n warlockVersions,\n];\n\nexport * from \"./helpers\";\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,MAAa,WAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
@@ -0,0 +1,170 @@
1
+ import { manualAt, replaceNode } from "./helpers.mjs";
2
+ import { parseVersion } from "../version.mjs";
3
+
4
+ //#region ../../packages/core/src/upgrade/codemods/vite-vitest.ts
5
+ /**
6
+ * The newest releases that are at least two weeks old on the day 5.36 shipped;
7
+ * `warlock add test` and `warlock add web` install the same ones.
8
+ */
9
+ const TARGETS = {
10
+ vite: {
11
+ major: 8,
12
+ version: "8.3.3"
13
+ },
14
+ vitest: {
15
+ major: 5,
16
+ version: "5.0.3"
17
+ },
18
+ "@vitest/coverage-v8": {
19
+ major: 5,
20
+ version: "5.0.3"
21
+ },
22
+ "@vitest/coverage-istanbul": {
23
+ major: 5,
24
+ version: "5.0.3"
25
+ },
26
+ "@vitest/ui": {
27
+ major: 5,
28
+ version: "5.0.3"
29
+ },
30
+ "@vitejs/plugin-react": {
31
+ major: 6,
32
+ version: "6.1.2"
33
+ }
34
+ };
35
+ const MIGRATION_GUIDES = "https://vite.dev/guide/migration and https://vitest.dev/guide/migration";
36
+ /** Version specs that are not a plain registry range and must not be rewritten. */
37
+ const NON_REGISTRY_SPEC = /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\*$|^latest$/;
38
+ /** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */
39
+ const REMOVED_ENTRY_POINTS = /* @__PURE__ */ new Set([
40
+ "vitest/coverage",
41
+ "vitest/reporters",
42
+ "vitest/environments",
43
+ "vitest/snapshot",
44
+ "vitest/suite",
45
+ "vitest/mocker",
46
+ "vitest/internal/module-runner"
47
+ ]);
48
+ const VITE_CONFIG_FILE = /^vite(st)?\.config\.(ts|mts|cts|js|mjs|cjs)$/;
49
+ function serialize(original, value) {
50
+ const indent = /^[ \t]+(?=")/m.exec(original)?.[0];
51
+ return (indent ? JSON.stringify(value, null, indent) : JSON.stringify(value)) + (original.endsWith("\n") ? "\n" : "");
52
+ }
53
+ function lineOf(source, needle) {
54
+ const index = source.indexOf(needle);
55
+ return index < 0 ? 1 : source.slice(0, index).split("\n").length;
56
+ }
57
+ /** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */
58
+ function upgradedSpec(spec, target) {
59
+ if (NON_REGISTRY_SPEC.test(spec)) return void 0;
60
+ const parsed = parseVersion(spec);
61
+ if (!parsed || parsed[0] >= target.major) return void 0;
62
+ return `${/^[\^~]/.exec(spec)?.[0] ?? (/^\d/.test(spec) ? "" : "^")}${target.version}`;
63
+ }
64
+ /**
65
+ * 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one
66
+ * family and must move together.
67
+ *
68
+ * - package.json: raises `vite`, `vitest`, `@vitest/*` and
69
+ * `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and
70
+ * `resolutions`) to the supported majors.
71
+ * - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the
72
+ * runner class is `TestRunner`, exported from `vitest`, and
73
+ * `onCleanupWorkerContext` is an optional property.
74
+ * - Lists removed Vitest entry points, a top-level `esbuild` option, and object
75
+ * `manualChunks` in Vite configs for review.
76
+ */
77
+ const viteVitest = {
78
+ id: "vite-8-vitest-5",
79
+ version: "5.36.0",
80
+ package: "@warlock.js/core",
81
+ title: "Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6",
82
+ skill: "upgrade-from-5-29",
83
+ run(context) {
84
+ const { Node, SyntaxKind } = context.tsMorph;
85
+ const output = {
86
+ edits: [],
87
+ manual: []
88
+ };
89
+ const source = context.text.read("package.json");
90
+ if (source !== void 0) {
91
+ const pkg = JSON.parse(source);
92
+ const changes = [];
93
+ for (const field of [
94
+ "dependencies",
95
+ "devDependencies",
96
+ "overrides",
97
+ "resolutions"
98
+ ]) {
99
+ const entries = pkg[field];
100
+ if (!entries) continue;
101
+ for (const [name, target] of Object.entries(TARGETS)) {
102
+ const spec = entries[name];
103
+ if (typeof spec !== "string") continue;
104
+ const next = upgradedSpec(spec, target);
105
+ if (!next) continue;
106
+ entries[name] = next;
107
+ changes.push({
108
+ name: field === "dependencies" || field === "devDependencies" ? name : `${field}.${name}`,
109
+ from: spec,
110
+ to: next
111
+ });
112
+ }
113
+ }
114
+ const first = changes[0];
115
+ if (first) context.text.write("package.json", serialize(source, pkg), {
116
+ line: lineOf(source, `"${first.name.replace(/^[a-z]+\./, "")}"`),
117
+ description: changes.map((change) => `${change.name} ${change.from} → ${change.to}`).join(", ")
118
+ });
119
+ }
120
+ for (const sourceFile of context.sourceFiles) for (const declaration of sourceFile.getImportDeclarations()) {
121
+ const moduleName = declaration.getModuleSpecifierValue();
122
+ if (REMOVED_ENTRY_POINTS.has(moduleName)) {
123
+ output.manual.push(manualAt(context, declaration, `Vitest 5 removed the "${moduleName}" entry point. Import from "vitest" or "vitest/node" instead; see ${MIGRATION_GUIDES}.`));
124
+ continue;
125
+ }
126
+ if (moduleName !== "vitest/runners") continue;
127
+ const specifiers = declaration.getNamedImports();
128
+ const runner = specifiers.find((specifier) => specifier.getName() === "VitestTestRunner");
129
+ if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {
130
+ output.manual.push(manualAt(context, declaration, "Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist."));
131
+ continue;
132
+ }
133
+ const alias = runner.getAliasNode()?.getText();
134
+ output.edits.push(replaceNode(context, declaration, alias ? `import { TestRunner as ${alias} } from "vitest";` : "import { TestRunner } from \"vitest\";", "imported the runner as TestRunner from \"vitest\""));
135
+ if (!alias) for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {
136
+ if (identifier.getText() !== "VitestTestRunner") continue;
137
+ if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;
138
+ output.edits.push(replaceNode(context, identifier, "TestRunner", "renamed VitestTestRunner to TestRunner"));
139
+ }
140
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
141
+ const callee = call.getExpression();
142
+ if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "onCleanupWorkerContext" || call.getQuestionDotTokenNode()) continue;
143
+ output.edits.push(replaceNode(context, callee, `${callee.getText()}?.`, "onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)"));
144
+ }
145
+ }
146
+ for (const file of context.text.list()) {
147
+ if (!VITE_CONFIG_FILE.test(file)) continue;
148
+ const config = context.text.read(file);
149
+ if (config === void 0) continue;
150
+ config.split("\n").forEach((text, index) => {
151
+ const line = index + 1;
152
+ if (/^\s*esbuild\s*:/.test(text)) output.manual.push({
153
+ file,
154
+ line,
155
+ hint: "Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules."
156
+ });
157
+ if (/\bmanualChunks\s*:\s*\{/.test(text)) output.manual.push({
158
+ file,
159
+ line,
160
+ hint: "Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`."
161
+ });
162
+ });
163
+ }
164
+ return output;
165
+ }
166
+ };
167
+
168
+ //#endregion
169
+ export { viteVitest };
170
+ //# sourceMappingURL=vite-vitest.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-vitest.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/vite-vitest.ts"],"sourcesContent":["import type { Codemod, CodemodOutput } from \"../types\";\nimport { parseVersion } from \"../version\";\nimport { manualAt, replaceNode } from \"./helpers\";\n\n/**\n * The newest releases that are at least two weeks old on the day 5.36 shipped;\n * `warlock add test` and `warlock add web` install the same ones.\n */\nconst TARGETS: Record<string, { major: number; version: string }> = {\n vite: { major: 8, version: \"8.3.3\" },\n vitest: { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-v8\": { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-istanbul\": { major: 5, version: \"5.0.3\" },\n \"@vitest/ui\": { major: 5, version: \"5.0.3\" },\n \"@vitejs/plugin-react\": { major: 6, version: \"6.1.2\" },\n};\n\nconst MIGRATION_GUIDES = \"https://vite.dev/guide/migration and https://vitest.dev/guide/migration\";\n\n/** Version specs that are not a plain registry range and must not be rewritten. */\nconst NON_REGISTRY_SPEC =\n /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\\*$|^latest$/;\n\n/** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */\nconst REMOVED_ENTRY_POINTS = new Set([\n \"vitest/coverage\",\n \"vitest/reporters\",\n \"vitest/environments\",\n \"vitest/snapshot\",\n \"vitest/suite\",\n \"vitest/mocker\",\n \"vitest/internal/module-runner\",\n]);\n\nconst VITE_CONFIG_FILE = /^vite(st)?\\.config\\.(ts|mts|cts|js|mjs|cjs)$/;\n\ntype PackageJson = Record<string, unknown> & {\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n overrides?: Record<string, unknown>;\n resolutions?: Record<string, unknown>;\n};\n\nfunction serialize(original: string, value: unknown): string {\n const indent = /^[ \\t]+(?=\")/m.exec(original)?.[0];\n const json = indent ? JSON.stringify(value, null, indent) : JSON.stringify(value);\n return json + (original.endsWith(\"\\n\") ? \"\\n\" : \"\");\n}\n\nfunction lineOf(source: string, needle: string): number {\n const index = source.indexOf(needle);\n return index < 0 ? 1 : source.slice(0, index).split(\"\\n\").length;\n}\n\n/** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */\nfunction upgradedSpec(spec: string, target: { major: number; version: string }) {\n if (NON_REGISTRY_SPEC.test(spec)) return undefined;\n const parsed = parseVersion(spec);\n // `>=7.3.5 <8` and ranges like it are rewritten too: the lower bound is what is read.\n if (!parsed || parsed[0] >= target.major) return undefined;\n const prefix = /^[\\^~]/.exec(spec)?.[0] ?? (/^\\d/.test(spec) ? \"\" : \"^\");\n return `${prefix}${target.version}`;\n}\n\n/**\n * 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one\n * family and must move together.\n *\n * - package.json: raises `vite`, `vitest`, `@vitest/*` and\n * `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and\n * `resolutions`) to the supported majors.\n * - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the\n * runner class is `TestRunner`, exported from `vitest`, and\n * `onCleanupWorkerContext` is an optional property.\n * - Lists removed Vitest entry points, a top-level `esbuild` option, and object\n * `manualChunks` in Vite configs for review.\n */\nexport const viteVitest: Codemod = {\n id: \"vite-8-vitest-5\",\n version: \"5.36.0\",\n package: \"@warlock.js/core\",\n title: \"Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6\",\n skill: \"upgrade-from-5-29\",\n run(context) {\n const { Node, SyntaxKind } = context.tsMorph;\n const output: CodemodOutput = { edits: [], manual: [] };\n\n const source = context.text.read(\"package.json\");\n if (source !== undefined) {\n const pkg = JSON.parse(source) as PackageJson;\n const changes: { name: string; from: string; to: string }[] = [];\n for (const field of [\n \"dependencies\",\n \"devDependencies\",\n \"overrides\",\n \"resolutions\",\n ] as const) {\n const entries = pkg[field];\n if (!entries) continue;\n for (const [name, target] of Object.entries(TARGETS)) {\n const spec = entries[name];\n if (typeof spec !== \"string\") continue;\n const next = upgradedSpec(spec, target);\n if (!next) continue;\n entries[name] = next;\n changes.push({\n name:\n field === \"dependencies\" || field === \"devDependencies\" ? name : `${field}.${name}`,\n from: spec,\n to: next,\n });\n }\n }\n const first = changes[0];\n if (first) {\n context.text.write(\"package.json\", serialize(source, pkg), {\n line: lineOf(source, `\"${first.name.replace(/^[a-z]+\\./, \"\")}\"`),\n description: changes\n .map((change) => `${change.name} ${change.from} → ${change.to}`)\n .join(\", \"),\n });\n }\n }\n\n for (const sourceFile of context.sourceFiles) {\n for (const declaration of sourceFile.getImportDeclarations()) {\n const moduleName = declaration.getModuleSpecifierValue();\n\n if (REMOVED_ENTRY_POINTS.has(moduleName)) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n `Vitest 5 removed the \"${moduleName}\" entry point. Import from \"vitest\" or \"vitest/node\" instead; see ${MIGRATION_GUIDES}.`,\n ),\n );\n continue;\n }\n if (moduleName !== \"vitest/runners\") continue;\n\n const specifiers = declaration.getNamedImports();\n const runner = specifiers.find((specifier) => specifier.getName() === \"VitestTestRunner\");\n if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n 'Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist.',\n ),\n );\n continue;\n }\n\n const alias = runner.getAliasNode()?.getText();\n output.edits.push(\n replaceNode(\n context,\n declaration,\n alias\n ? `import { TestRunner as ${alias} } from \"vitest\";`\n : 'import { TestRunner } from \"vitest\";',\n 'imported the runner as TestRunner from \"vitest\"',\n ),\n );\n\n if (!alias) {\n for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (identifier.getText() !== \"VitestTestRunner\") continue;\n if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;\n output.edits.push(\n replaceNode(\n context,\n identifier,\n \"TestRunner\",\n \"renamed VitestTestRunner to TestRunner\",\n ),\n );\n }\n }\n\n for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {\n const callee = call.getExpression();\n if (\n !Node.isPropertyAccessExpression(callee) ||\n callee.getName() !== \"onCleanupWorkerContext\" ||\n call.getQuestionDotTokenNode()\n ) {\n continue;\n }\n output.edits.push(\n replaceNode(\n context,\n callee,\n `${callee.getText()}?.`,\n \"onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)\",\n ),\n );\n }\n }\n }\n\n for (const file of context.text.list()) {\n if (!VITE_CONFIG_FILE.test(file)) continue;\n const config = context.text.read(file);\n if (config === undefined) continue;\n const lines = config.split(\"\\n\");\n lines.forEach((text, index) => {\n const line = index + 1;\n if (/^\\s*esbuild\\s*:/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules.\",\n });\n }\n if (/\\bmanualChunks\\s*:\\s*\\{/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`.\",\n });\n }\n });\n }\n\n return output;\n },\n};\n"],"mappings":"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"}
@@ -4,6 +4,10 @@
4
4
  * operations. Printed so the rollout plans for them.
5
5
  */
6
6
  const UPGRADE_NOTES = [
7
+ {
8
+ version: "5.36.0",
9
+ text: "Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown."
10
+ },
7
11
  {
8
12
  version: "5.34.0",
9
13
  text: "ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects."
@@ -1 +1 @@
1
- {"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
1
+ {"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.36.0\",\n text: \"Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown.\",\n },\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
@@ -1,2 +1,4 @@
1
1
  import { DecoratorLoweringPlugin, lowerStage3Decorators } from "./lower-stage3-decorators.mjs";
2
- export { DecoratorLoweringPlugin, lowerStage3Decorators };
2
+ import { recommendedTestWorkers } from "./test-workers.mjs";
3
+ import { TestSetupReporter } from "./test-setup-reporter.mjs";
4
+ export { DecoratorLoweringPlugin, TestSetupReporter, lowerStage3Decorators, recommendedTestWorkers };
@@ -1,3 +1,5 @@
1
1
  import { lowerStage3Decorators } from "./lower-stage3-decorators.mjs";
2
+ import { recommendedTestWorkers } from "./test-workers.mjs";
3
+ import { TestSetupReporter } from "./test-setup-reporter.mjs";
2
4
 
3
- export { lowerStage3Decorators };
5
+ export { TestSetupReporter, lowerStage3Decorators, recommendedTestWorkers };
@@ -0,0 +1,16 @@
1
+ //#region ../../packages/core/src/vite/test-setup-reporter.d.ts
2
+ /** Vitest reporter shape, with no runtime dependency on Vitest in the config helper. */
3
+ export declare class TestSetupReporter {
4
+ private files;
5
+ private bootstraps;
6
+ private workers;
7
+ private totals;
8
+ private shutdowns;
9
+ onTestRunStart(): void;
10
+ onUserConsoleLog(log: {
11
+ content: string;
12
+ }): void;
13
+ onTestRunEnd(): void;
14
+ }
15
+ //#endregion
16
+ //# sourceMappingURL=test-setup-reporter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup-reporter.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/vite/test-setup-reporter.ts"],"mappings":";;qBACa;UACH;UACA;UACA;UACA;UACA;EAED;EAQA,iBAAiB;IAAO;;EA6BxB"}