@ngockhoale/ukit 1.1.6

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 (344) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -0
  4. package/bin/ukit +30 -0
  5. package/manifests/platform.full.yaml +1194 -0
  6. package/package.json +71 -0
  7. package/scripts/bug/triage.mjs +37 -0
  8. package/scripts/index/build-index.mjs +35 -0
  9. package/scripts/index/query-index.mjs +92 -0
  10. package/scripts/index/refresh-index.mjs +85 -0
  11. package/scripts/release/verify-release.mjs +56 -0
  12. package/src/bug/triageBug.js +123 -0
  13. package/src/cli/adapters.js +148 -0
  14. package/src/cli/commands/diff.js +51 -0
  15. package/src/cli/commands/doctor.js +125 -0
  16. package/src/cli/commands/indexArgs.js +73 -0
  17. package/src/cli/commands/indexTools.js +509 -0
  18. package/src/cli/commands/install.js +293 -0
  19. package/src/cli/commands/memory.js +126 -0
  20. package/src/cli/commands/status.js +8 -0
  21. package/src/cli/commands/uninstall.js +51 -0
  22. package/src/cli/index.js +109 -0
  23. package/src/context/detectProjectContext.js +49 -0
  24. package/src/context/detectProviders.js +12 -0
  25. package/src/core/applyPlan.js +89 -0
  26. package/src/core/buildPlan.js +228 -0
  27. package/src/core/compact/index.js +294 -0
  28. package/src/core/compact/threshold.js +936 -0
  29. package/src/core/diffPlan.js +73 -0
  30. package/src/core/ensureGitignore.js +117 -0
  31. package/src/core/fileOps.js +188 -0
  32. package/src/core/memory/hygiene.js +160 -0
  33. package/src/core/memory/index.js +2 -0
  34. package/src/core/memory/retrieval.js +476 -0
  35. package/src/core/memory/store.js +202 -0
  36. package/src/core/metadata.js +132 -0
  37. package/src/core/migrateLegacy.js +139 -0
  38. package/src/core/output/index.js +1309 -0
  39. package/src/core/paths.js +13 -0
  40. package/src/core/report.js +17 -0
  41. package/src/core/router/advisor.js +42 -0
  42. package/src/core/router/index.js +2 -0
  43. package/src/core/router/router.js +164 -0
  44. package/src/core/runInstallPipeline.js +365 -0
  45. package/src/core/runtimeConfig.js +190 -0
  46. package/src/core/runtimePaths.js +24 -0
  47. package/src/core/status.js +186 -0
  48. package/src/core/token/index.js +328 -0
  49. package/src/core/uninstall.js +246 -0
  50. package/src/core/validation/confidence.js +89 -0
  51. package/src/core/validation/index.js +2 -0
  52. package/src/core/validation/validator.js +165 -0
  53. package/src/index/buildIndex.js +1392 -0
  54. package/src/index/gitHooks.js +109 -0
  55. package/src/index/importResolution.js +377 -0
  56. package/src/index/languageTools.js +127 -0
  57. package/src/index/paths.js +27 -0
  58. package/src/index/queryIndex.js +637 -0
  59. package/src/index/relatedTests.js +237 -0
  60. package/src/index/resolveContext.js +345 -0
  61. package/src/index/routeCatalog.js +258 -0
  62. package/src/index/taskRouting.js +677 -0
  63. package/src/index/verificationPlan.js +437 -0
  64. package/src/manifest/loadManifest.js +22 -0
  65. package/src/manifest/selectItems.js +78 -0
  66. package/src/manifest/validateManifest.js +115 -0
  67. package/src/render/buildVariables.js +39 -0
  68. package/src/render/renderTemplate.js +44 -0
  69. package/src/stack/detectStack.js +213 -0
  70. package/templates/.claude/agents/bug-debugger.md +57 -0
  71. package/templates/.claude/agents/feature-implementer.md +55 -0
  72. package/templates/.claude/config/providers.md +25 -0
  73. package/templates/.claude/hooks/auto-allow-bash.sh +155 -0
  74. package/templates/.claude/hooks/auto-prune-bash.sh +75 -0
  75. package/templates/.claude/hooks/block-dangerous.sh +54 -0
  76. package/templates/.claude/hooks/compress-output.sh +17 -0
  77. package/templates/.claude/hooks/protect-files.sh +37 -0
  78. package/templates/.claude/hooks/reinject-context.sh +28 -0
  79. package/templates/.claude/hooks/session-start.md +13 -0
  80. package/templates/.claude/hooks/skill-router.sh +1681 -0
  81. package/templates/.claude/hooks/verification-guard.sh +271 -0
  82. package/templates/.claude/settings.json +144 -0
  83. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  84. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  85. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  86. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  87. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  88. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  89. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  90. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  91. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  92. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  93. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  94. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  95. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  96. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  97. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  98. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  99. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  100. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  101. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  102. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  103. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  104. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  105. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  106. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  107. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  108. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  109. package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  110. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  111. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  112. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  113. package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  114. package/templates/.claude/skills/_shared/ooxml/schemas/mce/mc.xsd +75 -0
  115. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  116. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  117. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  118. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  119. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  120. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  121. package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  122. package/templates/.claude/skills/_shared/ooxml/scripts/pack.py +159 -0
  123. package/templates/.claude/skills/_shared/ooxml/scripts/unpack.py +29 -0
  124. package/templates/.claude/skills/_shared/ooxml/scripts/validate.py +69 -0
  125. package/templates/.claude/skills/_shared/ooxml/scripts/validation/__init__.py +15 -0
  126. package/templates/.claude/skills/_shared/ooxml/scripts/validation/base.py +951 -0
  127. package/templates/.claude/skills/_shared/ooxml/scripts/validation/docx.py +274 -0
  128. package/templates/.claude/skills/_shared/ooxml/scripts/validation/pptx.py +315 -0
  129. package/templates/.claude/skills/_shared/ooxml/scripts/validation/redlining.py +279 -0
  130. package/templates/.claude/skills/backend-api/SKILL.md +26 -0
  131. package/templates/.claude/skills/canvas-design/LICENSE.txt +202 -0
  132. package/templates/.claude/skills/canvas-design/SKILL.md +130 -0
  133. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  134. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  135. package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  136. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  137. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  138. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  139. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  140. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  141. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  142. package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  143. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  144. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  145. package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  146. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  147. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  148. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  149. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  150. package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  151. package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  152. package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  153. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  154. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  155. package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  156. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  157. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  158. package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  159. package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  160. package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  161. package/templates/.claude/skills/code-review/SKILL.md +97 -0
  162. package/templates/.claude/skills/debugging-toolkit/SKILL.md +156 -0
  163. package/templates/.claude/skills/delivery/SKILL.md +92 -0
  164. package/templates/.claude/skills/discover-security/SKILL.md +86 -0
  165. package/templates/.claude/skills/docker-packaging/SKILL.md +60 -0
  166. package/templates/.claude/skills/docs-manager/SKILL.md +465 -0
  167. package/templates/.claude/skills/docs-manager/init-project-docs.sh +70 -0
  168. package/templates/.claude/skills/docs-manager/templates/README.md.template +50 -0
  169. package/templates/.claude/skills/docs-manager/templates/agent-roles.md.template +24 -0
  170. package/templates/.claude/skills/docs-manager/templates/coding-conventions.md.template +28 -0
  171. package/templates/.claude/skills/docs-manager/templates/memory.md.template +30 -0
  172. package/templates/.claude/skills/docs-manager/templates/onboarding.md.template +20 -0
  173. package/templates/.claude/skills/docs-manager/templates/project.md.template +26 -0
  174. package/templates/.claude/skills/docs-quality/SKILL.md +148 -0
  175. package/templates/.claude/skills/docx/LICENSE.txt +30 -0
  176. package/templates/.claude/skills/docx/SKILL.md +197 -0
  177. package/templates/.claude/skills/docx/docx-js.md +350 -0
  178. package/templates/.claude/skills/docx/ooxml.md +610 -0
  179. package/templates/.claude/skills/docx/scripts/__init__.py +1 -0
  180. package/templates/.claude/skills/docx/scripts/document.py +1276 -0
  181. package/templates/.claude/skills/docx/scripts/templates/comments.xml +3 -0
  182. package/templates/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  183. package/templates/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  184. package/templates/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
  185. package/templates/.claude/skills/docx/scripts/templates/people.xml +3 -0
  186. package/templates/.claude/skills/docx/scripts/utilities.py +374 -0
  187. package/templates/.claude/skills/duraone/SKILL.md +204 -0
  188. package/templates/.claude/skills/duraone/references/backend.md +636 -0
  189. package/templates/.claude/skills/duraone/references/frontend.md +1506 -0
  190. package/templates/.claude/skills/duraone/references/sql.md +631 -0
  191. package/templates/.claude/skills/duraone/references/workflow.md +520 -0
  192. package/templates/.claude/skills/executing-plans/SKILL.md +76 -0
  193. package/templates/.claude/skills/file-organizer/SKILL.md +433 -0
  194. package/templates/.claude/skills/frontend/SKILL.md +26 -0
  195. package/templates/.claude/skills/frontend-design/LICENSE.txt +177 -0
  196. package/templates/.claude/skills/frontend-design/SKILL.md +42 -0
  197. package/templates/.claude/skills/frontend-vue/SKILL.md +127 -0
  198. package/templates/.claude/skills/frontend-vue/components/Control/Box.vue +137 -0
  199. package/templates/.claude/skills/frontend-vue/components/Control/Button.vue +93 -0
  200. package/templates/.claude/skills/frontend-vue/components/Control/ButtonBar.vue +29 -0
  201. package/templates/.claude/skills/frontend-vue/components/Control/ButtonFloat.vue +62 -0
  202. package/templates/.claude/skills/frontend-vue/components/Control/CheckButton.vue +75 -0
  203. package/templates/.claude/skills/frontend-vue/components/Control/Checkbox.vue +58 -0
  204. package/templates/.claude/skills/frontend-vue/components/Control/Datetime.vue +148 -0
  205. package/templates/.claude/skills/frontend-vue/components/Control/Dropdownlist.vue +156 -0
  206. package/templates/.claude/skills/frontend-vue/components/Control/Input.vue +106 -0
  207. package/templates/.claude/skills/frontend-vue/components/Control/Label.vue +38 -0
  208. package/templates/.claude/skills/frontend-vue/components/Control/Master/BoxColumn.vue +24 -0
  209. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Confirm.vue +33 -0
  210. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Info.vue +32 -0
  211. package/templates/.claude/skills/frontend-vue/components/Control/Popup/ModalInfo.vue +39 -0
  212. package/templates/.claude/skills/frontend-vue/components/Control/Popup/Reject.vue +64 -0
  213. package/templates/.claude/skills/frontend-vue/components/Control/Tag.vue +82 -0
  214. package/templates/.claude/skills/frontend-vue/components/Control/Upload.vue +61 -0
  215. package/templates/.claude/skills/frontend-vue/components/ControlMobile/Dropdownlist.vue +103 -0
  216. package/templates/.claude/skills/frontend-vue/components/ControlMobile/PagingBar.vue +108 -0
  217. package/templates/.claude/skills/frontend-vue/components/ControlMobile/UploadImage.vue +137 -0
  218. package/templates/.claude/skills/frontend-vue/components/Grid/AG.vue +806 -0
  219. package/templates/.claude/skills/frontend-vue/components/Grid/AntTable.vue +253 -0
  220. package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditor.vue +43 -0
  221. package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditorEnable.vue +55 -0
  222. package/templates/.claude/skills/frontend-vue/components/Grid/HtmlTable.vue +40 -0
  223. package/templates/.claude/skills/frontend-vue/components/PDFViewer.vue +25 -0
  224. package/templates/.claude/skills/frontend-vue/components/Panel/FormView.vue +309 -0
  225. package/templates/.claude/skills/frontend-vue/components/Partial/Footer.vue +23 -0
  226. package/templates/.claude/skills/frontend-vue/components/Partial/Header.vue +265 -0
  227. package/templates/.claude/skills/frontend-vue/components/Partial/Sidebar.vue +122 -0
  228. package/templates/.claude/skills/frontend-vue/components/Template.vue +16 -0
  229. package/templates/.claude/skills/frontend-vue/components/View/Form.vue +89 -0
  230. package/templates/.claude/skills/frontend-vue/composables/indexDBStore.js +140 -0
  231. package/templates/.claude/skills/frontend-vue/composables/masterApi.js +362 -0
  232. package/templates/.claude/skills/frontend-vue/composables/state.js +578 -0
  233. package/templates/.claude/skills/frontend-vue/composables/useRequest.js +221 -0
  234. package/templates/.claude/skills/frontend-vue/composables/useSession.js +179 -0
  235. package/templates/.claude/skills/frontend-vue/composables/useTranslation.js +54 -0
  236. package/templates/.claude/skills/frontend-vue/composables/useWebSocket.js +257 -0
  237. package/templates/.claude/skills/frontend-vue/composables/userObj.js +111 -0
  238. package/templates/.claude/skills/frontend-vue/composables/utils.js +322 -0
  239. package/templates/.claude/skills/frontend-vue/reference/composables-example.vue +320 -0
  240. package/templates/.claude/skills/frontend-vue/reference/form-example.vue +183 -0
  241. package/templates/.claude/skills/frontend-vue/reference/grid-example.vue +147 -0
  242. package/templates/.claude/skills/frontend-vue/reference/masterdata-example/[id].vue +106 -0
  243. package/templates/.claude/skills/frontend-vue/reference/masterdata-example/index.vue +58 -0
  244. package/templates/.claude/skills/frontend-vue/reference/popup-example.vue +159 -0
  245. package/templates/.claude/skills/pdf/LICENSE.txt +30 -0
  246. package/templates/.claude/skills/pdf/SKILL.md +294 -0
  247. package/templates/.claude/skills/pdf/forms.md +205 -0
  248. package/templates/.claude/skills/pdf/reference.md +612 -0
  249. package/templates/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  250. package/templates/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  251. package/templates/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
  252. package/templates/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  253. package/templates/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
  254. package/templates/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
  255. package/templates/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  256. package/templates/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  257. package/templates/.claude/skills/pdf-processing/SKILL.md +107 -0
  258. package/templates/.claude/skills/pdf-processing-pro/FORMS.md +610 -0
  259. package/templates/.claude/skills/pdf-processing-pro/OCR.md +137 -0
  260. package/templates/.claude/skills/pdf-processing-pro/SKILL.md +296 -0
  261. package/templates/.claude/skills/pdf-processing-pro/TABLES.md +626 -0
  262. package/templates/.claude/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
  263. package/templates/.claude/skills/postgres/SKILL.md +69 -0
  264. package/templates/.claude/skills/postgres/reference/fn_get_examples.sql +208 -0
  265. package/templates/.claude/skills/postgres/reference/fn_rpt_examples.sql +239 -0
  266. package/templates/.claude/skills/postgres/reference/utility_functions.sql +94 -0
  267. package/templates/.claude/skills/pptx/LICENSE.txt +30 -0
  268. package/templates/.claude/skills/pptx/SKILL.md +484 -0
  269. package/templates/.claude/skills/pptx/html2pptx.md +625 -0
  270. package/templates/.claude/skills/pptx/ooxml.md +427 -0
  271. package/templates/.claude/skills/pptx/scripts/html2pptx.js +979 -0
  272. package/templates/.claude/skills/pptx/scripts/inventory.py +1020 -0
  273. package/templates/.claude/skills/pptx/scripts/rearrange.py +231 -0
  274. package/templates/.claude/skills/pptx/scripts/replace.py +385 -0
  275. package/templates/.claude/skills/pptx/scripts/thumbnail.py +450 -0
  276. package/templates/.claude/skills/repo-maintenance/SKILL.md +97 -0
  277. package/templates/.claude/skills/research/EXAMPLES.md +434 -0
  278. package/templates/.claude/skills/research/REFERENCE.md +399 -0
  279. package/templates/.claude/skills/research/SKILL.md +136 -0
  280. package/templates/.claude/skills/root-cause-tracing/SKILL.md +174 -0
  281. package/templates/.claude/skills/root-cause-tracing/find-polluter.sh +63 -0
  282. package/templates/.claude/skills/sharing-skills/SKILL.md +194 -0
  283. package/templates/.claude/skills/sql-optimization-patterns/SKILL.md +493 -0
  284. package/templates/.claude/skills/subagent-driven-development/SKILL.md +189 -0
  285. package/templates/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
  286. package/templates/.claude/skills/systematic-debugging/SKILL.md +295 -0
  287. package/templates/.claude/skills/systematic-debugging/test-academic.md +14 -0
  288. package/templates/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
  289. package/templates/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
  290. package/templates/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
  291. package/templates/.claude/skills/test-driven-development/SKILL.md +364 -0
  292. package/templates/.claude/skills/testing-anti-patterns/SKILL.md +302 -0
  293. package/templates/.claude/skills/testing-quality/SKILL.md +97 -0
  294. package/templates/.claude/skills/verification-before-completion/SKILL.md +139 -0
  295. package/templates/.claude/skills/webapp-testing/LICENSE.txt +202 -0
  296. package/templates/.claude/skills/webapp-testing/SKILL.md +96 -0
  297. package/templates/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
  298. package/templates/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
  299. package/templates/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
  300. package/templates/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
  301. package/templates/.claude/ukit/index/build-index.mjs +28 -0
  302. package/templates/.claude/ukit/index/cache-utils.mjs +140 -0
  303. package/templates/.claude/ukit/index/lib/index-core.mjs +2800 -0
  304. package/templates/.claude/ukit/index/query-index.mjs +150 -0
  305. package/templates/.claude/ukit/index/refresh-index.mjs +57 -0
  306. package/templates/.claude/ukit/index/reset-auto-permissions.mjs +76 -0
  307. package/templates/.claude/ukit/index/resolve-context.mjs +279 -0
  308. package/templates/.claude/ukit/index/route-catalog.mjs +258 -0
  309. package/templates/.claude/ukit/index/route-task.mjs +1994 -0
  310. package/templates/.claude/ukit/index/triage.mjs +133 -0
  311. package/templates/.claude/ukit/index/verify-context.mjs +689 -0
  312. package/templates/.claude/ukit/runtime/compact-threshold.mjs +1013 -0
  313. package/templates/.claude/ukit/runtime/output-compression.mjs +1340 -0
  314. package/templates/.claude/ukit/runtime/reinject-context.mjs +874 -0
  315. package/templates/.claude/ukit/runtime/token-utils.mjs +500 -0
  316. package/templates/.codex/README.md +83 -0
  317. package/templates/.codex/settings.json +187 -0
  318. package/templates/.gitignore +75 -0
  319. package/templates/AGENTS.md +116 -0
  320. package/templates/CLAUDE.md +93 -0
  321. package/templates/adapter-presets/antigravity/README.md +22 -0
  322. package/templates/adapter-presets/antigravity/rules.md +49 -0
  323. package/templates/adapter-presets/claude/settings.local.json +42 -0
  324. package/templates/adapter-presets/codex/settings.local.json +6 -0
  325. package/templates/adapter-presets/opencode/opencode.template.json +1 -0
  326. package/templates/docs/BUGFIX.md +20 -0
  327. package/templates/docs/BUG_INDEX.md +12 -0
  328. package/templates/docs/BUG_METRICS.md +7 -0
  329. package/templates/docs/BUG_TEMPLATE.md +13 -0
  330. package/templates/docs/CODE_MAP.md +35 -0
  331. package/templates/docs/INSTALL.md +113 -0
  332. package/templates/docs/MEMORY.md +49 -0
  333. package/templates/docs/PROJECT.md +50 -0
  334. package/templates/docs/UKIT_USAGE_GUIDE.md +147 -0
  335. package/templates/docs/WORKLOG.md +10 -0
  336. package/templates/ukit/README.md +14 -0
  337. package/templates/ukit/storage/cache/compact-history.json +3 -0
  338. package/templates/ukit/storage/cache/compact-pressure.json +1 -0
  339. package/templates/ukit/storage/cache/output-history.json +3 -0
  340. package/templates/ukit/storage/cache/prompt-cache.json +3 -0
  341. package/templates/ukit/storage/config.json +37 -0
  342. package/templates/ukit/storage/memory/projects/.gitkeep +2 -0
  343. package/templates/ukit/storage/memory/sessions/.gitkeep +0 -0
  344. package/templates/ukit/storage/memory/user.json +5 -0
@@ -0,0 +1,636 @@
1
+ # DuraOne Backend Reference
2
+ ## Python Sanic + PostgreSQL (asyncpg)
3
+
4
+ > Nguồn sự thật cho phong cách code backend của LeNK.
5
+ > Stack: Python, Sanic (async web framework), asyncpg, psycopg2+pandas
6
+
7
+ ---
8
+
9
+ ## MỤC LỤC
10
+ 1. [Cấu trúc thư mục BE](#1-cấu-trúc-thư-mục-be)
11
+ 2. [Sanic App Setup (app.py)](#2-sanic-app-setup)
12
+ 3. [Blueprint API Pattern](#3-blueprint-api-pattern)
13
+ 4. [DB Helper — Async (v2_db.py)](#4-db-helper--async)
14
+ 5. [DB Helper — Sync (db.py)](#5-db-helper--sync)
15
+ 6. [Generic Endpoints (đã có sẵn)](#6-generic-endpoints)
16
+ 7. [Custom Endpoint Pattern](#7-custom-endpoint-pattern)
17
+ 8. [Config Management](#8-config-management)
18
+ 9. [Naming Conventions](#9-naming-conventions)
19
+ 10. [Error Handling](#10-error-handling)
20
+ 11. [Deploy](#11-deploy)
21
+
22
+ ---
23
+
24
+ ## 1. Cấu Trúc Thư Mục BE
25
+
26
+ ```
27
+ BE_DuraOne/
28
+ ├── app.py # Main Sanic application entry point
29
+ ├── mainapp/
30
+ │ ├── __init__.py
31
+ │ ├── config.py # Config loader (qas/prd/local)
32
+ │ ├── helper.py # Helper functions
33
+ │ └── lib/
34
+ │ ├── api.py # API routes (sync version)
35
+ │ ├── v2_api.py # API routes (async version — dùng cái này)
36
+ │ ├── db.py # DB_Helper class (sync, psycopg2 + pandas)
37
+ │ ├── v2_db.py # DB_Helper class (async, asyncpg)
38
+ │ ├── compress.py # GZIP compression
39
+ │ ├── google_storage.py # GCS file operations
40
+ │ ├── pandas_manage.py # Pandas utilities
41
+ │ ├── string_manage.py # String utilities
42
+ │ ├── log_manage.py # Logging
43
+ │ ├── pdf_manage.py # PDF generation
44
+ │ ├── file_handle.py # File operations
45
+ │ ├── chatgpt_func.py # OpenAI integration
46
+ │ └── send_noti.py # Notifications (Line/email)
47
+
48
+ ├── mainapp/SQL/
49
+ │ └── duraone/
50
+ │ ├── qas/ # QA/testing schema SQL
51
+ │ │ ├── view/ # v_*.sql files
52
+ │ │ ├── function/ # fn_*.sql files
53
+ │ │ ├── store/ # sp_*.sql files
54
+ │ │ └── [feature]/ # feature-specific SQL
55
+ │ └── prd/ # Production schema SQL (mirrors qas)
56
+
57
+ ├── config/
58
+ │ ├── db_ssl/ # PostgreSQL SSL certificates
59
+ │ │ ├── ca.crt
60
+ │ │ ├── client.crt
61
+ │ │ └── client.key
62
+ │ ├── fonts/
63
+ │ └── config.ini # DB credentials, paths
64
+
65
+ ├── duraone_m/ # Mobile app static files
66
+ ├── Dockerfile
67
+ ├── docker-compose.yml
68
+ ├── deploy_qas.sh
69
+ └── deploy_prd.sh
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 2. Sanic App Setup
75
+
76
+ ```python
77
+ # app.py
78
+ from sanic import Sanic
79
+ from sanic_cors import CORS
80
+ from mainapp.lib.v2_api import b_api
81
+ from mainapp.lib.v2_db import DB_Helper
82
+
83
+ app = Sanic(__name__)
84
+ CORS(app, resources={r"/*": {"origins": [
85
+ "http://localhost:3000",
86
+ "https://your-domain.com",
87
+ ]}})
88
+
89
+ # Register blueprint
90
+ app.blueprint(b_api)
91
+
92
+ # DB connection pool setup
93
+ db = DB_Helper()
94
+
95
+ @app.listener("before_server_start")
96
+ async def setup_db(app, loop):
97
+ await db.connect_db()
98
+
99
+ @app.listener("after_server_stop")
100
+ async def close_db(app, loop):
101
+ if db._pool:
102
+ await db._pool.close()
103
+
104
+ # Security headers
105
+ @app.middleware("response")
106
+ async def set_secure_headers(request, response):
107
+ response.headers["X-Content-Type-Options"] = "nosniff"
108
+ response.headers["X-Frame-Options"] = "SAMEORIGIN"
109
+
110
+ # Static files
111
+ PATH = os.path.dirname(os.path.abspath(__file__))
112
+ app.static("/duraone_m", PATH + "/duraone_m")
113
+ app.static("/static", PATH + "/www/static")
114
+
115
+ # SPA fallback — serve index.html for all FE routes
116
+ @app.route("/<path:path>", methods=["GET"])
117
+ async def index(request, path):
118
+ return await response.file(PATH + "/www/index.html")
119
+
120
+ if __name__ == "__main__":
121
+ app.run(host="0.0.0.0", port=8000, workers=4, debug=False)
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 3. Blueprint API Pattern
127
+
128
+ ```python
129
+ # mainapp/lib/v2_api.py
130
+ from sanic import Blueprint, response
131
+ from mainapp.lib.v2_db import DB_Helper
132
+ import json
133
+
134
+ b_api = Blueprint("b_api")
135
+ db = DB_Helper()
136
+
137
+ # ──────────────────────────────────────────────
138
+ # Pattern cho custom endpoint
139
+ # ──────────────────────────────────────────────
140
+ @b_api.route("/api/[feature]/[action]", methods=["POST", "GET"])
141
+ async def feature_action(request):
142
+ try:
143
+ val = request.json or {}
144
+
145
+ # Extract params
146
+ id_record = val.get("id_record")
147
+ schema = val.get("schema", "qas")
148
+
149
+ # Business logic
150
+ query = f"""
151
+ SELECT * FROM {schema}.v_[entity]
152
+ WHERE id_record = $1
153
+ """
154
+ result = await db.execute(query, (id_record,))
155
+
156
+ return response.json(result)
157
+
158
+ except Exception as e:
159
+ return response.json({"success": False, "message": str(e)}, status=500)
160
+
161
+
162
+ # ──────────────────────────────────────────────
163
+ # Pattern chạy stored procedure
164
+ # ──────────────────────────────────────────────
165
+ @b_api.route("/api/run_proc", methods=["POST", "GET"])
166
+ async def run_proc(request):
167
+ try:
168
+ val = request.json or {}
169
+ schema = val.get("schema", "qas")
170
+ proc_name = val.get("proc_name")
171
+ params = val.get("params", {})
172
+
173
+ # Build param list
174
+ param_values = list(params.values())
175
+ param_placeholders = ", ".join([f"${i+1}" for i in range(len(param_values))])
176
+
177
+ query = f"SELECT * FROM {schema}.{proc_name}({param_placeholders})"
178
+ result = await db.execute(query, param_values)
179
+
180
+ return response.json(result)
181
+
182
+ except Exception as e:
183
+ return response.json({"success": False, "message": str(e)}, status=500)
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 4. DB Helper — Async
189
+
190
+ ```python
191
+ # mainapp/lib/v2_db.py
192
+ import asyncpg
193
+ import json
194
+ from decimal import Decimal
195
+ from mainapp.config import get_config
196
+
197
+ class DB_Helper:
198
+ _pool = None
199
+
200
+ async def connect_db(self):
201
+ """Create asyncpg connection pool with SSL"""
202
+ config = get_config()
203
+ self._pool = await asyncpg.create_pool(
204
+ host=config["host"],
205
+ port=config["port"],
206
+ database=config["database"],
207
+ user=config["user"],
208
+ password=config["password"],
209
+ ssl=self._get_ssl_context(),
210
+ min_size=2,
211
+ max_size=10,
212
+ )
213
+
214
+ async def execute(self, query: str, params: tuple = ()) -> list:
215
+ """Execute query and return list of dicts"""
216
+ async with self._pool.acquire() as conn:
217
+ rows = await conn.fetch(query, *params)
218
+ result = [dict(row) for row in rows]
219
+ return self.convert_to_serializable(result)
220
+
221
+ async def execute_one(self, query: str, params: tuple = ()):
222
+ """Execute query and return single dict or None"""
223
+ result = await self.execute(query, params)
224
+ return result[0] if result else None
225
+
226
+ async def execute_void(self, query: str, params: tuple = ()):
227
+ """Execute INSERT/UPDATE/DELETE with no return"""
228
+ async with self._pool.acquire() as conn:
229
+ await conn.execute(query, *params)
230
+
231
+ def convert_to_serializable(self, data):
232
+ """Convert Decimal, datetime to JSON-serializable types"""
233
+ if isinstance(data, list):
234
+ return [self.convert_to_serializable(item) for item in data]
235
+ if isinstance(data, dict):
236
+ return {k: self.convert_to_serializable(v) for k, v in data.items()}
237
+ if isinstance(data, Decimal):
238
+ return float(data)
239
+ if hasattr(data, "isoformat"): # datetime, date
240
+ return data.isoformat()
241
+ return data
242
+
243
+ def check_form_json(self, v):
244
+ """Check if string is JSON array or object"""
245
+ if isinstance(v, str):
246
+ return v.startswith("[") or v.startswith("{")
247
+ return False
248
+
249
+ def _get_ssl_context(self):
250
+ """Load SSL context for PostgreSQL connection"""
251
+ import ssl
252
+ ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
253
+ ctx.load_cert_chain("config/db_ssl/client.crt", "config/db_ssl/client.key")
254
+ ctx.load_verify_locations("config/db_ssl/ca.crt")
255
+ return ctx
256
+ ```
257
+
258
+ ---
259
+
260
+ ## 5. DB Helper — Sync
261
+
262
+ ```python
263
+ # mainapp/lib/db.py — dùng khi cần pandas hoặc sync context
264
+ import psycopg2 as pg
265
+ import pandas as pd
266
+ import json
267
+ from mainapp.config import set_cdn
268
+
269
+ class DB_Helper:
270
+
271
+ @staticmethod
272
+ def selectbyquery(query, params=None):
273
+ """Execute SELECT, return pandas DataFrame"""
274
+ connection = pg.connect(set_cdn())
275
+ try:
276
+ df = pd.read_sql(query, connection, params=params)
277
+ # Handle nulls
278
+ numeric_cols = df.select_dtypes(include=["number"]).columns
279
+ df[numeric_cols] = df[numeric_cols].fillna(0)
280
+ df = df.fillna("")
281
+ return df.to_dict("records")
282
+ except Exception as e:
283
+ print(f"DB Error: {e}")
284
+ return []
285
+ finally:
286
+ connection.close()
287
+
288
+ @staticmethod
289
+ def insert(table_name: str, obj: dict, pk_field: str = None) -> str:
290
+ """Insert record, return new id (UUID string).
291
+
292
+ ⚠️ DuraOne convention: PK field là id_[entity] (e.g. id_agreement), KHÔNG phải 'id'.
293
+ Luôn truyền pk_field='id_agreement' (hoặc tên PK thực tế) để loại bỏ PK khỏi INSERT.
294
+ Nếu không truyền pk_field, PK sẽ bị include vào INSERT statement.
295
+
296
+ Note: Sync DB_Helper này dùng trong context pandas/scripts.
297
+ Cho generic CRUD từ FE, dùng generic /api/save endpoint (async) thay thế.
298
+ """
299
+ connection = pg.connect(set_cdn())
300
+ try:
301
+ # Loại bỏ PK field (vì DB tự generate UUID default)
302
+ exclude = {pk_field} if pk_field else set()
303
+ keys = [k for k in obj.keys() if k not in exclude]
304
+ values = []
305
+ for k in keys:
306
+ v = obj[k]
307
+ if isinstance(v, (dict, list)):
308
+ v = json.dumps(v, ensure_ascii=False)
309
+ values.append(v)
310
+
311
+ placeholders = ", ".join(["%s"] * len(keys))
312
+ columns = ", ".join(keys)
313
+ returning = pk_field if pk_field else "id"
314
+ query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders}) RETURNING {returning}"
315
+
316
+ with connection.cursor() as cursor:
317
+ cursor.execute(query, values)
318
+ new_id = cursor.fetchone()[0]
319
+ connection.commit()
320
+ return str(new_id)
321
+ except Exception as e:
322
+ connection.rollback()
323
+ print(f"Insert Error: {e}")
324
+ return ""
325
+ finally:
326
+ connection.close()
327
+
328
+ @staticmethod
329
+ def update(table_name: str, obj: dict, key_field: str = "id"):
330
+ """Update record by key field.
331
+
332
+ ⚠️ DuraOne convention: PK field là id_[entity], KHÔNG phải 'id'.
333
+ Luôn truyền key_field='id_agreement' (hoặc tên PK thực tế).
334
+ Ví dụ: DB_Helper.update("agreement", obj, key_field="id_agreement")
335
+ """
336
+ connection = pg.connect(set_cdn())
337
+ try:
338
+ key_val = obj[key_field]
339
+ fields = {k: v for k, v in obj.items() if k != key_field}
340
+
341
+ set_clause = ", ".join([f"{k} = %s" for k in fields.keys()])
342
+ values = list(fields.values()) + [key_val]
343
+
344
+ query = f"UPDATE {table_name} SET {set_clause} WHERE {key_field} = %s"
345
+
346
+ with connection.cursor() as cursor:
347
+ cursor.execute(query, values)
348
+ connection.commit()
349
+ except Exception as e:
350
+ connection.rollback()
351
+ print(f"Update Error: {e}")
352
+ finally:
353
+ connection.close()
354
+
355
+ @staticmethod
356
+ def removebykey(table_name: str, key_field: str, key_value):
357
+ """Delete record by key"""
358
+ connection = pg.connect(set_cdn())
359
+ try:
360
+ query = f"DELETE FROM {table_name} WHERE {key_field} = %s"
361
+ with connection.cursor() as cursor:
362
+ cursor.execute(query, (key_value,))
363
+ connection.commit()
364
+ except Exception as e:
365
+ connection.rollback()
366
+ finally:
367
+ connection.close()
368
+ ```
369
+
370
+ ---
371
+
372
+ ## 6. Generic Endpoints (Đã Có Sẵn)
373
+
374
+ Frontend dùng những endpoints này — KHÔNG cần tạo thêm cho CRUD thông thường:
375
+
376
+ ```
377
+ POST/GET /api/select — Query data (qua table/view + conditions)
378
+ POST /api/save — Insert hoặc Update (auto detect bằng id == '' → insert, id != '' → update)
379
+ POST /api/insert — Force insert
380
+ POST /api/update — Force update
381
+ POST /api/delete — Delete by key
382
+ POST/GET /api/run_proc — Run stored procedure
383
+ GET /api/clearcache — Clear server cache
384
+ POST /user/login — Authentication
385
+ ```
386
+
387
+ **Request format cho /api/select:**
388
+ ```json
389
+ {
390
+ "schema": "qas",
391
+ "table": "v_agreement",
392
+ "columns": "*",
393
+ "conditions": "{\"id_branch\": 5, \"status\": \"active\"}",
394
+ "order_by": ["created_at desc"],
395
+ "limit": 1000
396
+ }
397
+ ```
398
+
399
+ **Request format cho /api/save:**
400
+ ```json
401
+ {
402
+ "table": "agreement",
403
+ "key_array": ["id_agreement"],
404
+ "id_agreement": "",
405
+ "agreement_number": "AGR-001",
406
+ "id_branch": 5,
407
+ "amount": 50000
408
+ }
409
+ ```
410
+
411
+ ---
412
+
413
+ ## 7. Custom Endpoint Pattern
414
+
415
+ Chỉ tạo custom endpoint khi có business logic đặc biệt không xử lý được bằng generic:
416
+
417
+ ```python
418
+ # Naming: /api/[feature]/[action]
419
+ @b_api.route("/api/agreement/approve", methods=["POST"])
420
+ async def approve_agreement(request):
421
+ """Approve agreement — gửi notification + update status"""
422
+ try:
423
+ val = request.json or {}
424
+ schema = val.get("schema", "qas")
425
+ id_agreement = val.get("id_agreement")
426
+ # ✅ FE phải truyền: { username: state.username, ... }
427
+ # state.username là username của user đang login (từ session)
428
+ approved_by = val.get("username")
429
+
430
+ # 1. Update status
431
+ await db.execute_void(f"""
432
+ UPDATE {schema}.agreement
433
+ SET status = 'approved',
434
+ approved_by = $1,
435
+ approved_at = NOW()
436
+ WHERE id_agreement = $2
437
+ """, (approved_by, id_agreement))
438
+
439
+ # 2. Run stored procedure for post-processing
440
+ result = await db.execute(f"""
441
+ SELECT * FROM {schema}.sp_after_approve($1)
442
+ """, (id_agreement,))
443
+
444
+ # 3. Send notification
445
+ await send_line_notification(id_agreement, schema)
446
+
447
+ return response.json({"success": True, "data": result})
448
+
449
+ except Exception as e:
450
+ return response.json({"success": False, "message": str(e)}, status=500)
451
+
452
+
453
+ # Pattern: upload file
454
+ @b_api.route("/api/upload_to_cloud", methods=["POST"])
455
+ async def upload_to_cloud(request):
456
+ try:
457
+ file = request.files.get("file")
458
+ folder = request.form.get("folder", "uploads")
459
+ schema = request.form.get("schema", "qas")
460
+
461
+ # Upload to GCS
462
+ from mainapp.lib.google_storage import upload_blob
463
+ url = await upload_blob(file.body, file.name, folder)
464
+
465
+ return response.json({"success": True, "url": url})
466
+ except Exception as e:
467
+ return response.json({"success": False, "message": str(e)}, status=500)
468
+ ```
469
+
470
+ ---
471
+
472
+ ## 8. Config Management
473
+
474
+ ```python
475
+ # mainapp/config.py
476
+ import configparser
477
+ import os
478
+
479
+ config = configparser.ConfigParser()
480
+ config.read(os.path.join(os.path.dirname(__file__), "../config/config.ini"))
481
+
482
+ def get_env() -> str:
483
+ """Get current environment: local | qas | prd"""
484
+ return os.environ.get("APP_ENV", "local")
485
+
486
+ def get_config() -> dict:
487
+ env = get_env()
488
+ return {
489
+ "host": config[env]["DB_HOST"],
490
+ "port": int(config[env]["DB_PORT"]),
491
+ "database": config[env]["DB_NAME"],
492
+ "user": config[env]["DB_USER"],
493
+ "password": config[env]["DB_PASSWORD"],
494
+ }
495
+
496
+ def set_cdn() -> str:
497
+ """Return psycopg2 connection string"""
498
+ c = get_config()
499
+ return f"host={c['host']} port={c['port']} dbname={c['database']} user={c['user']} password={c['password']} sslmode=require"
500
+
501
+ def get_schema() -> str:
502
+ """Return schema name: qas | prd"""
503
+ env = get_env()
504
+ return "prd" if env == "prd" else "qas"
505
+ ```
506
+
507
+ ```ini
508
+ # config/config.ini
509
+ [local]
510
+ DB_HOST = localhost
511
+ DB_PORT = 5432
512
+ DB_NAME = duraone
513
+ DB_USER = postgres
514
+ DB_PASSWORD = password
515
+
516
+ [qas]
517
+ DB_HOST = <cloud-sql-host>
518
+ DB_PORT = 5432
519
+ DB_NAME = duraone
520
+ DB_USER = duraone_user
521
+ DB_PASSWORD = <secret>
522
+
523
+ [prd]
524
+ DB_HOST = <prd-cloud-sql-host>
525
+ DB_PORT = 5432
526
+ DB_NAME = duraone_prd
527
+ DB_USER = duraone_prd_user
528
+ DB_PASSWORD = <secret>
529
+ ```
530
+
531
+ ---
532
+
533
+ ## 9. Naming Conventions
534
+
535
+ ```python
536
+ # ── Files: snake_case ──
537
+ # v2_api.py, v2_db.py, string_manage.py, pdf_manage.py
538
+
539
+ # ── Classes: PascalCase ──
540
+ class DB_Helper: ...
541
+ class GoogleStorage: ...
542
+
543
+ # ── Functions: snake_case ──
544
+ async def get_agreement_by_id(id_agreement: str): ...
545
+ async def run_proc(schema: str, proc_name: str): ...
546
+ def format_date(date_str: str) -> str: ...
547
+
548
+ # ── Variables: snake_case ──
549
+ id_agreement = val.get("id_agreement")
550
+ schema = val.get("schema", "qas")
551
+ result_list = []
552
+
553
+ # ── Constants: UPPER_SNAKE ──
554
+ DEFAULT_SCHEMA = "qas"
555
+ MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
556
+
557
+ # ── Route paths: lowercase + underscore ──
558
+ # /api/agreement/get_list
559
+ # /api/clearance/approve
560
+ # /api/upload_to_cloud
561
+
562
+ # ── Blueprint name ──
563
+ b_api = Blueprint("b_api")
564
+ ```
565
+
566
+ ---
567
+
568
+ ## 10. Error Handling
569
+
570
+ ```python
571
+ # Pattern chuẩn cho mọi endpoint
572
+ @b_api.route("/api/feature/action", methods=["POST"])
573
+ async def endpoint(request):
574
+ try:
575
+ val = request.json or {}
576
+ # ... logic ...
577
+ return response.json(result)
578
+
579
+ except ValueError as e:
580
+ # Input validation error
581
+ return response.json({"success": False, "message": str(e)}, status=400)
582
+ except Exception as e:
583
+ # Unexpected error — log và trả về 500
584
+ print(f"[ERROR] endpoint: {e}")
585
+ return response.json({"success": False, "message": "Internal server error"}, status=500)
586
+
587
+ # DB error handling trong DB_Helper
588
+ async def execute(self, query, params=()):
589
+ try:
590
+ async with self._pool.acquire() as conn:
591
+ rows = await conn.fetch(query, *params)
592
+ return [dict(row) for row in rows]
593
+ except asyncpg.PostgresError as e:
594
+ print(f"[DB ERROR] {e}")
595
+ return []
596
+ ```
597
+
598
+ ---
599
+
600
+ ## 11. Deploy
601
+
602
+ ```bash
603
+ # deploy_qas.sh
604
+ #!/bin/bash
605
+ export APP_ENV=qas
606
+ pip install -r requirements.txt
607
+ python app.py
608
+
609
+ # deploy_prd.sh
610
+ #!/bin/bash
611
+ export APP_ENV=prd
612
+ # ... same pattern
613
+
614
+ # Dockerfile pattern
615
+ FROM python:3.11-slim
616
+ WORKDIR /app
617
+ COPY requirements.txt .
618
+ RUN pip install -r requirements.txt
619
+ COPY . .
620
+ ENV APP_ENV=qas
621
+ CMD ["python", "app.py"]
622
+ ```
623
+
624
+ ```yaml
625
+ # docker-compose.yml pattern
626
+ version: "3.8"
627
+ services:
628
+ backend:
629
+ build: .
630
+ ports:
631
+ - "8000:8000"
632
+ environment:
633
+ - APP_ENV=qas
634
+ volumes:
635
+ - ./config:/app/config
636
+ ```