@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.
- package/CHANGELOG.md +179 -0
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/bin/ukit +30 -0
- package/manifests/platform.full.yaml +1194 -0
- package/package.json +71 -0
- package/scripts/bug/triage.mjs +37 -0
- package/scripts/index/build-index.mjs +35 -0
- package/scripts/index/query-index.mjs +92 -0
- package/scripts/index/refresh-index.mjs +85 -0
- package/scripts/release/verify-release.mjs +56 -0
- package/src/bug/triageBug.js +123 -0
- package/src/cli/adapters.js +148 -0
- package/src/cli/commands/diff.js +51 -0
- package/src/cli/commands/doctor.js +125 -0
- package/src/cli/commands/indexArgs.js +73 -0
- package/src/cli/commands/indexTools.js +509 -0
- package/src/cli/commands/install.js +293 -0
- package/src/cli/commands/memory.js +126 -0
- package/src/cli/commands/status.js +8 -0
- package/src/cli/commands/uninstall.js +51 -0
- package/src/cli/index.js +109 -0
- package/src/context/detectProjectContext.js +49 -0
- package/src/context/detectProviders.js +12 -0
- package/src/core/applyPlan.js +89 -0
- package/src/core/buildPlan.js +228 -0
- package/src/core/compact/index.js +294 -0
- package/src/core/compact/threshold.js +936 -0
- package/src/core/diffPlan.js +73 -0
- package/src/core/ensureGitignore.js +117 -0
- package/src/core/fileOps.js +188 -0
- package/src/core/memory/hygiene.js +160 -0
- package/src/core/memory/index.js +2 -0
- package/src/core/memory/retrieval.js +476 -0
- package/src/core/memory/store.js +202 -0
- package/src/core/metadata.js +132 -0
- package/src/core/migrateLegacy.js +139 -0
- package/src/core/output/index.js +1309 -0
- package/src/core/paths.js +13 -0
- package/src/core/report.js +17 -0
- package/src/core/router/advisor.js +42 -0
- package/src/core/router/index.js +2 -0
- package/src/core/router/router.js +164 -0
- package/src/core/runInstallPipeline.js +365 -0
- package/src/core/runtimeConfig.js +190 -0
- package/src/core/runtimePaths.js +24 -0
- package/src/core/status.js +186 -0
- package/src/core/token/index.js +328 -0
- package/src/core/uninstall.js +246 -0
- package/src/core/validation/confidence.js +89 -0
- package/src/core/validation/index.js +2 -0
- package/src/core/validation/validator.js +165 -0
- package/src/index/buildIndex.js +1392 -0
- package/src/index/gitHooks.js +109 -0
- package/src/index/importResolution.js +377 -0
- package/src/index/languageTools.js +127 -0
- package/src/index/paths.js +27 -0
- package/src/index/queryIndex.js +637 -0
- package/src/index/relatedTests.js +237 -0
- package/src/index/resolveContext.js +345 -0
- package/src/index/routeCatalog.js +258 -0
- package/src/index/taskRouting.js +677 -0
- package/src/index/verificationPlan.js +437 -0
- package/src/manifest/loadManifest.js +22 -0
- package/src/manifest/selectItems.js +78 -0
- package/src/manifest/validateManifest.js +115 -0
- package/src/render/buildVariables.js +39 -0
- package/src/render/renderTemplate.js +44 -0
- package/src/stack/detectStack.js +213 -0
- package/templates/.claude/agents/bug-debugger.md +57 -0
- package/templates/.claude/agents/feature-implementer.md +55 -0
- package/templates/.claude/config/providers.md +25 -0
- package/templates/.claude/hooks/auto-allow-bash.sh +155 -0
- package/templates/.claude/hooks/auto-prune-bash.sh +75 -0
- package/templates/.claude/hooks/block-dangerous.sh +54 -0
- package/templates/.claude/hooks/compress-output.sh +17 -0
- package/templates/.claude/hooks/protect-files.sh +37 -0
- package/templates/.claude/hooks/reinject-context.sh +28 -0
- package/templates/.claude/hooks/session-start.md +13 -0
- package/templates/.claude/hooks/skill-router.sh +1681 -0
- package/templates/.claude/hooks/verification-guard.sh +271 -0
- package/templates/.claude/settings.json +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/mce/mc.xsd +75 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/pack.py +159 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/unpack.py +29 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validate.py +69 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/base.py +951 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/docx.py +274 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/.claude/skills/backend-api/SKILL.md +26 -0
- package/templates/.claude/skills/canvas-design/LICENSE.txt +202 -0
- package/templates/.claude/skills/canvas-design/SKILL.md +130 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/code-review/SKILL.md +97 -0
- package/templates/.claude/skills/debugging-toolkit/SKILL.md +156 -0
- package/templates/.claude/skills/delivery/SKILL.md +92 -0
- package/templates/.claude/skills/discover-security/SKILL.md +86 -0
- package/templates/.claude/skills/docker-packaging/SKILL.md +60 -0
- package/templates/.claude/skills/docs-manager/SKILL.md +465 -0
- package/templates/.claude/skills/docs-manager/init-project-docs.sh +70 -0
- package/templates/.claude/skills/docs-manager/templates/README.md.template +50 -0
- package/templates/.claude/skills/docs-manager/templates/agent-roles.md.template +24 -0
- package/templates/.claude/skills/docs-manager/templates/coding-conventions.md.template +28 -0
- package/templates/.claude/skills/docs-manager/templates/memory.md.template +30 -0
- package/templates/.claude/skills/docs-manager/templates/onboarding.md.template +20 -0
- package/templates/.claude/skills/docs-manager/templates/project.md.template +26 -0
- package/templates/.claude/skills/docs-quality/SKILL.md +148 -0
- package/templates/.claude/skills/docx/LICENSE.txt +30 -0
- package/templates/.claude/skills/docx/SKILL.md +197 -0
- package/templates/.claude/skills/docx/docx-js.md +350 -0
- package/templates/.claude/skills/docx/ooxml.md +610 -0
- package/templates/.claude/skills/docx/scripts/__init__.py +1 -0
- package/templates/.claude/skills/docx/scripts/document.py +1276 -0
- package/templates/.claude/skills/docx/scripts/templates/comments.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/people.xml +3 -0
- package/templates/.claude/skills/docx/scripts/utilities.py +374 -0
- package/templates/.claude/skills/duraone/SKILL.md +204 -0
- package/templates/.claude/skills/duraone/references/backend.md +636 -0
- package/templates/.claude/skills/duraone/references/frontend.md +1506 -0
- package/templates/.claude/skills/duraone/references/sql.md +631 -0
- package/templates/.claude/skills/duraone/references/workflow.md +520 -0
- package/templates/.claude/skills/executing-plans/SKILL.md +76 -0
- package/templates/.claude/skills/file-organizer/SKILL.md +433 -0
- package/templates/.claude/skills/frontend/SKILL.md +26 -0
- package/templates/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/templates/.claude/skills/frontend-design/SKILL.md +42 -0
- package/templates/.claude/skills/frontend-vue/SKILL.md +127 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Box.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Button.vue +93 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonBar.vue +29 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonFloat.vue +62 -0
- package/templates/.claude/skills/frontend-vue/components/Control/CheckButton.vue +75 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Checkbox.vue +58 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Datetime.vue +148 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Dropdownlist.vue +156 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Input.vue +106 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Label.vue +38 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Master/BoxColumn.vue +24 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Confirm.vue +33 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Info.vue +32 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/ModalInfo.vue +39 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Reject.vue +64 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Tag.vue +82 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Upload.vue +61 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/Dropdownlist.vue +103 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/PagingBar.vue +108 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/UploadImage.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AG.vue +806 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AntTable.vue +253 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditor.vue +43 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditorEnable.vue +55 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/HtmlTable.vue +40 -0
- package/templates/.claude/skills/frontend-vue/components/PDFViewer.vue +25 -0
- package/templates/.claude/skills/frontend-vue/components/Panel/FormView.vue +309 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Footer.vue +23 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Header.vue +265 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Sidebar.vue +122 -0
- package/templates/.claude/skills/frontend-vue/components/Template.vue +16 -0
- package/templates/.claude/skills/frontend-vue/components/View/Form.vue +89 -0
- package/templates/.claude/skills/frontend-vue/composables/indexDBStore.js +140 -0
- package/templates/.claude/skills/frontend-vue/composables/masterApi.js +362 -0
- package/templates/.claude/skills/frontend-vue/composables/state.js +578 -0
- package/templates/.claude/skills/frontend-vue/composables/useRequest.js +221 -0
- package/templates/.claude/skills/frontend-vue/composables/useSession.js +179 -0
- package/templates/.claude/skills/frontend-vue/composables/useTranslation.js +54 -0
- package/templates/.claude/skills/frontend-vue/composables/useWebSocket.js +257 -0
- package/templates/.claude/skills/frontend-vue/composables/userObj.js +111 -0
- package/templates/.claude/skills/frontend-vue/composables/utils.js +322 -0
- package/templates/.claude/skills/frontend-vue/reference/composables-example.vue +320 -0
- package/templates/.claude/skills/frontend-vue/reference/form-example.vue +183 -0
- package/templates/.claude/skills/frontend-vue/reference/grid-example.vue +147 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/[id].vue +106 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/index.vue +58 -0
- package/templates/.claude/skills/frontend-vue/reference/popup-example.vue +159 -0
- package/templates/.claude/skills/pdf/LICENSE.txt +30 -0
- package/templates/.claude/skills/pdf/SKILL.md +294 -0
- package/templates/.claude/skills/pdf/forms.md +205 -0
- package/templates/.claude/skills/pdf/reference.md +612 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
- package/templates/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/.claude/skills/pdf-processing/SKILL.md +107 -0
- package/templates/.claude/skills/pdf-processing-pro/FORMS.md +610 -0
- package/templates/.claude/skills/pdf-processing-pro/OCR.md +137 -0
- package/templates/.claude/skills/pdf-processing-pro/SKILL.md +296 -0
- package/templates/.claude/skills/pdf-processing-pro/TABLES.md +626 -0
- package/templates/.claude/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
- package/templates/.claude/skills/postgres/SKILL.md +69 -0
- package/templates/.claude/skills/postgres/reference/fn_get_examples.sql +208 -0
- package/templates/.claude/skills/postgres/reference/fn_rpt_examples.sql +239 -0
- package/templates/.claude/skills/postgres/reference/utility_functions.sql +94 -0
- package/templates/.claude/skills/pptx/LICENSE.txt +30 -0
- package/templates/.claude/skills/pptx/SKILL.md +484 -0
- package/templates/.claude/skills/pptx/html2pptx.md +625 -0
- package/templates/.claude/skills/pptx/ooxml.md +427 -0
- package/templates/.claude/skills/pptx/scripts/html2pptx.js +979 -0
- package/templates/.claude/skills/pptx/scripts/inventory.py +1020 -0
- package/templates/.claude/skills/pptx/scripts/rearrange.py +231 -0
- package/templates/.claude/skills/pptx/scripts/replace.py +385 -0
- package/templates/.claude/skills/pptx/scripts/thumbnail.py +450 -0
- package/templates/.claude/skills/repo-maintenance/SKILL.md +97 -0
- package/templates/.claude/skills/research/EXAMPLES.md +434 -0
- package/templates/.claude/skills/research/REFERENCE.md +399 -0
- package/templates/.claude/skills/research/SKILL.md +136 -0
- package/templates/.claude/skills/root-cause-tracing/SKILL.md +174 -0
- package/templates/.claude/skills/root-cause-tracing/find-polluter.sh +63 -0
- package/templates/.claude/skills/sharing-skills/SKILL.md +194 -0
- package/templates/.claude/skills/sql-optimization-patterns/SKILL.md +493 -0
- package/templates/.claude/skills/subagent-driven-development/SKILL.md +189 -0
- package/templates/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/templates/.claude/skills/systematic-debugging/SKILL.md +295 -0
- package/templates/.claude/skills/systematic-debugging/test-academic.md +14 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/templates/.claude/skills/test-driven-development/SKILL.md +364 -0
- package/templates/.claude/skills/testing-anti-patterns/SKILL.md +302 -0
- package/templates/.claude/skills/testing-quality/SKILL.md +97 -0
- package/templates/.claude/skills/verification-before-completion/SKILL.md +139 -0
- package/templates/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/templates/.claude/skills/webapp-testing/SKILL.md +96 -0
- package/templates/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/templates/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/templates/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/templates/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/templates/.claude/ukit/index/build-index.mjs +28 -0
- package/templates/.claude/ukit/index/cache-utils.mjs +140 -0
- package/templates/.claude/ukit/index/lib/index-core.mjs +2800 -0
- package/templates/.claude/ukit/index/query-index.mjs +150 -0
- package/templates/.claude/ukit/index/refresh-index.mjs +57 -0
- package/templates/.claude/ukit/index/reset-auto-permissions.mjs +76 -0
- package/templates/.claude/ukit/index/resolve-context.mjs +279 -0
- package/templates/.claude/ukit/index/route-catalog.mjs +258 -0
- package/templates/.claude/ukit/index/route-task.mjs +1994 -0
- package/templates/.claude/ukit/index/triage.mjs +133 -0
- package/templates/.claude/ukit/index/verify-context.mjs +689 -0
- package/templates/.claude/ukit/runtime/compact-threshold.mjs +1013 -0
- package/templates/.claude/ukit/runtime/output-compression.mjs +1340 -0
- package/templates/.claude/ukit/runtime/reinject-context.mjs +874 -0
- package/templates/.claude/ukit/runtime/token-utils.mjs +500 -0
- package/templates/.codex/README.md +83 -0
- package/templates/.codex/settings.json +187 -0
- package/templates/.gitignore +75 -0
- package/templates/AGENTS.md +116 -0
- package/templates/CLAUDE.md +93 -0
- package/templates/adapter-presets/antigravity/README.md +22 -0
- package/templates/adapter-presets/antigravity/rules.md +49 -0
- package/templates/adapter-presets/claude/settings.local.json +42 -0
- package/templates/adapter-presets/codex/settings.local.json +6 -0
- package/templates/adapter-presets/opencode/opencode.template.json +1 -0
- package/templates/docs/BUGFIX.md +20 -0
- package/templates/docs/BUG_INDEX.md +12 -0
- package/templates/docs/BUG_METRICS.md +7 -0
- package/templates/docs/BUG_TEMPLATE.md +13 -0
- package/templates/docs/CODE_MAP.md +35 -0
- package/templates/docs/INSTALL.md +113 -0
- package/templates/docs/MEMORY.md +49 -0
- package/templates/docs/PROJECT.md +50 -0
- package/templates/docs/UKIT_USAGE_GUIDE.md +147 -0
- package/templates/docs/WORKLOG.md +10 -0
- package/templates/ukit/README.md +14 -0
- package/templates/ukit/storage/cache/compact-history.json +3 -0
- package/templates/ukit/storage/cache/compact-pressure.json +1 -0
- package/templates/ukit/storage/cache/output-history.json +3 -0
- package/templates/ukit/storage/cache/prompt-cache.json +3 -0
- package/templates/ukit/storage/config.json +37 -0
- package/templates/ukit/storage/memory/projects/.gitkeep +2 -0
- package/templates/ukit/storage/memory/sessions/.gitkeep +0 -0
- package/templates/ukit/storage/memory/user.json +5 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<w:comments xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16sdtfl="http://schemas.microsoft.com/office/word/2024/wordml/sdtformatlock" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du wp14">
|
|
3
|
+
</w:comments>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<w15:commentsEx xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16sdtfl="http://schemas.microsoft.com/office/word/2024/wordml/sdtformatlock" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du wp14">
|
|
3
|
+
</w15:commentsEx>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<w16cex:commentsExtensible xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16sdtfl="http://schemas.microsoft.com/office/word/2024/wordml/sdtformatlock" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:cr="http://schemas.microsoft.com/office/comments/2020/reactions" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl cr w16du wp14">
|
|
3
|
+
</w16cex:commentsExtensible>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<w16cid:commentsIds xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16sdtfl="http://schemas.microsoft.com/office/word/2024/wordml/sdtformatlock" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du wp14">
|
|
3
|
+
</w16cid:commentsIds>
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Utilities for editing OOXML documents.
|
|
4
|
+
|
|
5
|
+
This module provides XMLEditor, a tool for manipulating XML files with support for
|
|
6
|
+
line-number-based node finding and DOM manipulation. Each element is automatically
|
|
7
|
+
annotated with its original line and column position during parsing.
|
|
8
|
+
|
|
9
|
+
Example usage:
|
|
10
|
+
editor = XMLEditor("document.xml")
|
|
11
|
+
|
|
12
|
+
# Find node by line number or range
|
|
13
|
+
elem = editor.get_node(tag="w:r", line_number=519)
|
|
14
|
+
elem = editor.get_node(tag="w:p", line_number=range(100, 200))
|
|
15
|
+
|
|
16
|
+
# Find node by text content
|
|
17
|
+
elem = editor.get_node(tag="w:p", contains="specific text")
|
|
18
|
+
|
|
19
|
+
# Find node by attributes
|
|
20
|
+
elem = editor.get_node(tag="w:r", attrs={"w:id": "target"})
|
|
21
|
+
|
|
22
|
+
# Combine filters
|
|
23
|
+
elem = editor.get_node(tag="w:p", line_number=range(1, 50), contains="text")
|
|
24
|
+
|
|
25
|
+
# Replace, insert, or manipulate
|
|
26
|
+
new_elem = editor.replace_node(elem, "<w:r><w:t>new text</w:t></w:r>")
|
|
27
|
+
editor.insert_after(new_elem, "<w:r><w:t>more</w:t></w:r>")
|
|
28
|
+
|
|
29
|
+
# Save changes
|
|
30
|
+
editor.save()
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import html
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Optional, Union
|
|
36
|
+
|
|
37
|
+
import defusedxml.minidom
|
|
38
|
+
import defusedxml.sax
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class XMLEditor:
|
|
42
|
+
"""
|
|
43
|
+
Editor for manipulating OOXML XML files with line-number-based node finding.
|
|
44
|
+
|
|
45
|
+
This class parses XML files and tracks the original line and column position
|
|
46
|
+
of each element. This enables finding nodes by their line number in the original
|
|
47
|
+
file, which is useful when working with Read tool output.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
xml_path: Path to the XML file being edited
|
|
51
|
+
encoding: Detected encoding of the XML file ('ascii' or 'utf-8')
|
|
52
|
+
dom: Parsed DOM tree with parse_position attributes on elements
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, xml_path):
|
|
56
|
+
"""
|
|
57
|
+
Initialize with path to XML file and parse with line number tracking.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
xml_path: Path to XML file to edit (str or Path)
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If the XML file does not exist
|
|
64
|
+
"""
|
|
65
|
+
self.xml_path = Path(xml_path)
|
|
66
|
+
if not self.xml_path.exists():
|
|
67
|
+
raise ValueError(f"XML file not found: {xml_path}")
|
|
68
|
+
|
|
69
|
+
with open(self.xml_path, "rb") as f:
|
|
70
|
+
header = f.read(200).decode("utf-8", errors="ignore")
|
|
71
|
+
self.encoding = "ascii" if 'encoding="ascii"' in header else "utf-8"
|
|
72
|
+
|
|
73
|
+
parser = _create_line_tracking_parser()
|
|
74
|
+
self.dom = defusedxml.minidom.parse(str(self.xml_path), parser)
|
|
75
|
+
|
|
76
|
+
def get_node(
|
|
77
|
+
self,
|
|
78
|
+
tag: str,
|
|
79
|
+
attrs: Optional[dict[str, str]] = None,
|
|
80
|
+
line_number: Optional[Union[int, range]] = None,
|
|
81
|
+
contains: Optional[str] = None,
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Get a DOM element by tag and identifier.
|
|
85
|
+
|
|
86
|
+
Finds an element by either its line number in the original file or by
|
|
87
|
+
matching attribute values. Exactly one match must be found.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
tag: The XML tag name (e.g., "w:del", "w:ins", "w:r")
|
|
91
|
+
attrs: Dictionary of attribute name-value pairs to match (e.g., {"w:id": "1"})
|
|
92
|
+
line_number: Line number (int) or line range (range) in original XML file (1-indexed)
|
|
93
|
+
contains: Text string that must appear in any text node within the element.
|
|
94
|
+
Supports both entity notation (“) and Unicode characters (\u201c).
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
defusedxml.minidom.Element: The matching DOM element
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: If node not found or multiple matches found
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
elem = editor.get_node(tag="w:r", line_number=519)
|
|
104
|
+
elem = editor.get_node(tag="w:r", line_number=range(100, 200))
|
|
105
|
+
elem = editor.get_node(tag="w:del", attrs={"w:id": "1"})
|
|
106
|
+
elem = editor.get_node(tag="w:p", attrs={"w14:paraId": "12345678"})
|
|
107
|
+
elem = editor.get_node(tag="w:commentRangeStart", attrs={"w:id": "0"})
|
|
108
|
+
elem = editor.get_node(tag="w:p", contains="specific text")
|
|
109
|
+
elem = editor.get_node(tag="w:t", contains="“Agreement") # Entity notation
|
|
110
|
+
elem = editor.get_node(tag="w:t", contains="\u201cAgreement") # Unicode character
|
|
111
|
+
"""
|
|
112
|
+
matches = []
|
|
113
|
+
for elem in self.dom.getElementsByTagName(tag):
|
|
114
|
+
# Check line_number filter
|
|
115
|
+
if line_number is not None:
|
|
116
|
+
parse_pos = getattr(elem, "parse_position", (None,))
|
|
117
|
+
elem_line = parse_pos[0]
|
|
118
|
+
|
|
119
|
+
# Handle both single line number and range
|
|
120
|
+
if isinstance(line_number, range):
|
|
121
|
+
if elem_line not in line_number:
|
|
122
|
+
continue
|
|
123
|
+
else:
|
|
124
|
+
if elem_line != line_number:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
# Check attrs filter
|
|
128
|
+
if attrs is not None:
|
|
129
|
+
if not all(
|
|
130
|
+
elem.getAttribute(attr_name) == attr_value
|
|
131
|
+
for attr_name, attr_value in attrs.items()
|
|
132
|
+
):
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Check contains filter
|
|
136
|
+
if contains is not None:
|
|
137
|
+
elem_text = self._get_element_text(elem)
|
|
138
|
+
# Normalize the search string: convert HTML entities to Unicode characters
|
|
139
|
+
# This allows searching for both "“Rowan" and ""Rowan"
|
|
140
|
+
normalized_contains = html.unescape(contains)
|
|
141
|
+
if normalized_contains not in elem_text:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
# If all applicable filters passed, this is a match
|
|
145
|
+
matches.append(elem)
|
|
146
|
+
|
|
147
|
+
if not matches:
|
|
148
|
+
# Build descriptive error message
|
|
149
|
+
filters = []
|
|
150
|
+
if line_number is not None:
|
|
151
|
+
line_str = (
|
|
152
|
+
f"lines {line_number.start}-{line_number.stop - 1}"
|
|
153
|
+
if isinstance(line_number, range)
|
|
154
|
+
else f"line {line_number}"
|
|
155
|
+
)
|
|
156
|
+
filters.append(f"at {line_str}")
|
|
157
|
+
if attrs is not None:
|
|
158
|
+
filters.append(f"with attributes {attrs}")
|
|
159
|
+
if contains is not None:
|
|
160
|
+
filters.append(f"containing '{contains}'")
|
|
161
|
+
|
|
162
|
+
filter_desc = " ".join(filters) if filters else ""
|
|
163
|
+
base_msg = f"Node not found: <{tag}> {filter_desc}".strip()
|
|
164
|
+
|
|
165
|
+
# Add helpful hint based on filters used
|
|
166
|
+
if contains:
|
|
167
|
+
hint = "Text may be split across elements or use different wording."
|
|
168
|
+
elif line_number:
|
|
169
|
+
hint = "Line numbers may have changed if document was modified."
|
|
170
|
+
elif attrs:
|
|
171
|
+
hint = "Verify attribute values are correct."
|
|
172
|
+
else:
|
|
173
|
+
hint = "Try adding filters (attrs, line_number, or contains)."
|
|
174
|
+
|
|
175
|
+
raise ValueError(f"{base_msg}. {hint}")
|
|
176
|
+
if len(matches) > 1:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Multiple nodes found: <{tag}>. "
|
|
179
|
+
f"Add more filters (attrs, line_number, or contains) to narrow the search."
|
|
180
|
+
)
|
|
181
|
+
return matches[0]
|
|
182
|
+
|
|
183
|
+
def _get_element_text(self, elem):
|
|
184
|
+
"""
|
|
185
|
+
Recursively extract all text content from an element.
|
|
186
|
+
|
|
187
|
+
Skips text nodes that contain only whitespace (spaces, tabs, newlines),
|
|
188
|
+
which typically represent XML formatting rather than document content.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
elem: defusedxml.minidom.Element to extract text from
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
str: Concatenated text from all non-whitespace text nodes within the element
|
|
195
|
+
"""
|
|
196
|
+
text_parts = []
|
|
197
|
+
for node in elem.childNodes:
|
|
198
|
+
if node.nodeType == node.TEXT_NODE:
|
|
199
|
+
# Skip whitespace-only text nodes (XML formatting)
|
|
200
|
+
if node.data.strip():
|
|
201
|
+
text_parts.append(node.data)
|
|
202
|
+
elif node.nodeType == node.ELEMENT_NODE:
|
|
203
|
+
text_parts.append(self._get_element_text(node))
|
|
204
|
+
return "".join(text_parts)
|
|
205
|
+
|
|
206
|
+
def replace_node(self, elem, new_content):
|
|
207
|
+
"""
|
|
208
|
+
Replace a DOM element with new XML content.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
elem: defusedxml.minidom.Element to replace
|
|
212
|
+
new_content: String containing XML to replace the node with
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
List[defusedxml.minidom.Node]: All inserted nodes
|
|
216
|
+
|
|
217
|
+
Example:
|
|
218
|
+
new_nodes = editor.replace_node(old_elem, "<w:r><w:t>text</w:t></w:r>")
|
|
219
|
+
"""
|
|
220
|
+
parent = elem.parentNode
|
|
221
|
+
nodes = self._parse_fragment(new_content)
|
|
222
|
+
for node in nodes:
|
|
223
|
+
parent.insertBefore(node, elem)
|
|
224
|
+
parent.removeChild(elem)
|
|
225
|
+
return nodes
|
|
226
|
+
|
|
227
|
+
def insert_after(self, elem, xml_content):
|
|
228
|
+
"""
|
|
229
|
+
Insert XML content after a DOM element.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
elem: defusedxml.minidom.Element to insert after
|
|
233
|
+
xml_content: String containing XML to insert
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
List[defusedxml.minidom.Node]: All inserted nodes
|
|
237
|
+
|
|
238
|
+
Example:
|
|
239
|
+
new_nodes = editor.insert_after(elem, "<w:r><w:t>text</w:t></w:r>")
|
|
240
|
+
"""
|
|
241
|
+
parent = elem.parentNode
|
|
242
|
+
next_sibling = elem.nextSibling
|
|
243
|
+
nodes = self._parse_fragment(xml_content)
|
|
244
|
+
for node in nodes:
|
|
245
|
+
if next_sibling:
|
|
246
|
+
parent.insertBefore(node, next_sibling)
|
|
247
|
+
else:
|
|
248
|
+
parent.appendChild(node)
|
|
249
|
+
return nodes
|
|
250
|
+
|
|
251
|
+
def insert_before(self, elem, xml_content):
|
|
252
|
+
"""
|
|
253
|
+
Insert XML content before a DOM element.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
elem: defusedxml.minidom.Element to insert before
|
|
257
|
+
xml_content: String containing XML to insert
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
List[defusedxml.minidom.Node]: All inserted nodes
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
new_nodes = editor.insert_before(elem, "<w:r><w:t>text</w:t></w:r>")
|
|
264
|
+
"""
|
|
265
|
+
parent = elem.parentNode
|
|
266
|
+
nodes = self._parse_fragment(xml_content)
|
|
267
|
+
for node in nodes:
|
|
268
|
+
parent.insertBefore(node, elem)
|
|
269
|
+
return nodes
|
|
270
|
+
|
|
271
|
+
def append_to(self, elem, xml_content):
|
|
272
|
+
"""
|
|
273
|
+
Append XML content as a child of a DOM element.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
elem: defusedxml.minidom.Element to append to
|
|
277
|
+
xml_content: String containing XML to append
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List[defusedxml.minidom.Node]: All inserted nodes
|
|
281
|
+
|
|
282
|
+
Example:
|
|
283
|
+
new_nodes = editor.append_to(elem, "<w:r><w:t>text</w:t></w:r>")
|
|
284
|
+
"""
|
|
285
|
+
nodes = self._parse_fragment(xml_content)
|
|
286
|
+
for node in nodes:
|
|
287
|
+
elem.appendChild(node)
|
|
288
|
+
return nodes
|
|
289
|
+
|
|
290
|
+
def get_next_rid(self):
|
|
291
|
+
"""Get the next available rId for relationships files."""
|
|
292
|
+
max_id = 0
|
|
293
|
+
for rel_elem in self.dom.getElementsByTagName("Relationship"):
|
|
294
|
+
rel_id = rel_elem.getAttribute("Id")
|
|
295
|
+
if rel_id.startswith("rId"):
|
|
296
|
+
try:
|
|
297
|
+
max_id = max(max_id, int(rel_id[3:]))
|
|
298
|
+
except ValueError:
|
|
299
|
+
pass
|
|
300
|
+
return f"rId{max_id + 1}"
|
|
301
|
+
|
|
302
|
+
def save(self):
|
|
303
|
+
"""
|
|
304
|
+
Save the edited XML back to the file.
|
|
305
|
+
|
|
306
|
+
Serializes the DOM tree and writes it back to the original file path,
|
|
307
|
+
preserving the original encoding (ascii or utf-8).
|
|
308
|
+
"""
|
|
309
|
+
content = self.dom.toxml(encoding=self.encoding)
|
|
310
|
+
self.xml_path.write_bytes(content)
|
|
311
|
+
|
|
312
|
+
def _parse_fragment(self, xml_content):
|
|
313
|
+
"""
|
|
314
|
+
Parse XML fragment and return list of imported nodes.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
xml_content: String containing XML fragment
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
List of defusedxml.minidom.Node objects imported into this document
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
AssertionError: If fragment contains no element nodes
|
|
324
|
+
"""
|
|
325
|
+
# Extract namespace declarations from the root document element
|
|
326
|
+
root_elem = self.dom.documentElement
|
|
327
|
+
namespaces = []
|
|
328
|
+
if root_elem and root_elem.attributes:
|
|
329
|
+
for i in range(root_elem.attributes.length):
|
|
330
|
+
attr = root_elem.attributes.item(i)
|
|
331
|
+
if attr.name.startswith("xmlns"): # type: ignore
|
|
332
|
+
namespaces.append(f'{attr.name}="{attr.value}"') # type: ignore
|
|
333
|
+
|
|
334
|
+
ns_decl = " ".join(namespaces)
|
|
335
|
+
wrapper = f"<root {ns_decl}>{xml_content}</root>"
|
|
336
|
+
fragment_doc = defusedxml.minidom.parseString(wrapper)
|
|
337
|
+
nodes = [
|
|
338
|
+
self.dom.importNode(child, deep=True)
|
|
339
|
+
for child in fragment_doc.documentElement.childNodes # type: ignore
|
|
340
|
+
]
|
|
341
|
+
elements = [n for n in nodes if n.nodeType == n.ELEMENT_NODE]
|
|
342
|
+
assert elements, "Fragment must contain at least one element"
|
|
343
|
+
return nodes
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _create_line_tracking_parser():
|
|
347
|
+
"""
|
|
348
|
+
Create a SAX parser that tracks line and column numbers for each element.
|
|
349
|
+
|
|
350
|
+
Monkey patches the SAX content handler to store the current line and column
|
|
351
|
+
position from the underlying expat parser onto each element as a parse_position
|
|
352
|
+
attribute (line, column) tuple.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
defusedxml.sax.xmlreader.XMLReader: Configured SAX parser
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
def set_content_handler(dom_handler):
|
|
359
|
+
def startElementNS(name, tagName, attrs):
|
|
360
|
+
orig_start_cb(name, tagName, attrs)
|
|
361
|
+
cur_elem = dom_handler.elementStack[-1]
|
|
362
|
+
cur_elem.parse_position = (
|
|
363
|
+
parser._parser.CurrentLineNumber, # type: ignore
|
|
364
|
+
parser._parser.CurrentColumnNumber, # type: ignore
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
orig_start_cb = dom_handler.startElementNS
|
|
368
|
+
dom_handler.startElementNS = startElementNS
|
|
369
|
+
orig_set_content_handler(dom_handler)
|
|
370
|
+
|
|
371
|
+
parser = defusedxml.sax.make_parser()
|
|
372
|
+
orig_set_content_handler = parser.setContentHandler
|
|
373
|
+
parser.setContentHandler = set_content_handler # type: ignore
|
|
374
|
+
return parser
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: duraone
|
|
3
|
+
description: >
|
|
4
|
+
DuraOne coding style — bộ quy tắc lập trình cá nhân của LeNK. Đây là skill CHÍNH và được dùng
|
|
5
|
+
nhiều nhất trong UKit. LUÔN dùng skill này khi làm việc trên bất kỳ dự án nào theo phong cách
|
|
6
|
+
DuraOne: tạo trang Vue mới, tạo component, viết composable, tạo API endpoint, viết SQL view /
|
|
7
|
+
function / procedure, đặt tên file/folder, viết backend Python Sanic, tạo form, tạo grid/list,
|
|
8
|
+
load dropdown data, xử lý datetime, validate form, thiết kế luồng tính năng, hay bất kỳ task code
|
|
9
|
+
nào liên quan đến FE_DuraOne, BE_DuraOne, hoặc dự án cùng stack (Nuxt 3 + Sanic + PostgreSQL).
|
|
10
|
+
Skill này là nguồn sự thật duy nhất — không đoán mò, không dùng raw input, không dùng new Date().
|
|
11
|
+
Đọc reference phù hợp TRƯỚC KHI code bất kỳ thứ gì.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# DuraOne Skill — Phong Cách Code Cá Nhân
|
|
15
|
+
|
|
16
|
+
Đây là bộ quy tắc lập trình của **LeNK** cho toàn bộ hệ thống DuraOne.
|
|
17
|
+
Stack: **Nuxt 3 (Vue 3 Options API) + Python Sanic + PostgreSQL**.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## TRIẾT LÝ DURAONE: "NHÌN TÊN ĐOÁN VỊ TRÍ — DATA MAPPING 1:1"
|
|
22
|
+
|
|
23
|
+
> Đây là kim chỉ nam xuyên suốt. Mọi quy tắc bên dưới đều phục vụ triết lý này.
|
|
24
|
+
|
|
25
|
+
### 1. Kiến Trúc Độc Lập (Cross-Project)
|
|
26
|
+
- FE (Nuxt 3) và BE (Python Sanic) là **2 project tách biệt hoàn toàn**.
|
|
27
|
+
- **CẤM** tạo/đẻ file Python hoặc SQL vào project FE.
|
|
28
|
+
- **CẤM** viết mã Vue/JS vào project BE.
|
|
29
|
+
- Mỗi project có repo riêng, deploy riêng, không share code trực tiếp.
|
|
30
|
+
|
|
31
|
+
### 2. Data Mapping 1:1 với Database
|
|
32
|
+
- Thuộc tính trong object chứa data (đặc biệt `workingObj`) **BẮT BUỘC giữ nguyên gốc `snake_case`** y chang tên cột PostgreSQL.
|
|
33
|
+
- **CẤM AI tự ý convert sang camelCase** khi map data từ DB.
|
|
34
|
+
- ĐÚNG: `workingObj.id_user`, `workingObj.created_at`, `workingObj.full_name`
|
|
35
|
+
- SAI: `workingObj.idUser`, `workingObj.createdAt`, `workingObj.fullName`
|
|
36
|
+
|
|
37
|
+
### 3. Quy Tắc workingObj & Cấm Từ "form"
|
|
38
|
+
- Mọi trang chứa form bắt buộc dùng đúng một biến tên là **`workingObj`**.
|
|
39
|
+
- **CẤM từ khóa "form" trong tên biến:**
|
|
40
|
+
- SAI: `formData`, `userForm`, `isFormValid`, `formErrors`, `submitForm`
|
|
41
|
+
- ĐÚNG: `workingObj`, `userDetail`, `isValid`, `errors`, `saveData`
|
|
42
|
+
- workingObj chứa toàn bộ fields của form, mapping 1:1 với DB columns (xem mục 2 ở trên).
|
|
43
|
+
|
|
44
|
+
### 4. Đặt Tên Hàm — Phân Biệt Global vs Local
|
|
45
|
+
- **Global (Composables — `masterApi.js`, `utils.js`):** dùng `snake_case` + động từ hành động.
|
|
46
|
+
- Ví dụ: `get_master_data()`, `format_date_string()`, `check_is_null_or_blank()`, `convert_to_dropdown()`
|
|
47
|
+
- **Local (methods trong Pages/Components):** dùng `camelCase`.
|
|
48
|
+
- Ví dụ: `saveData()`, `closeModal()`, `loadDropdowns()`, `resetWorkingObj()`
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Tổng Quan Stack
|
|
53
|
+
|
|
54
|
+
| Layer | Technology |
|
|
55
|
+
|-------|-----------|
|
|
56
|
+
| Frontend | Nuxt 3, Vue 3 **Options API**, JavaScript, Ant Design Vue |
|
|
57
|
+
| HTTP Client | Axios + GZIP compression (`useRequest.js`) |
|
|
58
|
+
| State | `reactive()` trực tiếp — KHÔNG dùng Vuex/Pinia |
|
|
59
|
+
| Backend | Python Sanic (async), Blueprint pattern |
|
|
60
|
+
| DB Driver | `asyncpg` (async) / `psycopg2` + pandas (sync) |
|
|
61
|
+
| Database | PostgreSQL với schema `qas` (test) và `prd` (production) |
|
|
62
|
+
| UI Components | Ant Design Vue, AG Grid, ECharts |
|
|
63
|
+
| CSS | Bootstrap grid + SCSS |
|
|
64
|
+
| Package | Yarn (FE), pip (BE) |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quy Tắc Đặt Tên — QUAN TRỌNG
|
|
69
|
+
|
|
70
|
+
### Files & Folders
|
|
71
|
+
| Loại | Convention | Ví dụ |
|
|
72
|
+
|------|-----------|-------|
|
|
73
|
+
| Vue pages | PascalCase | `AgreementTodo.vue`, `UserList.vue` |
|
|
74
|
+
| Vue components | PascalCase | `ControlInput.vue`, `GridAG.vue` |
|
|
75
|
+
| Composables | camelCase | `masterApi.js`, `useRequest.js`, `state.js` |
|
|
76
|
+
| Folders (FE pages) | PascalCase | `Agreement/`, `Master/Users/` |
|
|
77
|
+
| Python files | snake_case | `db.py`, `v2_api.py`, `string_manage.py` |
|
|
78
|
+
| SQL files | snake_case | `v_agreement.sql`, `fn_get_agreement.sql` |
|
|
79
|
+
|
|
80
|
+
### Variables & Functions
|
|
81
|
+
| Loại | Convention | Ví dụ |
|
|
82
|
+
|------|-----------|-------|
|
|
83
|
+
| JS hàm LOCAL (methods trong page/component) | camelCase | `saveData()`, `closeModal()`, `loadDropdowns()` |
|
|
84
|
+
| JS hàm GLOBAL (composables: masterApi, utils) | snake_case + động từ | `get_master_data()`, `format_date_string()`, `check_is_null_or_blank()` |
|
|
85
|
+
| JS variables | camelCase | `workingObj`, `fromDate` |
|
|
86
|
+
| Data properties (map DB) | snake_case gốc | `workingObj.id_agreement`, `workingObj.created_at` — KHÔNG convert sang camelCase |
|
|
87
|
+
| DB field names | snake_case | `id_agreement`, `created_at` |
|
|
88
|
+
| ID fields | `id_` prefix | `id_agreement`, `id_user`, `id_type_of_media` |
|
|
89
|
+
| Boolean fields | `can_`/`is_`/`has_`/`show_` | `can_edit`, `is_locked`, `has_files` |
|
|
90
|
+
| API functions | `get_` prefix | `get_agreement_by_id`, `get_user_list_dropdown` |
|
|
91
|
+
| Dropdown functions | `_list_dropdown` suffix | `get_branch_list_dropdown()` |
|
|
92
|
+
| Constants | UPPER_SNAKE_CASE | `SESSION_KEY`, `MAX_RETRIES` |
|
|
93
|
+
| Python functions | snake_case | `get_table_columns`, `run_proc` |
|
|
94
|
+
| SQL views | `v_` prefix | `v_agreement`, `v_clearance_request` |
|
|
95
|
+
| SQL functions | `fn_` prefix | `fn_get_agreement`, `fn_get_review_list` |
|
|
96
|
+
| SQL procedures | `sp_` prefix | `sp_generate_running_no` |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Khi Nào Đọc Reference Nào
|
|
101
|
+
|
|
102
|
+
Trước khi code bất kỳ thứ gì, đọc reference phù hợp:
|
|
103
|
+
|
|
104
|
+
- **Tạo trang Vue, component, form, grid** → đọc `references/frontend.md`
|
|
105
|
+
- **Viết API endpoint, backend logic, Python** → đọc `references/backend.md`
|
|
106
|
+
- **Viết SQL view, function, procedure** → đọc `references/sql.md`
|
|
107
|
+
- **Thiết kế luồng tính năng mới, project flow** → đọc `references/workflow.md`
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Luồng Tạo Tính Năng Mới (Quick Reference)
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
1. SQL Layer
|
|
115
|
+
├── Tạo view: qas.v_[entity]
|
|
116
|
+
├── Tạo function nếu cần filter: qas.fn_get_[entity]
|
|
117
|
+
└── Tạo stored proc nếu cần logic: qas.sp_[action]_[entity]
|
|
118
|
+
|
|
119
|
+
2. Backend Layer
|
|
120
|
+
├── Generic endpoints đã có (/api/select, /api/save, /api/delete)
|
|
121
|
+
└── Custom endpoint nếu cần business logic đặc biệt
|
|
122
|
+
|
|
123
|
+
3. Frontend Layer
|
|
124
|
+
├── masterApi.js: thêm function get_[entity]_by_id / get_[entity]_list
|
|
125
|
+
├── pages/[Feature]/[PageName].vue: trang chính
|
|
126
|
+
└── components/: reuse ControlInput, ControlButton, GridAG, v.v.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Xem chi tiết trong `references/workflow.md`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Quick Rules (Không Được Vi Phạm)
|
|
134
|
+
|
|
135
|
+
1. **Vue dùng Options API** — KHÔNG dùng `<script setup>` hay Composition API
|
|
136
|
+
2. **ID field luôn prefix `id_`** — `id_agreement`, không phải `agreement_id`
|
|
137
|
+
3. **Boolean field luôn prefix** — `can_edit`, `is_locked`, không phải `editable`, `locked`
|
|
138
|
+
4. **Datetime LUÔN dùng `dayjs()`** — KHÔNG dùng `new Date()`, `Date.now()`, `.getFullYear()`, v.v.
|
|
139
|
+
- `dayjs().year()` thay `new Date().getFullYear()`
|
|
140
|
+
- `dayjs().startOf("month").format("YYYY-MM-DD")` thay chuỗi string thủ công
|
|
141
|
+
- Mọi biến date trong `data()` phải khởi tạo bằng dayjs
|
|
142
|
+
5. **CẤM TUYỆT ĐỐI GỌI API RAW — KHÔNG fetch(), KHÔNG axios.get/post/put/delete():**
|
|
143
|
+
- MỌI luồng giao tiếp với server BẮT BUỘC phải dùng các hàm định nghĩa sẵn từ `composables/useRequest.js`.
|
|
144
|
+
- **Nếu phát hiện code sinh ra chứa `fetch(...)`, `axios(...)`, `axios.get(...)`, `axios.post(...)` → LÀM LẠI NGAY LẬP TỨC.**
|
|
145
|
+
- Ba hàm hợp lệ DUY NHẤT:
|
|
146
|
+
- `request(url, data, method)` — Gọi API chuẩn có GZIP + loading state (mặc định GET/POST qua wrapper này)
|
|
147
|
+
- `requestForm(url, formData)` — Dành riêng cho upload file (multipart/form-data)
|
|
148
|
+
- `request_origin(url, data)` — Gọi custom endpoint (không qua generic `/api/select`)
|
|
149
|
+
- KHÔNG CÓ NGOẠI LỆ. Kể cả khi "chỉ test nhanh", "chỉ gọi 1 lần", hay "endpoint bên ngoài" — vẫn phải dùng 3 hàm trên.
|
|
150
|
+
6. **Schema luôn dùng `get_schema()`** — không hardcode `qas` hay `prd`
|
|
151
|
+
7. **Không dùng Vuex/Pinia** — dùng `state.js` reactive trực tiếp
|
|
152
|
+
8. **Error handling**: trả về `[]` hoặc `{}` — không throw exception lên UI
|
|
153
|
+
9. **SQL views luôn có `COALESCE`** cho numeric fields để tránh null
|
|
154
|
+
10. **Dropdown data format**: `{ value: ..., label: ... }` — không đổi key khác
|
|
155
|
+
11. **workingObj pattern** cho form data — một object chứa toàn bộ form fields
|
|
156
|
+
- Tên biến BẮT BUỘC là `workingObj` — **CẤM dùng từ "form" trong tên biến** (SAI: `formData`, `userForm`, `isFormValid`. ĐÚNG: `workingObj`, `isValid`)
|
|
157
|
+
- Thuộc tính bên trong **giữ nguyên snake_case** mapping 1:1 với DB columns (SAI: `workingObj.idUser`. ĐÚNG: `workingObj.id_user`)
|
|
158
|
+
12. **Primary Key luôn là UUID varchar** — dùng uuid_in(overlay(...)) pattern, KHÔNG dùng SERIAL/INT
|
|
159
|
+
13. **Foreign key cũng là varchar** — tham chiếu đến UUID của table khác
|
|
160
|
+
14. **workingObj.id mới = `''`** — KHÔNG dùng `= 0`. UUID là varchar, empty string = record mới chưa có id
|
|
161
|
+
15. **Dùng `check_is_null_or_blank(val)`** để check null/blank/array rỗng/object rỗng — hàm này xử lý mọi loại dữ liệu
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Nguyên Tắc Tái Sử Dụng — QUAN TRỌNG
|
|
166
|
+
|
|
167
|
+
### 1. Không Tự Tạo Input / UI Thô
|
|
168
|
+
LUÔN dùng Control components đã có. KHÔNG viết `<input>`, `<select>`, `<button>` raw.
|
|
169
|
+
Xem đầy đủ danh sách trong `references/frontend.md` → mục 4 (Controls), mục 5 (Popups), mục 6 (Grids).
|
|
170
|
+
|
|
171
|
+
### 2. Phân Loại Logic — Đặt Đúng Chỗ
|
|
172
|
+
|
|
173
|
+
| Logic thuộc loại | Đặt ở đâu |
|
|
174
|
+
|-----------------|-----------|
|
|
175
|
+
| Get data cho nhiều trang | `composables/masterApi.js` |
|
|
176
|
+
| Format, convert, validate (dùng nhiều nơi) | `composables/utils.js` |
|
|
177
|
+
| UI pattern dùng lại ≥ 2 trang | `components/Control/` hoặc `components/Grid/` |
|
|
178
|
+
| State toàn app (user, language, loading) | `composables/state.js` |
|
|
179
|
+
| HTTP request | `composables/useRequest.js` — KHÔNG gọi axios trực tiếp |
|
|
180
|
+
| Logic chỉ dùng riêng 1 file | `methods` trong component đó |
|
|
181
|
+
|
|
182
|
+
### 3. Quyết Định Khi Tạo Function Mới
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
Trước khi viết function mới, hỏi:
|
|
186
|
+
|
|
187
|
+
Đã có sẵn trong utils.js / masterApi.js / state.js chưa?
|
|
188
|
+
├── CÓ → dùng trực tiếp (auto-import, không cần import)
|
|
189
|
+
└── KHÔNG
|
|
190
|
+
├── Dùng ở 1 file duy nhất? → viết vào methods: {} của component đó
|
|
191
|
+
└── Dùng ở ≥ 2 file?
|
|
192
|
+
├── Logic get data từ DB → thêm vào composables/masterApi.js
|
|
193
|
+
├── Logic UI/format → thêm vào composables/utils.js
|
|
194
|
+
└── UI component → tạo mới trong components/Control/
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 4. Ưu Tiên Thứ Tự
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
1. Dùng component/composable đã có ← ưu tiên tuyệt đối
|
|
201
|
+
2. Thêm function vào composable hiện có ← nếu logic chung
|
|
202
|
+
3. Tạo component mới trong Control/ ← nếu UI tái dùng
|
|
203
|
+
4. Viết method riêng trong component ← chỉ khi logic đặc thù 1 file
|
|
204
|
+
```
|