amd-gaia 0.14.1__py3-none-any.whl

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 (800) hide show
  1. amd_gaia-0.14.1.dist-info/METADATA +768 -0
  2. amd_gaia-0.14.1.dist-info/RECORD +800 -0
  3. amd_gaia-0.14.1.dist-info/WHEEL +5 -0
  4. amd_gaia-0.14.1.dist-info/entry_points.txt +5 -0
  5. amd_gaia-0.14.1.dist-info/licenses/LICENSE.md +21 -0
  6. amd_gaia-0.14.1.dist-info/top_level.txt +1 -0
  7. gaia/__init__.py +2 -0
  8. gaia/agents/__init__.py +19 -0
  9. gaia/agents/base/__init__.py +9 -0
  10. gaia/agents/base/agent.py +2072 -0
  11. gaia/agents/base/api_agent.py +120 -0
  12. gaia/agents/base/console.py +1457 -0
  13. gaia/agents/base/mcp_agent.py +86 -0
  14. gaia/agents/base/tools.py +83 -0
  15. gaia/agents/blender/agent.py +556 -0
  16. gaia/agents/blender/agent_simple.py +135 -0
  17. gaia/agents/blender/app.py +211 -0
  18. gaia/agents/blender/app_simple.py +41 -0
  19. gaia/agents/blender/core/__init__.py +16 -0
  20. gaia/agents/blender/core/materials.py +506 -0
  21. gaia/agents/blender/core/objects.py +316 -0
  22. gaia/agents/blender/core/rendering.py +225 -0
  23. gaia/agents/blender/core/scene.py +220 -0
  24. gaia/agents/blender/core/view.py +146 -0
  25. gaia/agents/chat/__init__.py +9 -0
  26. gaia/agents/chat/agent.py +975 -0
  27. gaia/agents/chat/app.py +1058 -0
  28. gaia/agents/chat/session.py +508 -0
  29. gaia/agents/chat/tools/__init__.py +15 -0
  30. gaia/agents/chat/tools/file_tools.py +96 -0
  31. gaia/agents/chat/tools/rag_tools.py +1729 -0
  32. gaia/agents/chat/tools/shell_tools.py +436 -0
  33. gaia/agents/code/__init__.py +7 -0
  34. gaia/agents/code/agent.py +547 -0
  35. gaia/agents/code/app.py +266 -0
  36. gaia/agents/code/models.py +135 -0
  37. gaia/agents/code/orchestration/__init__.py +24 -0
  38. gaia/agents/code/orchestration/checklist_executor.py +1739 -0
  39. gaia/agents/code/orchestration/checklist_generator.py +709 -0
  40. gaia/agents/code/orchestration/factories/__init__.py +9 -0
  41. gaia/agents/code/orchestration/factories/base.py +63 -0
  42. gaia/agents/code/orchestration/factories/nextjs_factory.py +118 -0
  43. gaia/agents/code/orchestration/factories/python_factory.py +106 -0
  44. gaia/agents/code/orchestration/orchestrator.py +610 -0
  45. gaia/agents/code/orchestration/project_analyzer.py +391 -0
  46. gaia/agents/code/orchestration/steps/__init__.py +67 -0
  47. gaia/agents/code/orchestration/steps/base.py +188 -0
  48. gaia/agents/code/orchestration/steps/error_handler.py +314 -0
  49. gaia/agents/code/orchestration/steps/nextjs.py +828 -0
  50. gaia/agents/code/orchestration/steps/python.py +307 -0
  51. gaia/agents/code/orchestration/template_catalog.py +463 -0
  52. gaia/agents/code/orchestration/workflows/__init__.py +14 -0
  53. gaia/agents/code/orchestration/workflows/base.py +80 -0
  54. gaia/agents/code/orchestration/workflows/nextjs.py +186 -0
  55. gaia/agents/code/orchestration/workflows/python.py +94 -0
  56. gaia/agents/code/prompts/__init__.py +11 -0
  57. gaia/agents/code/prompts/base_prompt.py +77 -0
  58. gaia/agents/code/prompts/code_patterns.py +1925 -0
  59. gaia/agents/code/prompts/nextjs_prompt.py +40 -0
  60. gaia/agents/code/prompts/python_prompt.py +109 -0
  61. gaia/agents/code/schema_inference.py +365 -0
  62. gaia/agents/code/system_prompt.py +41 -0
  63. gaia/agents/code/tools/__init__.py +42 -0
  64. gaia/agents/code/tools/cli_tools.py +1138 -0
  65. gaia/agents/code/tools/code_formatting.py +319 -0
  66. gaia/agents/code/tools/code_tools.py +769 -0
  67. gaia/agents/code/tools/error_fixing.py +1347 -0
  68. gaia/agents/code/tools/external_tools.py +180 -0
  69. gaia/agents/code/tools/file_io.py +845 -0
  70. gaia/agents/code/tools/prisma_tools.py +190 -0
  71. gaia/agents/code/tools/project_management.py +1016 -0
  72. gaia/agents/code/tools/testing.py +321 -0
  73. gaia/agents/code/tools/typescript_tools.py +122 -0
  74. gaia/agents/code/tools/validation_parsing.py +461 -0
  75. gaia/agents/code/tools/validation_tools.py +803 -0
  76. gaia/agents/code/tools/web_dev_tools.py +1744 -0
  77. gaia/agents/code/validators/__init__.py +16 -0
  78. gaia/agents/code/validators/antipattern_checker.py +241 -0
  79. gaia/agents/code/validators/ast_analyzer.py +197 -0
  80. gaia/agents/code/validators/requirements_validator.py +145 -0
  81. gaia/agents/code/validators/syntax_validator.py +171 -0
  82. gaia/agents/docker/__init__.py +7 -0
  83. gaia/agents/docker/agent.py +642 -0
  84. gaia/agents/jira/__init__.py +11 -0
  85. gaia/agents/jira/agent.py +894 -0
  86. gaia/agents/jira/jql_templates.py +299 -0
  87. gaia/agents/routing/__init__.py +7 -0
  88. gaia/agents/routing/agent.py +512 -0
  89. gaia/agents/routing/system_prompt.py +75 -0
  90. gaia/api/__init__.py +23 -0
  91. gaia/api/agent_registry.py +238 -0
  92. gaia/api/app.py +305 -0
  93. gaia/api/openai_server.py +575 -0
  94. gaia/api/schemas.py +186 -0
  95. gaia/api/sse_handler.py +370 -0
  96. gaia/apps/__init__.py +4 -0
  97. gaia/apps/llm/__init__.py +6 -0
  98. gaia/apps/llm/app.py +169 -0
  99. gaia/apps/summarize/app.py +633 -0
  100. gaia/apps/summarize/html_viewer.py +133 -0
  101. gaia/apps/summarize/pdf_formatter.py +284 -0
  102. gaia/audio/__init__.py +2 -0
  103. gaia/audio/audio_client.py +439 -0
  104. gaia/audio/audio_recorder.py +269 -0
  105. gaia/audio/kokoro_tts.py +599 -0
  106. gaia/audio/whisper_asr.py +432 -0
  107. gaia/chat/__init__.py +16 -0
  108. gaia/chat/app.py +430 -0
  109. gaia/chat/prompts.py +522 -0
  110. gaia/chat/sdk.py +1200 -0
  111. gaia/cli.py +5621 -0
  112. gaia/eval/batch_experiment.py +2332 -0
  113. gaia/eval/claude.py +542 -0
  114. gaia/eval/config.py +37 -0
  115. gaia/eval/email_generator.py +512 -0
  116. gaia/eval/eval.py +3179 -0
  117. gaia/eval/groundtruth.py +1130 -0
  118. gaia/eval/transcript_generator.py +582 -0
  119. gaia/eval/webapp/README.md +168 -0
  120. gaia/eval/webapp/node_modules/.bin/mime +16 -0
  121. gaia/eval/webapp/node_modules/.bin/mime.cmd +17 -0
  122. gaia/eval/webapp/node_modules/.bin/mime.ps1 +28 -0
  123. gaia/eval/webapp/node_modules/.package-lock.json +865 -0
  124. gaia/eval/webapp/node_modules/accepts/HISTORY.md +243 -0
  125. gaia/eval/webapp/node_modules/accepts/LICENSE +23 -0
  126. gaia/eval/webapp/node_modules/accepts/README.md +140 -0
  127. gaia/eval/webapp/node_modules/accepts/index.js +238 -0
  128. gaia/eval/webapp/node_modules/accepts/package.json +47 -0
  129. gaia/eval/webapp/node_modules/array-flatten/LICENSE +21 -0
  130. gaia/eval/webapp/node_modules/array-flatten/README.md +43 -0
  131. gaia/eval/webapp/node_modules/array-flatten/array-flatten.js +64 -0
  132. gaia/eval/webapp/node_modules/array-flatten/package.json +39 -0
  133. gaia/eval/webapp/node_modules/body-parser/HISTORY.md +672 -0
  134. gaia/eval/webapp/node_modules/body-parser/LICENSE +23 -0
  135. gaia/eval/webapp/node_modules/body-parser/README.md +476 -0
  136. gaia/eval/webapp/node_modules/body-parser/SECURITY.md +25 -0
  137. gaia/eval/webapp/node_modules/body-parser/index.js +156 -0
  138. gaia/eval/webapp/node_modules/body-parser/lib/read.js +205 -0
  139. gaia/eval/webapp/node_modules/body-parser/lib/types/json.js +247 -0
  140. gaia/eval/webapp/node_modules/body-parser/lib/types/raw.js +101 -0
  141. gaia/eval/webapp/node_modules/body-parser/lib/types/text.js +121 -0
  142. gaia/eval/webapp/node_modules/body-parser/lib/types/urlencoded.js +307 -0
  143. gaia/eval/webapp/node_modules/body-parser/package.json +56 -0
  144. gaia/eval/webapp/node_modules/bytes/History.md +97 -0
  145. gaia/eval/webapp/node_modules/bytes/LICENSE +23 -0
  146. gaia/eval/webapp/node_modules/bytes/Readme.md +152 -0
  147. gaia/eval/webapp/node_modules/bytes/index.js +170 -0
  148. gaia/eval/webapp/node_modules/bytes/package.json +42 -0
  149. gaia/eval/webapp/node_modules/call-bind-apply-helpers/.eslintrc +17 -0
  150. gaia/eval/webapp/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +12 -0
  151. gaia/eval/webapp/node_modules/call-bind-apply-helpers/.nycrc +9 -0
  152. gaia/eval/webapp/node_modules/call-bind-apply-helpers/CHANGELOG.md +30 -0
  153. gaia/eval/webapp/node_modules/call-bind-apply-helpers/LICENSE +21 -0
  154. gaia/eval/webapp/node_modules/call-bind-apply-helpers/README.md +62 -0
  155. gaia/eval/webapp/node_modules/call-bind-apply-helpers/actualApply.d.ts +1 -0
  156. gaia/eval/webapp/node_modules/call-bind-apply-helpers/actualApply.js +10 -0
  157. gaia/eval/webapp/node_modules/call-bind-apply-helpers/applyBind.d.ts +19 -0
  158. gaia/eval/webapp/node_modules/call-bind-apply-helpers/applyBind.js +10 -0
  159. gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionApply.d.ts +1 -0
  160. gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionApply.js +4 -0
  161. gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionCall.d.ts +1 -0
  162. gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionCall.js +4 -0
  163. gaia/eval/webapp/node_modules/call-bind-apply-helpers/index.d.ts +64 -0
  164. gaia/eval/webapp/node_modules/call-bind-apply-helpers/index.js +15 -0
  165. gaia/eval/webapp/node_modules/call-bind-apply-helpers/package.json +85 -0
  166. gaia/eval/webapp/node_modules/call-bind-apply-helpers/reflectApply.d.ts +3 -0
  167. gaia/eval/webapp/node_modules/call-bind-apply-helpers/reflectApply.js +4 -0
  168. gaia/eval/webapp/node_modules/call-bind-apply-helpers/test/index.js +63 -0
  169. gaia/eval/webapp/node_modules/call-bind-apply-helpers/tsconfig.json +9 -0
  170. gaia/eval/webapp/node_modules/call-bound/.eslintrc +13 -0
  171. gaia/eval/webapp/node_modules/call-bound/.github/FUNDING.yml +12 -0
  172. gaia/eval/webapp/node_modules/call-bound/.nycrc +9 -0
  173. gaia/eval/webapp/node_modules/call-bound/CHANGELOG.md +42 -0
  174. gaia/eval/webapp/node_modules/call-bound/LICENSE +21 -0
  175. gaia/eval/webapp/node_modules/call-bound/README.md +53 -0
  176. gaia/eval/webapp/node_modules/call-bound/index.d.ts +94 -0
  177. gaia/eval/webapp/node_modules/call-bound/index.js +19 -0
  178. gaia/eval/webapp/node_modules/call-bound/package.json +99 -0
  179. gaia/eval/webapp/node_modules/call-bound/test/index.js +61 -0
  180. gaia/eval/webapp/node_modules/call-bound/tsconfig.json +10 -0
  181. gaia/eval/webapp/node_modules/content-disposition/HISTORY.md +60 -0
  182. gaia/eval/webapp/node_modules/content-disposition/LICENSE +22 -0
  183. gaia/eval/webapp/node_modules/content-disposition/README.md +142 -0
  184. gaia/eval/webapp/node_modules/content-disposition/index.js +458 -0
  185. gaia/eval/webapp/node_modules/content-disposition/package.json +44 -0
  186. gaia/eval/webapp/node_modules/content-type/HISTORY.md +29 -0
  187. gaia/eval/webapp/node_modules/content-type/LICENSE +22 -0
  188. gaia/eval/webapp/node_modules/content-type/README.md +94 -0
  189. gaia/eval/webapp/node_modules/content-type/index.js +225 -0
  190. gaia/eval/webapp/node_modules/content-type/package.json +42 -0
  191. gaia/eval/webapp/node_modules/cookie/LICENSE +24 -0
  192. gaia/eval/webapp/node_modules/cookie/README.md +317 -0
  193. gaia/eval/webapp/node_modules/cookie/SECURITY.md +25 -0
  194. gaia/eval/webapp/node_modules/cookie/index.js +334 -0
  195. gaia/eval/webapp/node_modules/cookie/package.json +44 -0
  196. gaia/eval/webapp/node_modules/cookie-signature/.npmignore +4 -0
  197. gaia/eval/webapp/node_modules/cookie-signature/History.md +38 -0
  198. gaia/eval/webapp/node_modules/cookie-signature/Readme.md +42 -0
  199. gaia/eval/webapp/node_modules/cookie-signature/index.js +51 -0
  200. gaia/eval/webapp/node_modules/cookie-signature/package.json +18 -0
  201. gaia/eval/webapp/node_modules/debug/.coveralls.yml +1 -0
  202. gaia/eval/webapp/node_modules/debug/.eslintrc +11 -0
  203. gaia/eval/webapp/node_modules/debug/.npmignore +9 -0
  204. gaia/eval/webapp/node_modules/debug/.travis.yml +14 -0
  205. gaia/eval/webapp/node_modules/debug/CHANGELOG.md +362 -0
  206. gaia/eval/webapp/node_modules/debug/LICENSE +19 -0
  207. gaia/eval/webapp/node_modules/debug/Makefile +50 -0
  208. gaia/eval/webapp/node_modules/debug/README.md +312 -0
  209. gaia/eval/webapp/node_modules/debug/component.json +19 -0
  210. gaia/eval/webapp/node_modules/debug/karma.conf.js +70 -0
  211. gaia/eval/webapp/node_modules/debug/node.js +1 -0
  212. gaia/eval/webapp/node_modules/debug/package.json +49 -0
  213. gaia/eval/webapp/node_modules/debug/src/browser.js +185 -0
  214. gaia/eval/webapp/node_modules/debug/src/debug.js +202 -0
  215. gaia/eval/webapp/node_modules/debug/src/index.js +10 -0
  216. gaia/eval/webapp/node_modules/debug/src/inspector-log.js +15 -0
  217. gaia/eval/webapp/node_modules/debug/src/node.js +248 -0
  218. gaia/eval/webapp/node_modules/depd/History.md +103 -0
  219. gaia/eval/webapp/node_modules/depd/LICENSE +22 -0
  220. gaia/eval/webapp/node_modules/depd/Readme.md +280 -0
  221. gaia/eval/webapp/node_modules/depd/index.js +538 -0
  222. gaia/eval/webapp/node_modules/depd/lib/browser/index.js +77 -0
  223. gaia/eval/webapp/node_modules/depd/package.json +45 -0
  224. gaia/eval/webapp/node_modules/destroy/LICENSE +23 -0
  225. gaia/eval/webapp/node_modules/destroy/README.md +63 -0
  226. gaia/eval/webapp/node_modules/destroy/index.js +209 -0
  227. gaia/eval/webapp/node_modules/destroy/package.json +48 -0
  228. gaia/eval/webapp/node_modules/dunder-proto/.eslintrc +5 -0
  229. gaia/eval/webapp/node_modules/dunder-proto/.github/FUNDING.yml +12 -0
  230. gaia/eval/webapp/node_modules/dunder-proto/.nycrc +13 -0
  231. gaia/eval/webapp/node_modules/dunder-proto/CHANGELOG.md +24 -0
  232. gaia/eval/webapp/node_modules/dunder-proto/LICENSE +21 -0
  233. gaia/eval/webapp/node_modules/dunder-proto/README.md +54 -0
  234. gaia/eval/webapp/node_modules/dunder-proto/get.d.ts +5 -0
  235. gaia/eval/webapp/node_modules/dunder-proto/get.js +30 -0
  236. gaia/eval/webapp/node_modules/dunder-proto/package.json +76 -0
  237. gaia/eval/webapp/node_modules/dunder-proto/set.d.ts +5 -0
  238. gaia/eval/webapp/node_modules/dunder-proto/set.js +35 -0
  239. gaia/eval/webapp/node_modules/dunder-proto/test/get.js +34 -0
  240. gaia/eval/webapp/node_modules/dunder-proto/test/index.js +4 -0
  241. gaia/eval/webapp/node_modules/dunder-proto/test/set.js +50 -0
  242. gaia/eval/webapp/node_modules/dunder-proto/tsconfig.json +9 -0
  243. gaia/eval/webapp/node_modules/ee-first/LICENSE +22 -0
  244. gaia/eval/webapp/node_modules/ee-first/README.md +80 -0
  245. gaia/eval/webapp/node_modules/ee-first/index.js +95 -0
  246. gaia/eval/webapp/node_modules/ee-first/package.json +29 -0
  247. gaia/eval/webapp/node_modules/encodeurl/LICENSE +22 -0
  248. gaia/eval/webapp/node_modules/encodeurl/README.md +109 -0
  249. gaia/eval/webapp/node_modules/encodeurl/index.js +60 -0
  250. gaia/eval/webapp/node_modules/encodeurl/package.json +40 -0
  251. gaia/eval/webapp/node_modules/es-define-property/.eslintrc +13 -0
  252. gaia/eval/webapp/node_modules/es-define-property/.github/FUNDING.yml +12 -0
  253. gaia/eval/webapp/node_modules/es-define-property/.nycrc +9 -0
  254. gaia/eval/webapp/node_modules/es-define-property/CHANGELOG.md +29 -0
  255. gaia/eval/webapp/node_modules/es-define-property/LICENSE +21 -0
  256. gaia/eval/webapp/node_modules/es-define-property/README.md +49 -0
  257. gaia/eval/webapp/node_modules/es-define-property/index.d.ts +3 -0
  258. gaia/eval/webapp/node_modules/es-define-property/index.js +14 -0
  259. gaia/eval/webapp/node_modules/es-define-property/package.json +81 -0
  260. gaia/eval/webapp/node_modules/es-define-property/test/index.js +56 -0
  261. gaia/eval/webapp/node_modules/es-define-property/tsconfig.json +10 -0
  262. gaia/eval/webapp/node_modules/es-errors/.eslintrc +5 -0
  263. gaia/eval/webapp/node_modules/es-errors/.github/FUNDING.yml +12 -0
  264. gaia/eval/webapp/node_modules/es-errors/CHANGELOG.md +40 -0
  265. gaia/eval/webapp/node_modules/es-errors/LICENSE +21 -0
  266. gaia/eval/webapp/node_modules/es-errors/README.md +55 -0
  267. gaia/eval/webapp/node_modules/es-errors/eval.d.ts +3 -0
  268. gaia/eval/webapp/node_modules/es-errors/eval.js +4 -0
  269. gaia/eval/webapp/node_modules/es-errors/index.d.ts +3 -0
  270. gaia/eval/webapp/node_modules/es-errors/index.js +4 -0
  271. gaia/eval/webapp/node_modules/es-errors/package.json +80 -0
  272. gaia/eval/webapp/node_modules/es-errors/range.d.ts +3 -0
  273. gaia/eval/webapp/node_modules/es-errors/range.js +4 -0
  274. gaia/eval/webapp/node_modules/es-errors/ref.d.ts +3 -0
  275. gaia/eval/webapp/node_modules/es-errors/ref.js +4 -0
  276. gaia/eval/webapp/node_modules/es-errors/syntax.d.ts +3 -0
  277. gaia/eval/webapp/node_modules/es-errors/syntax.js +4 -0
  278. gaia/eval/webapp/node_modules/es-errors/test/index.js +19 -0
  279. gaia/eval/webapp/node_modules/es-errors/tsconfig.json +49 -0
  280. gaia/eval/webapp/node_modules/es-errors/type.d.ts +3 -0
  281. gaia/eval/webapp/node_modules/es-errors/type.js +4 -0
  282. gaia/eval/webapp/node_modules/es-errors/uri.d.ts +3 -0
  283. gaia/eval/webapp/node_modules/es-errors/uri.js +4 -0
  284. gaia/eval/webapp/node_modules/es-object-atoms/.eslintrc +16 -0
  285. gaia/eval/webapp/node_modules/es-object-atoms/.github/FUNDING.yml +12 -0
  286. gaia/eval/webapp/node_modules/es-object-atoms/CHANGELOG.md +37 -0
  287. gaia/eval/webapp/node_modules/es-object-atoms/LICENSE +21 -0
  288. gaia/eval/webapp/node_modules/es-object-atoms/README.md +63 -0
  289. gaia/eval/webapp/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +3 -0
  290. gaia/eval/webapp/node_modules/es-object-atoms/RequireObjectCoercible.js +11 -0
  291. gaia/eval/webapp/node_modules/es-object-atoms/ToObject.d.ts +7 -0
  292. gaia/eval/webapp/node_modules/es-object-atoms/ToObject.js +10 -0
  293. gaia/eval/webapp/node_modules/es-object-atoms/index.d.ts +3 -0
  294. gaia/eval/webapp/node_modules/es-object-atoms/index.js +4 -0
  295. gaia/eval/webapp/node_modules/es-object-atoms/isObject.d.ts +3 -0
  296. gaia/eval/webapp/node_modules/es-object-atoms/isObject.js +6 -0
  297. gaia/eval/webapp/node_modules/es-object-atoms/package.json +80 -0
  298. gaia/eval/webapp/node_modules/es-object-atoms/test/index.js +38 -0
  299. gaia/eval/webapp/node_modules/es-object-atoms/tsconfig.json +6 -0
  300. gaia/eval/webapp/node_modules/escape-html/LICENSE +24 -0
  301. gaia/eval/webapp/node_modules/escape-html/Readme.md +43 -0
  302. gaia/eval/webapp/node_modules/escape-html/index.js +78 -0
  303. gaia/eval/webapp/node_modules/escape-html/package.json +24 -0
  304. gaia/eval/webapp/node_modules/etag/HISTORY.md +83 -0
  305. gaia/eval/webapp/node_modules/etag/LICENSE +22 -0
  306. gaia/eval/webapp/node_modules/etag/README.md +159 -0
  307. gaia/eval/webapp/node_modules/etag/index.js +131 -0
  308. gaia/eval/webapp/node_modules/etag/package.json +47 -0
  309. gaia/eval/webapp/node_modules/express/History.md +3656 -0
  310. gaia/eval/webapp/node_modules/express/LICENSE +24 -0
  311. gaia/eval/webapp/node_modules/express/Readme.md +260 -0
  312. gaia/eval/webapp/node_modules/express/index.js +11 -0
  313. gaia/eval/webapp/node_modules/express/lib/application.js +661 -0
  314. gaia/eval/webapp/node_modules/express/lib/express.js +116 -0
  315. gaia/eval/webapp/node_modules/express/lib/middleware/init.js +43 -0
  316. gaia/eval/webapp/node_modules/express/lib/middleware/query.js +47 -0
  317. gaia/eval/webapp/node_modules/express/lib/request.js +525 -0
  318. gaia/eval/webapp/node_modules/express/lib/response.js +1179 -0
  319. gaia/eval/webapp/node_modules/express/lib/router/index.js +673 -0
  320. gaia/eval/webapp/node_modules/express/lib/router/layer.js +181 -0
  321. gaia/eval/webapp/node_modules/express/lib/router/route.js +230 -0
  322. gaia/eval/webapp/node_modules/express/lib/utils.js +303 -0
  323. gaia/eval/webapp/node_modules/express/lib/view.js +182 -0
  324. gaia/eval/webapp/node_modules/express/package.json +102 -0
  325. gaia/eval/webapp/node_modules/finalhandler/HISTORY.md +210 -0
  326. gaia/eval/webapp/node_modules/finalhandler/LICENSE +22 -0
  327. gaia/eval/webapp/node_modules/finalhandler/README.md +147 -0
  328. gaia/eval/webapp/node_modules/finalhandler/SECURITY.md +25 -0
  329. gaia/eval/webapp/node_modules/finalhandler/index.js +341 -0
  330. gaia/eval/webapp/node_modules/finalhandler/package.json +47 -0
  331. gaia/eval/webapp/node_modules/forwarded/HISTORY.md +21 -0
  332. gaia/eval/webapp/node_modules/forwarded/LICENSE +22 -0
  333. gaia/eval/webapp/node_modules/forwarded/README.md +57 -0
  334. gaia/eval/webapp/node_modules/forwarded/index.js +90 -0
  335. gaia/eval/webapp/node_modules/forwarded/package.json +45 -0
  336. gaia/eval/webapp/node_modules/fresh/HISTORY.md +70 -0
  337. gaia/eval/webapp/node_modules/fresh/LICENSE +23 -0
  338. gaia/eval/webapp/node_modules/fresh/README.md +119 -0
  339. gaia/eval/webapp/node_modules/fresh/index.js +137 -0
  340. gaia/eval/webapp/node_modules/fresh/package.json +46 -0
  341. gaia/eval/webapp/node_modules/fs/README.md +9 -0
  342. gaia/eval/webapp/node_modules/fs/package.json +20 -0
  343. gaia/eval/webapp/node_modules/function-bind/.eslintrc +21 -0
  344. gaia/eval/webapp/node_modules/function-bind/.github/FUNDING.yml +12 -0
  345. gaia/eval/webapp/node_modules/function-bind/.github/SECURITY.md +3 -0
  346. gaia/eval/webapp/node_modules/function-bind/.nycrc +13 -0
  347. gaia/eval/webapp/node_modules/function-bind/CHANGELOG.md +136 -0
  348. gaia/eval/webapp/node_modules/function-bind/LICENSE +20 -0
  349. gaia/eval/webapp/node_modules/function-bind/README.md +46 -0
  350. gaia/eval/webapp/node_modules/function-bind/implementation.js +84 -0
  351. gaia/eval/webapp/node_modules/function-bind/index.js +5 -0
  352. gaia/eval/webapp/node_modules/function-bind/package.json +87 -0
  353. gaia/eval/webapp/node_modules/function-bind/test/.eslintrc +9 -0
  354. gaia/eval/webapp/node_modules/function-bind/test/index.js +252 -0
  355. gaia/eval/webapp/node_modules/get-intrinsic/.eslintrc +42 -0
  356. gaia/eval/webapp/node_modules/get-intrinsic/.github/FUNDING.yml +12 -0
  357. gaia/eval/webapp/node_modules/get-intrinsic/.nycrc +9 -0
  358. gaia/eval/webapp/node_modules/get-intrinsic/CHANGELOG.md +186 -0
  359. gaia/eval/webapp/node_modules/get-intrinsic/LICENSE +21 -0
  360. gaia/eval/webapp/node_modules/get-intrinsic/README.md +71 -0
  361. gaia/eval/webapp/node_modules/get-intrinsic/index.js +378 -0
  362. gaia/eval/webapp/node_modules/get-intrinsic/package.json +97 -0
  363. gaia/eval/webapp/node_modules/get-intrinsic/test/GetIntrinsic.js +274 -0
  364. gaia/eval/webapp/node_modules/get-proto/.eslintrc +10 -0
  365. gaia/eval/webapp/node_modules/get-proto/.github/FUNDING.yml +12 -0
  366. gaia/eval/webapp/node_modules/get-proto/.nycrc +9 -0
  367. gaia/eval/webapp/node_modules/get-proto/CHANGELOG.md +21 -0
  368. gaia/eval/webapp/node_modules/get-proto/LICENSE +21 -0
  369. gaia/eval/webapp/node_modules/get-proto/Object.getPrototypeOf.d.ts +5 -0
  370. gaia/eval/webapp/node_modules/get-proto/Object.getPrototypeOf.js +6 -0
  371. gaia/eval/webapp/node_modules/get-proto/README.md +50 -0
  372. gaia/eval/webapp/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +3 -0
  373. gaia/eval/webapp/node_modules/get-proto/Reflect.getPrototypeOf.js +4 -0
  374. gaia/eval/webapp/node_modules/get-proto/index.d.ts +5 -0
  375. gaia/eval/webapp/node_modules/get-proto/index.js +27 -0
  376. gaia/eval/webapp/node_modules/get-proto/package.json +81 -0
  377. gaia/eval/webapp/node_modules/get-proto/test/index.js +68 -0
  378. gaia/eval/webapp/node_modules/get-proto/tsconfig.json +9 -0
  379. gaia/eval/webapp/node_modules/gopd/.eslintrc +16 -0
  380. gaia/eval/webapp/node_modules/gopd/.github/FUNDING.yml +12 -0
  381. gaia/eval/webapp/node_modules/gopd/CHANGELOG.md +45 -0
  382. gaia/eval/webapp/node_modules/gopd/LICENSE +21 -0
  383. gaia/eval/webapp/node_modules/gopd/README.md +40 -0
  384. gaia/eval/webapp/node_modules/gopd/gOPD.d.ts +1 -0
  385. gaia/eval/webapp/node_modules/gopd/gOPD.js +4 -0
  386. gaia/eval/webapp/node_modules/gopd/index.d.ts +5 -0
  387. gaia/eval/webapp/node_modules/gopd/index.js +15 -0
  388. gaia/eval/webapp/node_modules/gopd/package.json +77 -0
  389. gaia/eval/webapp/node_modules/gopd/test/index.js +36 -0
  390. gaia/eval/webapp/node_modules/gopd/tsconfig.json +9 -0
  391. gaia/eval/webapp/node_modules/has-symbols/.eslintrc +11 -0
  392. gaia/eval/webapp/node_modules/has-symbols/.github/FUNDING.yml +12 -0
  393. gaia/eval/webapp/node_modules/has-symbols/.nycrc +9 -0
  394. gaia/eval/webapp/node_modules/has-symbols/CHANGELOG.md +91 -0
  395. gaia/eval/webapp/node_modules/has-symbols/LICENSE +21 -0
  396. gaia/eval/webapp/node_modules/has-symbols/README.md +46 -0
  397. gaia/eval/webapp/node_modules/has-symbols/index.d.ts +3 -0
  398. gaia/eval/webapp/node_modules/has-symbols/index.js +14 -0
  399. gaia/eval/webapp/node_modules/has-symbols/package.json +111 -0
  400. gaia/eval/webapp/node_modules/has-symbols/shams.d.ts +3 -0
  401. gaia/eval/webapp/node_modules/has-symbols/shams.js +45 -0
  402. gaia/eval/webapp/node_modules/has-symbols/test/index.js +22 -0
  403. gaia/eval/webapp/node_modules/has-symbols/test/shams/core-js.js +29 -0
  404. gaia/eval/webapp/node_modules/has-symbols/test/shams/get-own-property-symbols.js +29 -0
  405. gaia/eval/webapp/node_modules/has-symbols/test/tests.js +58 -0
  406. gaia/eval/webapp/node_modules/has-symbols/tsconfig.json +10 -0
  407. gaia/eval/webapp/node_modules/hasown/.eslintrc +5 -0
  408. gaia/eval/webapp/node_modules/hasown/.github/FUNDING.yml +12 -0
  409. gaia/eval/webapp/node_modules/hasown/.nycrc +13 -0
  410. gaia/eval/webapp/node_modules/hasown/CHANGELOG.md +40 -0
  411. gaia/eval/webapp/node_modules/hasown/LICENSE +21 -0
  412. gaia/eval/webapp/node_modules/hasown/README.md +40 -0
  413. gaia/eval/webapp/node_modules/hasown/index.d.ts +3 -0
  414. gaia/eval/webapp/node_modules/hasown/index.js +8 -0
  415. gaia/eval/webapp/node_modules/hasown/package.json +92 -0
  416. gaia/eval/webapp/node_modules/hasown/tsconfig.json +6 -0
  417. gaia/eval/webapp/node_modules/http-errors/HISTORY.md +180 -0
  418. gaia/eval/webapp/node_modules/http-errors/LICENSE +23 -0
  419. gaia/eval/webapp/node_modules/http-errors/README.md +169 -0
  420. gaia/eval/webapp/node_modules/http-errors/index.js +289 -0
  421. gaia/eval/webapp/node_modules/http-errors/package.json +50 -0
  422. gaia/eval/webapp/node_modules/iconv-lite/Changelog.md +162 -0
  423. gaia/eval/webapp/node_modules/iconv-lite/LICENSE +21 -0
  424. gaia/eval/webapp/node_modules/iconv-lite/README.md +156 -0
  425. gaia/eval/webapp/node_modules/iconv-lite/encodings/dbcs-codec.js +555 -0
  426. gaia/eval/webapp/node_modules/iconv-lite/encodings/dbcs-data.js +176 -0
  427. gaia/eval/webapp/node_modules/iconv-lite/encodings/index.js +22 -0
  428. gaia/eval/webapp/node_modules/iconv-lite/encodings/internal.js +188 -0
  429. gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-codec.js +72 -0
  430. gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-data-generated.js +451 -0
  431. gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-data.js +174 -0
  432. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/big5-added.json +122 -0
  433. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp936.json +264 -0
  434. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp949.json +273 -0
  435. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp950.json +177 -0
  436. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/eucjp.json +182 -0
  437. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +1 -0
  438. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/gbk-added.json +55 -0
  439. gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/shiftjis.json +125 -0
  440. gaia/eval/webapp/node_modules/iconv-lite/encodings/utf16.js +177 -0
  441. gaia/eval/webapp/node_modules/iconv-lite/encodings/utf7.js +290 -0
  442. gaia/eval/webapp/node_modules/iconv-lite/lib/bom-handling.js +52 -0
  443. gaia/eval/webapp/node_modules/iconv-lite/lib/extend-node.js +217 -0
  444. gaia/eval/webapp/node_modules/iconv-lite/lib/index.d.ts +24 -0
  445. gaia/eval/webapp/node_modules/iconv-lite/lib/index.js +153 -0
  446. gaia/eval/webapp/node_modules/iconv-lite/lib/streams.js +121 -0
  447. gaia/eval/webapp/node_modules/iconv-lite/package.json +46 -0
  448. gaia/eval/webapp/node_modules/inherits/LICENSE +16 -0
  449. gaia/eval/webapp/node_modules/inherits/README.md +42 -0
  450. gaia/eval/webapp/node_modules/inherits/inherits.js +9 -0
  451. gaia/eval/webapp/node_modules/inherits/inherits_browser.js +27 -0
  452. gaia/eval/webapp/node_modules/inherits/package.json +29 -0
  453. gaia/eval/webapp/node_modules/ipaddr.js/LICENSE +19 -0
  454. gaia/eval/webapp/node_modules/ipaddr.js/README.md +233 -0
  455. gaia/eval/webapp/node_modules/ipaddr.js/ipaddr.min.js +1 -0
  456. gaia/eval/webapp/node_modules/ipaddr.js/lib/ipaddr.js +673 -0
  457. gaia/eval/webapp/node_modules/ipaddr.js/lib/ipaddr.js.d.ts +68 -0
  458. gaia/eval/webapp/node_modules/ipaddr.js/package.json +35 -0
  459. gaia/eval/webapp/node_modules/math-intrinsics/.eslintrc +16 -0
  460. gaia/eval/webapp/node_modules/math-intrinsics/.github/FUNDING.yml +12 -0
  461. gaia/eval/webapp/node_modules/math-intrinsics/CHANGELOG.md +24 -0
  462. gaia/eval/webapp/node_modules/math-intrinsics/LICENSE +21 -0
  463. gaia/eval/webapp/node_modules/math-intrinsics/README.md +50 -0
  464. gaia/eval/webapp/node_modules/math-intrinsics/abs.d.ts +1 -0
  465. gaia/eval/webapp/node_modules/math-intrinsics/abs.js +4 -0
  466. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +3 -0
  467. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxArrayLength.js +4 -0
  468. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +3 -0
  469. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxSafeInteger.js +5 -0
  470. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxValue.d.ts +3 -0
  471. gaia/eval/webapp/node_modules/math-intrinsics/constants/maxValue.js +5 -0
  472. gaia/eval/webapp/node_modules/math-intrinsics/floor.d.ts +1 -0
  473. gaia/eval/webapp/node_modules/math-intrinsics/floor.js +4 -0
  474. gaia/eval/webapp/node_modules/math-intrinsics/isFinite.d.ts +3 -0
  475. gaia/eval/webapp/node_modules/math-intrinsics/isFinite.js +12 -0
  476. gaia/eval/webapp/node_modules/math-intrinsics/isInteger.d.ts +3 -0
  477. gaia/eval/webapp/node_modules/math-intrinsics/isInteger.js +16 -0
  478. gaia/eval/webapp/node_modules/math-intrinsics/isNaN.d.ts +1 -0
  479. gaia/eval/webapp/node_modules/math-intrinsics/isNaN.js +6 -0
  480. gaia/eval/webapp/node_modules/math-intrinsics/isNegativeZero.d.ts +3 -0
  481. gaia/eval/webapp/node_modules/math-intrinsics/isNegativeZero.js +6 -0
  482. gaia/eval/webapp/node_modules/math-intrinsics/max.d.ts +1 -0
  483. gaia/eval/webapp/node_modules/math-intrinsics/max.js +4 -0
  484. gaia/eval/webapp/node_modules/math-intrinsics/min.d.ts +1 -0
  485. gaia/eval/webapp/node_modules/math-intrinsics/min.js +4 -0
  486. gaia/eval/webapp/node_modules/math-intrinsics/mod.d.ts +3 -0
  487. gaia/eval/webapp/node_modules/math-intrinsics/mod.js +9 -0
  488. gaia/eval/webapp/node_modules/math-intrinsics/package.json +86 -0
  489. gaia/eval/webapp/node_modules/math-intrinsics/pow.d.ts +1 -0
  490. gaia/eval/webapp/node_modules/math-intrinsics/pow.js +4 -0
  491. gaia/eval/webapp/node_modules/math-intrinsics/round.d.ts +1 -0
  492. gaia/eval/webapp/node_modules/math-intrinsics/round.js +4 -0
  493. gaia/eval/webapp/node_modules/math-intrinsics/sign.d.ts +3 -0
  494. gaia/eval/webapp/node_modules/math-intrinsics/sign.js +11 -0
  495. gaia/eval/webapp/node_modules/math-intrinsics/test/index.js +192 -0
  496. gaia/eval/webapp/node_modules/math-intrinsics/tsconfig.json +3 -0
  497. gaia/eval/webapp/node_modules/media-typer/HISTORY.md +22 -0
  498. gaia/eval/webapp/node_modules/media-typer/LICENSE +22 -0
  499. gaia/eval/webapp/node_modules/media-typer/README.md +81 -0
  500. gaia/eval/webapp/node_modules/media-typer/index.js +270 -0
  501. gaia/eval/webapp/node_modules/media-typer/package.json +26 -0
  502. gaia/eval/webapp/node_modules/merge-descriptors/HISTORY.md +21 -0
  503. gaia/eval/webapp/node_modules/merge-descriptors/LICENSE +23 -0
  504. gaia/eval/webapp/node_modules/merge-descriptors/README.md +49 -0
  505. gaia/eval/webapp/node_modules/merge-descriptors/index.js +60 -0
  506. gaia/eval/webapp/node_modules/merge-descriptors/package.json +39 -0
  507. gaia/eval/webapp/node_modules/methods/HISTORY.md +29 -0
  508. gaia/eval/webapp/node_modules/methods/LICENSE +24 -0
  509. gaia/eval/webapp/node_modules/methods/README.md +51 -0
  510. gaia/eval/webapp/node_modules/methods/index.js +69 -0
  511. gaia/eval/webapp/node_modules/methods/package.json +36 -0
  512. gaia/eval/webapp/node_modules/mime/.npmignore +0 -0
  513. gaia/eval/webapp/node_modules/mime/CHANGELOG.md +164 -0
  514. gaia/eval/webapp/node_modules/mime/LICENSE +21 -0
  515. gaia/eval/webapp/node_modules/mime/README.md +90 -0
  516. gaia/eval/webapp/node_modules/mime/cli.js +8 -0
  517. gaia/eval/webapp/node_modules/mime/mime.js +108 -0
  518. gaia/eval/webapp/node_modules/mime/package.json +44 -0
  519. gaia/eval/webapp/node_modules/mime/src/build.js +53 -0
  520. gaia/eval/webapp/node_modules/mime/src/test.js +60 -0
  521. gaia/eval/webapp/node_modules/mime/types.json +1 -0
  522. gaia/eval/webapp/node_modules/mime-db/HISTORY.md +507 -0
  523. gaia/eval/webapp/node_modules/mime-db/LICENSE +23 -0
  524. gaia/eval/webapp/node_modules/mime-db/README.md +100 -0
  525. gaia/eval/webapp/node_modules/mime-db/db.json +8519 -0
  526. gaia/eval/webapp/node_modules/mime-db/index.js +12 -0
  527. gaia/eval/webapp/node_modules/mime-db/package.json +60 -0
  528. gaia/eval/webapp/node_modules/mime-types/HISTORY.md +397 -0
  529. gaia/eval/webapp/node_modules/mime-types/LICENSE +23 -0
  530. gaia/eval/webapp/node_modules/mime-types/README.md +113 -0
  531. gaia/eval/webapp/node_modules/mime-types/index.js +188 -0
  532. gaia/eval/webapp/node_modules/mime-types/package.json +44 -0
  533. gaia/eval/webapp/node_modules/ms/index.js +152 -0
  534. gaia/eval/webapp/node_modules/ms/license.md +21 -0
  535. gaia/eval/webapp/node_modules/ms/package.json +37 -0
  536. gaia/eval/webapp/node_modules/ms/readme.md +51 -0
  537. gaia/eval/webapp/node_modules/negotiator/HISTORY.md +108 -0
  538. gaia/eval/webapp/node_modules/negotiator/LICENSE +24 -0
  539. gaia/eval/webapp/node_modules/negotiator/README.md +203 -0
  540. gaia/eval/webapp/node_modules/negotiator/index.js +82 -0
  541. gaia/eval/webapp/node_modules/negotiator/lib/charset.js +169 -0
  542. gaia/eval/webapp/node_modules/negotiator/lib/encoding.js +184 -0
  543. gaia/eval/webapp/node_modules/negotiator/lib/language.js +179 -0
  544. gaia/eval/webapp/node_modules/negotiator/lib/mediaType.js +294 -0
  545. gaia/eval/webapp/node_modules/negotiator/package.json +42 -0
  546. gaia/eval/webapp/node_modules/object-inspect/.eslintrc +53 -0
  547. gaia/eval/webapp/node_modules/object-inspect/.github/FUNDING.yml +12 -0
  548. gaia/eval/webapp/node_modules/object-inspect/.nycrc +13 -0
  549. gaia/eval/webapp/node_modules/object-inspect/CHANGELOG.md +424 -0
  550. gaia/eval/webapp/node_modules/object-inspect/LICENSE +21 -0
  551. gaia/eval/webapp/node_modules/object-inspect/example/all.js +23 -0
  552. gaia/eval/webapp/node_modules/object-inspect/example/circular.js +6 -0
  553. gaia/eval/webapp/node_modules/object-inspect/example/fn.js +5 -0
  554. gaia/eval/webapp/node_modules/object-inspect/example/inspect.js +10 -0
  555. gaia/eval/webapp/node_modules/object-inspect/index.js +544 -0
  556. gaia/eval/webapp/node_modules/object-inspect/package-support.json +20 -0
  557. gaia/eval/webapp/node_modules/object-inspect/package.json +105 -0
  558. gaia/eval/webapp/node_modules/object-inspect/readme.markdown +84 -0
  559. gaia/eval/webapp/node_modules/object-inspect/test/bigint.js +58 -0
  560. gaia/eval/webapp/node_modules/object-inspect/test/browser/dom.js +15 -0
  561. gaia/eval/webapp/node_modules/object-inspect/test/circular.js +16 -0
  562. gaia/eval/webapp/node_modules/object-inspect/test/deep.js +12 -0
  563. gaia/eval/webapp/node_modules/object-inspect/test/element.js +53 -0
  564. gaia/eval/webapp/node_modules/object-inspect/test/err.js +48 -0
  565. gaia/eval/webapp/node_modules/object-inspect/test/fakes.js +29 -0
  566. gaia/eval/webapp/node_modules/object-inspect/test/fn.js +76 -0
  567. gaia/eval/webapp/node_modules/object-inspect/test/global.js +17 -0
  568. gaia/eval/webapp/node_modules/object-inspect/test/has.js +15 -0
  569. gaia/eval/webapp/node_modules/object-inspect/test/holes.js +15 -0
  570. gaia/eval/webapp/node_modules/object-inspect/test/indent-option.js +271 -0
  571. gaia/eval/webapp/node_modules/object-inspect/test/inspect.js +139 -0
  572. gaia/eval/webapp/node_modules/object-inspect/test/lowbyte.js +12 -0
  573. gaia/eval/webapp/node_modules/object-inspect/test/number.js +58 -0
  574. gaia/eval/webapp/node_modules/object-inspect/test/quoteStyle.js +26 -0
  575. gaia/eval/webapp/node_modules/object-inspect/test/toStringTag.js +40 -0
  576. gaia/eval/webapp/node_modules/object-inspect/test/undef.js +12 -0
  577. gaia/eval/webapp/node_modules/object-inspect/test/values.js +261 -0
  578. gaia/eval/webapp/node_modules/object-inspect/test-core-js.js +26 -0
  579. gaia/eval/webapp/node_modules/object-inspect/util.inspect.js +1 -0
  580. gaia/eval/webapp/node_modules/on-finished/HISTORY.md +98 -0
  581. gaia/eval/webapp/node_modules/on-finished/LICENSE +23 -0
  582. gaia/eval/webapp/node_modules/on-finished/README.md +162 -0
  583. gaia/eval/webapp/node_modules/on-finished/index.js +234 -0
  584. gaia/eval/webapp/node_modules/on-finished/package.json +39 -0
  585. gaia/eval/webapp/node_modules/parseurl/HISTORY.md +58 -0
  586. gaia/eval/webapp/node_modules/parseurl/LICENSE +24 -0
  587. gaia/eval/webapp/node_modules/parseurl/README.md +133 -0
  588. gaia/eval/webapp/node_modules/parseurl/index.js +158 -0
  589. gaia/eval/webapp/node_modules/parseurl/package.json +40 -0
  590. gaia/eval/webapp/node_modules/path/.npmignore +1 -0
  591. gaia/eval/webapp/node_modules/path/LICENSE +18 -0
  592. gaia/eval/webapp/node_modules/path/README.md +15 -0
  593. gaia/eval/webapp/node_modules/path/package.json +24 -0
  594. gaia/eval/webapp/node_modules/path/path.js +628 -0
  595. gaia/eval/webapp/node_modules/path-to-regexp/LICENSE +21 -0
  596. gaia/eval/webapp/node_modules/path-to-regexp/Readme.md +35 -0
  597. gaia/eval/webapp/node_modules/path-to-regexp/index.js +156 -0
  598. gaia/eval/webapp/node_modules/path-to-regexp/package.json +30 -0
  599. gaia/eval/webapp/node_modules/process/.eslintrc +21 -0
  600. gaia/eval/webapp/node_modules/process/LICENSE +22 -0
  601. gaia/eval/webapp/node_modules/process/README.md +26 -0
  602. gaia/eval/webapp/node_modules/process/browser.js +184 -0
  603. gaia/eval/webapp/node_modules/process/index.js +2 -0
  604. gaia/eval/webapp/node_modules/process/package.json +27 -0
  605. gaia/eval/webapp/node_modules/process/test.js +199 -0
  606. gaia/eval/webapp/node_modules/proxy-addr/HISTORY.md +161 -0
  607. gaia/eval/webapp/node_modules/proxy-addr/LICENSE +22 -0
  608. gaia/eval/webapp/node_modules/proxy-addr/README.md +139 -0
  609. gaia/eval/webapp/node_modules/proxy-addr/index.js +327 -0
  610. gaia/eval/webapp/node_modules/proxy-addr/package.json +47 -0
  611. gaia/eval/webapp/node_modules/qs/.editorconfig +46 -0
  612. gaia/eval/webapp/node_modules/qs/.eslintrc +38 -0
  613. gaia/eval/webapp/node_modules/qs/.github/FUNDING.yml +12 -0
  614. gaia/eval/webapp/node_modules/qs/.nycrc +13 -0
  615. gaia/eval/webapp/node_modules/qs/CHANGELOG.md +600 -0
  616. gaia/eval/webapp/node_modules/qs/LICENSE.md +29 -0
  617. gaia/eval/webapp/node_modules/qs/README.md +709 -0
  618. gaia/eval/webapp/node_modules/qs/dist/qs.js +90 -0
  619. gaia/eval/webapp/node_modules/qs/lib/formats.js +23 -0
  620. gaia/eval/webapp/node_modules/qs/lib/index.js +11 -0
  621. gaia/eval/webapp/node_modules/qs/lib/parse.js +296 -0
  622. gaia/eval/webapp/node_modules/qs/lib/stringify.js +351 -0
  623. gaia/eval/webapp/node_modules/qs/lib/utils.js +265 -0
  624. gaia/eval/webapp/node_modules/qs/package.json +91 -0
  625. gaia/eval/webapp/node_modules/qs/test/empty-keys-cases.js +267 -0
  626. gaia/eval/webapp/node_modules/qs/test/parse.js +1170 -0
  627. gaia/eval/webapp/node_modules/qs/test/stringify.js +1298 -0
  628. gaia/eval/webapp/node_modules/qs/test/utils.js +136 -0
  629. gaia/eval/webapp/node_modules/range-parser/HISTORY.md +56 -0
  630. gaia/eval/webapp/node_modules/range-parser/LICENSE +23 -0
  631. gaia/eval/webapp/node_modules/range-parser/README.md +84 -0
  632. gaia/eval/webapp/node_modules/range-parser/index.js +162 -0
  633. gaia/eval/webapp/node_modules/range-parser/package.json +44 -0
  634. gaia/eval/webapp/node_modules/raw-body/HISTORY.md +308 -0
  635. gaia/eval/webapp/node_modules/raw-body/LICENSE +22 -0
  636. gaia/eval/webapp/node_modules/raw-body/README.md +223 -0
  637. gaia/eval/webapp/node_modules/raw-body/SECURITY.md +24 -0
  638. gaia/eval/webapp/node_modules/raw-body/index.d.ts +87 -0
  639. gaia/eval/webapp/node_modules/raw-body/index.js +336 -0
  640. gaia/eval/webapp/node_modules/raw-body/package.json +49 -0
  641. gaia/eval/webapp/node_modules/safe-buffer/LICENSE +21 -0
  642. gaia/eval/webapp/node_modules/safe-buffer/README.md +584 -0
  643. gaia/eval/webapp/node_modules/safe-buffer/index.d.ts +187 -0
  644. gaia/eval/webapp/node_modules/safe-buffer/index.js +65 -0
  645. gaia/eval/webapp/node_modules/safe-buffer/package.json +51 -0
  646. gaia/eval/webapp/node_modules/safer-buffer/LICENSE +21 -0
  647. gaia/eval/webapp/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  648. gaia/eval/webapp/node_modules/safer-buffer/Readme.md +156 -0
  649. gaia/eval/webapp/node_modules/safer-buffer/dangerous.js +58 -0
  650. gaia/eval/webapp/node_modules/safer-buffer/package.json +34 -0
  651. gaia/eval/webapp/node_modules/safer-buffer/safer.js +77 -0
  652. gaia/eval/webapp/node_modules/safer-buffer/tests.js +406 -0
  653. gaia/eval/webapp/node_modules/send/HISTORY.md +526 -0
  654. gaia/eval/webapp/node_modules/send/LICENSE +23 -0
  655. gaia/eval/webapp/node_modules/send/README.md +327 -0
  656. gaia/eval/webapp/node_modules/send/SECURITY.md +24 -0
  657. gaia/eval/webapp/node_modules/send/index.js +1142 -0
  658. gaia/eval/webapp/node_modules/send/node_modules/encodeurl/HISTORY.md +14 -0
  659. gaia/eval/webapp/node_modules/send/node_modules/encodeurl/LICENSE +22 -0
  660. gaia/eval/webapp/node_modules/send/node_modules/encodeurl/README.md +128 -0
  661. gaia/eval/webapp/node_modules/send/node_modules/encodeurl/index.js +60 -0
  662. gaia/eval/webapp/node_modules/send/node_modules/encodeurl/package.json +40 -0
  663. gaia/eval/webapp/node_modules/send/node_modules/ms/index.js +162 -0
  664. gaia/eval/webapp/node_modules/send/node_modules/ms/license.md +21 -0
  665. gaia/eval/webapp/node_modules/send/node_modules/ms/package.json +38 -0
  666. gaia/eval/webapp/node_modules/send/node_modules/ms/readme.md +59 -0
  667. gaia/eval/webapp/node_modules/send/package.json +62 -0
  668. gaia/eval/webapp/node_modules/serve-static/HISTORY.md +487 -0
  669. gaia/eval/webapp/node_modules/serve-static/LICENSE +25 -0
  670. gaia/eval/webapp/node_modules/serve-static/README.md +257 -0
  671. gaia/eval/webapp/node_modules/serve-static/index.js +209 -0
  672. gaia/eval/webapp/node_modules/serve-static/package.json +42 -0
  673. gaia/eval/webapp/node_modules/setprototypeof/LICENSE +13 -0
  674. gaia/eval/webapp/node_modules/setprototypeof/README.md +31 -0
  675. gaia/eval/webapp/node_modules/setprototypeof/index.d.ts +2 -0
  676. gaia/eval/webapp/node_modules/setprototypeof/index.js +17 -0
  677. gaia/eval/webapp/node_modules/setprototypeof/package.json +38 -0
  678. gaia/eval/webapp/node_modules/setprototypeof/test/index.js +24 -0
  679. gaia/eval/webapp/node_modules/side-channel/.editorconfig +9 -0
  680. gaia/eval/webapp/node_modules/side-channel/.eslintrc +12 -0
  681. gaia/eval/webapp/node_modules/side-channel/.github/FUNDING.yml +12 -0
  682. gaia/eval/webapp/node_modules/side-channel/.nycrc +13 -0
  683. gaia/eval/webapp/node_modules/side-channel/CHANGELOG.md +110 -0
  684. gaia/eval/webapp/node_modules/side-channel/LICENSE +21 -0
  685. gaia/eval/webapp/node_modules/side-channel/README.md +61 -0
  686. gaia/eval/webapp/node_modules/side-channel/index.d.ts +14 -0
  687. gaia/eval/webapp/node_modules/side-channel/index.js +43 -0
  688. gaia/eval/webapp/node_modules/side-channel/package.json +85 -0
  689. gaia/eval/webapp/node_modules/side-channel/test/index.js +104 -0
  690. gaia/eval/webapp/node_modules/side-channel/tsconfig.json +9 -0
  691. gaia/eval/webapp/node_modules/side-channel-list/.editorconfig +9 -0
  692. gaia/eval/webapp/node_modules/side-channel-list/.eslintrc +11 -0
  693. gaia/eval/webapp/node_modules/side-channel-list/.github/FUNDING.yml +12 -0
  694. gaia/eval/webapp/node_modules/side-channel-list/.nycrc +13 -0
  695. gaia/eval/webapp/node_modules/side-channel-list/CHANGELOG.md +15 -0
  696. gaia/eval/webapp/node_modules/side-channel-list/LICENSE +21 -0
  697. gaia/eval/webapp/node_modules/side-channel-list/README.md +62 -0
  698. gaia/eval/webapp/node_modules/side-channel-list/index.d.ts +13 -0
  699. gaia/eval/webapp/node_modules/side-channel-list/index.js +113 -0
  700. gaia/eval/webapp/node_modules/side-channel-list/list.d.ts +14 -0
  701. gaia/eval/webapp/node_modules/side-channel-list/package.json +77 -0
  702. gaia/eval/webapp/node_modules/side-channel-list/test/index.js +104 -0
  703. gaia/eval/webapp/node_modules/side-channel-list/tsconfig.json +9 -0
  704. gaia/eval/webapp/node_modules/side-channel-map/.editorconfig +9 -0
  705. gaia/eval/webapp/node_modules/side-channel-map/.eslintrc +11 -0
  706. gaia/eval/webapp/node_modules/side-channel-map/.github/FUNDING.yml +12 -0
  707. gaia/eval/webapp/node_modules/side-channel-map/.nycrc +13 -0
  708. gaia/eval/webapp/node_modules/side-channel-map/CHANGELOG.md +22 -0
  709. gaia/eval/webapp/node_modules/side-channel-map/LICENSE +21 -0
  710. gaia/eval/webapp/node_modules/side-channel-map/README.md +62 -0
  711. gaia/eval/webapp/node_modules/side-channel-map/index.d.ts +15 -0
  712. gaia/eval/webapp/node_modules/side-channel-map/index.js +68 -0
  713. gaia/eval/webapp/node_modules/side-channel-map/package.json +80 -0
  714. gaia/eval/webapp/node_modules/side-channel-map/test/index.js +114 -0
  715. gaia/eval/webapp/node_modules/side-channel-map/tsconfig.json +9 -0
  716. gaia/eval/webapp/node_modules/side-channel-weakmap/.editorconfig +9 -0
  717. gaia/eval/webapp/node_modules/side-channel-weakmap/.eslintrc +12 -0
  718. gaia/eval/webapp/node_modules/side-channel-weakmap/.github/FUNDING.yml +12 -0
  719. gaia/eval/webapp/node_modules/side-channel-weakmap/.nycrc +13 -0
  720. gaia/eval/webapp/node_modules/side-channel-weakmap/CHANGELOG.md +28 -0
  721. gaia/eval/webapp/node_modules/side-channel-weakmap/LICENSE +21 -0
  722. gaia/eval/webapp/node_modules/side-channel-weakmap/README.md +62 -0
  723. gaia/eval/webapp/node_modules/side-channel-weakmap/index.d.ts +15 -0
  724. gaia/eval/webapp/node_modules/side-channel-weakmap/index.js +84 -0
  725. gaia/eval/webapp/node_modules/side-channel-weakmap/package.json +87 -0
  726. gaia/eval/webapp/node_modules/side-channel-weakmap/test/index.js +114 -0
  727. gaia/eval/webapp/node_modules/side-channel-weakmap/tsconfig.json +9 -0
  728. gaia/eval/webapp/node_modules/statuses/HISTORY.md +82 -0
  729. gaia/eval/webapp/node_modules/statuses/LICENSE +23 -0
  730. gaia/eval/webapp/node_modules/statuses/README.md +136 -0
  731. gaia/eval/webapp/node_modules/statuses/codes.json +65 -0
  732. gaia/eval/webapp/node_modules/statuses/index.js +146 -0
  733. gaia/eval/webapp/node_modules/statuses/package.json +49 -0
  734. gaia/eval/webapp/node_modules/toidentifier/HISTORY.md +9 -0
  735. gaia/eval/webapp/node_modules/toidentifier/LICENSE +21 -0
  736. gaia/eval/webapp/node_modules/toidentifier/README.md +61 -0
  737. gaia/eval/webapp/node_modules/toidentifier/index.js +32 -0
  738. gaia/eval/webapp/node_modules/toidentifier/package.json +38 -0
  739. gaia/eval/webapp/node_modules/type-is/HISTORY.md +259 -0
  740. gaia/eval/webapp/node_modules/type-is/LICENSE +23 -0
  741. gaia/eval/webapp/node_modules/type-is/README.md +170 -0
  742. gaia/eval/webapp/node_modules/type-is/index.js +266 -0
  743. gaia/eval/webapp/node_modules/type-is/package.json +45 -0
  744. gaia/eval/webapp/node_modules/unpipe/HISTORY.md +4 -0
  745. gaia/eval/webapp/node_modules/unpipe/LICENSE +22 -0
  746. gaia/eval/webapp/node_modules/unpipe/README.md +43 -0
  747. gaia/eval/webapp/node_modules/unpipe/index.js +69 -0
  748. gaia/eval/webapp/node_modules/unpipe/package.json +27 -0
  749. gaia/eval/webapp/node_modules/util/LICENSE +18 -0
  750. gaia/eval/webapp/node_modules/util/README.md +15 -0
  751. gaia/eval/webapp/node_modules/util/node_modules/inherits/LICENSE +16 -0
  752. gaia/eval/webapp/node_modules/util/node_modules/inherits/README.md +42 -0
  753. gaia/eval/webapp/node_modules/util/node_modules/inherits/inherits.js +7 -0
  754. gaia/eval/webapp/node_modules/util/node_modules/inherits/inherits_browser.js +23 -0
  755. gaia/eval/webapp/node_modules/util/node_modules/inherits/package.json +29 -0
  756. gaia/eval/webapp/node_modules/util/package.json +35 -0
  757. gaia/eval/webapp/node_modules/util/support/isBuffer.js +3 -0
  758. gaia/eval/webapp/node_modules/util/support/isBufferBrowser.js +6 -0
  759. gaia/eval/webapp/node_modules/util/util.js +586 -0
  760. gaia/eval/webapp/node_modules/utils-merge/.npmignore +9 -0
  761. gaia/eval/webapp/node_modules/utils-merge/LICENSE +20 -0
  762. gaia/eval/webapp/node_modules/utils-merge/README.md +34 -0
  763. gaia/eval/webapp/node_modules/utils-merge/index.js +23 -0
  764. gaia/eval/webapp/node_modules/utils-merge/package.json +40 -0
  765. gaia/eval/webapp/node_modules/vary/HISTORY.md +39 -0
  766. gaia/eval/webapp/node_modules/vary/LICENSE +22 -0
  767. gaia/eval/webapp/node_modules/vary/README.md +101 -0
  768. gaia/eval/webapp/node_modules/vary/index.js +149 -0
  769. gaia/eval/webapp/node_modules/vary/package.json +43 -0
  770. gaia/eval/webapp/package-lock.json +875 -0
  771. gaia/eval/webapp/package.json +21 -0
  772. gaia/eval/webapp/public/app.js +3403 -0
  773. gaia/eval/webapp/public/index.html +88 -0
  774. gaia/eval/webapp/public/styles.css +3661 -0
  775. gaia/eval/webapp/server.js +416 -0
  776. gaia/eval/webapp/test-setup.js +73 -0
  777. gaia/llm/__init__.py +2 -0
  778. gaia/llm/lemonade_client.py +3083 -0
  779. gaia/llm/lemonade_manager.py +269 -0
  780. gaia/llm/llm_client.py +729 -0
  781. gaia/llm/vlm_client.py +307 -0
  782. gaia/logger.py +189 -0
  783. gaia/mcp/agent_mcp_server.py +245 -0
  784. gaia/mcp/blender_mcp_client.py +138 -0
  785. gaia/mcp/blender_mcp_server.py +648 -0
  786. gaia/mcp/context7_cache.py +332 -0
  787. gaia/mcp/external_services.py +518 -0
  788. gaia/mcp/mcp_bridge.py +550 -0
  789. gaia/mcp/servers/__init__.py +6 -0
  790. gaia/mcp/servers/docker_mcp.py +83 -0
  791. gaia/rag/__init__.py +10 -0
  792. gaia/rag/app.py +293 -0
  793. gaia/rag/demo.py +304 -0
  794. gaia/rag/pdf_utils.py +235 -0
  795. gaia/rag/sdk.py +2194 -0
  796. gaia/security.py +163 -0
  797. gaia/talk/app.py +289 -0
  798. gaia/talk/sdk.py +538 -0
  799. gaia/util.py +46 -0
  800. gaia/version.py +100 -0
@@ -0,0 +1,1744 @@
1
+ # Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
2
+ # SPDX-License-Identifier: MIT
3
+ """Generic web development tools for Code Agent.
4
+
5
+ This mixin provides flexible, framework-agnostic tools for web development:
6
+ - API endpoint generation with actual Prisma queries
7
+ - React component generation (server and client)
8
+ - Database schema management
9
+ - Configuration updates
10
+
11
+ Tools use the manage_* prefix to indicate they handle both creation and modification.
12
+ All file I/O operations are delegated to FileIOToolsMixin for clean separation of concerns.
13
+ """
14
+
15
+ import logging
16
+ import re
17
+ import subprocess
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Optional
20
+
21
+ from gaia.agents.base.tools import tool
22
+ from gaia.agents.code.prompts.code_patterns import (
23
+ API_ROUTE_DYNAMIC_DELETE,
24
+ API_ROUTE_DYNAMIC_GET,
25
+ API_ROUTE_DYNAMIC_PATCH,
26
+ API_ROUTE_GET,
27
+ API_ROUTE_GET_PAGINATED,
28
+ API_ROUTE_POST,
29
+ APP_GLOBALS_CSS,
30
+ APP_LAYOUT,
31
+ CLIENT_COMPONENT_FORM,
32
+ COMPONENT_TEST_ACTIONS,
33
+ COMPONENT_TEST_FORM,
34
+ LANDING_PAGE_WITH_LINKS,
35
+ SERVER_COMPONENT_LIST,
36
+ generate_actions_component,
37
+ generate_api_imports,
38
+ generate_detail_page,
39
+ generate_field_display,
40
+ generate_form_field,
41
+ generate_form_field_assertions,
42
+ generate_form_fill_actions,
43
+ generate_new_page,
44
+ generate_test_data_fields,
45
+ generate_zod_schema,
46
+ pluralize,
47
+ )
48
+
49
+ logger = logging.getLogger(__name__)
50
+
51
+
52
+ def read_prisma_model(project_dir: str, model_name: str) -> Dict[str, Any]:
53
+ """Read model definition from Prisma schema.
54
+
55
+ Parses the Prisma schema file to extract field definitions and metadata
56
+ for a specific model. This allows tools to adapt to the actual schema
57
+ instead of relying on hardcoded assumptions.
58
+
59
+ Args:
60
+ project_dir: Path to the project directory
61
+ model_name: Name of the model to read (case-insensitive)
62
+
63
+ Returns:
64
+ Dictionary with:
65
+ success: Whether the model was found
66
+ model_name: The model name (as defined in schema)
67
+ fields: Dict of field names to types
68
+ has_timestamps: Whether model has createdAt/updatedAt
69
+ error: Error message if failed
70
+ """
71
+ schema_path = Path(project_dir) / "prisma" / "schema.prisma"
72
+ if not schema_path.exists():
73
+ return {
74
+ "success": False,
75
+ "error": "Schema file not found at prisma/schema.prisma",
76
+ }
77
+
78
+ try:
79
+ content = schema_path.read_text()
80
+ model_pattern = rf"model\s+{model_name}\s*\{{([^}}]+)\}}"
81
+ match = re.search(model_pattern, content, re.DOTALL | re.IGNORECASE)
82
+
83
+ if not match:
84
+ return {
85
+ "success": False,
86
+ "error": f"Model {model_name} not found in schema",
87
+ }
88
+
89
+ # Parse fields from model body
90
+ fields = {}
91
+ for line in match.group(1).strip().split("\n"):
92
+ line = line.strip()
93
+ if not line or line.startswith("//") or line.startswith("@@"):
94
+ continue
95
+ # Skip field decorators like @id, @default, etc.
96
+ if line.startswith("@") and not line.startswith("@@"):
97
+ continue
98
+ parts = line.split()
99
+ if len(parts) >= 2:
100
+ field_name = parts[0]
101
+ field_type = parts[1].rstrip("?[]") # Remove optional/array markers
102
+ fields[field_name] = field_type
103
+
104
+ return {
105
+ "success": True,
106
+ "model_name": model_name,
107
+ "fields": fields,
108
+ "has_timestamps": "createdAt" in fields and "updatedAt" in fields,
109
+ }
110
+ except Exception as e:
111
+ return {"success": False, "error": f"Failed to parse schema: {str(e)}"}
112
+
113
+
114
+ class WebToolsMixin:
115
+ """Mixin providing generic web development tools for the Code Agent.
116
+
117
+ Tools are designed to be framework-agnostic where possible, with
118
+ framework-specific logic in prompts rather than hardcoded in tools.
119
+
120
+ All tools delegate file operations to FileIOToolsMixin to maintain
121
+ clean separation of concerns.
122
+ """
123
+
124
+ def register_web_tools(self) -> None:
125
+ """Register generic web development tools with the agent."""
126
+
127
+ @tool
128
+ def setup_app_styling(
129
+ project_dir: str,
130
+ app_title: str = "My App",
131
+ app_description: str = "A modern web application",
132
+ ) -> Dict[str, Any]:
133
+ """Set up app-wide styling with modern design system.
134
+
135
+ Creates/updates the root layout and globals.css with a modern dark theme
136
+ that all pages will inherit. This should be run early in the project
137
+ setup, after create-next-app.
138
+
139
+ The design system includes:
140
+ - Dark gradient background at the layout level
141
+ - Glass morphism card effects (.glass-card)
142
+ - Modern button variants (.btn-primary, .btn-secondary, .btn-danger)
143
+ - Input field styling (.input-field)
144
+ - Custom checkbox styling (.checkbox-modern)
145
+ - Gradient text for titles (.page-title)
146
+ - Back link styling (.link-back)
147
+ - Custom scrollbar styling
148
+
149
+ Args:
150
+ project_dir: Path to the Next.js project directory
151
+ app_title: Application title for metadata
152
+ app_description: Application description for metadata
153
+
154
+ Returns:
155
+ Dictionary with success status and created/updated files
156
+ """
157
+ try:
158
+ project_path = Path(project_dir)
159
+ app_dir = project_path / "src" / "app"
160
+
161
+ if not app_dir.exists():
162
+ return {
163
+ "success": False,
164
+ "error": f"App directory not found: {app_dir}",
165
+ "hint": "Run create-next-app first to initialize the project",
166
+ }
167
+
168
+ files_created = []
169
+
170
+ # Generate layout.tsx
171
+ layout_path = app_dir / "layout.tsx"
172
+ layout_content = APP_LAYOUT.format(
173
+ app_title=app_title,
174
+ app_description=app_description,
175
+ )
176
+
177
+ # Write layout file using FileIOToolsMixin
178
+ if hasattr(self, "write_file"):
179
+ result = self.write_file(str(layout_path), layout_content)
180
+ if result.get("success"):
181
+ files_created.append(str(layout_path))
182
+ else:
183
+ layout_path.write_text(layout_content)
184
+ files_created.append(str(layout_path))
185
+
186
+ # Generate globals.css
187
+ globals_path = app_dir / "globals.css"
188
+ globals_content = APP_GLOBALS_CSS
189
+
190
+ # Write globals file
191
+ if hasattr(self, "write_file"):
192
+ result = self.write_file(str(globals_path), globals_content)
193
+ if result.get("success"):
194
+ files_created.append(str(globals_path))
195
+ else:
196
+ globals_path.write_text(globals_content)
197
+ files_created.append(str(globals_path))
198
+
199
+ logger.info(f"Set up app-wide styling: {files_created}")
200
+ return {
201
+ "success": True,
202
+ "message": "App-wide styling configured successfully",
203
+ "files": files_created,
204
+ "design_system": [
205
+ ".glass-card - Glass morphism card effect",
206
+ ".btn-primary - Primary gradient button",
207
+ ".btn-secondary - Secondary button",
208
+ ".btn-danger - Danger/delete button",
209
+ ".input-field - Styled form input",
210
+ ".checkbox-modern - Modern checkbox styling",
211
+ ".page-title - Gradient title text",
212
+ ".link-back - Back navigation link",
213
+ ],
214
+ }
215
+ except Exception as e:
216
+ logger.exception("Failed to set up app styling")
217
+ return {"success": False, "error": str(e)}
218
+
219
+ @tool
220
+ def manage_api_endpoint(
221
+ project_dir: str,
222
+ resource_name: str,
223
+ operations: List[str] = None,
224
+ fields: Optional[Dict[str, str]] = None,
225
+ enable_pagination: bool = False,
226
+ ) -> Dict[str, Any]:
227
+ """Manage API endpoints with actual Prisma operations.
228
+
229
+ Creates or updates API routes with functional CRUD operations,
230
+ validation, and error handling. Works for ANY resource type.
231
+
232
+ REQUIREMENTS (Tier 2 - Prerequisites):
233
+ - Must be called AFTER manage_data_model (needs Prisma model to exist)
234
+ - Ensure 'prisma generate' was run (manage_data_model does this automatically)
235
+ - API routes always import: NextResponse, prisma, z (zod)
236
+ - Use try/catch with appropriate status codes (200, 201, 400, 500)
237
+
238
+ Args:
239
+ project_dir: Path to the web project directory
240
+ resource_name: Resource name (e.g., "todo", "user", "product")
241
+ operations: HTTP methods to implement (default: ["GET", "POST"])
242
+ fields: Resource fields with types (for validation schema)
243
+ enable_pagination: Whether to add pagination to GET endpoint
244
+
245
+ Returns:
246
+ Dictionary with success status and created files
247
+ """
248
+ try:
249
+ operations = operations or ["GET", "POST"]
250
+
251
+ project_path = Path(project_dir)
252
+
253
+ # Phase 1 Fix (Issue #885): Read from Prisma schema instead of
254
+ # using dangerous defaults. This makes tools schema-aware.
255
+ if not fields:
256
+ model_info = read_prisma_model(
257
+ project_dir, resource_name.capitalize()
258
+ )
259
+ if model_info["success"]:
260
+ # Convert Prisma types to our field types and filter out auto-fields
261
+ prisma_to_field_type = {
262
+ "String": "string",
263
+ "Int": "number",
264
+ "Float": "float",
265
+ "Boolean": "boolean",
266
+ "DateTime": "datetime",
267
+ }
268
+ fields = {}
269
+ for field_name, prisma_type in model_info["fields"].items():
270
+ # Skip auto-generated fields
271
+ if field_name.lower() in {"id", "createdat", "updatedat"}:
272
+ continue
273
+ fields[field_name] = prisma_to_field_type.get(
274
+ prisma_type, "string"
275
+ )
276
+
277
+ if not fields:
278
+ return {
279
+ "success": False,
280
+ "error": f"Model {resource_name} has no user-facing fields in Prisma schema",
281
+ "hint": "Run manage_data_model first to create the model with fields",
282
+ }
283
+ logger.info(
284
+ f"Auto-read fields from Prisma schema for {resource_name}: {fields}"
285
+ )
286
+ else:
287
+ return {
288
+ "success": False,
289
+ "error": f"Cannot find model {resource_name} in Prisma schema. {model_info.get('error', '')}",
290
+ "hint": "Run manage_data_model first to create the Prisma model",
291
+ }
292
+
293
+ if not project_path.exists():
294
+ return {
295
+ "success": False,
296
+ "error": f"Project directory does not exist: {project_dir}",
297
+ }
298
+
299
+ # Sanitize resource_name: remove path components, brackets, slashes
300
+ # This prevents malformed paths like "todos/[id]s/route.ts"
301
+ clean_resource = resource_name.strip()
302
+ # Remove common path patterns that shouldn't be in resource names
303
+ clean_resource = clean_resource.replace("/[id]", "").replace("[id]", "")
304
+ clean_resource = clean_resource.rstrip("/").lstrip("/")
305
+ # Extract just the base resource name if path-like
306
+ if "/" in clean_resource:
307
+ clean_resource = clean_resource.split("/")[0]
308
+ # Remove any remaining special characters
309
+ clean_resource = re.sub(r"[^\w]", "", clean_resource)
310
+
311
+ if not clean_resource:
312
+ return {
313
+ "success": False,
314
+ "error": f"Invalid resource_name: '{resource_name}' - must be a simple name like 'todo' or 'product'",
315
+ "hint": "Use singular form without paths, e.g., 'todo' not 'todos/[id]'",
316
+ }
317
+
318
+ # Safety net: Ensure Prisma singleton exists before creating routes
319
+ singleton_path = project_path / "src" / "lib" / "prisma.ts"
320
+ if not singleton_path.exists():
321
+ from gaia.agents.code.tools.prisma_tools import (
322
+ PRISMA_SINGLETON_TEMPLATE,
323
+ )
324
+
325
+ singleton_path.parent.mkdir(parents=True, exist_ok=True)
326
+ singleton_path.write_text(
327
+ PRISMA_SINGLETON_TEMPLATE, encoding="utf-8"
328
+ )
329
+ logger.info(f"Auto-created Prisma singleton at {singleton_path}")
330
+
331
+ # Generate resource variants from cleaned name
332
+ resource = clean_resource.lower()
333
+ Resource = clean_resource.capitalize()
334
+ resource_plural = pluralize(resource)
335
+
336
+ # Build API route content
337
+ # Phase 4 Fix (Issue #885): Check all operations that need validation
338
+ needs_validation = any(
339
+ op in operations for op in ["POST", "PATCH", "PUT"]
340
+ )
341
+ imports = generate_api_imports(
342
+ operations, uses_validation=needs_validation
343
+ )
344
+
345
+ # Generate validation schema if needed
346
+ validation_schema = ""
347
+ if needs_validation:
348
+ print("WE ARE IN THE DUDUUUUUUDE")
349
+ validation_schema = generate_zod_schema(Resource, fields)
350
+
351
+ # Generate handlers based on operations
352
+ handlers = []
353
+ for op in operations:
354
+ if op == "GET":
355
+ pattern = (
356
+ API_ROUTE_GET_PAGINATED
357
+ if enable_pagination
358
+ else API_ROUTE_GET
359
+ )
360
+ handlers.append(
361
+ pattern.format(
362
+ resource=resource,
363
+ Resource=Resource,
364
+ resource_plural=resource_plural,
365
+ )
366
+ )
367
+ elif op == "POST":
368
+ handlers.append(
369
+ API_ROUTE_POST.format(
370
+ resource=resource,
371
+ Resource=Resource,
372
+ resource_plural=resource_plural,
373
+ )
374
+ )
375
+
376
+ # Combine into complete file - use \n\n to separate handlers
377
+ full_content = f"{imports}\n\n{validation_schema}\n\n{(chr(10) + chr(10)).join(handlers)}"
378
+
379
+ # Write API route file
380
+ api_file_path = Path(
381
+ f"{project_dir}/src/app/api/{resource_plural}/route.ts"
382
+ )
383
+ api_file_path.parent.mkdir(parents=True, exist_ok=True)
384
+
385
+ # Only write collection route if POST is in operations OR route doesn't exist
386
+ # This prevents dynamic route calls from overwriting collection route
387
+ created_files = []
388
+ if "POST" in operations or not api_file_path.exists():
389
+ api_file_path.write_text(full_content, encoding="utf-8")
390
+ created_files.append(str(api_file_path))
391
+ result = {"success": True}
392
+
393
+ # Create dynamic route if PATCH or DELETE requested
394
+ if (
395
+ "PATCH" in operations
396
+ or "DELETE" in operations
397
+ or "PUT" in operations
398
+ ):
399
+ dynamic_handlers = []
400
+ if "GET" in operations:
401
+ dynamic_handlers.append(
402
+ API_ROUTE_DYNAMIC_GET.format(
403
+ resource=resource, Resource=Resource
404
+ )
405
+ )
406
+ if "PATCH" in operations or "PUT" in operations:
407
+ dynamic_handlers.append(
408
+ API_ROUTE_DYNAMIC_PATCH.format(
409
+ resource=resource, Resource=Resource
410
+ )
411
+ )
412
+ if "DELETE" in operations:
413
+ dynamic_handlers.append(
414
+ API_ROUTE_DYNAMIC_DELETE.format(resource=resource)
415
+ )
416
+
417
+ dynamic_content = f"{imports}\n\n{validation_schema}\n\n{(chr(10) + chr(10)).join(dynamic_handlers)}"
418
+ dynamic_file_path = Path(
419
+ f"{project_dir}/src/app/api/{resource_plural}/[id]/route.ts"
420
+ )
421
+ dynamic_file_path.parent.mkdir(parents=True, exist_ok=True)
422
+ dynamic_file_path.write_text(dynamic_content, encoding="utf-8")
423
+ created_files.append(str(dynamic_file_path))
424
+
425
+ logger.info(f"Created API endpoint for {resource}")
426
+
427
+ return {
428
+ "success": result.get("success", True),
429
+ "resource": resource,
430
+ "operations": operations,
431
+ "files": created_files,
432
+ }
433
+
434
+ except Exception as e:
435
+ logger.error(f"Error managing API endpoint: {e}")
436
+ return {
437
+ "success": False,
438
+ "error": str(e),
439
+ "error_type": "api_endpoint_error",
440
+ "hint": "Check project directory structure and permissions",
441
+ }
442
+
443
+ @tool
444
+ def manage_react_component(
445
+ project_dir: str,
446
+ component_name: str,
447
+ component_type: str = "server",
448
+ resource_name: Optional[str] = None,
449
+ fields: Optional[Dict[str, str]] = None,
450
+ variant: str = "list",
451
+ ) -> Dict[str, Any]:
452
+ """Manage React components with functional implementations.
453
+
454
+ Creates or updates React components with real data fetching,
455
+ state management, and event handlers. Works for ANY resource.
456
+
457
+ REQUIREMENTS (Tier 2 - Prerequisites):
458
+ - Must be called AFTER manage_api_endpoint (components need API routes)
459
+ - Must be called AFTER manage_data_model (components need Prisma types)
460
+ - Use 'import type { X } from @prisma/client' for type imports
461
+ - Server components: import { prisma } from '@/lib/prisma'
462
+ - Client components: NEVER import prisma directly - use API routes
463
+
464
+ Args:
465
+ project_dir: Path to the web project directory
466
+ component_name: Component name (e.g., "TodoList", "UserForm")
467
+ component_type: "server" or "client" component
468
+ resource_name: Associated resource (for data operations)
469
+ fields: Resource fields (for forms and display)
470
+ variant: Component variant:
471
+ - "list": List page showing all items (server component)
472
+ - "form": Reusable form component for create/edit (client component)
473
+ - "new": Create new item page (client page using form)
474
+ - "detail": View/edit single item page with delete (client page)
475
+ - "actions": Delete/edit button component (client component)
476
+
477
+ Returns:
478
+ Dictionary with success status and component path
479
+ """
480
+ try:
481
+ project_path = Path(project_dir)
482
+ if not project_path.exists():
483
+ return {
484
+ "success": False,
485
+ "error": f"Project directory does not exist: {project_dir}",
486
+ }
487
+
488
+ # Sanitize resource_name if provided (same logic as manage_api_endpoint)
489
+ clean_resource_name = resource_name
490
+ if resource_name:
491
+ clean_resource = resource_name.strip()
492
+ clean_resource = clean_resource.replace("/[id]", "").replace(
493
+ "[id]", ""
494
+ )
495
+ clean_resource = clean_resource.rstrip("/").lstrip("/")
496
+ if "/" in clean_resource:
497
+ clean_resource = clean_resource.split("/")[0]
498
+ clean_resource = re.sub(r"[^\w]", "", clean_resource)
499
+ clean_resource_name = (
500
+ clean_resource if clean_resource else resource_name
501
+ )
502
+
503
+ # Auto-set component_type for client-side variants
504
+ # These variants always generate client components with "use client"
505
+ # This prevents the stub fallback when variant="form" but component_type
506
+ # defaults to "server"
507
+ if variant in ["form", "new", "detail", "actions"]:
508
+ component_type = "client"
509
+
510
+ # Phase 1 Fix (Issue #885): Read from Prisma schema instead of
511
+ # using dangerous defaults. This makes tools schema-aware.
512
+ if not fields and clean_resource_name:
513
+ model_info = read_prisma_model(
514
+ project_dir, clean_resource_name.capitalize()
515
+ )
516
+ if model_info["success"]:
517
+ # Convert Prisma types to our field types and filter out auto-fields
518
+ prisma_to_field_type = {
519
+ "String": "string",
520
+ "Int": "number",
521
+ "Float": "float",
522
+ "Boolean": "boolean",
523
+ "DateTime": "datetime",
524
+ }
525
+ fields = {}
526
+ for field_name, prisma_type in model_info["fields"].items():
527
+ # Skip auto-generated fields
528
+ if field_name.lower() in {"id", "createdat", "updatedat"}:
529
+ continue
530
+ fields[field_name] = prisma_to_field_type.get(
531
+ prisma_type, "string"
532
+ )
533
+ if fields:
534
+ logger.info(
535
+ f"Auto-read fields from Prisma schema for {clean_resource_name}: {fields}"
536
+ )
537
+ # Note: We don't fail here - some components don't need fields
538
+
539
+ content = ""
540
+
541
+ if (
542
+ component_type == "server"
543
+ and variant == "list"
544
+ and clean_resource_name
545
+ ):
546
+ # Generate server component with data fetching
547
+ resource = clean_resource_name.lower()
548
+ Resource = clean_resource_name.capitalize()
549
+ resource_plural = pluralize(resource)
550
+
551
+ field_display = generate_field_display(fields or {})
552
+
553
+ content = SERVER_COMPONENT_LIST.format(
554
+ resource=resource,
555
+ Resource=Resource,
556
+ resource_plural=resource_plural,
557
+ field_display=field_display,
558
+ )
559
+
560
+ elif (
561
+ component_type == "client"
562
+ and variant == "form"
563
+ and clean_resource_name
564
+ ):
565
+ # Generate client component with form and state
566
+ resource = clean_resource_name.lower()
567
+ Resource = clean_resource_name.capitalize()
568
+
569
+ # Phase 3 Fix (Issue #885): Fail clearly if no fields available
570
+ # instead of using dangerous defaults
571
+ if not fields:
572
+ return {
573
+ "success": False,
574
+ "error": f"No fields available for {clean_resource_name} form component",
575
+ "hint": "Run manage_data_model first to create the Prisma model with fields",
576
+ }
577
+
578
+ # Generate form state fields
579
+ form_state = []
580
+ date_field_names = []
581
+ for field_name, field_type in fields.items():
582
+ if field_name not in ["id", "createdAt", "updatedAt"]:
583
+ normalized_type = (
584
+ field_type.lower()
585
+ if isinstance(field_type, str)
586
+ else str(field_type).lower()
587
+ )
588
+ default = (
589
+ "0"
590
+ if normalized_type
591
+ in {"number", "int", "integer", "float"}
592
+ else "false" if normalized_type == "boolean" else '""'
593
+ )
594
+ form_state.append(f" {field_name}: {default}")
595
+ if normalized_type in {"date", "datetime", "timestamp"}:
596
+ date_field_names.append(f'"{field_name}"')
597
+
598
+ # Generate form fields
599
+ form_fields = []
600
+ for field_name, field_type in fields.items():
601
+ if field_name not in ["id", "createdAt", "updatedAt"]:
602
+ form_fields.append(
603
+ generate_form_field(field_name, field_type)
604
+ )
605
+
606
+ content = CLIENT_COMPONENT_FORM.format(
607
+ resource=resource,
608
+ Resource=Resource,
609
+ form_state_fields=",\n".join(form_state),
610
+ date_fields=(
611
+ f"[{', '.join(date_field_names)}] as const"
612
+ if date_field_names
613
+ else "[] as const"
614
+ ),
615
+ form_fields="\n".join(form_fields),
616
+ )
617
+
618
+ elif variant == "new" and clean_resource_name:
619
+ # Generate "new" page that uses the form component
620
+ content = generate_new_page(clean_resource_name)
621
+
622
+ elif variant == "detail" and clean_resource_name:
623
+ # Generate detail/edit page with form and delete functionality
624
+ # Phase 3 Fix (Issue #885): Fail clearly if no fields available
625
+ if not fields:
626
+ return {
627
+ "success": False,
628
+ "error": f"No fields available for {clean_resource_name} detail page",
629
+ "hint": "Run manage_data_model first to create the Prisma model with fields",
630
+ }
631
+ content = generate_detail_page(clean_resource_name, fields)
632
+
633
+ elif variant == "actions" and clean_resource_name:
634
+ # Generate actions component with delete functionality
635
+ content = generate_actions_component(clean_resource_name)
636
+
637
+ else:
638
+ # Generic component template
639
+ content = f"""interface {component_name}Props {{
640
+ // Add props here
641
+ }}
642
+
643
+ export function {component_name}({{ }}: {component_name}Props) {{
644
+ return (
645
+ <div>
646
+ <h2>{component_name}</h2>
647
+ </div>
648
+ );
649
+ }}"""
650
+
651
+ # Determine file path (use clean_resource_name to avoid malformed paths)
652
+ if (
653
+ component_type == "server"
654
+ and variant == "list"
655
+ and clean_resource_name
656
+ ):
657
+ file_path = Path(
658
+ f"{project_dir}/src/app/{pluralize(clean_resource_name)}/page.tsx"
659
+ )
660
+ elif variant == "form":
661
+ file_path = Path(
662
+ f"{project_dir}/src/components/{component_name}.tsx"
663
+ )
664
+ elif variant == "new" and clean_resource_name:
665
+ file_path = Path(
666
+ f"{project_dir}/src/app/{pluralize(clean_resource_name)}/new/page.tsx"
667
+ )
668
+ elif variant == "detail" and clean_resource_name:
669
+ file_path = Path(
670
+ f"{project_dir}/src/app/{pluralize(clean_resource_name)}/[id]/page.tsx"
671
+ )
672
+ elif variant == "actions" and clean_resource_name:
673
+ file_path = Path(
674
+ f"{project_dir}/src/components/{clean_resource_name.capitalize()}Actions.tsx"
675
+ )
676
+ else:
677
+ file_path = Path(
678
+ f"{project_dir}/src/components/{component_name}.tsx"
679
+ )
680
+
681
+ # Write component file
682
+ file_path.parent.mkdir(parents=True, exist_ok=True)
683
+ file_path.write_text(content, encoding="utf-8")
684
+ result = {"success": True}
685
+ created_files = [str(file_path)]
686
+
687
+ # Generate component tests for form and actions variants
688
+ if variant in ["form", "actions"] and clean_resource_name and fields:
689
+ try:
690
+ resource = clean_resource_name.lower()
691
+ Resource = clean_resource_name.capitalize()
692
+ resource_plural = pluralize(resource)
693
+ test_data_fields = generate_test_data_fields(fields, variant=1)
694
+
695
+ if variant == "form":
696
+ # Generate form component test
697
+ form_field_assertions = generate_form_field_assertions(
698
+ fields
699
+ )
700
+ form_fill_actions = generate_form_fill_actions(fields)
701
+
702
+ form_test_content = COMPONENT_TEST_FORM.format(
703
+ Resource=Resource,
704
+ resource_plural=resource_plural,
705
+ form_field_assertions=form_field_assertions,
706
+ form_fill_actions=form_fill_actions,
707
+ test_data_fields=test_data_fields,
708
+ )
709
+ form_test_path = Path(
710
+ f"{project_dir}/src/components/__tests__/{Resource}Form.test.tsx"
711
+ )
712
+ form_test_path.parent.mkdir(parents=True, exist_ok=True)
713
+ form_test_path.write_text(
714
+ form_test_content, encoding="utf-8"
715
+ )
716
+ created_files.append(str(form_test_path))
717
+ logger.info(f"Created form component test for {Resource}")
718
+
719
+ elif variant == "actions":
720
+ # Generate actions component test
721
+ actions_test_content = COMPONENT_TEST_ACTIONS.format(
722
+ Resource=Resource,
723
+ resource=resource,
724
+ resource_plural=resource_plural,
725
+ )
726
+ actions_test_path = Path(
727
+ f"{project_dir}/src/components/__tests__/{Resource}Actions.test.tsx"
728
+ )
729
+ actions_test_path.parent.mkdir(parents=True, exist_ok=True)
730
+ actions_test_path.write_text(
731
+ actions_test_content, encoding="utf-8"
732
+ )
733
+ created_files.append(str(actions_test_path))
734
+ logger.info(
735
+ f"Created actions component test for {Resource}"
736
+ )
737
+
738
+ except Exception as test_error:
739
+ logger.warning(
740
+ f"Could not generate component test: {test_error}"
741
+ )
742
+
743
+ logger.info(f"Created React component: {component_name}")
744
+
745
+ return {
746
+ "success": result.get("success", True),
747
+ "component": component_name,
748
+ "type": component_type,
749
+ "file_path": str(file_path),
750
+ "files": created_files,
751
+ }
752
+
753
+ except Exception as e:
754
+ logger.error(f"Error managing React component: {e}")
755
+ return {
756
+ "success": False,
757
+ "error": str(e),
758
+ "error_type": "component_error",
759
+ "hint": "Check project structure and component syntax",
760
+ }
761
+
762
+ @tool
763
+ def update_landing_page(
764
+ project_dir: str,
765
+ resource_name: str,
766
+ description: Optional[str] = None,
767
+ ) -> Dict[str, Any]:
768
+ """Update the landing page to include a link to the new resource.
769
+
770
+ Modifies src/app/page.tsx to add navigation to the newly created
771
+ resource pages. This ensures users can easily access the new features
772
+ from the main page.
773
+
774
+ Args:
775
+ project_dir: Path to the Next.js project directory
776
+ resource_name: Name of the resource (e.g., "todo", "product")
777
+ description: Optional description for the link
778
+
779
+ Returns:
780
+ Dictionary with success status and updated file path
781
+ """
782
+ try:
783
+ project_path = Path(project_dir)
784
+ page_path = project_path / "src" / "app" / "page.tsx"
785
+
786
+ if not page_path.exists():
787
+ return {
788
+ "success": False,
789
+ "error": f"Landing page not found: {page_path}",
790
+ "hint": "Ensure this is a Next.js project with app router",
791
+ }
792
+
793
+ resource = resource_name.lower()
794
+ Resource = resource_name.capitalize()
795
+ resource_plural = pluralize(resource)
796
+ link_description = description or f"Manage your {resource_plural}"
797
+
798
+ # Read current content
799
+ current_content = page_path.read_text(encoding="utf-8")
800
+
801
+ # Check if link already exists
802
+ if (
803
+ f'href="/{resource_plural}"' in current_content
804
+ or f"href='/{resource_plural}'" in current_content
805
+ ):
806
+ return {
807
+ "success": True,
808
+ "message": f"Link to /{resource_plural} already exists in landing page",
809
+ "file_path": str(page_path),
810
+ "already_exists": True,
811
+ }
812
+
813
+ # Generate new landing page with link to resource using dark theme
814
+ new_content = LANDING_PAGE_WITH_LINKS.format(
815
+ resource_plural=resource_plural,
816
+ Resource=Resource,
817
+ link_description=link_description,
818
+ )
819
+
820
+ # Write updated content
821
+ page_path.write_text(new_content, encoding="utf-8")
822
+
823
+ logger.info(f"Updated landing page with link to /{resource_plural}")
824
+
825
+ return {
826
+ "success": True,
827
+ "message": f"Landing page updated with link to /{resource_plural}",
828
+ "file_path": str(page_path),
829
+ "resource": resource_name,
830
+ "link_path": f"/{resource_plural}",
831
+ }
832
+
833
+ except Exception as e:
834
+ logger.error(f"Error updating landing page: {e}")
835
+ return {
836
+ "success": False,
837
+ "error": str(e),
838
+ "hint": "Check that src/app/page.tsx exists and is writable",
839
+ }
840
+
841
+ @tool
842
+ def setup_nextjs_testing(
843
+ project_dir: str,
844
+ resource_name: Optional[str] = None,
845
+ ) -> Dict[str, Any]:
846
+ """Set up Vitest testing infrastructure for a Next.js project.
847
+
848
+ Installs testing dependencies and creates configuration files:
849
+ - Vitest + React Testing Library
850
+ - vitest.config.ts with proper aliases and jsdom environment
851
+ - tests/setup.ts with common mocks (next/navigation, Prisma)
852
+ - Updates package.json with test scripts
853
+
854
+ Should be called after the project is initialized but before running tests.
855
+
856
+ Args:
857
+ project_dir: Path to the Next.js project directory
858
+ resource_name: Optional resource name to customize Prisma mocks
859
+
860
+ Returns:
861
+ Dictionary with success status and created files
862
+ """
863
+ from gaia.agents.code.prompts.code_patterns import TEST_SETUP, VITEST_CONFIG
864
+
865
+ try:
866
+ project_path = Path(project_dir)
867
+ if not project_path.exists():
868
+ return {
869
+ "success": False,
870
+ "error": f"Project directory does not exist: {project_dir}",
871
+ }
872
+
873
+ created_files = []
874
+ resource = resource_name.lower() if resource_name else "todo"
875
+
876
+ # Install testing dependencies
877
+ install_cmd = (
878
+ "npm install -D vitest @vitejs/plugin-react jsdom "
879
+ "@testing-library/react @testing-library/jest-dom @testing-library/user-event "
880
+ "@types/node"
881
+ )
882
+ install_result = subprocess.run(
883
+ install_cmd,
884
+ shell=True,
885
+ cwd=project_dir,
886
+ capture_output=True,
887
+ text=True,
888
+ timeout=1200,
889
+ check=False,
890
+ )
891
+
892
+ if install_result.returncode != 0:
893
+ return {
894
+ "success": False,
895
+ "error": "Failed to install testing dependencies",
896
+ "details": install_result.stderr,
897
+ "hint": "Check npm configuration and network connectivity",
898
+ }
899
+
900
+ # Create vitest.config.ts
901
+ vitest_config_path = project_path / "vitest.config.ts"
902
+ vitest_config_path.write_text(VITEST_CONFIG, encoding="utf-8")
903
+ created_files.append(str(vitest_config_path))
904
+
905
+ # Create tests/setup.ts with resource-specific Prisma mocks
906
+ tests_dir = project_path / "tests"
907
+ tests_dir.mkdir(exist_ok=True)
908
+
909
+ setup_content = TEST_SETUP.format(resource=resource)
910
+ setup_path = tests_dir / "setup.ts"
911
+ setup_path.write_text(setup_content, encoding="utf-8")
912
+ created_files.append(str(setup_path))
913
+
914
+ # Update package.json to add test scripts
915
+ package_json_path = project_path / "package.json"
916
+ if package_json_path.exists():
917
+ import json
918
+
919
+ package_data = json.loads(
920
+ package_json_path.read_text(encoding="utf-8")
921
+ )
922
+
923
+ if "scripts" not in package_data:
924
+ package_data["scripts"] = {}
925
+
926
+ # Add test scripts if not present
927
+ if "test" not in package_data["scripts"]:
928
+ package_data["scripts"]["test"] = "vitest run"
929
+ if "test:watch" not in package_data["scripts"]:
930
+ package_data["scripts"]["test:watch"] = "vitest"
931
+ if "test:coverage" not in package_data["scripts"]:
932
+ package_data["scripts"][
933
+ "test:coverage"
934
+ ] = "vitest run --coverage"
935
+
936
+ package_json_path.write_text(
937
+ json.dumps(package_data, indent=2) + "\n", encoding="utf-8"
938
+ )
939
+ created_files.append(str(package_json_path))
940
+
941
+ logger.info(f"Set up Vitest testing infrastructure in {project_dir}")
942
+
943
+ return {
944
+ "success": True,
945
+ "message": "Testing infrastructure configured successfully",
946
+ "files": created_files,
947
+ "dependencies_installed": [
948
+ "vitest",
949
+ "@vitejs/plugin-react",
950
+ "jsdom",
951
+ "@testing-library/react",
952
+ "@testing-library/jest-dom",
953
+ "@testing-library/user-event",
954
+ ],
955
+ "scripts_added": {
956
+ "test": "vitest run",
957
+ "test:watch": "vitest",
958
+ "test:coverage": "vitest run --coverage",
959
+ },
960
+ }
961
+
962
+ except subprocess.TimeoutExpired:
963
+ return {
964
+ "success": False,
965
+ "error": "npm install timed out",
966
+ "hint": "Check network connectivity and try again",
967
+ }
968
+ except Exception as e:
969
+ logger.error(f"Error setting up testing: {e}")
970
+ return {
971
+ "success": False,
972
+ "error": str(e),
973
+ "hint": "Check project structure and npm configuration",
974
+ }
975
+
976
+ @tool
977
+ def validate_crud_completeness(
978
+ project_dir: str, resource_name: str
979
+ ) -> Dict[str, Any]:
980
+ """Validate that all necessary CRUD files exist for a resource.
981
+
982
+ Checks for the presence of all required files for a complete CRUD application:
983
+ - API routes (collection and item endpoints)
984
+ - Pages (list, new, detail/edit)
985
+ - Components (form)
986
+ - Database model
987
+
988
+ Args:
989
+ project_dir: Path to the project directory
990
+ resource_name: Resource name to validate (e.g., "todo", "user")
991
+
992
+ Returns:
993
+ Dictionary with validation results and lists of existing/missing files
994
+ """
995
+ try:
996
+ project_path = Path(project_dir)
997
+ if not project_path.exists():
998
+ return {
999
+ "success": False,
1000
+ "error": f"Project directory does not exist: {project_dir}",
1001
+ }
1002
+
1003
+ resource = resource_name.lower()
1004
+ Resource = resource_name.capitalize()
1005
+ resource_plural = pluralize(resource)
1006
+
1007
+ # Define expected files
1008
+ expected_files = {
1009
+ "api_routes": {
1010
+ f"src/app/api/{resource_plural}/route.ts": "Collection API route (GET list, POST create)",
1011
+ f"src/app/api/{resource_plural}/[id]/route.ts": "Item API route (GET single, PATCH update, DELETE)",
1012
+ },
1013
+ "pages": {
1014
+ f"src/app/{resource_plural}/page.tsx": "List page showing all items",
1015
+ f"src/app/{resource_plural}/new/page.tsx": "Create new item page",
1016
+ f"src/app/{resource_plural}/[id]/page.tsx": "View/edit single item page",
1017
+ },
1018
+ "components": {
1019
+ f"src/components/{Resource}Form.tsx": "Reusable form component for create/edit"
1020
+ },
1021
+ }
1022
+
1023
+ # Check which files exist
1024
+ missing_files = {}
1025
+ existing_files = {}
1026
+
1027
+ for category, files in expected_files.items():
1028
+ missing_files[category] = []
1029
+ existing_files[category] = []
1030
+
1031
+ for file_path, description in files.items():
1032
+ full_path = project_path / file_path
1033
+ if full_path.exists():
1034
+ existing_files[category].append(
1035
+ {"path": file_path, "description": description}
1036
+ )
1037
+ else:
1038
+ missing_files[category].append(
1039
+ {"path": file_path, "description": description}
1040
+ )
1041
+
1042
+ # Check if Prisma model exists
1043
+ schema_file = project_path / "prisma" / "schema.prisma"
1044
+ model_exists = False
1045
+ if schema_file.exists():
1046
+ schema_content = schema_file.read_text()
1047
+ model_exists = f"model {Resource}" in schema_content
1048
+
1049
+ # Calculate completeness
1050
+ total_files = sum(len(files) for files in expected_files.values())
1051
+ existing_count = sum(len(files) for files in existing_files.values())
1052
+ missing_count = sum(len(files) for files in missing_files.values())
1053
+
1054
+ all_complete = missing_count == 0 and model_exists
1055
+
1056
+ logger.info(
1057
+ f"CRUD completeness check for {resource}: {existing_count}/{total_files} files exist"
1058
+ )
1059
+
1060
+ return {
1061
+ "success": True,
1062
+ "complete": all_complete,
1063
+ "resource": resource,
1064
+ "model_exists": model_exists,
1065
+ "existing_files": existing_files,
1066
+ "missing_files": missing_files,
1067
+ "stats": {
1068
+ "total": total_files,
1069
+ "existing": existing_count,
1070
+ "missing": missing_count,
1071
+ },
1072
+ }
1073
+
1074
+ except Exception as e:
1075
+ logger.error(f"Error validating CRUD completeness: {e}")
1076
+ return {
1077
+ "success": False,
1078
+ "error": str(e),
1079
+ "error_type": "validation_error",
1080
+ }
1081
+
1082
+ @tool
1083
+ def generate_crud_scaffold(
1084
+ project_dir: str, resource_name: str, fields: Dict[str, str]
1085
+ ) -> Dict[str, Any]:
1086
+ """Generate a complete CRUD scaffold with all necessary files.
1087
+
1088
+ This high-level tool orchestrates multiple operations to create
1089
+ a fully functional CRUD application for a resource. It generates:
1090
+ - API routes for all CRUD operations
1091
+ - List page to view all items
1092
+ - Form component for create/edit
1093
+ - Create page (new item)
1094
+ - Detail/edit page (single item with delete)
1095
+
1096
+ Args:
1097
+ project_dir: Path to the project directory
1098
+ resource_name: Resource name (e.g., "todo", "product")
1099
+ fields: Dictionary of field names to types
1100
+
1101
+ Returns:
1102
+ Dictionary with generation results and validation status
1103
+ """
1104
+ try:
1105
+ results = {
1106
+ "api_routes": [],
1107
+ "pages": [],
1108
+ "components": [],
1109
+ "errors": [],
1110
+ }
1111
+
1112
+ logger.info(f"Generating complete CRUD scaffold for {resource_name}...")
1113
+
1114
+ # 1. Generate API endpoints (all CRUD operations)
1115
+ logger.info(" → Generating API routes...")
1116
+ api_result = manage_api_endpoint(
1117
+ project_dir=project_dir,
1118
+ resource_name=resource_name,
1119
+ operations=["GET", "POST", "PATCH", "DELETE"],
1120
+ fields=fields,
1121
+ enable_pagination=True,
1122
+ )
1123
+ if api_result.get("success"):
1124
+ results["api_routes"].extend(api_result.get("files", []))
1125
+ else:
1126
+ results["errors"].append(
1127
+ f"API generation failed: {api_result.get('error')}"
1128
+ )
1129
+
1130
+ # 2. Generate list page (server component)
1131
+ logger.info(" → Generating list page...")
1132
+ list_result = manage_react_component(
1133
+ project_dir=project_dir,
1134
+ component_name=f"{resource_name.capitalize()}List",
1135
+ component_type="server",
1136
+ resource_name=resource_name,
1137
+ fields=fields,
1138
+ variant="list",
1139
+ )
1140
+ if list_result.get("success"):
1141
+ results["pages"].append(list_result.get("file_path"))
1142
+ else:
1143
+ results["errors"].append(
1144
+ f"List page generation failed: {list_result.get('error')}"
1145
+ )
1146
+
1147
+ # 3. Generate form component (reusable for create/edit)
1148
+ logger.info(" → Generating form component...")
1149
+ form_result = manage_react_component(
1150
+ project_dir=project_dir,
1151
+ component_name=f"{resource_name.capitalize()}Form",
1152
+ component_type="client",
1153
+ resource_name=resource_name,
1154
+ fields=fields,
1155
+ variant="form",
1156
+ )
1157
+ if form_result.get("success"):
1158
+ results["components"].append(form_result.get("file_path"))
1159
+ else:
1160
+ results["errors"].append(
1161
+ f"Form component generation failed: {form_result.get('error')}"
1162
+ )
1163
+
1164
+ # 4. Generate new page (create page)
1165
+ logger.info(" → Generating create (new) page...")
1166
+ new_result = manage_react_component(
1167
+ project_dir=project_dir,
1168
+ component_name=f"New{resource_name.capitalize()}Page",
1169
+ component_type="client",
1170
+ resource_name=resource_name,
1171
+ fields=fields,
1172
+ variant="new",
1173
+ )
1174
+ if new_result.get("success"):
1175
+ results["pages"].append(new_result.get("file_path"))
1176
+ else:
1177
+ results["errors"].append(
1178
+ f"New page generation failed: {new_result.get('error')}"
1179
+ )
1180
+
1181
+ # 5. Generate detail page (view/edit page with delete)
1182
+ logger.info(" → Generating detail/edit page...")
1183
+ detail_result = manage_react_component(
1184
+ project_dir=project_dir,
1185
+ component_name=f"{resource_name.capitalize()}DetailPage",
1186
+ component_type="client",
1187
+ resource_name=resource_name,
1188
+ fields=fields,
1189
+ variant="detail",
1190
+ )
1191
+ if detail_result.get("success"):
1192
+ results["pages"].append(detail_result.get("file_path"))
1193
+ else:
1194
+ results["errors"].append(
1195
+ f"Detail page generation failed: {detail_result.get('error')}"
1196
+ )
1197
+
1198
+ # 6. Generate actions component (edit/delete buttons)
1199
+ logger.info(" → Generating actions component...")
1200
+ actions_result = manage_react_component(
1201
+ project_dir=project_dir,
1202
+ component_name=f"{resource_name.capitalize()}Actions",
1203
+ component_type="client",
1204
+ resource_name=resource_name,
1205
+ fields=fields,
1206
+ variant="actions",
1207
+ )
1208
+ if actions_result.get("success"):
1209
+ results["components"].append(actions_result.get("file_path"))
1210
+ else:
1211
+ results["errors"].append(
1212
+ f"Actions component generation failed: {actions_result.get('error')}"
1213
+ )
1214
+
1215
+ # 7. Validate completeness
1216
+ logger.info(" → Validating completeness...")
1217
+ validation = validate_crud_completeness(project_dir, resource_name)
1218
+
1219
+ success = len(results["errors"]) == 0
1220
+ logger.info(
1221
+ f"CRUD scaffold generation {'succeeded' if success else 'completed with errors'}"
1222
+ )
1223
+
1224
+ return {
1225
+ "success": success,
1226
+ "resource": resource_name,
1227
+ "generated": results,
1228
+ "validation": validation,
1229
+ }
1230
+
1231
+ except Exception as e:
1232
+ logger.error(f"Error generating CRUD scaffold: {e}")
1233
+ return {
1234
+ "success": False,
1235
+ "error": str(e),
1236
+ "error_type": "scaffold_generation_error",
1237
+ }
1238
+
1239
+ @tool
1240
+ def manage_data_model(
1241
+ project_dir: str,
1242
+ model_name: str,
1243
+ fields: Dict[str, str],
1244
+ relationships: Optional[List[Dict[str, str]]] = None,
1245
+ ) -> Dict[str, Any]:
1246
+ """Manage database models with Prisma ORM.
1247
+
1248
+ Creates or updates Prisma model definitions. Works for ANY model type.
1249
+
1250
+ Args:
1251
+ project_dir: Path to the project directory
1252
+ model_name: Model name (singular, PascalCase, e.g., "User", "Product")
1253
+ fields: Dictionary of field names to types
1254
+ Supported: "string", "text", "number", "float", "boolean",
1255
+ "date", "datetime", "timestamp", "email", "url"
1256
+ relationships: Optional list of relationships
1257
+ [{"type": "hasMany", "model": "Post"}]
1258
+
1259
+ Returns:
1260
+ Dictionary with success status and schema file path
1261
+ """
1262
+ try:
1263
+ project_path = Path(project_dir)
1264
+ if not project_path.exists():
1265
+ return {
1266
+ "success": False,
1267
+ "error": f"Project directory does not exist: {project_dir}",
1268
+ }
1269
+
1270
+ schema_file = project_path / "prisma" / "schema.prisma"
1271
+
1272
+ if not schema_file.exists():
1273
+ return {
1274
+ "success": False,
1275
+ "error": "schema.prisma not found. Initialize Prisma first.",
1276
+ }
1277
+
1278
+ # Read existing schema
1279
+ schema_content = schema_file.read_text()
1280
+
1281
+ # Validate schema doesn't have forbidden output field in generator block
1282
+ if "output" in schema_content:
1283
+ # Check if it's in generator client block specifically
1284
+ generator_match = re.search(
1285
+ r"generator\s+client\s*\{[^}]*output[^}]*\}",
1286
+ schema_content,
1287
+ re.DOTALL,
1288
+ )
1289
+ if generator_match:
1290
+ # Auto-fix: remove the output line
1291
+ fixed_content = re.sub(
1292
+ r'\n\s*output\s*=\s*"[^"]*"', "", schema_content
1293
+ )
1294
+ schema_file.write_text(fixed_content, encoding="utf-8")
1295
+ schema_content = fixed_content
1296
+ logger.warning(
1297
+ "Removed invalid 'output' field from generator client block"
1298
+ )
1299
+
1300
+ # Generate field definitions
1301
+ field_lines = []
1302
+ field_lines.append(" id Int @id @default(autoincrement())")
1303
+
1304
+ # Map types to Prisma types
1305
+ type_mapping = {
1306
+ "string": "String",
1307
+ "text": "String",
1308
+ "number": "Int",
1309
+ "float": "Float",
1310
+ "boolean": "Boolean",
1311
+ "date": "DateTime",
1312
+ "datetime": "DateTime",
1313
+ "timestamp": "DateTime",
1314
+ "email": "String",
1315
+ "url": "String",
1316
+ }
1317
+
1318
+ # Define reserved fields that are auto-generated
1319
+ reserved_fields = {"id", "createdat", "updatedat"}
1320
+
1321
+ # Build field lines from user input (skip reserved fields)
1322
+ for field_name, field_type in fields.items():
1323
+ if field_name.lower() in reserved_fields:
1324
+ logger.warning(
1325
+ f"Skipping reserved field '{field_name}' - auto-generated by Prisma"
1326
+ )
1327
+ continue
1328
+ prisma_type = type_mapping.get(field_type.lower(), "String")
1329
+ field_lines.append(f" {field_name:<12} {prisma_type}")
1330
+
1331
+ # Add relationships if provided
1332
+ if relationships:
1333
+ for rel in relationships:
1334
+ rel_type = rel.get("type", "hasMany")
1335
+ rel_model = rel.get("model")
1336
+ if rel_type == "hasMany":
1337
+ field_lines.append(
1338
+ f" {rel_model.lower()}s {rel_model}[]"
1339
+ )
1340
+ elif rel_type == "hasOne":
1341
+ field_lines.append(
1342
+ f" {rel_model.lower()} {rel_model}?"
1343
+ )
1344
+
1345
+ # Always add timestamps - they're standard for Prisma and our templates expect them
1346
+ # Note: Reserved fields (including createdAt/updatedAt) are already skipped
1347
+ # from user input above, so there's no risk of duplication
1348
+ field_lines.append(" createdAt DateTime @default(now())")
1349
+ field_lines.append(" updatedAt DateTime @updatedAt")
1350
+
1351
+ # Check if model already exists in schema
1352
+ model_pattern = rf"model\s+{model_name}\s*\{{"
1353
+ if re.search(model_pattern, schema_content):
1354
+ return {
1355
+ "success": False,
1356
+ "error": f"Model '{model_name}' already exists in schema",
1357
+ "error_type": "duplicate_model",
1358
+ "hint": "Use a different model name or edit the existing model",
1359
+ "suggested_fix": f"Read schema.prisma to see existing {model_name} definition",
1360
+ }
1361
+
1362
+ # Generate model definition
1363
+ model_definition = f"""
1364
+
1365
+ model {model_name} {{
1366
+ {chr(10).join(field_lines)}
1367
+ }}
1368
+ """
1369
+
1370
+ # Save original schema for rollback
1371
+ original_schema = schema_content
1372
+
1373
+ # Append to schema
1374
+ schema_content += model_definition
1375
+
1376
+ # Write new schema
1377
+ schema_file.write_text(schema_content, encoding="utf-8")
1378
+
1379
+ # Validate with prisma format
1380
+ validate_result = subprocess.run(
1381
+ f'npx prisma format --schema="{schema_file}"',
1382
+ cwd=str(project_path),
1383
+ shell=True,
1384
+ capture_output=True,
1385
+ text=True,
1386
+ encoding="utf-8",
1387
+ errors="replace",
1388
+ timeout=600,
1389
+ check=False,
1390
+ )
1391
+
1392
+ if validate_result.returncode != 0:
1393
+ # Rollback to original schema
1394
+ schema_file.write_text(original_schema, encoding="utf-8")
1395
+ return {
1396
+ "success": False,
1397
+ "error": f"Schema validation failed: {validate_result.stderr}",
1398
+ "error_type": "schema_validation_error",
1399
+ "hint": "The schema changes caused validation errors",
1400
+ "suggested_fix": "Check field types and model syntax",
1401
+ }
1402
+
1403
+ result = {"success": True}
1404
+
1405
+ logger.info(f"Added Prisma model: {model_name}")
1406
+
1407
+ # Auto-generate Prisma client types
1408
+ prisma_generated = False
1409
+ generation_note = ""
1410
+
1411
+ try:
1412
+ # Format schema first
1413
+ subprocess.run(
1414
+ f'npx prisma format --schema="{schema_file}"',
1415
+ cwd=str(project_path),
1416
+ shell=True,
1417
+ capture_output=True,
1418
+ text=True,
1419
+ encoding="utf-8",
1420
+ errors="replace",
1421
+ timeout=600,
1422
+ check=False,
1423
+ )
1424
+
1425
+ # Generate Prisma client types
1426
+ generate_result = subprocess.run(
1427
+ f'npx prisma generate --schema="{schema_file}"',
1428
+ cwd=str(project_path),
1429
+ shell=True,
1430
+ capture_output=True,
1431
+ text=True,
1432
+ encoding="utf-8",
1433
+ errors="replace",
1434
+ timeout=1200,
1435
+ check=False,
1436
+ )
1437
+
1438
+ if generate_result.returncode != 0:
1439
+ stderr = generate_result.stderr
1440
+ logger.error(f"prisma generate failed: {stderr}")
1441
+ return {
1442
+ "success": False,
1443
+ "error": f"prisma generate failed: {stderr}",
1444
+ "schema_file": str(schema_file),
1445
+ "fix_hint": "Check schema.prisma for syntax errors",
1446
+ }
1447
+
1448
+ # Verify Prisma client was actually generated
1449
+ client_index = (
1450
+ project_path
1451
+ / "node_modules"
1452
+ / ".prisma"
1453
+ / "client"
1454
+ / "index.js"
1455
+ )
1456
+ if not client_index.exists():
1457
+ logger.error("Prisma client not generated despite no errors")
1458
+ return {
1459
+ "success": False,
1460
+ "error": "Prisma client not generated despite no errors",
1461
+ "fix_hint": "Run 'npm install' then 'npx prisma generate'",
1462
+ }
1463
+
1464
+ prisma_generated = True
1465
+ generation_note = (
1466
+ "Schema updated and Prisma client generated successfully"
1467
+ )
1468
+ logger.info(generation_note)
1469
+
1470
+ # Push schema changes to database
1471
+ logger.info(f"Running prisma db push in {project_dir}")
1472
+ db_push_result = subprocess.run(
1473
+ "npx prisma db push",
1474
+ cwd=str(project_path),
1475
+ shell=True,
1476
+ capture_output=True,
1477
+ text=True,
1478
+ encoding="utf-8",
1479
+ errors="replace",
1480
+ timeout=1200,
1481
+ check=False,
1482
+ )
1483
+
1484
+ if db_push_result.returncode != 0:
1485
+ logger.error(f"prisma db push failed: {db_push_result.stderr}")
1486
+ return {
1487
+ "success": False,
1488
+ "error": f"prisma db push failed: {db_push_result.stderr}",
1489
+ "fix_hint": "Check DATABASE_URL in .env file",
1490
+ "generated": True, # Client was generated successfully
1491
+ "pushed": False,
1492
+ }
1493
+
1494
+ generation_note = "Schema updated, Prisma client generated, and database pushed successfully"
1495
+ logger.info(generation_note)
1496
+
1497
+ except Exception as e:
1498
+ # Prisma generation failed - block the operation
1499
+ logger.error(f"Could not generate Prisma client: {e}")
1500
+ return {
1501
+ "success": False,
1502
+ "error": f"Could not generate Prisma client: {e}",
1503
+ "fix_hint": "Ensure prisma is installed (npm install)",
1504
+ }
1505
+
1506
+ return {
1507
+ "success": result.get("success", True),
1508
+ "model_name": model_name,
1509
+ "schema_file": str(schema_file),
1510
+ "schema_updated": True,
1511
+ "prisma_generated": prisma_generated,
1512
+ "note": generation_note,
1513
+ }
1514
+
1515
+ except Exception as e:
1516
+ logger.error(f"Error managing data model: {e}")
1517
+ return {
1518
+ "success": False,
1519
+ "error": str(e),
1520
+ "error_type": "data_model_error",
1521
+ }
1522
+
1523
+ @tool
1524
+ def manage_prisma_client(project_dir: str) -> Dict[str, Any]:
1525
+ """Manage Prisma client generation and database sync.
1526
+
1527
+ Generates the Prisma client and pushes schema changes to the database.
1528
+
1529
+ Args:
1530
+ project_dir: Path to the project directory
1531
+
1532
+ Returns:
1533
+ Dictionary with success status and commands to run
1534
+ """
1535
+ try:
1536
+ project_path = Path(project_dir)
1537
+ if not project_path.exists():
1538
+ return {
1539
+ "success": False,
1540
+ "error": f"Project directory does not exist: {project_dir}",
1541
+ }
1542
+
1543
+ # Check if Prisma is configured
1544
+ schema_file = project_path / "prisma" / "schema.prisma"
1545
+ if not schema_file.exists():
1546
+ return {
1547
+ "success": False,
1548
+ "error": "Prisma not initialized. schema.prisma not found.",
1549
+ }
1550
+
1551
+ # Provide guidance for Prisma operations
1552
+ commands = [
1553
+ "npm run db:generate # Generate Prisma Client",
1554
+ "npm run db:push # Push schema to database",
1555
+ "npm run db:studio # Open Prisma Studio (optional)",
1556
+ ]
1557
+
1558
+ logger.info("Prisma client management guidance provided")
1559
+
1560
+ return {
1561
+ "success": True,
1562
+ "commands": commands,
1563
+ "working_dir": str(project_path),
1564
+ }
1565
+
1566
+ except Exception as e:
1567
+ logger.error(f"Error managing Prisma client: {e}")
1568
+ return {
1569
+ "success": False,
1570
+ "error": str(e),
1571
+ "error_type": "prisma_client_error",
1572
+ }
1573
+
1574
+ @tool
1575
+ def manage_web_config(
1576
+ project_dir: str, config_type: str, updates: Dict[str, Any]
1577
+ ) -> Dict[str, Any]:
1578
+ """Manage web application configuration files.
1579
+
1580
+ Updates configuration files like .env, next.config.js, etc.
1581
+ Delegates actual file operations to file_io.
1582
+
1583
+ Args:
1584
+ project_dir: Path to the project directory
1585
+ config_type: Type of config ("env", "nextjs", "tailwind")
1586
+ updates: Dictionary of configuration updates
1587
+
1588
+ Returns:
1589
+ Dictionary with success status
1590
+ """
1591
+ try:
1592
+ project_path = Path(project_dir)
1593
+ if not project_path.exists():
1594
+ return {
1595
+ "success": False,
1596
+ "error": f"Project directory does not exist: {project_dir}",
1597
+ }
1598
+
1599
+ if config_type == "env":
1600
+ env_file = project_path / ".env"
1601
+ if not env_file.exists():
1602
+ # Create new .env file
1603
+ content = "\n".join(f"{k}={v}" for k, v in updates.items())
1604
+ else:
1605
+ # Update existing
1606
+ content = env_file.read_text()
1607
+ for key, value in updates.items():
1608
+ if f"{key}=" in content:
1609
+ lines = content.split("\n")
1610
+ content = "\n".join(
1611
+ (
1612
+ f"{key}={value}"
1613
+ if line.startswith(f"{key}=")
1614
+ else line
1615
+ )
1616
+ for line in lines
1617
+ )
1618
+ else:
1619
+ content += f"\n{key}={value}"
1620
+
1621
+ env_file.write_text(content, encoding="utf-8")
1622
+
1623
+ return {
1624
+ "success": True,
1625
+ "config_type": config_type,
1626
+ "file": str(env_file),
1627
+ "updates": updates,
1628
+ }
1629
+ else:
1630
+ return {
1631
+ "success": True,
1632
+ "config_type": config_type,
1633
+ "updates": updates,
1634
+ }
1635
+
1636
+ except Exception as e:
1637
+ logger.error(f"Error managing config: {e}")
1638
+ return {
1639
+ "success": False,
1640
+ "error": str(e),
1641
+ "error_type": "config_error",
1642
+ }
1643
+
1644
+ @tool
1645
+ def generate_style_tests(
1646
+ project_dir: str, resource_name: str = "Item"
1647
+ ) -> Dict[str, Any]:
1648
+ """Generate CSS and styling tests for the project.
1649
+
1650
+ Creates test files that validate:
1651
+ 1. CSS file integrity (no TypeScript in CSS - Issue #1002)
1652
+ 2. Tailwind directive presence
1653
+ 3. Design system class definitions
1654
+ 4. Layout imports globals.css
1655
+ 5. App router structure
1656
+
1657
+ Tests are placed in the project's /tests directory.
1658
+
1659
+ Args:
1660
+ project_dir: Path to the Next.js project directory
1661
+ resource_name: Resource name for component styling tests
1662
+
1663
+ Returns:
1664
+ Dictionary with success status and generated files
1665
+ """
1666
+ from gaia.agents.code.prompts.code_patterns import (
1667
+ generate_routes_test_content,
1668
+ generate_style_test_content,
1669
+ )
1670
+
1671
+ try:
1672
+ project_path = Path(project_dir)
1673
+ tests_dir = project_path / "tests"
1674
+ styling_dir = tests_dir / "styling"
1675
+
1676
+ # Ensure directories exist
1677
+ tests_dir.mkdir(parents=True, exist_ok=True)
1678
+ styling_dir.mkdir(parents=True, exist_ok=True)
1679
+
1680
+ files_created = []
1681
+
1682
+ # 1. Generate styles.test.ts (main CSS integrity test)
1683
+ styles_test_path = tests_dir / "styles.test.ts"
1684
+ styles_content = generate_style_test_content(resource_name)
1685
+
1686
+ if hasattr(self, "write_file"):
1687
+ result = self.write_file(str(styles_test_path), styles_content)
1688
+ if result.get("success"):
1689
+ files_created.append(str(styles_test_path))
1690
+ else:
1691
+ styles_test_path.write_text(styles_content)
1692
+ files_created.append(str(styles_test_path))
1693
+
1694
+ # 2. Generate routes.test.ts (app router structure test)
1695
+ routes_test_path = styling_dir / "routes.test.ts"
1696
+ routes_content = generate_routes_test_content(resource_name)
1697
+
1698
+ if hasattr(self, "write_file"):
1699
+ result = self.write_file(str(routes_test_path), routes_content)
1700
+ if result.get("success"):
1701
+ files_created.append(str(routes_test_path))
1702
+ else:
1703
+ routes_test_path.write_text(routes_content)
1704
+ files_created.append(str(routes_test_path))
1705
+
1706
+ # 3. Install glob package if not present (needed for tests)
1707
+ package_json = project_path / "package.json"
1708
+ if package_json.exists():
1709
+ pkg_content = package_json.read_text()
1710
+ if '"glob"' not in pkg_content:
1711
+ try:
1712
+ subprocess.run(
1713
+ ["npm", "install", "--save-dev", "glob", "@types/glob"],
1714
+ cwd=str(project_path),
1715
+ capture_output=True,
1716
+ text=True,
1717
+ timeout=600,
1718
+ check=False,
1719
+ )
1720
+ logger.info("Installed glob package for style tests")
1721
+ except Exception as e:
1722
+ logger.warning(f"Could not install glob package: {e}")
1723
+
1724
+ logger.info(
1725
+ f"Generated {len(files_created)} style test files for {resource_name}"
1726
+ )
1727
+
1728
+ return {
1729
+ "success": True,
1730
+ "files": files_created,
1731
+ "message": f"Generated style tests for {resource_name}",
1732
+ "tests_description": [
1733
+ "styles.test.ts - CSS integrity (TypeScript detection, Tailwind, braces)",
1734
+ "styling/routes.test.ts - App router structure and styling consistency",
1735
+ ],
1736
+ }
1737
+
1738
+ except Exception as e:
1739
+ logger.error(f"Error generating style tests: {e}")
1740
+ return {
1741
+ "success": False,
1742
+ "error": str(e),
1743
+ "error_type": "test_generation_error",
1744
+ }