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.
- amd_gaia-0.14.1.dist-info/METADATA +768 -0
- amd_gaia-0.14.1.dist-info/RECORD +800 -0
- amd_gaia-0.14.1.dist-info/WHEEL +5 -0
- amd_gaia-0.14.1.dist-info/entry_points.txt +5 -0
- amd_gaia-0.14.1.dist-info/licenses/LICENSE.md +21 -0
- amd_gaia-0.14.1.dist-info/top_level.txt +1 -0
- gaia/__init__.py +2 -0
- gaia/agents/__init__.py +19 -0
- gaia/agents/base/__init__.py +9 -0
- gaia/agents/base/agent.py +2072 -0
- gaia/agents/base/api_agent.py +120 -0
- gaia/agents/base/console.py +1457 -0
- gaia/agents/base/mcp_agent.py +86 -0
- gaia/agents/base/tools.py +83 -0
- gaia/agents/blender/agent.py +556 -0
- gaia/agents/blender/agent_simple.py +135 -0
- gaia/agents/blender/app.py +211 -0
- gaia/agents/blender/app_simple.py +41 -0
- gaia/agents/blender/core/__init__.py +16 -0
- gaia/agents/blender/core/materials.py +506 -0
- gaia/agents/blender/core/objects.py +316 -0
- gaia/agents/blender/core/rendering.py +225 -0
- gaia/agents/blender/core/scene.py +220 -0
- gaia/agents/blender/core/view.py +146 -0
- gaia/agents/chat/__init__.py +9 -0
- gaia/agents/chat/agent.py +975 -0
- gaia/agents/chat/app.py +1058 -0
- gaia/agents/chat/session.py +508 -0
- gaia/agents/chat/tools/__init__.py +15 -0
- gaia/agents/chat/tools/file_tools.py +96 -0
- gaia/agents/chat/tools/rag_tools.py +1729 -0
- gaia/agents/chat/tools/shell_tools.py +436 -0
- gaia/agents/code/__init__.py +7 -0
- gaia/agents/code/agent.py +547 -0
- gaia/agents/code/app.py +266 -0
- gaia/agents/code/models.py +135 -0
- gaia/agents/code/orchestration/__init__.py +24 -0
- gaia/agents/code/orchestration/checklist_executor.py +1739 -0
- gaia/agents/code/orchestration/checklist_generator.py +709 -0
- gaia/agents/code/orchestration/factories/__init__.py +9 -0
- gaia/agents/code/orchestration/factories/base.py +63 -0
- gaia/agents/code/orchestration/factories/nextjs_factory.py +118 -0
- gaia/agents/code/orchestration/factories/python_factory.py +106 -0
- gaia/agents/code/orchestration/orchestrator.py +610 -0
- gaia/agents/code/orchestration/project_analyzer.py +391 -0
- gaia/agents/code/orchestration/steps/__init__.py +67 -0
- gaia/agents/code/orchestration/steps/base.py +188 -0
- gaia/agents/code/orchestration/steps/error_handler.py +314 -0
- gaia/agents/code/orchestration/steps/nextjs.py +828 -0
- gaia/agents/code/orchestration/steps/python.py +307 -0
- gaia/agents/code/orchestration/template_catalog.py +463 -0
- gaia/agents/code/orchestration/workflows/__init__.py +14 -0
- gaia/agents/code/orchestration/workflows/base.py +80 -0
- gaia/agents/code/orchestration/workflows/nextjs.py +186 -0
- gaia/agents/code/orchestration/workflows/python.py +94 -0
- gaia/agents/code/prompts/__init__.py +11 -0
- gaia/agents/code/prompts/base_prompt.py +77 -0
- gaia/agents/code/prompts/code_patterns.py +1925 -0
- gaia/agents/code/prompts/nextjs_prompt.py +40 -0
- gaia/agents/code/prompts/python_prompt.py +109 -0
- gaia/agents/code/schema_inference.py +365 -0
- gaia/agents/code/system_prompt.py +41 -0
- gaia/agents/code/tools/__init__.py +42 -0
- gaia/agents/code/tools/cli_tools.py +1138 -0
- gaia/agents/code/tools/code_formatting.py +319 -0
- gaia/agents/code/tools/code_tools.py +769 -0
- gaia/agents/code/tools/error_fixing.py +1347 -0
- gaia/agents/code/tools/external_tools.py +180 -0
- gaia/agents/code/tools/file_io.py +845 -0
- gaia/agents/code/tools/prisma_tools.py +190 -0
- gaia/agents/code/tools/project_management.py +1016 -0
- gaia/agents/code/tools/testing.py +321 -0
- gaia/agents/code/tools/typescript_tools.py +122 -0
- gaia/agents/code/tools/validation_parsing.py +461 -0
- gaia/agents/code/tools/validation_tools.py +803 -0
- gaia/agents/code/tools/web_dev_tools.py +1744 -0
- gaia/agents/code/validators/__init__.py +16 -0
- gaia/agents/code/validators/antipattern_checker.py +241 -0
- gaia/agents/code/validators/ast_analyzer.py +197 -0
- gaia/agents/code/validators/requirements_validator.py +145 -0
- gaia/agents/code/validators/syntax_validator.py +171 -0
- gaia/agents/docker/__init__.py +7 -0
- gaia/agents/docker/agent.py +642 -0
- gaia/agents/jira/__init__.py +11 -0
- gaia/agents/jira/agent.py +894 -0
- gaia/agents/jira/jql_templates.py +299 -0
- gaia/agents/routing/__init__.py +7 -0
- gaia/agents/routing/agent.py +512 -0
- gaia/agents/routing/system_prompt.py +75 -0
- gaia/api/__init__.py +23 -0
- gaia/api/agent_registry.py +238 -0
- gaia/api/app.py +305 -0
- gaia/api/openai_server.py +575 -0
- gaia/api/schemas.py +186 -0
- gaia/api/sse_handler.py +370 -0
- gaia/apps/__init__.py +4 -0
- gaia/apps/llm/__init__.py +6 -0
- gaia/apps/llm/app.py +169 -0
- gaia/apps/summarize/app.py +633 -0
- gaia/apps/summarize/html_viewer.py +133 -0
- gaia/apps/summarize/pdf_formatter.py +284 -0
- gaia/audio/__init__.py +2 -0
- gaia/audio/audio_client.py +439 -0
- gaia/audio/audio_recorder.py +269 -0
- gaia/audio/kokoro_tts.py +599 -0
- gaia/audio/whisper_asr.py +432 -0
- gaia/chat/__init__.py +16 -0
- gaia/chat/app.py +430 -0
- gaia/chat/prompts.py +522 -0
- gaia/chat/sdk.py +1200 -0
- gaia/cli.py +5621 -0
- gaia/eval/batch_experiment.py +2332 -0
- gaia/eval/claude.py +542 -0
- gaia/eval/config.py +37 -0
- gaia/eval/email_generator.py +512 -0
- gaia/eval/eval.py +3179 -0
- gaia/eval/groundtruth.py +1130 -0
- gaia/eval/transcript_generator.py +582 -0
- gaia/eval/webapp/README.md +168 -0
- gaia/eval/webapp/node_modules/.bin/mime +16 -0
- gaia/eval/webapp/node_modules/.bin/mime.cmd +17 -0
- gaia/eval/webapp/node_modules/.bin/mime.ps1 +28 -0
- gaia/eval/webapp/node_modules/.package-lock.json +865 -0
- gaia/eval/webapp/node_modules/accepts/HISTORY.md +243 -0
- gaia/eval/webapp/node_modules/accepts/LICENSE +23 -0
- gaia/eval/webapp/node_modules/accepts/README.md +140 -0
- gaia/eval/webapp/node_modules/accepts/index.js +238 -0
- gaia/eval/webapp/node_modules/accepts/package.json +47 -0
- gaia/eval/webapp/node_modules/array-flatten/LICENSE +21 -0
- gaia/eval/webapp/node_modules/array-flatten/README.md +43 -0
- gaia/eval/webapp/node_modules/array-flatten/array-flatten.js +64 -0
- gaia/eval/webapp/node_modules/array-flatten/package.json +39 -0
- gaia/eval/webapp/node_modules/body-parser/HISTORY.md +672 -0
- gaia/eval/webapp/node_modules/body-parser/LICENSE +23 -0
- gaia/eval/webapp/node_modules/body-parser/README.md +476 -0
- gaia/eval/webapp/node_modules/body-parser/SECURITY.md +25 -0
- gaia/eval/webapp/node_modules/body-parser/index.js +156 -0
- gaia/eval/webapp/node_modules/body-parser/lib/read.js +205 -0
- gaia/eval/webapp/node_modules/body-parser/lib/types/json.js +247 -0
- gaia/eval/webapp/node_modules/body-parser/lib/types/raw.js +101 -0
- gaia/eval/webapp/node_modules/body-parser/lib/types/text.js +121 -0
- gaia/eval/webapp/node_modules/body-parser/lib/types/urlencoded.js +307 -0
- gaia/eval/webapp/node_modules/body-parser/package.json +56 -0
- gaia/eval/webapp/node_modules/bytes/History.md +97 -0
- gaia/eval/webapp/node_modules/bytes/LICENSE +23 -0
- gaia/eval/webapp/node_modules/bytes/Readme.md +152 -0
- gaia/eval/webapp/node_modules/bytes/index.js +170 -0
- gaia/eval/webapp/node_modules/bytes/package.json +42 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/.eslintrc +17 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/.nycrc +9 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/CHANGELOG.md +30 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/LICENSE +21 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/README.md +62 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/actualApply.d.ts +1 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/actualApply.js +10 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/applyBind.d.ts +19 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/applyBind.js +10 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionApply.d.ts +1 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionApply.js +4 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionCall.d.ts +1 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/functionCall.js +4 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/index.d.ts +64 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/index.js +15 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/package.json +85 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/reflectApply.d.ts +3 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/reflectApply.js +4 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/test/index.js +63 -0
- gaia/eval/webapp/node_modules/call-bind-apply-helpers/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/call-bound/.eslintrc +13 -0
- gaia/eval/webapp/node_modules/call-bound/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/call-bound/.nycrc +9 -0
- gaia/eval/webapp/node_modules/call-bound/CHANGELOG.md +42 -0
- gaia/eval/webapp/node_modules/call-bound/LICENSE +21 -0
- gaia/eval/webapp/node_modules/call-bound/README.md +53 -0
- gaia/eval/webapp/node_modules/call-bound/index.d.ts +94 -0
- gaia/eval/webapp/node_modules/call-bound/index.js +19 -0
- gaia/eval/webapp/node_modules/call-bound/package.json +99 -0
- gaia/eval/webapp/node_modules/call-bound/test/index.js +61 -0
- gaia/eval/webapp/node_modules/call-bound/tsconfig.json +10 -0
- gaia/eval/webapp/node_modules/content-disposition/HISTORY.md +60 -0
- gaia/eval/webapp/node_modules/content-disposition/LICENSE +22 -0
- gaia/eval/webapp/node_modules/content-disposition/README.md +142 -0
- gaia/eval/webapp/node_modules/content-disposition/index.js +458 -0
- gaia/eval/webapp/node_modules/content-disposition/package.json +44 -0
- gaia/eval/webapp/node_modules/content-type/HISTORY.md +29 -0
- gaia/eval/webapp/node_modules/content-type/LICENSE +22 -0
- gaia/eval/webapp/node_modules/content-type/README.md +94 -0
- gaia/eval/webapp/node_modules/content-type/index.js +225 -0
- gaia/eval/webapp/node_modules/content-type/package.json +42 -0
- gaia/eval/webapp/node_modules/cookie/LICENSE +24 -0
- gaia/eval/webapp/node_modules/cookie/README.md +317 -0
- gaia/eval/webapp/node_modules/cookie/SECURITY.md +25 -0
- gaia/eval/webapp/node_modules/cookie/index.js +334 -0
- gaia/eval/webapp/node_modules/cookie/package.json +44 -0
- gaia/eval/webapp/node_modules/cookie-signature/.npmignore +4 -0
- gaia/eval/webapp/node_modules/cookie-signature/History.md +38 -0
- gaia/eval/webapp/node_modules/cookie-signature/Readme.md +42 -0
- gaia/eval/webapp/node_modules/cookie-signature/index.js +51 -0
- gaia/eval/webapp/node_modules/cookie-signature/package.json +18 -0
- gaia/eval/webapp/node_modules/debug/.coveralls.yml +1 -0
- gaia/eval/webapp/node_modules/debug/.eslintrc +11 -0
- gaia/eval/webapp/node_modules/debug/.npmignore +9 -0
- gaia/eval/webapp/node_modules/debug/.travis.yml +14 -0
- gaia/eval/webapp/node_modules/debug/CHANGELOG.md +362 -0
- gaia/eval/webapp/node_modules/debug/LICENSE +19 -0
- gaia/eval/webapp/node_modules/debug/Makefile +50 -0
- gaia/eval/webapp/node_modules/debug/README.md +312 -0
- gaia/eval/webapp/node_modules/debug/component.json +19 -0
- gaia/eval/webapp/node_modules/debug/karma.conf.js +70 -0
- gaia/eval/webapp/node_modules/debug/node.js +1 -0
- gaia/eval/webapp/node_modules/debug/package.json +49 -0
- gaia/eval/webapp/node_modules/debug/src/browser.js +185 -0
- gaia/eval/webapp/node_modules/debug/src/debug.js +202 -0
- gaia/eval/webapp/node_modules/debug/src/index.js +10 -0
- gaia/eval/webapp/node_modules/debug/src/inspector-log.js +15 -0
- gaia/eval/webapp/node_modules/debug/src/node.js +248 -0
- gaia/eval/webapp/node_modules/depd/History.md +103 -0
- gaia/eval/webapp/node_modules/depd/LICENSE +22 -0
- gaia/eval/webapp/node_modules/depd/Readme.md +280 -0
- gaia/eval/webapp/node_modules/depd/index.js +538 -0
- gaia/eval/webapp/node_modules/depd/lib/browser/index.js +77 -0
- gaia/eval/webapp/node_modules/depd/package.json +45 -0
- gaia/eval/webapp/node_modules/destroy/LICENSE +23 -0
- gaia/eval/webapp/node_modules/destroy/README.md +63 -0
- gaia/eval/webapp/node_modules/destroy/index.js +209 -0
- gaia/eval/webapp/node_modules/destroy/package.json +48 -0
- gaia/eval/webapp/node_modules/dunder-proto/.eslintrc +5 -0
- gaia/eval/webapp/node_modules/dunder-proto/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/dunder-proto/.nycrc +13 -0
- gaia/eval/webapp/node_modules/dunder-proto/CHANGELOG.md +24 -0
- gaia/eval/webapp/node_modules/dunder-proto/LICENSE +21 -0
- gaia/eval/webapp/node_modules/dunder-proto/README.md +54 -0
- gaia/eval/webapp/node_modules/dunder-proto/get.d.ts +5 -0
- gaia/eval/webapp/node_modules/dunder-proto/get.js +30 -0
- gaia/eval/webapp/node_modules/dunder-proto/package.json +76 -0
- gaia/eval/webapp/node_modules/dunder-proto/set.d.ts +5 -0
- gaia/eval/webapp/node_modules/dunder-proto/set.js +35 -0
- gaia/eval/webapp/node_modules/dunder-proto/test/get.js +34 -0
- gaia/eval/webapp/node_modules/dunder-proto/test/index.js +4 -0
- gaia/eval/webapp/node_modules/dunder-proto/test/set.js +50 -0
- gaia/eval/webapp/node_modules/dunder-proto/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/ee-first/LICENSE +22 -0
- gaia/eval/webapp/node_modules/ee-first/README.md +80 -0
- gaia/eval/webapp/node_modules/ee-first/index.js +95 -0
- gaia/eval/webapp/node_modules/ee-first/package.json +29 -0
- gaia/eval/webapp/node_modules/encodeurl/LICENSE +22 -0
- gaia/eval/webapp/node_modules/encodeurl/README.md +109 -0
- gaia/eval/webapp/node_modules/encodeurl/index.js +60 -0
- gaia/eval/webapp/node_modules/encodeurl/package.json +40 -0
- gaia/eval/webapp/node_modules/es-define-property/.eslintrc +13 -0
- gaia/eval/webapp/node_modules/es-define-property/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/es-define-property/.nycrc +9 -0
- gaia/eval/webapp/node_modules/es-define-property/CHANGELOG.md +29 -0
- gaia/eval/webapp/node_modules/es-define-property/LICENSE +21 -0
- gaia/eval/webapp/node_modules/es-define-property/README.md +49 -0
- gaia/eval/webapp/node_modules/es-define-property/index.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-define-property/index.js +14 -0
- gaia/eval/webapp/node_modules/es-define-property/package.json +81 -0
- gaia/eval/webapp/node_modules/es-define-property/test/index.js +56 -0
- gaia/eval/webapp/node_modules/es-define-property/tsconfig.json +10 -0
- gaia/eval/webapp/node_modules/es-errors/.eslintrc +5 -0
- gaia/eval/webapp/node_modules/es-errors/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/es-errors/CHANGELOG.md +40 -0
- gaia/eval/webapp/node_modules/es-errors/LICENSE +21 -0
- gaia/eval/webapp/node_modules/es-errors/README.md +55 -0
- gaia/eval/webapp/node_modules/es-errors/eval.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/eval.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/index.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/index.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/package.json +80 -0
- gaia/eval/webapp/node_modules/es-errors/range.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/range.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/ref.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/ref.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/syntax.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/syntax.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/test/index.js +19 -0
- gaia/eval/webapp/node_modules/es-errors/tsconfig.json +49 -0
- gaia/eval/webapp/node_modules/es-errors/type.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/type.js +4 -0
- gaia/eval/webapp/node_modules/es-errors/uri.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-errors/uri.js +4 -0
- gaia/eval/webapp/node_modules/es-object-atoms/.eslintrc +16 -0
- gaia/eval/webapp/node_modules/es-object-atoms/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/es-object-atoms/CHANGELOG.md +37 -0
- gaia/eval/webapp/node_modules/es-object-atoms/LICENSE +21 -0
- gaia/eval/webapp/node_modules/es-object-atoms/README.md +63 -0
- gaia/eval/webapp/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-object-atoms/RequireObjectCoercible.js +11 -0
- gaia/eval/webapp/node_modules/es-object-atoms/ToObject.d.ts +7 -0
- gaia/eval/webapp/node_modules/es-object-atoms/ToObject.js +10 -0
- gaia/eval/webapp/node_modules/es-object-atoms/index.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-object-atoms/index.js +4 -0
- gaia/eval/webapp/node_modules/es-object-atoms/isObject.d.ts +3 -0
- gaia/eval/webapp/node_modules/es-object-atoms/isObject.js +6 -0
- gaia/eval/webapp/node_modules/es-object-atoms/package.json +80 -0
- gaia/eval/webapp/node_modules/es-object-atoms/test/index.js +38 -0
- gaia/eval/webapp/node_modules/es-object-atoms/tsconfig.json +6 -0
- gaia/eval/webapp/node_modules/escape-html/LICENSE +24 -0
- gaia/eval/webapp/node_modules/escape-html/Readme.md +43 -0
- gaia/eval/webapp/node_modules/escape-html/index.js +78 -0
- gaia/eval/webapp/node_modules/escape-html/package.json +24 -0
- gaia/eval/webapp/node_modules/etag/HISTORY.md +83 -0
- gaia/eval/webapp/node_modules/etag/LICENSE +22 -0
- gaia/eval/webapp/node_modules/etag/README.md +159 -0
- gaia/eval/webapp/node_modules/etag/index.js +131 -0
- gaia/eval/webapp/node_modules/etag/package.json +47 -0
- gaia/eval/webapp/node_modules/express/History.md +3656 -0
- gaia/eval/webapp/node_modules/express/LICENSE +24 -0
- gaia/eval/webapp/node_modules/express/Readme.md +260 -0
- gaia/eval/webapp/node_modules/express/index.js +11 -0
- gaia/eval/webapp/node_modules/express/lib/application.js +661 -0
- gaia/eval/webapp/node_modules/express/lib/express.js +116 -0
- gaia/eval/webapp/node_modules/express/lib/middleware/init.js +43 -0
- gaia/eval/webapp/node_modules/express/lib/middleware/query.js +47 -0
- gaia/eval/webapp/node_modules/express/lib/request.js +525 -0
- gaia/eval/webapp/node_modules/express/lib/response.js +1179 -0
- gaia/eval/webapp/node_modules/express/lib/router/index.js +673 -0
- gaia/eval/webapp/node_modules/express/lib/router/layer.js +181 -0
- gaia/eval/webapp/node_modules/express/lib/router/route.js +230 -0
- gaia/eval/webapp/node_modules/express/lib/utils.js +303 -0
- gaia/eval/webapp/node_modules/express/lib/view.js +182 -0
- gaia/eval/webapp/node_modules/express/package.json +102 -0
- gaia/eval/webapp/node_modules/finalhandler/HISTORY.md +210 -0
- gaia/eval/webapp/node_modules/finalhandler/LICENSE +22 -0
- gaia/eval/webapp/node_modules/finalhandler/README.md +147 -0
- gaia/eval/webapp/node_modules/finalhandler/SECURITY.md +25 -0
- gaia/eval/webapp/node_modules/finalhandler/index.js +341 -0
- gaia/eval/webapp/node_modules/finalhandler/package.json +47 -0
- gaia/eval/webapp/node_modules/forwarded/HISTORY.md +21 -0
- gaia/eval/webapp/node_modules/forwarded/LICENSE +22 -0
- gaia/eval/webapp/node_modules/forwarded/README.md +57 -0
- gaia/eval/webapp/node_modules/forwarded/index.js +90 -0
- gaia/eval/webapp/node_modules/forwarded/package.json +45 -0
- gaia/eval/webapp/node_modules/fresh/HISTORY.md +70 -0
- gaia/eval/webapp/node_modules/fresh/LICENSE +23 -0
- gaia/eval/webapp/node_modules/fresh/README.md +119 -0
- gaia/eval/webapp/node_modules/fresh/index.js +137 -0
- gaia/eval/webapp/node_modules/fresh/package.json +46 -0
- gaia/eval/webapp/node_modules/fs/README.md +9 -0
- gaia/eval/webapp/node_modules/fs/package.json +20 -0
- gaia/eval/webapp/node_modules/function-bind/.eslintrc +21 -0
- gaia/eval/webapp/node_modules/function-bind/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/function-bind/.github/SECURITY.md +3 -0
- gaia/eval/webapp/node_modules/function-bind/.nycrc +13 -0
- gaia/eval/webapp/node_modules/function-bind/CHANGELOG.md +136 -0
- gaia/eval/webapp/node_modules/function-bind/LICENSE +20 -0
- gaia/eval/webapp/node_modules/function-bind/README.md +46 -0
- gaia/eval/webapp/node_modules/function-bind/implementation.js +84 -0
- gaia/eval/webapp/node_modules/function-bind/index.js +5 -0
- gaia/eval/webapp/node_modules/function-bind/package.json +87 -0
- gaia/eval/webapp/node_modules/function-bind/test/.eslintrc +9 -0
- gaia/eval/webapp/node_modules/function-bind/test/index.js +252 -0
- gaia/eval/webapp/node_modules/get-intrinsic/.eslintrc +42 -0
- gaia/eval/webapp/node_modules/get-intrinsic/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/get-intrinsic/.nycrc +9 -0
- gaia/eval/webapp/node_modules/get-intrinsic/CHANGELOG.md +186 -0
- gaia/eval/webapp/node_modules/get-intrinsic/LICENSE +21 -0
- gaia/eval/webapp/node_modules/get-intrinsic/README.md +71 -0
- gaia/eval/webapp/node_modules/get-intrinsic/index.js +378 -0
- gaia/eval/webapp/node_modules/get-intrinsic/package.json +97 -0
- gaia/eval/webapp/node_modules/get-intrinsic/test/GetIntrinsic.js +274 -0
- gaia/eval/webapp/node_modules/get-proto/.eslintrc +10 -0
- gaia/eval/webapp/node_modules/get-proto/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/get-proto/.nycrc +9 -0
- gaia/eval/webapp/node_modules/get-proto/CHANGELOG.md +21 -0
- gaia/eval/webapp/node_modules/get-proto/LICENSE +21 -0
- gaia/eval/webapp/node_modules/get-proto/Object.getPrototypeOf.d.ts +5 -0
- gaia/eval/webapp/node_modules/get-proto/Object.getPrototypeOf.js +6 -0
- gaia/eval/webapp/node_modules/get-proto/README.md +50 -0
- gaia/eval/webapp/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +3 -0
- gaia/eval/webapp/node_modules/get-proto/Reflect.getPrototypeOf.js +4 -0
- gaia/eval/webapp/node_modules/get-proto/index.d.ts +5 -0
- gaia/eval/webapp/node_modules/get-proto/index.js +27 -0
- gaia/eval/webapp/node_modules/get-proto/package.json +81 -0
- gaia/eval/webapp/node_modules/get-proto/test/index.js +68 -0
- gaia/eval/webapp/node_modules/get-proto/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/gopd/.eslintrc +16 -0
- gaia/eval/webapp/node_modules/gopd/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/gopd/CHANGELOG.md +45 -0
- gaia/eval/webapp/node_modules/gopd/LICENSE +21 -0
- gaia/eval/webapp/node_modules/gopd/README.md +40 -0
- gaia/eval/webapp/node_modules/gopd/gOPD.d.ts +1 -0
- gaia/eval/webapp/node_modules/gopd/gOPD.js +4 -0
- gaia/eval/webapp/node_modules/gopd/index.d.ts +5 -0
- gaia/eval/webapp/node_modules/gopd/index.js +15 -0
- gaia/eval/webapp/node_modules/gopd/package.json +77 -0
- gaia/eval/webapp/node_modules/gopd/test/index.js +36 -0
- gaia/eval/webapp/node_modules/gopd/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/has-symbols/.eslintrc +11 -0
- gaia/eval/webapp/node_modules/has-symbols/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/has-symbols/.nycrc +9 -0
- gaia/eval/webapp/node_modules/has-symbols/CHANGELOG.md +91 -0
- gaia/eval/webapp/node_modules/has-symbols/LICENSE +21 -0
- gaia/eval/webapp/node_modules/has-symbols/README.md +46 -0
- gaia/eval/webapp/node_modules/has-symbols/index.d.ts +3 -0
- gaia/eval/webapp/node_modules/has-symbols/index.js +14 -0
- gaia/eval/webapp/node_modules/has-symbols/package.json +111 -0
- gaia/eval/webapp/node_modules/has-symbols/shams.d.ts +3 -0
- gaia/eval/webapp/node_modules/has-symbols/shams.js +45 -0
- gaia/eval/webapp/node_modules/has-symbols/test/index.js +22 -0
- gaia/eval/webapp/node_modules/has-symbols/test/shams/core-js.js +29 -0
- gaia/eval/webapp/node_modules/has-symbols/test/shams/get-own-property-symbols.js +29 -0
- gaia/eval/webapp/node_modules/has-symbols/test/tests.js +58 -0
- gaia/eval/webapp/node_modules/has-symbols/tsconfig.json +10 -0
- gaia/eval/webapp/node_modules/hasown/.eslintrc +5 -0
- gaia/eval/webapp/node_modules/hasown/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/hasown/.nycrc +13 -0
- gaia/eval/webapp/node_modules/hasown/CHANGELOG.md +40 -0
- gaia/eval/webapp/node_modules/hasown/LICENSE +21 -0
- gaia/eval/webapp/node_modules/hasown/README.md +40 -0
- gaia/eval/webapp/node_modules/hasown/index.d.ts +3 -0
- gaia/eval/webapp/node_modules/hasown/index.js +8 -0
- gaia/eval/webapp/node_modules/hasown/package.json +92 -0
- gaia/eval/webapp/node_modules/hasown/tsconfig.json +6 -0
- gaia/eval/webapp/node_modules/http-errors/HISTORY.md +180 -0
- gaia/eval/webapp/node_modules/http-errors/LICENSE +23 -0
- gaia/eval/webapp/node_modules/http-errors/README.md +169 -0
- gaia/eval/webapp/node_modules/http-errors/index.js +289 -0
- gaia/eval/webapp/node_modules/http-errors/package.json +50 -0
- gaia/eval/webapp/node_modules/iconv-lite/Changelog.md +162 -0
- gaia/eval/webapp/node_modules/iconv-lite/LICENSE +21 -0
- gaia/eval/webapp/node_modules/iconv-lite/README.md +156 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/dbcs-codec.js +555 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/dbcs-data.js +176 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/index.js +22 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/internal.js +188 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-codec.js +72 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-data-generated.js +451 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/sbcs-data.js +174 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/big5-added.json +122 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp936.json +264 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp949.json +273 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/cp950.json +177 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/eucjp.json +182 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +1 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/gbk-added.json +55 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/tables/shiftjis.json +125 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/utf16.js +177 -0
- gaia/eval/webapp/node_modules/iconv-lite/encodings/utf7.js +290 -0
- gaia/eval/webapp/node_modules/iconv-lite/lib/bom-handling.js +52 -0
- gaia/eval/webapp/node_modules/iconv-lite/lib/extend-node.js +217 -0
- gaia/eval/webapp/node_modules/iconv-lite/lib/index.d.ts +24 -0
- gaia/eval/webapp/node_modules/iconv-lite/lib/index.js +153 -0
- gaia/eval/webapp/node_modules/iconv-lite/lib/streams.js +121 -0
- gaia/eval/webapp/node_modules/iconv-lite/package.json +46 -0
- gaia/eval/webapp/node_modules/inherits/LICENSE +16 -0
- gaia/eval/webapp/node_modules/inherits/README.md +42 -0
- gaia/eval/webapp/node_modules/inherits/inherits.js +9 -0
- gaia/eval/webapp/node_modules/inherits/inherits_browser.js +27 -0
- gaia/eval/webapp/node_modules/inherits/package.json +29 -0
- gaia/eval/webapp/node_modules/ipaddr.js/LICENSE +19 -0
- gaia/eval/webapp/node_modules/ipaddr.js/README.md +233 -0
- gaia/eval/webapp/node_modules/ipaddr.js/ipaddr.min.js +1 -0
- gaia/eval/webapp/node_modules/ipaddr.js/lib/ipaddr.js +673 -0
- gaia/eval/webapp/node_modules/ipaddr.js/lib/ipaddr.js.d.ts +68 -0
- gaia/eval/webapp/node_modules/ipaddr.js/package.json +35 -0
- gaia/eval/webapp/node_modules/math-intrinsics/.eslintrc +16 -0
- gaia/eval/webapp/node_modules/math-intrinsics/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/math-intrinsics/CHANGELOG.md +24 -0
- gaia/eval/webapp/node_modules/math-intrinsics/LICENSE +21 -0
- gaia/eval/webapp/node_modules/math-intrinsics/README.md +50 -0
- gaia/eval/webapp/node_modules/math-intrinsics/abs.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/abs.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxArrayLength.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxSafeInteger.js +5 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxValue.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/constants/maxValue.js +5 -0
- gaia/eval/webapp/node_modules/math-intrinsics/floor.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/floor.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isFinite.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isFinite.js +12 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isInteger.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isInteger.js +16 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isNaN.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isNaN.js +6 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isNegativeZero.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/isNegativeZero.js +6 -0
- gaia/eval/webapp/node_modules/math-intrinsics/max.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/max.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/min.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/min.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/mod.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/mod.js +9 -0
- gaia/eval/webapp/node_modules/math-intrinsics/package.json +86 -0
- gaia/eval/webapp/node_modules/math-intrinsics/pow.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/pow.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/round.d.ts +1 -0
- gaia/eval/webapp/node_modules/math-intrinsics/round.js +4 -0
- gaia/eval/webapp/node_modules/math-intrinsics/sign.d.ts +3 -0
- gaia/eval/webapp/node_modules/math-intrinsics/sign.js +11 -0
- gaia/eval/webapp/node_modules/math-intrinsics/test/index.js +192 -0
- gaia/eval/webapp/node_modules/math-intrinsics/tsconfig.json +3 -0
- gaia/eval/webapp/node_modules/media-typer/HISTORY.md +22 -0
- gaia/eval/webapp/node_modules/media-typer/LICENSE +22 -0
- gaia/eval/webapp/node_modules/media-typer/README.md +81 -0
- gaia/eval/webapp/node_modules/media-typer/index.js +270 -0
- gaia/eval/webapp/node_modules/media-typer/package.json +26 -0
- gaia/eval/webapp/node_modules/merge-descriptors/HISTORY.md +21 -0
- gaia/eval/webapp/node_modules/merge-descriptors/LICENSE +23 -0
- gaia/eval/webapp/node_modules/merge-descriptors/README.md +49 -0
- gaia/eval/webapp/node_modules/merge-descriptors/index.js +60 -0
- gaia/eval/webapp/node_modules/merge-descriptors/package.json +39 -0
- gaia/eval/webapp/node_modules/methods/HISTORY.md +29 -0
- gaia/eval/webapp/node_modules/methods/LICENSE +24 -0
- gaia/eval/webapp/node_modules/methods/README.md +51 -0
- gaia/eval/webapp/node_modules/methods/index.js +69 -0
- gaia/eval/webapp/node_modules/methods/package.json +36 -0
- gaia/eval/webapp/node_modules/mime/.npmignore +0 -0
- gaia/eval/webapp/node_modules/mime/CHANGELOG.md +164 -0
- gaia/eval/webapp/node_modules/mime/LICENSE +21 -0
- gaia/eval/webapp/node_modules/mime/README.md +90 -0
- gaia/eval/webapp/node_modules/mime/cli.js +8 -0
- gaia/eval/webapp/node_modules/mime/mime.js +108 -0
- gaia/eval/webapp/node_modules/mime/package.json +44 -0
- gaia/eval/webapp/node_modules/mime/src/build.js +53 -0
- gaia/eval/webapp/node_modules/mime/src/test.js +60 -0
- gaia/eval/webapp/node_modules/mime/types.json +1 -0
- gaia/eval/webapp/node_modules/mime-db/HISTORY.md +507 -0
- gaia/eval/webapp/node_modules/mime-db/LICENSE +23 -0
- gaia/eval/webapp/node_modules/mime-db/README.md +100 -0
- gaia/eval/webapp/node_modules/mime-db/db.json +8519 -0
- gaia/eval/webapp/node_modules/mime-db/index.js +12 -0
- gaia/eval/webapp/node_modules/mime-db/package.json +60 -0
- gaia/eval/webapp/node_modules/mime-types/HISTORY.md +397 -0
- gaia/eval/webapp/node_modules/mime-types/LICENSE +23 -0
- gaia/eval/webapp/node_modules/mime-types/README.md +113 -0
- gaia/eval/webapp/node_modules/mime-types/index.js +188 -0
- gaia/eval/webapp/node_modules/mime-types/package.json +44 -0
- gaia/eval/webapp/node_modules/ms/index.js +152 -0
- gaia/eval/webapp/node_modules/ms/license.md +21 -0
- gaia/eval/webapp/node_modules/ms/package.json +37 -0
- gaia/eval/webapp/node_modules/ms/readme.md +51 -0
- gaia/eval/webapp/node_modules/negotiator/HISTORY.md +108 -0
- gaia/eval/webapp/node_modules/negotiator/LICENSE +24 -0
- gaia/eval/webapp/node_modules/negotiator/README.md +203 -0
- gaia/eval/webapp/node_modules/negotiator/index.js +82 -0
- gaia/eval/webapp/node_modules/negotiator/lib/charset.js +169 -0
- gaia/eval/webapp/node_modules/negotiator/lib/encoding.js +184 -0
- gaia/eval/webapp/node_modules/negotiator/lib/language.js +179 -0
- gaia/eval/webapp/node_modules/negotiator/lib/mediaType.js +294 -0
- gaia/eval/webapp/node_modules/negotiator/package.json +42 -0
- gaia/eval/webapp/node_modules/object-inspect/.eslintrc +53 -0
- gaia/eval/webapp/node_modules/object-inspect/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/object-inspect/.nycrc +13 -0
- gaia/eval/webapp/node_modules/object-inspect/CHANGELOG.md +424 -0
- gaia/eval/webapp/node_modules/object-inspect/LICENSE +21 -0
- gaia/eval/webapp/node_modules/object-inspect/example/all.js +23 -0
- gaia/eval/webapp/node_modules/object-inspect/example/circular.js +6 -0
- gaia/eval/webapp/node_modules/object-inspect/example/fn.js +5 -0
- gaia/eval/webapp/node_modules/object-inspect/example/inspect.js +10 -0
- gaia/eval/webapp/node_modules/object-inspect/index.js +544 -0
- gaia/eval/webapp/node_modules/object-inspect/package-support.json +20 -0
- gaia/eval/webapp/node_modules/object-inspect/package.json +105 -0
- gaia/eval/webapp/node_modules/object-inspect/readme.markdown +84 -0
- gaia/eval/webapp/node_modules/object-inspect/test/bigint.js +58 -0
- gaia/eval/webapp/node_modules/object-inspect/test/browser/dom.js +15 -0
- gaia/eval/webapp/node_modules/object-inspect/test/circular.js +16 -0
- gaia/eval/webapp/node_modules/object-inspect/test/deep.js +12 -0
- gaia/eval/webapp/node_modules/object-inspect/test/element.js +53 -0
- gaia/eval/webapp/node_modules/object-inspect/test/err.js +48 -0
- gaia/eval/webapp/node_modules/object-inspect/test/fakes.js +29 -0
- gaia/eval/webapp/node_modules/object-inspect/test/fn.js +76 -0
- gaia/eval/webapp/node_modules/object-inspect/test/global.js +17 -0
- gaia/eval/webapp/node_modules/object-inspect/test/has.js +15 -0
- gaia/eval/webapp/node_modules/object-inspect/test/holes.js +15 -0
- gaia/eval/webapp/node_modules/object-inspect/test/indent-option.js +271 -0
- gaia/eval/webapp/node_modules/object-inspect/test/inspect.js +139 -0
- gaia/eval/webapp/node_modules/object-inspect/test/lowbyte.js +12 -0
- gaia/eval/webapp/node_modules/object-inspect/test/number.js +58 -0
- gaia/eval/webapp/node_modules/object-inspect/test/quoteStyle.js +26 -0
- gaia/eval/webapp/node_modules/object-inspect/test/toStringTag.js +40 -0
- gaia/eval/webapp/node_modules/object-inspect/test/undef.js +12 -0
- gaia/eval/webapp/node_modules/object-inspect/test/values.js +261 -0
- gaia/eval/webapp/node_modules/object-inspect/test-core-js.js +26 -0
- gaia/eval/webapp/node_modules/object-inspect/util.inspect.js +1 -0
- gaia/eval/webapp/node_modules/on-finished/HISTORY.md +98 -0
- gaia/eval/webapp/node_modules/on-finished/LICENSE +23 -0
- gaia/eval/webapp/node_modules/on-finished/README.md +162 -0
- gaia/eval/webapp/node_modules/on-finished/index.js +234 -0
- gaia/eval/webapp/node_modules/on-finished/package.json +39 -0
- gaia/eval/webapp/node_modules/parseurl/HISTORY.md +58 -0
- gaia/eval/webapp/node_modules/parseurl/LICENSE +24 -0
- gaia/eval/webapp/node_modules/parseurl/README.md +133 -0
- gaia/eval/webapp/node_modules/parseurl/index.js +158 -0
- gaia/eval/webapp/node_modules/parseurl/package.json +40 -0
- gaia/eval/webapp/node_modules/path/.npmignore +1 -0
- gaia/eval/webapp/node_modules/path/LICENSE +18 -0
- gaia/eval/webapp/node_modules/path/README.md +15 -0
- gaia/eval/webapp/node_modules/path/package.json +24 -0
- gaia/eval/webapp/node_modules/path/path.js +628 -0
- gaia/eval/webapp/node_modules/path-to-regexp/LICENSE +21 -0
- gaia/eval/webapp/node_modules/path-to-regexp/Readme.md +35 -0
- gaia/eval/webapp/node_modules/path-to-regexp/index.js +156 -0
- gaia/eval/webapp/node_modules/path-to-regexp/package.json +30 -0
- gaia/eval/webapp/node_modules/process/.eslintrc +21 -0
- gaia/eval/webapp/node_modules/process/LICENSE +22 -0
- gaia/eval/webapp/node_modules/process/README.md +26 -0
- gaia/eval/webapp/node_modules/process/browser.js +184 -0
- gaia/eval/webapp/node_modules/process/index.js +2 -0
- gaia/eval/webapp/node_modules/process/package.json +27 -0
- gaia/eval/webapp/node_modules/process/test.js +199 -0
- gaia/eval/webapp/node_modules/proxy-addr/HISTORY.md +161 -0
- gaia/eval/webapp/node_modules/proxy-addr/LICENSE +22 -0
- gaia/eval/webapp/node_modules/proxy-addr/README.md +139 -0
- gaia/eval/webapp/node_modules/proxy-addr/index.js +327 -0
- gaia/eval/webapp/node_modules/proxy-addr/package.json +47 -0
- gaia/eval/webapp/node_modules/qs/.editorconfig +46 -0
- gaia/eval/webapp/node_modules/qs/.eslintrc +38 -0
- gaia/eval/webapp/node_modules/qs/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/qs/.nycrc +13 -0
- gaia/eval/webapp/node_modules/qs/CHANGELOG.md +600 -0
- gaia/eval/webapp/node_modules/qs/LICENSE.md +29 -0
- gaia/eval/webapp/node_modules/qs/README.md +709 -0
- gaia/eval/webapp/node_modules/qs/dist/qs.js +90 -0
- gaia/eval/webapp/node_modules/qs/lib/formats.js +23 -0
- gaia/eval/webapp/node_modules/qs/lib/index.js +11 -0
- gaia/eval/webapp/node_modules/qs/lib/parse.js +296 -0
- gaia/eval/webapp/node_modules/qs/lib/stringify.js +351 -0
- gaia/eval/webapp/node_modules/qs/lib/utils.js +265 -0
- gaia/eval/webapp/node_modules/qs/package.json +91 -0
- gaia/eval/webapp/node_modules/qs/test/empty-keys-cases.js +267 -0
- gaia/eval/webapp/node_modules/qs/test/parse.js +1170 -0
- gaia/eval/webapp/node_modules/qs/test/stringify.js +1298 -0
- gaia/eval/webapp/node_modules/qs/test/utils.js +136 -0
- gaia/eval/webapp/node_modules/range-parser/HISTORY.md +56 -0
- gaia/eval/webapp/node_modules/range-parser/LICENSE +23 -0
- gaia/eval/webapp/node_modules/range-parser/README.md +84 -0
- gaia/eval/webapp/node_modules/range-parser/index.js +162 -0
- gaia/eval/webapp/node_modules/range-parser/package.json +44 -0
- gaia/eval/webapp/node_modules/raw-body/HISTORY.md +308 -0
- gaia/eval/webapp/node_modules/raw-body/LICENSE +22 -0
- gaia/eval/webapp/node_modules/raw-body/README.md +223 -0
- gaia/eval/webapp/node_modules/raw-body/SECURITY.md +24 -0
- gaia/eval/webapp/node_modules/raw-body/index.d.ts +87 -0
- gaia/eval/webapp/node_modules/raw-body/index.js +336 -0
- gaia/eval/webapp/node_modules/raw-body/package.json +49 -0
- gaia/eval/webapp/node_modules/safe-buffer/LICENSE +21 -0
- gaia/eval/webapp/node_modules/safe-buffer/README.md +584 -0
- gaia/eval/webapp/node_modules/safe-buffer/index.d.ts +187 -0
- gaia/eval/webapp/node_modules/safe-buffer/index.js +65 -0
- gaia/eval/webapp/node_modules/safe-buffer/package.json +51 -0
- gaia/eval/webapp/node_modules/safer-buffer/LICENSE +21 -0
- gaia/eval/webapp/node_modules/safer-buffer/Porting-Buffer.md +268 -0
- gaia/eval/webapp/node_modules/safer-buffer/Readme.md +156 -0
- gaia/eval/webapp/node_modules/safer-buffer/dangerous.js +58 -0
- gaia/eval/webapp/node_modules/safer-buffer/package.json +34 -0
- gaia/eval/webapp/node_modules/safer-buffer/safer.js +77 -0
- gaia/eval/webapp/node_modules/safer-buffer/tests.js +406 -0
- gaia/eval/webapp/node_modules/send/HISTORY.md +526 -0
- gaia/eval/webapp/node_modules/send/LICENSE +23 -0
- gaia/eval/webapp/node_modules/send/README.md +327 -0
- gaia/eval/webapp/node_modules/send/SECURITY.md +24 -0
- gaia/eval/webapp/node_modules/send/index.js +1142 -0
- gaia/eval/webapp/node_modules/send/node_modules/encodeurl/HISTORY.md +14 -0
- gaia/eval/webapp/node_modules/send/node_modules/encodeurl/LICENSE +22 -0
- gaia/eval/webapp/node_modules/send/node_modules/encodeurl/README.md +128 -0
- gaia/eval/webapp/node_modules/send/node_modules/encodeurl/index.js +60 -0
- gaia/eval/webapp/node_modules/send/node_modules/encodeurl/package.json +40 -0
- gaia/eval/webapp/node_modules/send/node_modules/ms/index.js +162 -0
- gaia/eval/webapp/node_modules/send/node_modules/ms/license.md +21 -0
- gaia/eval/webapp/node_modules/send/node_modules/ms/package.json +38 -0
- gaia/eval/webapp/node_modules/send/node_modules/ms/readme.md +59 -0
- gaia/eval/webapp/node_modules/send/package.json +62 -0
- gaia/eval/webapp/node_modules/serve-static/HISTORY.md +487 -0
- gaia/eval/webapp/node_modules/serve-static/LICENSE +25 -0
- gaia/eval/webapp/node_modules/serve-static/README.md +257 -0
- gaia/eval/webapp/node_modules/serve-static/index.js +209 -0
- gaia/eval/webapp/node_modules/serve-static/package.json +42 -0
- gaia/eval/webapp/node_modules/setprototypeof/LICENSE +13 -0
- gaia/eval/webapp/node_modules/setprototypeof/README.md +31 -0
- gaia/eval/webapp/node_modules/setprototypeof/index.d.ts +2 -0
- gaia/eval/webapp/node_modules/setprototypeof/index.js +17 -0
- gaia/eval/webapp/node_modules/setprototypeof/package.json +38 -0
- gaia/eval/webapp/node_modules/setprototypeof/test/index.js +24 -0
- gaia/eval/webapp/node_modules/side-channel/.editorconfig +9 -0
- gaia/eval/webapp/node_modules/side-channel/.eslintrc +12 -0
- gaia/eval/webapp/node_modules/side-channel/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/side-channel/.nycrc +13 -0
- gaia/eval/webapp/node_modules/side-channel/CHANGELOG.md +110 -0
- gaia/eval/webapp/node_modules/side-channel/LICENSE +21 -0
- gaia/eval/webapp/node_modules/side-channel/README.md +61 -0
- gaia/eval/webapp/node_modules/side-channel/index.d.ts +14 -0
- gaia/eval/webapp/node_modules/side-channel/index.js +43 -0
- gaia/eval/webapp/node_modules/side-channel/package.json +85 -0
- gaia/eval/webapp/node_modules/side-channel/test/index.js +104 -0
- gaia/eval/webapp/node_modules/side-channel/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/side-channel-list/.editorconfig +9 -0
- gaia/eval/webapp/node_modules/side-channel-list/.eslintrc +11 -0
- gaia/eval/webapp/node_modules/side-channel-list/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/side-channel-list/.nycrc +13 -0
- gaia/eval/webapp/node_modules/side-channel-list/CHANGELOG.md +15 -0
- gaia/eval/webapp/node_modules/side-channel-list/LICENSE +21 -0
- gaia/eval/webapp/node_modules/side-channel-list/README.md +62 -0
- gaia/eval/webapp/node_modules/side-channel-list/index.d.ts +13 -0
- gaia/eval/webapp/node_modules/side-channel-list/index.js +113 -0
- gaia/eval/webapp/node_modules/side-channel-list/list.d.ts +14 -0
- gaia/eval/webapp/node_modules/side-channel-list/package.json +77 -0
- gaia/eval/webapp/node_modules/side-channel-list/test/index.js +104 -0
- gaia/eval/webapp/node_modules/side-channel-list/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/side-channel-map/.editorconfig +9 -0
- gaia/eval/webapp/node_modules/side-channel-map/.eslintrc +11 -0
- gaia/eval/webapp/node_modules/side-channel-map/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/side-channel-map/.nycrc +13 -0
- gaia/eval/webapp/node_modules/side-channel-map/CHANGELOG.md +22 -0
- gaia/eval/webapp/node_modules/side-channel-map/LICENSE +21 -0
- gaia/eval/webapp/node_modules/side-channel-map/README.md +62 -0
- gaia/eval/webapp/node_modules/side-channel-map/index.d.ts +15 -0
- gaia/eval/webapp/node_modules/side-channel-map/index.js +68 -0
- gaia/eval/webapp/node_modules/side-channel-map/package.json +80 -0
- gaia/eval/webapp/node_modules/side-channel-map/test/index.js +114 -0
- gaia/eval/webapp/node_modules/side-channel-map/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/.editorconfig +9 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/.eslintrc +12 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/.github/FUNDING.yml +12 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/.nycrc +13 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/CHANGELOG.md +28 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/LICENSE +21 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/README.md +62 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/index.d.ts +15 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/index.js +84 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/package.json +87 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/test/index.js +114 -0
- gaia/eval/webapp/node_modules/side-channel-weakmap/tsconfig.json +9 -0
- gaia/eval/webapp/node_modules/statuses/HISTORY.md +82 -0
- gaia/eval/webapp/node_modules/statuses/LICENSE +23 -0
- gaia/eval/webapp/node_modules/statuses/README.md +136 -0
- gaia/eval/webapp/node_modules/statuses/codes.json +65 -0
- gaia/eval/webapp/node_modules/statuses/index.js +146 -0
- gaia/eval/webapp/node_modules/statuses/package.json +49 -0
- gaia/eval/webapp/node_modules/toidentifier/HISTORY.md +9 -0
- gaia/eval/webapp/node_modules/toidentifier/LICENSE +21 -0
- gaia/eval/webapp/node_modules/toidentifier/README.md +61 -0
- gaia/eval/webapp/node_modules/toidentifier/index.js +32 -0
- gaia/eval/webapp/node_modules/toidentifier/package.json +38 -0
- gaia/eval/webapp/node_modules/type-is/HISTORY.md +259 -0
- gaia/eval/webapp/node_modules/type-is/LICENSE +23 -0
- gaia/eval/webapp/node_modules/type-is/README.md +170 -0
- gaia/eval/webapp/node_modules/type-is/index.js +266 -0
- gaia/eval/webapp/node_modules/type-is/package.json +45 -0
- gaia/eval/webapp/node_modules/unpipe/HISTORY.md +4 -0
- gaia/eval/webapp/node_modules/unpipe/LICENSE +22 -0
- gaia/eval/webapp/node_modules/unpipe/README.md +43 -0
- gaia/eval/webapp/node_modules/unpipe/index.js +69 -0
- gaia/eval/webapp/node_modules/unpipe/package.json +27 -0
- gaia/eval/webapp/node_modules/util/LICENSE +18 -0
- gaia/eval/webapp/node_modules/util/README.md +15 -0
- gaia/eval/webapp/node_modules/util/node_modules/inherits/LICENSE +16 -0
- gaia/eval/webapp/node_modules/util/node_modules/inherits/README.md +42 -0
- gaia/eval/webapp/node_modules/util/node_modules/inherits/inherits.js +7 -0
- gaia/eval/webapp/node_modules/util/node_modules/inherits/inherits_browser.js +23 -0
- gaia/eval/webapp/node_modules/util/node_modules/inherits/package.json +29 -0
- gaia/eval/webapp/node_modules/util/package.json +35 -0
- gaia/eval/webapp/node_modules/util/support/isBuffer.js +3 -0
- gaia/eval/webapp/node_modules/util/support/isBufferBrowser.js +6 -0
- gaia/eval/webapp/node_modules/util/util.js +586 -0
- gaia/eval/webapp/node_modules/utils-merge/.npmignore +9 -0
- gaia/eval/webapp/node_modules/utils-merge/LICENSE +20 -0
- gaia/eval/webapp/node_modules/utils-merge/README.md +34 -0
- gaia/eval/webapp/node_modules/utils-merge/index.js +23 -0
- gaia/eval/webapp/node_modules/utils-merge/package.json +40 -0
- gaia/eval/webapp/node_modules/vary/HISTORY.md +39 -0
- gaia/eval/webapp/node_modules/vary/LICENSE +22 -0
- gaia/eval/webapp/node_modules/vary/README.md +101 -0
- gaia/eval/webapp/node_modules/vary/index.js +149 -0
- gaia/eval/webapp/node_modules/vary/package.json +43 -0
- gaia/eval/webapp/package-lock.json +875 -0
- gaia/eval/webapp/package.json +21 -0
- gaia/eval/webapp/public/app.js +3403 -0
- gaia/eval/webapp/public/index.html +88 -0
- gaia/eval/webapp/public/styles.css +3661 -0
- gaia/eval/webapp/server.js +416 -0
- gaia/eval/webapp/test-setup.js +73 -0
- gaia/llm/__init__.py +2 -0
- gaia/llm/lemonade_client.py +3083 -0
- gaia/llm/lemonade_manager.py +269 -0
- gaia/llm/llm_client.py +729 -0
- gaia/llm/vlm_client.py +307 -0
- gaia/logger.py +189 -0
- gaia/mcp/agent_mcp_server.py +245 -0
- gaia/mcp/blender_mcp_client.py +138 -0
- gaia/mcp/blender_mcp_server.py +648 -0
- gaia/mcp/context7_cache.py +332 -0
- gaia/mcp/external_services.py +518 -0
- gaia/mcp/mcp_bridge.py +550 -0
- gaia/mcp/servers/__init__.py +6 -0
- gaia/mcp/servers/docker_mcp.py +83 -0
- gaia/rag/__init__.py +10 -0
- gaia/rag/app.py +293 -0
- gaia/rag/demo.py +304 -0
- gaia/rag/pdf_utils.py +235 -0
- gaia/rag/sdk.py +2194 -0
- gaia/security.py +163 -0
- gaia/talk/app.py +289 -0
- gaia/talk/sdk.py +538 -0
- gaia/util.py +46 -0
- 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
|
+
}
|