framework-m-studio 0.8.4__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/.gitignore +2 -1
  2. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/CHANGELOG.md +27 -0
  3. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/PKG-INFO +2 -2
  4. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/pyproject.toml +2 -2
  5. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/deps.py +108 -7
  6. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/new.py +68 -226
  7. framework_m_studio-0.9.0/src/framework_m_studio/cli/release/graph.py +162 -0
  8. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/orchestrator.py +43 -14
  9. framework_m_studio-0.8.4/src/framework_m_studio/static/assets/index-C7HXLlUt.js → framework_m_studio-0.9.0/src/framework_m_studio/static/assets/index-DM0cHKco.js +2 -2
  10. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/static/index.html +1 -1
  11. framework_m_studio-0.9.0/src/framework_m_studio/templates/app/README.md.template +36 -0
  12. framework_m_studio-0.9.0/src/framework_m_studio/templates/app/__init__.py.template +11 -0
  13. framework_m_studio-0.9.0/src/framework_m_studio/templates/app/doctypes_init.py.template +3 -0
  14. framework_m_studio-0.9.0/src/framework_m_studio/templates/app/pyproject.toml.template +24 -0
  15. framework_m_studio-0.9.0/src/framework_m_studio/templates/doctype/__init__.py.template +9 -0
  16. framework_m_studio-0.9.0/src/framework_m_studio/templates/doctype/controller.py.template +26 -0
  17. framework_m_studio-0.9.0/src/framework_m_studio/templates/doctype/doctype.py.template +38 -0
  18. framework_m_studio-0.9.0/src/framework_m_studio/templates/doctype/test.py.template +23 -0
  19. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/.gitignore.template +63 -0
  20. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/Dashboard.tsx.template +52 -0
  21. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/favicon.png +0 -0
  22. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/index.html → framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/index.html.template +6 -4
  23. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/index.tsx.template +33 -0
  24. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/package.json → framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/package.json.template +11 -4
  25. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/theme.css.template +166 -0
  26. framework_m_studio-0.9.0/src/framework_m_studio/templates/plugin/vite.config.ts.template +20 -0
  27. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/package.json +5 -5
  28. framework_m_studio-0.9.0/studio_ui/public/favicon.ico +0 -0
  29. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/cli/test_new.py +15 -60
  30. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_deps_cli.py +143 -0
  31. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_new_cli.py +2 -2
  32. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_release_cli.py +58 -7
  33. framework_m_studio-0.9.0/tests/test_release_graph.py +180 -0
  34. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/.gitignore +0 -32
  35. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/.npmrc +0 -10
  36. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/README.md +0 -85
  37. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/App.tsx +0 -1088
  38. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/components/AppNavbar.tsx +0 -233
  39. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/components/AppSidebar.tsx +0 -173
  40. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/index.tsx +0 -20
  41. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/pages/LoginPage.tsx +0 -217
  42. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/src/theme.css +0 -293
  43. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/tsconfig.json +0 -19
  44. framework_m_studio-0.8.4/src/framework_m_studio/templates/frontend/vite.config.ts +0 -33
  45. framework_m_studio-0.8.4/src/framework_m_studio/templates/plugin/Dashboard.tsx.template +0 -10
  46. framework_m_studio-0.8.4/src/framework_m_studio/templates/plugin/package.json.template +0 -33
  47. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/LICENSE +0 -0
  48. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/README.md +0 -0
  49. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/docs/api/index.md +0 -0
  50. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/docs/user-guide.md +0 -0
  51. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/__init__.py +0 -0
  52. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/app.py +0 -0
  53. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/__init__.py +0 -0
  54. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/codegen.py +0 -0
  55. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/constants.py +0 -0
  56. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/contrib.py +0 -0
  57. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/dev.py +0 -0
  58. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/docs.py +0 -0
  59. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/quality.py +0 -0
  60. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/__init__.py +0 -0
  61. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/cli.py +0 -0
  62. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/gitops.py +0 -0
  63. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/models.py +0 -0
  64. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/release/versioning.py +0 -0
  65. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/studio.py +0 -0
  66. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/utility.py +0 -0
  67. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/cli/verify.py +0 -0
  68. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/__init__.py +0 -0
  69. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/generator.py +0 -0
  70. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/parser.py +0 -0
  71. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/templates/doctype.py.jinja2 +0 -0
  72. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/templates/test_doctype.py.jinja2 +0 -0
  73. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/test_generator.py +0 -0
  74. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/codegen/transformer.py +0 -0
  75. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/core/frontend.py +0 -0
  76. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/core/gitops.py +0 -0
  77. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/core/shell.py +0 -0
  78. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/discovery.py +0 -0
  79. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/ast_parsers.py +0 -0
  80. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/audit.py +0 -0
  81. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/exporters.py +0 -0
  82. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/formatters.py +0 -0
  83. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/generator.py +0 -0
  84. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/docs/openapi.py +0 -0
  85. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/git/__init__.py +0 -0
  86. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/git/adapter.py +0 -0
  87. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/git/github_provider.py +0 -0
  88. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/git/protocol.py +0 -0
  89. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/protocol_scanner.py +0 -0
  90. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/py.typed +0 -0
  91. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/routes.py +0 -0
  92. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/sdk_generator.py +0 -0
  93. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/static/.gitkeep +0 -0
  94. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/static/assets/index-BXzUquLi.css +0 -0
  95. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/static/favicon.ico +0 -0
  96. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/templates/plugin/README.md.template +0 -0
  97. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/templates/plugin/plugin.config.ts.template +0 -0
  98. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/templates/plugin/tsconfig.json.template +0 -0
  99. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/templates/plugin/vite.config.mfe.ts.template +0 -0
  100. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/src/framework_m_studio/workspace.py +0 -0
  101. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/.gitignore +0 -0
  102. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/.npmrc +0 -0
  103. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/CHANGELOG.md +0 -0
  104. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/Dockerfile +0 -0
  105. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/README.md +0 -0
  106. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/eslint.config.js +0 -0
  107. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/index.html +0 -0
  108. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/pnpm-lock.yaml +0 -0
  109. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/App.css +0 -0
  110. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/App.test.tsx +0 -0
  111. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/App.tsx +0 -0
  112. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/AutoForm.tsx +0 -0
  113. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/AutoTable.tsx +0 -0
  114. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/CodePreview.tsx +0 -0
  115. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/ControllerEditor.tsx +0 -0
  116. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/ERDView.tsx +0 -0
  117. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/FieldEditor.tsx +0 -0
  118. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/LayoutDesigner.tsx +0 -0
  119. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/ModuleExplorer.tsx +0 -0
  120. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/NestedEditorDrawer.tsx +0 -0
  121. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/SandboxPreview.tsx +0 -0
  122. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/TableFieldEditor.tsx +0 -0
  123. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/breadcrumb/index.tsx +0 -0
  124. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/fields/DefaultFieldComponents.tsx +0 -0
  125. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/fields/index.ts +0 -0
  126. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/fields/resolveFieldComponent.ts +0 -0
  127. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/layout/index.tsx +0 -0
  128. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/components/menu/index.tsx +0 -0
  129. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/index.css +0 -0
  130. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/index.tsx +0 -0
  131. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/pages/doctypes/edit.tsx +0 -0
  132. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/pages/doctypes/index.ts +0 -0
  133. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/pages/doctypes/list.tsx +0 -0
  134. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/pages/index.ts +0 -0
  135. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/providers/ThemeContext.ts +0 -0
  136. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/providers/constants.ts +0 -0
  137. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/providers/data.ts +0 -0
  138. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/providers/theme.tsx +0 -0
  139. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/providers/useTheme.ts +0 -0
  140. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/registry/defaultComponents.ts +0 -0
  141. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/registry/fieldComponents.ts +0 -0
  142. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/registry/index.ts +0 -0
  143. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/services/mock_data.ts +0 -0
  144. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/setupTests.ts +0 -0
  145. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/utils/mockDataGenerator.ts +0 -0
  146. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/src/vite-env.d.ts +0 -0
  147. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/tsconfig.json +0 -0
  148. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/tsconfig.node.json +0 -0
  149. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/vite.config.ts +0 -0
  150. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/studio_ui/vitest.config.ts +0 -0
  151. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/cli/test_verify.py +0 -0
  152. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/conftest.py +0 -0
  153. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/core/test_frontend.py +0 -0
  154. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/core/test_gitops.py +0 -0
  155. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/core/test_shell.py +0 -0
  156. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_api_routes.py +0 -0
  157. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_cli.py +0 -0
  158. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_cli_openapi.py +0 -0
  159. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_contrib_cli.py +0 -0
  160. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_dev.py +0 -0
  161. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_dev_cli_coverage.py +0 -0
  162. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_discovery.py +0 -0
  163. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_docs_audit.py +0 -0
  164. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_docs_generator.py +0 -0
  165. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_docs_generator_enhanced.py +0 -0
  166. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_frontend_integration.py +0 -0
  167. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_generator.py +0 -0
  168. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_git_adapter.py +0 -0
  169. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_git_protocol.py +0 -0
  170. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_github_provider.py +0 -0
  171. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_parser.py +0 -0
  172. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_protocol_scanner.py +0 -0
  173. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_quality_cli.py +0 -0
  174. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_screenshot_pipeline.py +0 -0
  175. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_sdk_generator.py +0 -0
  176. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_studio.py +0 -0
  177. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_studio_codegen_generator.py +0 -0
  178. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_transformer.py +0 -0
  179. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_utility_cli.py +0 -0
  180. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_utility_cli_enhanced.py +0 -0
  181. {framework_m_studio-0.8.4 → framework_m_studio-0.9.0}/tests/test_workspace.py +0 -0
@@ -46,6 +46,7 @@ htmlcov/
46
46
  .tox/
47
47
  .nox/
48
48
  coverage.xml
49
+ coverage/
49
50
 
50
51
  # Type checking
51
52
  .mypy_cache/
@@ -129,4 +130,4 @@ website/build/
129
130
  website/node_modules/
130
131
 
131
132
  # GitLab Pages
132
- public/
133
+ /public/
@@ -1,3 +1,30 @@
1
+ ## framework-m-studio v0.9.0
2
+
3
+ ### Features
4
+
5
+ - update plugin scaffolding for refined shell architecture (3dbc0f6)
6
+ - consolidate frontend scaffolding into unified plugin templates (c12ae9a)
7
+ - consolidated and externalized CLI scaffolding (f5633b2)
8
+ - update plugin scaffolding for refined shell architecture (96a23b0)
9
+ - consolidate frontend scaffolding into unified plugin templates (9f34e3a)
10
+ - consolidated and externalized CLI scaffolding (fae62f2)
11
+ - implement negative-filter discovery with regression tests (9305ad4)
12
+ - improve automated MR formatting (3168236)
13
+ - implement brand-aligned shell layout and improved frontend templates (7bf913e)
14
+ - automate dependency discovery for release (879bd49)
15
+ - add standalone landing page to plugin scaffold (4d9f834)
16
+
17
+ ### Bug Fixes
18
+
19
+ - robust dependency discovery and orchestrator compatibility (7def0e4)
20
+ - improve deps upgrade semantic commits and discovery (49f0f01)
21
+
22
+ ## framework-m-studio v0.8.5
23
+
24
+ ### Bug Fixes
25
+
26
+ - add node types to tsconfig and sync related apps (da1934f)
27
+
1
28
  ## framework-m-studio v0.8.4
2
29
 
3
30
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: framework-m-studio
3
- Version: 0.8.4
3
+ Version: 0.9.0
4
4
  Summary: Framework M Studio - Visual DocType Builder & Developer Tools
5
5
  Project-URL: Homepage, https://gitlab.com/castlecraft/framework-m
6
6
  Project-URL: Documentation, https://gitlab.com/castlecraft/framework-m#readme
@@ -21,7 +21,7 @@ Requires-Python: >=3.12
21
21
  Requires-Dist: bandit>=1.9.4
22
22
  Requires-Dist: build>=1.0.0
23
23
  Requires-Dist: framework-m-core>=0.11.4
24
- Requires-Dist: framework-m>=0.6.8
24
+ Requires-Dist: framework-m>=0.6.9
25
25
  Requires-Dist: honcho>=1.1.0
26
26
  Requires-Dist: httpx>=0.27.0
27
27
  Requires-Dist: ipython>=9.11.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "framework-m-studio"
3
- version = "0.8.4"
3
+ version = "0.9.0"
4
4
  description = "Framework M Studio - Visual DocType Builder & Developer Tools"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -22,7 +22,7 @@ classifiers = [
22
22
 
23
23
  dependencies = [
24
24
  "framework-m-core>=0.11.4",
25
- "framework-m>=0.6.8",
25
+ "framework-m>=0.6.9",
26
26
  # Code generation
27
27
  "libcst>=1.0.0",
28
28
  "jinja2>=3.1.0",
@@ -33,8 +33,8 @@ def _check_uv_outdated(root: Path) -> list[dict[str, str]]:
33
33
 
34
34
 
35
35
  def _check_pnpm_outdated(root: Path) -> list[dict[str, str]]:
36
- """Parse pnpm outdated --format json output."""
37
- output = run_cmd("pnpm outdated --format json", cwd=root, check=False)
36
+ """Parse pnpm outdated -r --format json output."""
37
+ output = run_cmd("pnpm outdated -r --format json", cwd=root, check=False)
38
38
  outdated: list[dict[str, str]] = []
39
39
  if not output:
40
40
  return outdated
@@ -54,6 +54,80 @@ def _check_pnpm_outdated(root: Path) -> list[dict[str, str]]:
54
54
  return outdated
55
55
 
56
56
 
57
+ def _get_workspace_projects(
58
+ root: Path,
59
+ manager: str,
60
+ base_dirs: list[str] | None = None,
61
+ updated_packages: list[str] | None = None,
62
+ ) -> list[str]:
63
+ """Get all workspace projects from the given base directories that depend on updated_packages."""
64
+ projects = []
65
+
66
+ # If no specific packages were updated, we can't easily filter
67
+ if not updated_packages:
68
+ return []
69
+
70
+ for base_dir in base_dirs or ["apps", "libs"]:
71
+ dir_path = root / base_dir
72
+ if not dir_path.exists():
73
+ continue
74
+
75
+ for item in dir_path.iterdir():
76
+ if not item.is_dir() or item.name.startswith("."):
77
+ continue
78
+
79
+ if manager == "uv":
80
+ pyproject = item / "pyproject.toml"
81
+ if pyproject.exists():
82
+ try:
83
+ content = pyproject.read_text()
84
+ # Check if this project depends on any of the updated packages
85
+ # We check for '"pkg-name"' or 'pkg-name>=' to avoid partial matches
86
+ has_dep = False
87
+ for pkg in updated_packages:
88
+ if (
89
+ f'"{pkg}"' in content
90
+ or f"'{pkg}'" in content
91
+ or f"{pkg}>=" in content
92
+ ):
93
+ has_dep = True
94
+ break
95
+
96
+ if has_dep:
97
+ for line in content.splitlines():
98
+ if line.startswith("name ="):
99
+ proj_name = (
100
+ line.split("=", 1)[1]
101
+ .strip()
102
+ .strip('"')
103
+ .strip("'")
104
+ )
105
+ projects.append(proj_name)
106
+ break
107
+ except Exception:
108
+ pass
109
+ elif manager == "pnpm":
110
+ pkg_json = item / "package.json"
111
+ if pkg_json.exists():
112
+ try:
113
+ data = json.loads(pkg_json.read_text())
114
+ # Check all dependency types
115
+ all_deps = {
116
+ **data.get("dependencies", {}),
117
+ **data.get("devDependencies", {}),
118
+ **data.get("peerDependencies", {}),
119
+ }
120
+
121
+ if (
122
+ any(pkg in all_deps for pkg in updated_packages)
123
+ and "name" in data
124
+ ):
125
+ projects.append(data["name"])
126
+ except Exception:
127
+ pass
128
+ return sorted(set(projects))
129
+
130
+
57
131
  def _push_branch(branch: str, token: str) -> None:
58
132
  """Push branch to origin."""
59
133
  host = os.environ.get("CI_SERVER_HOST", "gitlab.com")
@@ -123,6 +197,13 @@ async def deps_upgrade_command(
123
197
  name="--skip-branch", help="Skip branch creation, keep changes unstaged"
124
198
  ),
125
199
  ] = False,
200
+ dirs: Annotated[
201
+ list[str] | None,
202
+ cyclopts.Parameter(
203
+ name="--dirs",
204
+ help="Base directories to discover workspace projects [default: apps libs]",
205
+ ),
206
+ ] = None,
126
207
  ) -> None:
127
208
  """Upgrade a dependency and create a merge request."""
128
209
  root_path = Path(root) if root else Path.cwd()
@@ -141,6 +222,8 @@ async def deps_upgrade_command(
141
222
  uv_mtime = uv_lock.stat().st_mtime if uv_lock.exists() else 0
142
223
  pnpm_mtime = pnpm_lock.stat().st_mtime if pnpm_lock.exists() else 0
143
224
 
225
+ outdated: list[dict[str, str]] = []
226
+
144
227
  if not package:
145
228
  print(f"Checking for all outdated packages using {manager}...")
146
229
  if manager == "uv":
@@ -156,6 +239,7 @@ async def deps_upgrade_command(
156
239
  )
157
240
  run_cmd(f"uv lock {pkg_args}", cwd=root_path)
158
241
  elif manager == "pnpm":
242
+ outdated = _check_pnpm_outdated(root_path)
159
243
  run_cmd("pnpm update -r", cwd=root_path)
160
244
  print("Running pnpm audit fix...")
161
245
  run_cmd("pnpm audit fix", cwd=root_path, check=False)
@@ -183,9 +267,28 @@ async def deps_upgrade_command(
183
267
  print(f"No changes found after updating {package or 'all'}. Skipping MR.")
184
268
  return
185
269
 
270
+ # Generate commit message and discover affected projects
271
+ if package == "all":
272
+ mr_title = f"fix({manager}): update dependencies"
273
+ body = f"Automated upgrade of all {manager} dependencies."
274
+ else:
275
+ mr_title = f"fix({manager}): update {package}"
276
+ body = f"Automated upgrade of {package}."
277
+
278
+ # Discovery
279
+ updated_pkgs = [package] if package != "all" else [pkg["name"] for pkg in outdated]
280
+ projects = _get_workspace_projects(root_path, manager, dirs, updated_pkgs)
281
+ if projects:
282
+ releases = ", ".join(projects)
283
+ body += f"\n\nReleases: {releases}"
284
+
186
285
  if dry_run or skip_branch:
187
286
  if dry_run:
188
- print(f"[DRY RUN] Would create MR for {package or 'all'}")
287
+ print("\n[DRY RUN] Would create MR:")
288
+ print(f"Title: {mr_title}")
289
+ print(f"Body:\n{body}\n")
290
+ else:
291
+ print("Changes applied to lockfiles. skipping branch/commit as requested.")
189
292
  return
190
293
 
191
294
  # Create and switch to new branch
@@ -204,9 +307,7 @@ async def deps_upgrade_command(
204
307
 
205
308
  run_cmd("git add .", cwd=root_path)
206
309
 
207
- mr_title = f"chore(deps): update {package}"
208
- commit_msg = f"{mr_title}\n\nAutomated upgrade of {package}."
209
- run_cmd(f'git commit -m "{commit_msg}"', cwd=root_path)
310
+ run_cmd(["git", "commit", "-m", mr_title, "-m", body], cwd=root_path)
210
311
 
211
312
  if final_token:
212
313
  _push_branch(branch_name, final_token)
@@ -215,7 +316,7 @@ async def deps_upgrade_command(
215
316
  client = GitLabProvider(token=final_token, project_id=final_project_id)
216
317
  await client.create_mr(
217
318
  title=mr_title,
218
- description=f"Automated dependency upgrade for `{package}`.",
319
+ description=body,
219
320
  source_branch=branch_name,
220
321
  target_branch="main",
221
322
  )
@@ -11,7 +11,6 @@ import shutil
11
11
  import subprocess
12
12
  import sys
13
13
  from importlib.metadata import version as get_version
14
- from importlib.resources import files
15
14
  from pathlib import Path
16
15
  from typing import Annotated
17
16
 
@@ -24,116 +23,36 @@ new_app = cyclopts.App(
24
23
  )
25
24
 
26
25
  # =============================================================================
27
- # Template Content (Embedded for simplicity)
26
+ # Template Utilities
28
27
  # =============================================================================
29
28
 
30
- DOCTYPE_TEMPLATE = '''\
31
- """{{ class_name }} DocType.
29
+ TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
32
30
 
33
- Auto-generated by `m new:doctype {{ name }}`.
34
- """
35
-
36
- from __future__ import annotations
37
-
38
- from typing import ClassVar
39
-
40
- from framework_m import DocType, Field
41
31
 
32
+ def _get_template_path(category: str, name: str) -> Path:
33
+ """Get the path to a template file.
42
34
 
43
- class {{ class_name }}(DocType):
44
- """{{ class_name }} DocType definition.
35
+ Args:
36
+ category: Template category (e.g., "plugin", "app", "doctype")
37
+ name: Template filename (e.g., "package.json.template")
45
38
 
46
- Attributes:
47
- name: Unique identifier for this {{ name }}.
39
+ Returns:
40
+ Path to the template file
48
41
  """
42
+ return TEMPLATES_DIR / category / name
49
43
 
50
- __doctype_name__: ClassVar[str] = "{{ class_name }}"
51
-
52
- name: str = Field(description="Unique identifier")
53
-
54
- class Meta:
55
- """DocType metadata."""
56
-
57
- requires_auth: ClassVar[bool] = False # Allow access without login (dev)
58
- apply_rls: ClassVar[bool] = False # All users can access all records (dev)
59
- naming_rule: ClassVar[str] = "autoincrement"
60
- is_submittable: ClassVar[bool] = False
61
- api_resource: ClassVar[bool] = True # Expose via REST API
62
- show_in_desk: ClassVar[bool] = True # Show in Desk UI
63
- permissions: ClassVar[dict[str, list[str]]] = {
64
- "create": ["All"],
65
- "read": ["All"],
66
- "write": ["All"],
67
- "delete": ["All"],
68
- }
69
- '''
70
44
 
71
- CONTROLLER_TEMPLATE = '''\
72
- """{{ class_name }} Controller.
45
+ def _load_template(category: str, name: str) -> str:
46
+ """Load a template file's content.
73
47
 
74
- Auto-generated by `m new:doctype {{ name }}`.
75
- """
76
-
77
- from __future__ import annotations
78
-
79
- from framework_m import Controller
80
- from .doctype import {{ class_name }}
81
-
82
-
83
- class {{ class_name }}Controller(Controller[{{ class_name }}]):
84
- """Controller for {{ class_name }} DocType.
48
+ Args:
49
+ category: Template category
50
+ name: Template filename
85
51
 
86
- Implement custom business logic here.
52
+ Returns:
53
+ Template content
87
54
  """
88
-
89
- doctype = {{ class_name }}
90
-
91
- async def before_save(self, doc: {{ class_name }}) -> None:
92
- """Called before saving a document."""
93
- pass
94
-
95
- async def after_save(self, doc: {{ class_name }}) -> None:
96
- """Called after saving a document."""
97
- pass
98
- '''
99
-
100
- TEST_TEMPLATE = '''\
101
- """Tests for {{ class_name }} DocType.
102
-
103
- Auto-generated by `m new:doctype {{ name }}`.
104
- """
105
-
106
- from __future__ import annotations
107
-
108
- import pytest
109
-
110
- from .doctype import {{ class_name }}
111
-
112
-
113
- class Test{{ class_name }}:
114
- """Tests for {{ class_name }}."""
115
-
116
- def test_create_{{ snake_name }}(self) -> None:
117
- """{{ class_name }} should be creatable."""
118
- doc = {{ class_name }}(name="test-001")
119
- assert doc.name == "test-001"
120
-
121
- def test_{{ snake_name }}_doctype_name(self) -> None:
122
- """{{ class_name }} should have correct doctype name."""
123
- assert {{ class_name }}.__doctype_name__ == "{{ class_name }}"
124
- '''
125
-
126
- INIT_TEMPLATE = '''\
127
- """{{ class_name }} DocType package.
128
-
129
- Auto-generated by `m new:doctype {{ name }}`.
130
- """
131
-
132
- from .controller import {{ class_name }}Controller
133
- from .doctype import {{ class_name }}
134
-
135
- __all__ = ["{{ class_name }}", "{{ class_name }}Controller"]
136
- '''
55
+ return _get_template_path(category, name).read_text()
137
56
 
138
57
 
139
58
  # =============================================================================
@@ -428,14 +347,15 @@ def scaffold_doctype(
428
347
 
429
348
  # Create doctype files (go in src/)
430
349
  doctype_files = [
431
- ("__init__.py", INIT_TEMPLATE),
432
- ("doctype.py", DOCTYPE_TEMPLATE),
433
- ("controller.py", CONTROLLER_TEMPLATE),
350
+ ("__init__.py", "doctype", "__init__.py.template"),
351
+ ("doctype.py", "doctype", "doctype.py.template"),
352
+ ("controller.py", "doctype", "controller.py.template"),
434
353
  ]
435
354
 
436
- for filename, template in doctype_files:
355
+ for filename, category, template_name in doctype_files:
437
356
  filepath = output_dir / filename
438
- content = render_template(template, context)
357
+ template_content = _load_template(category, template_name)
358
+ content = render_template(template_content, context)
439
359
  filepath.write_text(content)
440
360
  created_files[filename] = filepath
441
361
 
@@ -444,7 +364,8 @@ def scaffold_doctype(
444
364
  test_dir.mkdir(parents=True, exist_ok=True)
445
365
  test_filename = f"test_{snake_name}.py"
446
366
  test_filepath = test_dir / test_filename
447
- test_content = render_template(TEST_TEMPLATE, context)
367
+ template_content = _load_template("doctype", "test.py.template")
368
+ test_content = render_template(template_content, context)
448
369
  test_filepath.write_text(test_content)
449
370
  created_files[test_filename] = test_filepath
450
371
 
@@ -556,81 +477,11 @@ def new_doctype_command(
556
477
  # New App Command
557
478
  # =============================================================================
558
479
 
559
- # App template files (embedded for simplicity)
560
- APP_PYPROJECT_TEMPLATE = """\
561
- [project]
562
- name = "{{ app_name }}"
563
- version = "0.1.0"
564
- description = "A Framework M application"
565
- readme = "README.md"
566
- requires-python = ">=3.12"
567
- dependencies = [
568
- "framework-m>={{ framework_version }}",
569
- ]
570
-
571
- [dependency-groups]
572
- dev = [
573
- "framework-m-studio>={{ studio_version }}",
574
- ]
575
-
576
- [project.entry-points."framework_m.apps"]
577
- {{ app_name }} = "{{ app_name }}:app"
578
-
579
- [build-system]
580
- requires = ["hatchling"]
581
- build-backend = "hatchling.build"
582
-
583
- [tool.hatch.build.targets.wheel]
584
- packages = ["src/{{ app_name }}"]
585
- """
586
-
587
- APP_README_TEMPLATE = """\
588
- # {{ class_name }}
589
-
590
- A Framework M application.
591
-
592
- ## Getting Started
593
-
594
- ```bash
595
- cd {{ app_name }}
596
- uv sync
597
- m dev
598
- ```
599
-
600
- ## Structure
601
-
602
- ```
603
- {{ app_name }}/
604
- ├── src/
605
- │ └── {{ app_name }}/ # App namespace
606
- │ ├── __init__.py # App config
607
- │ ├── doctypes/ # Your DocTypes go here
608
- │ ├── services/ # Business logic (optional)
609
- │ └── utils/ # Utilities (optional)
610
- ├── tests/ # Test files
611
- ├── pyproject.toml
612
- └── README.md
613
- ```
614
-
615
- ## Adding DocTypes
616
-
617
- ```bash
618
- m new:doctype Invoice
619
- ```
620
-
621
- This creates:
622
- - `src/{{ app_name }}/doctypes/invoice/` - DocType files
623
- - `tests/doctypes/invoice/` - Test files
624
- """
625
-
626
480
 
627
481
  # =============================================================================
628
482
  # Frontend Plugin Templates (loaded from template files)
629
483
  # =============================================================================
630
484
 
631
- # Directory containing plugin template files
632
- PLUGIN_TEMPLATES_DIR = Path(__file__).parent.parent / "templates" / "plugin"
633
-
634
485
 
635
486
  def _load_plugin_template(name: str) -> str:
636
487
  """Load a plugin template file.
@@ -641,8 +492,7 @@ def _load_plugin_template(name: str) -> str:
641
492
  Returns:
642
493
  Template file contents
643
494
  """
644
- template_path = PLUGIN_TEMPLATES_DIR / name
645
- return template_path.read_text()
495
+ return _load_template("plugin", name)
646
496
 
647
497
 
648
498
  def scaffold_frontend_plugin(
@@ -677,9 +527,9 @@ def scaffold_frontend_plugin(
677
527
  src_dir = frontend_dir / "src"
678
528
  src_dir.mkdir(parents=True, exist_ok=True)
679
529
 
680
- # Create subdirectories
681
- for subdir in ["pages", "components", "services"]:
682
- (src_dir / subdir).mkdir(parents=True, exist_ok=True)
530
+ # Ensure required subdirectories exist (ADR-0008)
531
+ for sub in ["pages", "components", "services"]:
532
+ (src_dir / sub).mkdir(parents=True, exist_ok=True)
683
533
 
684
534
  created_files: dict[str, Path] = {}
685
535
 
@@ -687,20 +537,35 @@ def scaffold_frontend_plugin(
687
537
  template_map = {
688
538
  "package.json.template": frontend_dir / "package.json",
689
539
  "plugin.config.ts.template": src_dir / "plugin.config.ts",
540
+ "index.tsx.template": src_dir / "index.tsx",
541
+ "index.html.template": frontend_dir / "index.html",
542
+ "theme.css.template": src_dir / "theme.css",
543
+ "vite.config.ts.template": frontend_dir / "vite.config.ts",
690
544
  "vite.config.mfe.ts.template": frontend_dir / "vite.config.mfe.ts",
691
545
  "tsconfig.json.template": frontend_dir / "tsconfig.json",
692
546
  "README.md.template": frontend_dir / "README.md",
693
547
  "Dashboard.tsx.template": src_dir / "pages" / "Dashboard.tsx",
548
+ ".gitignore.template": frontend_dir / ".gitignore",
694
549
  }
695
550
 
696
551
  for template_name, output_path in template_map.items():
697
552
  template_content = _load_plugin_template(template_name)
698
553
  rendered = render_template(template_content, context)
554
+ output_path.parent.mkdir(parents=True, exist_ok=True)
699
555
  output_path.write_text(rendered)
556
+ created_files[str(output_path.relative_to(app_dir))] = output_path
700
557
 
701
- # Use relative path from app_dir as key
702
- rel_path = str(output_path.relative_to(app_dir))
703
- created_files[rel_path] = output_path
558
+ # Static files (copied as-is)
559
+ static_files = {
560
+ "favicon.png": frontend_dir / "public" / "favicon.png",
561
+ }
562
+
563
+ for static_name, output_path in static_files.items():
564
+ source_path = _get_template_path("plugin", static_name)
565
+ if source_path.exists():
566
+ output_path.parent.mkdir(parents=True, exist_ok=True)
567
+ shutil.copy2(source_path, output_path)
568
+ created_files[str(output_path.relative_to(app_dir))] = output_path
704
569
 
705
570
  return created_files
706
571
 
@@ -758,27 +623,17 @@ def scaffold_app(
758
623
  created_files: dict[str, Path] = {}
759
624
 
760
625
  # Create <app_name>/__init__.py with app configuration
761
- app_init_content = f'''"""{class_name} App.
762
-
763
- A Framework M application with namespaced package structure.
764
- """
765
-
766
- # App configuration - discovered via entry points
767
- app = {{
768
- "name": "{snake_name}",
769
- "title": "{class_name}",
770
- "description": "A Framework M application",
771
- }}
772
- '''
626
+ app_init_content = render_template(
627
+ _load_template("app", "__init__.py.template"), context
628
+ )
773
629
  app_init_path = namespace_dir / "__init__.py"
774
630
  app_init_path.write_text(app_init_content)
775
631
  created_files[f"src/{snake_name}/__init__.py"] = app_init_path
776
632
 
777
633
  # Create <app_name>/doctypes/__init__.py
778
- doctypes_init_content = f'''"""{class_name} DocTypes Package."""
779
-
780
- # DocTypes are auto-discovered from this package
781
- '''
634
+ doctypes_init_content = render_template(
635
+ _load_template("app", "doctypes_init.py.template"), context
636
+ )
782
637
  doctypes_init_path = doctypes_dir / "__init__.py"
783
638
  doctypes_init_path.write_text(doctypes_init_content)
784
639
  created_files[f"src/{snake_name}/doctypes/__init__.py"] = doctypes_init_path
@@ -789,13 +644,17 @@ app = {{
789
644
  created_files["tests/__init__.py"] = tests_init_path
790
645
 
791
646
  # Create pyproject.toml
792
- pyproject_content = render_template(APP_PYPROJECT_TEMPLATE, context)
647
+ pyproject_content = render_template(
648
+ _load_template("app", "pyproject.toml.template"), context
649
+ )
793
650
  pyproject_path = app_dir / "pyproject.toml"
794
651
  pyproject_path.write_text(pyproject_content)
795
652
  created_files["pyproject.toml"] = pyproject_path
796
653
 
797
654
  # Create README.md
798
- readme_content = render_template(APP_README_TEMPLATE, context)
655
+ readme_content = render_template(
656
+ _load_template("app", "README.md.template"), context
657
+ )
799
658
  readme_path = app_dir / "README.md"
800
659
  readme_path.write_text(readme_content)
801
660
  created_files["README.md"] = readme_path
@@ -944,38 +803,21 @@ def new_frontend_command(
944
803
  and installs dependencies. The template includes:
945
804
  - React + TypeScript + Vite
946
805
  - @framework-m/desk npm package integration
947
- - Refine.dev setup with authProvider, dataProvider, liveProvider
948
- - Tailwind CSS + shadcn/ui components
806
+ - Unified Sandbox Architecture (Shell + Plugin mode)
807
+ - Standardized library-driven tokens (@framework-m/ui)
949
808
 
950
809
  Examples:
951
810
  m new:frontend # Initialize in ./frontend
952
811
  m new:frontend ../my-frontend # Custom location
953
812
  """
954
- # Default to ./frontend
955
- if target is None:
956
- target = Path("frontend")
957
-
958
- # Resolve to absolute path
959
- target = target.resolve()
960
-
961
- # Find template source from framework_m package
962
- try:
963
- template_resource = files("framework_m_studio") / "templates" / "frontend"
964
- if not template_resource.is_dir():
965
- msg = "Frontend template not found in framework_m package"
966
- raise RuntimeError(msg)
967
-
968
- # Convert to Path for file operations
969
- template_source = Path(str(template_resource))
970
- except Exception as e:
971
- msg = f"Failed to locate template: {e}"
972
- raise RuntimeError(msg) from e
813
+ # Detect app name for context
814
+ app_name = detect_app() or "frontend_app"
973
815
 
974
- print(f"Initializing frontend at {target}")
975
- print(f"Template source: {template_source}")
816
+ # Default to ./frontend if target is not provided
817
+ target = target or Path("frontend")
976
818
 
977
- # Copy template
978
- _copy_template(template_source, target)
819
+ # Use unified template-rendering scaffolding
820
+ scaffold_frontend_plugin(app_name, target.parent, target.name)
979
821
 
980
822
  # Install dependencies
981
823
  _install_dependencies(target)