@ohos-cpf/3rdloop 0.0.2 → 0.0.4
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/README.md +290 -244
- package/bin/3rdloop.mjs +46 -46
- package/lib/cli.js +781 -781
- package/lib/config-cmd.js +451 -451
- package/lib/config.js +318 -318
- package/lib/doctor.js +113 -113
- package/lib/exit-codes.js +30 -30
- package/lib/opencode.js +155 -155
- package/lib/orch.js +426 -426
- package/lib/queries.js +263 -263
- package/lib/step.js +325 -325
- package/lib/ui.js +222 -222
- package/lib/update.js +279 -279
- package/lib/workflow.js +308 -308
- package/lib/workflows.js +131 -131
- package/package.json +50 -50
- package/vendor/Server/Agent/SkillSelector/README.md +343 -343
- package/vendor/Server/Agent/SkillSelector/core/Agent.js +207 -207
- package/vendor/Server/Agent/SkillSelector/index.js +14 -14
- package/vendor/Server/Agent/SkillSelector/llm/llmClient.js +313 -313
- package/vendor/Server/Agent/SkillSelector/llm/prompts.js +161 -161
- package/vendor/Server/Agent/SkillSelector/llm/reactSteps.js +144 -144
- package/vendor/Server/Agent/SkillSelector/matcher/fuzzyMatcher.js +433 -433
- package/vendor/Server/Agent/SkillSelector/scanner/skillScanner.js +382 -382
- package/vendor/Server/Agent/SkillSelector/utils/actionWords.js +59 -59
- package/vendor/Server/Brain/AnalysisStrategies.js +422 -422
- package/vendor/Server/Brain/Brain.js +686 -686
- package/vendor/Server/Brain/DecompositionStrategies.js +621 -621
- package/vendor/Server/Brain/PlanPersistence.js +196 -196
- package/vendor/Server/Brain/SkillExecutor.js +251 -251
- package/vendor/Server/Brain/index.js +11 -11
- package/vendor/Server/CLI/cli.js +275 -275
- package/vendor/Server/CLI/deveco-code/index.js +71 -71
- package/vendor/Server/CLI/deveco-code.js +5 -5
- package/vendor/Server/CLI/opencode/index.js +817 -818
- package/vendor/Server/CLI/opencode.js +5 -5
- package/vendor/Server/DbUse/MessageArchive.js +61 -61
- package/vendor/Server/DbUse/StorageManager.js +107 -107
- package/vendor/Server/DbUse/adapters/BaseAdapter.js +94 -94
- package/vendor/Server/DbUse/adapters/FileAdapter.js +138 -138
- package/vendor/Server/DbUse/adapters/JsonAdapter.js +138 -138
- package/vendor/Server/DbUse/adapters/JsonlAdapter.js +114 -114
- package/vendor/Server/DbUse/adapters/SqliteAdapter.js +157 -157
- package/vendor/Server/DbUse/index.js +160 -160
- package/vendor/Server/DbUse/stores/SessionStore.js +279 -279
- package/vendor/Server/DbUse/stores/TaskExecutionStore.js +133 -133
- package/vendor/Server/DbUse/stores/TaskRegistryStore.js +262 -262
- package/vendor/Server/DbUse/stores/TaskStepStore.js +279 -279
- package/vendor/Server/FlexRunner/FlexRunner.js +1759 -1759
- package/vendor/Server/FlexRunner/index.js +12 -12
- package/vendor/Server/Knowledge/KnowledgeImporter.js +523 -523
- package/vendor/Server/Knowledge/KnowledgeManager.js +341 -341
- package/vendor/Server/Knowledge/index.js +33 -33
- package/vendor/Server/LoopEngine/LoopEngine.js +1133 -1133
- package/vendor/Server/LoopEngine/index.js +11 -11
- package/vendor/Server/Orchestrator/Orchestrator.js +0 -3
- package/vendor/Server/Orchestrator/StepNode.js +312 -312
- package/vendor/Server/Orchestrator/index.js +13 -13
- package/vendor/Server/Routes/controllers/BrainController.js +241 -241
- package/vendor/Server/Routes/controllers/FlexRunnerController.js +1125 -1125
- package/vendor/Server/Routes/controllers/HealthController.js +44 -44
- package/vendor/Server/Routes/controllers/KnowledgeImportController.js +355 -355
- package/vendor/Server/Routes/controllers/LoopEngineController.js +788 -788
- package/vendor/Server/Routes/controllers/StatsController.js +57 -57
- package/vendor/Server/Routes/controllers/StreamController.js +67 -67
- package/vendor/Server/Routes/library/logger.js +179 -179
- package/vendor/Server/Routes/library/zipArchive.js +172 -172
- package/vendor/Server/Routes/router/Router.js +204 -204
- package/vendor/Server/Routes/routes/brain.js +47 -47
- package/vendor/Server/Routes/routes/flexrunner.js +72 -72
- package/vendor/Server/Routes/routes/health.js +28 -28
- package/vendor/Server/Routes/routes/index.js +57 -57
- package/vendor/Server/Routes/routes/knowledge.js +55 -55
- package/vendor/Server/Routes/routes/loopengine.js +58 -58
- package/vendor/Server/Routes/routes/orchestrator.js +61 -61
- package/vendor/Server/Routes/routes/registry.js +97 -97
- package/vendor/Server/Routes/routes/stats.js +23 -23
- package/vendor/Server/Routes/routes/stream.js +30 -30
- package/vendor/Server/Routes/server.js +203 -203
- package/vendor/Server/Routes/sse/SseHub.js +308 -308
- package/vendor/Server/Skills/acceptance-check/SKILL.md +191 -191
- package/vendor/Server/Skills/acceptance-check/references/PHASE1_ARTIFACT_COLLECTION.md +52 -52
- package/vendor/Server/Skills/acceptance-check/references/PHASE2_CRITERIA_EVALUATION.md +77 -77
- package/vendor/Server/Skills/acceptance-check/references/PHASE2_EVIDENCE_VERIFICATION.md +70 -70
- package/vendor/Server/Skills/arkts-code-check/SKILL.md +320 -320
- package/vendor/Server/Skills/arkts-code-check/references/CODE_CHECK_GUIDE.md +210 -210
- package/vendor/Server/Skills/arkts-code-check/references/FILE_TYPE_GUIDE.md +28 -28
- package/vendor/Server/Skills/arkts-code-check/references/PHASE1_INFO_COLLECTION.md +233 -233
- package/vendor/Server/Skills/arkts-code-check/references/REPORT_TEMPLATE.md +219 -219
- package/vendor/Server/Skills/arkts-library-compile-validate/SKILL.md +328 -328
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-demo-code/SKILL.md +291 -291
- package/vendor/Server/Skills/arkts-library-demo-code/references/API_SIGNATURE_CHECKS.md +68 -68
- package/vendor/Server/Skills/arkts-library-demo-code/references/BUILD_VALIDATION.md +184 -184
- package/vendor/Server/Skills/arkts-library-demo-code/references/CODE_STANDARDS.md +337 -337
- package/vendor/Server/Skills/arkts-library-demo-code/references/EXAMPLES.md +400 -400
- package/vendor/Server/Skills/arkts-library-demo-code/references/SYSTEM_API_LOOKUP.md +98 -98
- package/vendor/Server/Skills/arkts-library-demo-code/references/TEMPLATE.md +544 -544
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-demo-coverage/SKILL.md +174 -174
- package/vendor/Server/Skills/arkts-library-demo-scenario/SKILL.md +321 -321
- package/vendor/Server/Skills/arkts-library-demo-scenario/references/EXAMPLES.md +100 -100
- package/vendor/Server/Skills/arkts-library-demo-scenario/references/TEMPLATE.md +185 -185
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/SKILL.md +233 -233
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +102 -102
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/CHECK_DIMENSIONS.md +89 -89
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/COMPLIANCE_CHECKLIST.md +62 -62
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/KB_QUERY_GUIDE.md +107 -107
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/MCP_TOOL_DISCOVERY.md +124 -124
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/REPORT_TEMPLATE.md +90 -90
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/SCORING_FORMULA.md +57 -57
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/arkts-library-interface-spec/SKILL.md +97 -97
- package/vendor/Server/Skills/arkts-library-memory-leak-check/SKILL.md +391 -391
- package/vendor/Server/Skills/arkts-library-source-clone/SKILL.md +113 -113
- package/vendor/Server/Skills/arkts-library-stability-check/SKILL.md +312 -312
- package/vendor/Server/Skills/arkts-library-stability-check/assets/REPORT_TEMPLATE.md +154 -154
- package/vendor/Server/Skills/arkts-library-stability-check/references/CHECKLIST.md +165 -165
- package/vendor/Server/Skills/arkts-library-stability-check/references/CODE_EXAMPLES.md +308 -308
- package/vendor/Server/Skills/arkts-library-stability-check/references/SCORING_RULES.md +90 -90
- package/vendor/Server/Skills/arkts-library-stability-check/scripts/validate-skill.cjs +282 -282
- package/vendor/Server/Skills/arkts-library-test-coverage-check/SKILL.md +360 -360
- package/vendor/Server/Skills/arkts-library-test-coverage-check/assets/REPORT_TEMPLATE.md +145 -145
- package/vendor/Server/Skills/arkts-library-test-coverage-check/references/CHECKLIST.md +252 -252
- package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +114 -114
- package/vendor/Server/Skills/arkts-library-test-coverage-check/scripts/validate-skill.cjs +262 -262
- package/vendor/Server/Skills/arkts-library-xts-code/SKILL.md +455 -455
- package/vendor/Server/Skills/arkts-library-xts-code/assets/report-template.md +60 -60
- package/vendor/Server/Skills/arkts-library-xts-code/references/ACCESS_MODIFIER_PRECHECK.md +76 -76
- package/vendor/Server/Skills/arkts-library-xts-code/references/BUILD_VERIFICATION.md +200 -200
- package/vendor/Server/Skills/arkts-library-xts-code/references/CODE_STANDARDS.md +518 -518
- package/vendor/Server/Skills/arkts-library-xts-code/references/SUBAGENT_PROMPT_TEMPLATE.md +276 -276
- package/vendor/Server/Skills/arkts-library-xts-code/references/SYSTEM_API_LOOKUP.md +92 -92
- package/vendor/Server/Skills/arkts-library-xts-code/references/XTS_TEMPLATE.md +767 -767
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/precheck-access-modifiers.cjs +221 -221
- package/vendor/Server/Skills/arkts-library-xts-code-fix/SKILL.md +228 -228
- package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +97 -97
- package/vendor/Server/Skills/arkts-library-xts-doc/SKILL.md +194 -194
- package/vendor/Server/Skills/arkts-library-xts-doc/references/EXAMPLES.md +575 -575
- package/vendor/Server/Skills/arkts-library-xts-doc/references/TEMPLATE.md +574 -574
- package/vendor/Server/Skills/arkts-library-xts-doc/references/TESTABILITY_GUIDE.md +182 -182
- package/vendor/Server/Skills/arkts-library-xts-execution/SKILL.md +490 -490
- package/vendor/Server/Skills/arkts-library-xts-execution/references/error-fixing-examples.md +217 -217
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/flutter-code-check/SKILL.md +299 -299
- package/vendor/Server/Skills/flutter-code-check/assets/OUTPUT_TEMPLATE.md +311 -311
- package/vendor/Server/Skills/flutter-code-check/references/CHECKLIST.md +190 -190
- package/vendor/Server/Skills/flutter-code-check/references/PLATFORMS.md +133 -133
- package/vendor/Server/Skills/flutter-code-check/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/flutter-interface/README.md +75 -75
- package/vendor/Server/Skills/flutter-interface/SKILL.md +291 -291
- package/vendor/Server/Skills/flutter-interface/references/EXAMPLES.md +336 -336
- package/vendor/Server/Skills/flutter-interface/references/GIT_CLONE_GUIDE.md +190 -190
- package/vendor/Server/Skills/flutter-interface/references/SCRIPT_REFERENCE.md +258 -258
- package/vendor/Server/Skills/flutter-interface/references/TEMPLATE.md +254 -254
- package/vendor/Server/Skills/flutter-library-consistency-check/SKILL.md +517 -517
- package/vendor/Server/Skills/flutter-library-consistency-check/references/CHECKLIST.md +381 -381
- package/vendor/Server/Skills/flutter-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +341 -341
- package/vendor/Server/Skills/flutter-library-consistency-check/references/PLATFORM_LIMITATION_GUIDE.md +71 -71
- package/vendor/Server/Skills/flutter-library-documentation-quality-check/SKILL.md +518 -518
- package/vendor/Server/Skills/flutter-library-documentation-quality-check/references/CHECKLIST.md +406 -406
- package/vendor/Server/Skills/flutter-library-memory-leak-check/SKILL.md +652 -652
- package/vendor/Server/Skills/flutter-library-memory-leak-check/references/CHECKLIST.md +396 -396
- package/vendor/Server/Skills/flutter-library-stability-check/SKILL.md +744 -744
- package/vendor/Server/Skills/flutter-library-stability-check/references/CHECKLIST.md +720 -720
- package/vendor/Server/Skills/flutter-library-test-coverage-check/SKILL.md +414 -414
- package/vendor/Server/Skills/flutter-library-test-coverage-check/assets/REPORT_TEMPLATE.md +150 -150
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/CHECKLIST.md +304 -304
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCORING_RULES.md +265 -265
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCRIPT_USAGE.md +286 -286
- package/vendor/Server/Skills/knowledge-import/SKILL.md +383 -383
- package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +165 -165
- package/vendor/Server/Skills/knowledge-import/references/SKILL_LOCATOR_GUIDE.md +91 -91
- package/vendor/Server/Skills/knowledge-import/references/SKILL_OPTIMIZATION_GUIDE.md +127 -127
- package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +453 -301
- package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_CHECKLIST.md +151 -0
- package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_ISSUE_TEMPLATE.md +257 -105
- package/vendor/Server/Skills/ohos-lib-pr-push/references/GITCODE_API.md +191 -127
- package/vendor/Server/Skills/ohos-lib-pr-push/scripts/gitcode-api.cjs +317 -208
- package/vendor/Server/Skills/rn-code-check/SKILL.md +320 -320
- package/vendor/Server/Skills/rn-code-check/references/API-GUIDE.md +234 -234
- package/vendor/Server/Skills/rn-code-check/references/CHECKLIST.md +188 -188
- package/vendor/Server/Skills/rn-code-check/references/REPORT-TEMPLATE.md +246 -246
- package/vendor/Server/Skills/rn-lib-add-example/SKILL.md +198 -198
- package/vendor/Server/Skills/rn-lib-add-example/references/APP_RENAME_GUIDE.md +40 -40
- package/vendor/Server/Skills/rn-lib-add-example/references/SHELL_ADAPTATION_CHECKLIST.md +78 -78
- package/vendor/Server/Skills/rn-lib-add-example/references/TEMPLATE_REPO_GUIDE.md +70 -70
- package/vendor/Server/Skills/rn-lib-add-example/references/VERIFICATION_GUIDE.md +47 -47
- package/vendor/Server/Skills/rn-lib-add-example/scripts/create-example.cjs +252 -252
- package/vendor/Server/Skills/rn-lib-update-doc/SKILL.md +176 -176
- package/vendor/Server/Skills/rn-lib-update-doc/config.json +5 -5
- package/vendor/Server/Skills/rn-lib-update-doc/references/changelog-template.md +35 -35
- package/vendor/Server/Skills/rn-lib-update-doc/references/format-diff.md +137 -137
- package/vendor/Server/Skills/rn-lib-update-doc/references/link-registry.json +33 -33
- package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2-overview.md +40 -40
- package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2.md +429 -429
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/detect-readme-format.cjs +105 -105
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/find-usage-doc.cjs +90 -90
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/link-verify.cjs +213 -213
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-changelog.cjs +73 -73
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-package-meta.cjs +258 -258
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-readme-links.cjs +341 -341
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/resolve-usage-docs.cjs +67 -67
- package/vendor/Server/Skills/rn-library-consistency-check/SKILL.md +299 -299
- package/vendor/Server/Skills/rn-library-consistency-check/assets/REPORT_TEMPLATE.md +293 -293
- package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_COMPONENT_CHECK_GUIDE.md +857 -857
- package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_TURBOMODULE_CHECK_GUIDE.md +856 -856
- package/vendor/Server/Skills/rn-library-consistency-check/references/CHECKLIST.md +157 -157
- package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_COMPONENT_CHECK_GUIDE.md +801 -801
- package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_TURBOMODULE_CHECK_GUIDE.md +683 -683
- package/vendor/Server/Skills/rn-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +233 -233
- package/vendor/Server/Skills/rn-library-consistency-check/references/NATIVE_COMPONENT_IDENTIFICATION.md +1373 -1373
- package/vendor/Server/Skills/rn-library-consistency-check/references/SCORING_STANDARD.md +220 -220
- package/vendor/Server/Skills/rn-library-consistency-check/scripts/validate-skill.py +245 -245
- package/vendor/Server/Skills/rn-library-documentation-quality-check/SKILL.md +263 -263
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL.md +414 -414
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL_en.md +413 -413
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +311 -311
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CHECK_CHECKLIST.md +355 -355
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CROSS_VALIDATION_GUIDE.md +115 -115
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/DOC_COLLECTION_GUIDE.md +139 -139
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/EXAMPLES.md +586 -586
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/GOTCHAS.md +57 -57
- package/vendor/Server/Skills/rn-library-documentation-quality-check/scripts/validate-skill.py +259 -259
- package/vendor/Server/Skills/rn-library-interface-analyzer/SKILL.md +301 -301
- package/vendor/Server/Skills/rn-library-interface-analyzer/assets/REPORT_TEMPLATE.md +148 -148
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/DOC_ANNOTATION_GUIDE.md +118 -118
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/SCRIPT_USAGE_GUIDE.md +168 -168
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/URL_PARSING_RULES.md +176 -176
- package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +274 -274
- package/vendor/Server/Skills/rn-library-issue-generalizer/assets/case-template.md +45 -45
- package/vendor/Server/Skills/rn-library-issue-generalizer/assets/rule-template.md +25 -25
- package/vendor/Server/Skills/rn-library-issue-generalizer/references/PR_FETCH_GUIDE.md +61 -61
- package/vendor/Server/Skills/rn-library-issue-generalizer/references/RULE_TEMPLATE_GUIDE.md +165 -165
- package/vendor/Server/Skills/rn-library-issue-generalizer/scripts/fetch-pr-info.py +239 -239
- package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +263 -263
- package/vendor/Server/Skills/rn-library-known-issue-check/assets/REPORT_TEMPLATE.md +96 -96
- package/vendor/Server/Skills/rn-library-known-issue-check/references/COMPLIANCE_CHECKLIST.md +57 -57
- package/vendor/Server/Skills/rn-library-known-issue-check/references/INSPECTION_GUIDE.md +99 -99
- package/vendor/Server/Skills/rn-library-known-issue-check/references/PRESCREEN_GUIDE.md +64 -64
- package/vendor/Server/Skills/rn-library-known-issue-check/references/SOURCE_PREP_GUIDE.md +74 -74
- package/vendor/Server/Skills/rn-library-known-issue-check/scripts/validate-known-issue-report.py +164 -164
- package/vendor/Server/Skills/rn-library-memory-leak-check/SKILL.md +356 -339
- package/vendor/Server/Skills/rn-library-memory-leak-check/assets/REPORT_TEMPLATE.md +85 -83
- package/vendor/Server/Skills/rn-library-memory-leak-check/references/CHECKLIST.md +905 -859
- package/vendor/Server/Skills/rn-library-memory-leak-check/references/HARMONY_CODE_PATTERNS.md +124 -122
- package/vendor/Server/Skills/rn-library-memory-leak-check/scripts/scan_harmony_code.py +287 -279
- package/vendor/Server/Skills/rn-library-stability-check/SKILL.md +273 -273
- package/vendor/Server/Skills/rn-library-stability-check/assets/REPORT_TEMPLATE.md +129 -129
- package/vendor/Server/Skills/rn-library-stability-check/references/CHECKLIST.md +581 -581
- package/vendor/Server/Skills/rn-library-stability-check/references/SCORING_RULES.md +138 -138
- package/vendor/Server/Skills/rn-library-stability-check/scripts/scan-harmony-code.py +168 -168
- package/vendor/Server/Skills/rn-library-test-coverage-check/SKILL.md +526 -506
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/CHECKLIST.md +321 -280
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/DEMO_LOCATION.md +221 -221
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/REPORT_TEMPLATE.md +220 -209
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/SCRIPT_USAGE.md +325 -325
- package/vendor/Server/Skills/rn-ohos-xts-generator/SKILL.md +279 -279
- package/vendor/Server/Skills/rn-ohos-xts-generator/assets/TEST_FILE_TEMPLATES.md +404 -404
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_TROUBLESHOOTING.md +185 -185
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_WORKFLOW.md +265 -265
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/COMMON_CONFIG.md +189 -189
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/CONFIG_GUIDE.md +203 -203
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/COVERAGE_ANALYZER.md +147 -147
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/DESIGN_DOC_GUIDE.md +148 -148
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_FLOW.md +165 -165
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_STRATEGY.md +195 -195
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/HARMONY_SCANNER.md +249 -249
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/JEST_FRAMEWORK.md +235 -235
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/PLATFORM_COMPAT.md +161 -161
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/RN_TEST_PATTERNS.md +413 -413
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/SOURCE_PARSER.md +235 -235
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/TEST_CONVENTIONS.md +245 -245
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/TROUBLESHOOTING.md +282 -282
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/USAGE_GUIDE.md +251 -251
- package/vendor/Server/Skills/rnoh-cptf-api-test/SKILL.md +469 -469
- package/vendor/Server/Skills/rnoh-cptf-api-test/assets/CPTF_TEST_TEMPLATE.md +310 -310
- package/vendor/Server/Skills/rnoh-cptf-api-test/assets/DEMO_PAGE_TEMPLATE.md +303 -303
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/CPTF_TEST_GENERATION_GUIDE.md +421 -421
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/DEMO_GENERATION_GUIDE.md +358 -358
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/QUALITY_CHECKLIST.md +114 -114
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/SPEC_PARSING_GUIDE.md +274 -274
- package/vendor/Server/Skills/rnoh-cptf-api-test/scripts/validate-rnoh-cptf-api-test.sh +185 -185
- package/vendor/Server/Skills/rnoh-cptf-demo-test/SKILL.md +191 -191
- package/vendor/Server/Skills/rnoh-cptf-demo-test/assets/TEST_TEMPLATE.md +216 -216
- package/vendor/Server/Skills/rnoh-cptf-demo-test/references/CPTF_SETUP.md +107 -107
- package/vendor/Server/Skills/rnoh-cptf-demo-test/references/DEMO_ANALYSIS.md +120 -120
- package/vendor/Server/Skills/rnoh-cptf-demo-test/scripts/validate-rnoh-cptf-test.sh +108 -108
- package/vendor/Server/Skills/rnoh-cptf-test-run/SKILL.md +237 -237
- package/vendor/Server/Skills/rnoh-cptf-test-run/references/DEBUG_PLAYBOOK.md +261 -261
- package/vendor/Server/Skills/rnoh-cptf-test-run/scripts/validate-run-env.sh +112 -112
- package/vendor/Server/Skills/rnoh-lib-code-adapt/SKILL.md +497 -497
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/adapt-checklist.md +270 -270
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/api-migration-checklist.md +164 -164
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/common-compile-errors.md +556 -556
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/deveco-cli-tools.md +459 -459
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/edge-cases.md +65 -65
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/har-tgz-build.md +512 -512
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/version-compat-matrix.md +132 -132
- package/vendor/Server/Skills/rnoh-lib-code-adapt/scripts/auto-integrate.sh +424 -424
- package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +137 -137
- package/vendor/Server/Skills/rnoh-lib-demo-create/SKILL.md +306 -306
- package/vendor/Server/Skills/rnoh-lib-demo-create/assets/DEMO_PAGE_TEMPLATE.md +485 -485
- package/vendor/Server/Skills/rnoh-lib-demo-create/assets/INDEX_TEMPLATE.md +172 -172
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/DEMO_CODE_RULES.md +422 -422
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/QUALITY_CHECKLIST.md +95 -95
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/SOURCE_EXPLORATION.md +169 -169
- package/vendor/Server/Skills/rnoh-lib-demo-doc/SKILL.md +554 -554
- package/vendor/Server/Skills/rnoh-lib-demo-doc/assets/DEMO_DOC_TEMPLATE.md +106 -106
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/COVERAGE_INCREMENTAL.md +216 -216
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/DEMO_DESIGN_RULES.md +413 -413
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/OUTPUT_FORMAT.md +269 -269
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/QUALITY_CHECKLIST.md +98 -98
- package/vendor/Server/Skills/rnoh-lib-func-test/SKILL.md +337 -337
- package/vendor/Server/Skills/rnoh-lib-rnt-search/SKILL.md +722 -722
- package/vendor/Server/Skills/rnoh-lib-rnt-search/assets/PORT_REPORT_TEMPLATE.md +219 -219
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/DEMO_PORTING_GUIDE.md +345 -345
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/PORT_VERIFICATION.md +269 -269
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/RNT_PROJECT_STRUCTURE.md +173 -173
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/analyze-demo-deps.cjs +267 -267
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/detect-rnoh-version.cjs +174 -174
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/find-rnt-demo.sh +111 -111
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/port-demo.sh +186 -186
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/read-lib-meta.cjs +150 -150
- package/vendor/Server/Skills/rnoh-lib-test-align/SKILL.md +296 -296
- package/vendor/Server/Skills/rnoh-lib-test-align/references/jest-troubleshooting.md +371 -371
- package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +212 -212
- package/vendor/Server/Skills/rnoh-lib-test-align/references/runtime-troubleshooting.md +345 -345
- package/vendor/Server/Skills/rnoh-lib-test-align/references/unit-test-template.md +223 -223
- package/vendor/Server/Skills/rnoh-lib-test-align/scripts/port-test-demo.sh +1053 -1053
- package/vendor/Server/Skills/rnoh-lib-version-diff/SKILL.md +295 -255
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_ADAPTATION.md +82 -77
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_API_LOOKUP.md +224 -224
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/script-reference.md +175 -174
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/detect-upstream-version.js +967 -967
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/generate-diff.js +1798 -1798
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/package.json +5 -5
- package/vendor/Server/Skills/rnoh-lib-version-diff/templates/report-template.md +180 -137
- package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +145 -145
- package/vendor/Server/Skills/skill-generator/SKILL.md +365 -365
- package/vendor/Server/Skills/skill-generator/assets/SKILL_TEMPLATE.md +183 -183
- package/vendor/Server/Skills/skill-generator/references/COMPLIANCE_CHECKLIST.md +82 -82
- package/vendor/Server/Skills/skill-generator/references/KB_QUERY_GUIDE.md +110 -110
- package/vendor/Server/Skills/skill-generator/references/MCP_TOOL_DISCOVERY.md +122 -122
- package/vendor/Server/Skills/skill-generator/references/SKILL_SPEC.md +121 -121
- package/vendor/Server/Skills/skill-generator/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/task-analysis/SKILL.md +234 -234
- package/vendor/Server/Skills/task-analysis/assets/OUTPUT_EXAMPLES.md +160 -160
- package/vendor/Server/Skills/task-analysis/references/ACCEPTANCE_CRITERIA_GUIDE.md +190 -190
- package/vendor/Server/Skills/task-analysis/references/ANALYSIS_SCHEMA.md +224 -224
- package/vendor/Server/Skills/task-analysis/references/INTEGRATION_CONTEXT.md +162 -162
- package/vendor/Server/Skills/task-analysis/references/LESSONS_LEARNED.md +55 -55
- package/vendor/Server/Skills/task-analysis/references/REPLAN_CONTEXT_GUIDE.md +126 -126
- package/vendor/Server/Skills/task-analysis/scripts/validate-analysis-output.sh +256 -256
- package/vendor/Server/Skills/task-decomposition/SKILL.md +528 -528
- package/vendor/Server/Skills/task-decomposition/assets/OUTPUT_EXAMPLES.md +451 -451
- package/vendor/Server/Skills/task-decomposition/references/ACCEPTANCE_CRITERIA_GUIDE.md +179 -179
- package/vendor/Server/Skills/task-decomposition/references/DEPENDENCY_GRAPH_GUIDE.md +152 -152
- package/vendor/Server/Skills/task-decomposition/references/INTEGRATION_CONTEXT.md +175 -175
- package/vendor/Server/Skills/task-decomposition/references/REPLAN_CONTEXT_GUIDE.md +195 -195
- package/vendor/Server/Skills/task-decomposition/references/TASK_DECOMPOSITION_TEMPLATES.md +226 -226
- package/vendor/Server/Skills/task-decomposition/scripts/list-available-skills.cjs +263 -263
- package/vendor/Server/Skills/task-decomposition/scripts/skill-blacklist.json +29 -29
- package/vendor/Server/Skills/task-decomposition/scripts/validate-decomposition-output.cjs +376 -376
- package/vendor/Server/Skills/task-decomposition/scripts/validate-param-consistency.cjs +259 -259
- package/vendor/Server/Skills/task-decomposition/scripts/validate-specified-skills.cjs +216 -216
- package/vendor/Server/Skills/task-decomposition/scripts/validate-step-graph.cjs +424 -424
- package/vendor/Server/Skills/tpc-cpp-check/SKILL.md +427 -427
- package/vendor/Server/Skills/tpc-cpp-check/references/CHECKLIST.md +214 -214
- package/vendor/Server/Skills/tpc-cpp-check/references/HPKBUILD_GUIDE.md +235 -235
- package/vendor/Server/Skills/tpc-cpp-check/references/REPORT_TEMPLATE.md +250 -250
- package/vendor/Server/TestCheck/TestCheck.js +775 -775
- package/vendor/Server/TestCheck/index.js +11 -11
- package/vendor/Server/library/McpToolGuide.js +280 -280
- package/vendor/Server/library/StatsService.js +126 -126
- package/vendor/Server/library/cstTime.js +16 -16
- package/vendor/VERSION +3 -3
- package/workflows/pr-check.mjs +105 -105
- package/workflows/pr-push.mjs +192 -192
- package/vendor/Server/Agent/SkillSelector/types.js +0 -63
|
@@ -1,433 +1,433 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fuzzyMatcher.js — 模糊匹配工具(分词器 + BM25 + 召回编排)
|
|
3
|
-
*
|
|
4
|
-
* 合并自原 tokenizer.js / bm25.js / fuzzyMatcher.js 三文件,因三者强耦合
|
|
5
|
-
* (BM25 依赖分词器、编排依赖 BM25),且同属纯计算无外部 IO,合并为单文件
|
|
6
|
-
* 降低阅读跳转与维护成本。
|
|
7
|
-
*
|
|
8
|
-
* 职责:
|
|
9
|
-
* 1. 分词器:把文本切成小写 token 数组(英文停用词过滤 + 中文虚词切分)
|
|
10
|
-
* 2. BM25:纯 JS BM25 打分算法(k1=1.5, b=0.75),无 SQLite 依赖
|
|
11
|
-
* 3. 编排:根据 TaskAnalysis 在 SkillMeta[] 中召回 topK 候选(BM25 + legacy fallback)
|
|
12
|
-
*
|
|
13
|
-
* 设计原则:
|
|
14
|
-
* - 高内聚:分词 / BM25 / 编排集中于此
|
|
15
|
-
* - 低耦合:只依赖 JS 内置类型,不依赖 LLM/fs/Agent
|
|
16
|
-
* - 可替换:通过工厂函数 createFuzzyMatcher 暴露,便于注入真实算法
|
|
17
|
-
* - 纯函数:分词与 BM25 相同输入永远相同输出,无副作用,易单测
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
21
|
-
// 第 1 段:分词器(原 tokenizer.js)
|
|
22
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
23
|
-
|
|
24
|
-
import { isActionWord, isDomainWord, ACTION_WORD_WEIGHT } from '../utils/actionWords.js';
|
|
25
|
-
|
|
26
|
-
// ─── 英文停用词(参考 engine.py 的 _STOPWORDS)─────────────────────────────────
|
|
27
|
-
const STOPWORDS = new Set([
|
|
28
|
-
// engine.py 的停用词
|
|
29
|
-
'the', 'for', 'with', 'and', 'not', 'are', 'but', 'you', 'all',
|
|
30
|
-
'any', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'has',
|
|
31
|
-
'have', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old',
|
|
32
|
-
'see', 'them', 'then', 'this', 'will', 'your', 'from', 'that',
|
|
33
|
-
'make', 'error', 'warning', 'failed', 'file', 'line', 'into',
|
|
34
|
-
'such', 'than', 'were', 'when', 'what', 'been', 'some',
|
|
35
|
-
// 追加通用停用词
|
|
36
|
-
'of', 'to', 'in', 'is', 'it', 'on', 'or', 'an', 'as', 'by', 'at',
|
|
37
|
-
'be', 'no', 'so', 'if', 'we', 'they', 'there', 'here', 'about',
|
|
38
|
-
]);
|
|
39
|
-
|
|
40
|
-
// 最小 token 长度(参考 engine.py:长度 ≥ 2 的英文 token 才保留)
|
|
41
|
-
const MIN_TOKEN_LENGTH = 2;
|
|
42
|
-
|
|
43
|
-
// 中文单字虚词/停用词(用于切分黏连的中文 chunk)
|
|
44
|
-
// 如 "的鸿蒙化" → 切成 "鸿蒙化"(去掉虚词"的")
|
|
45
|
-
const CN_STOPCHARS = new Set([
|
|
46
|
-
'的', '了', '是', '在', '和', '与', '或', '及', '等', '把', '被',
|
|
47
|
-
'将', '对', '向', '为', '以', '于', '由', '从', '到', '给', '让',
|
|
48
|
-
'使', '这', '那', '其', '之', '而', '也', '都', '就', '还', '又',
|
|
49
|
-
'已', '能', '可', '会', '要', '应', '需', '该', '此', '个', '们',
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 分词:把文本切成小写 token 数组
|
|
54
|
-
*
|
|
55
|
-
* 策略:
|
|
56
|
-
* 1. 英文:提取 [a-zA-Z_][a-zA-Z0-9_]{1,}(长度≥2),去停用词
|
|
57
|
-
* 2. 中文:按非字母数字字符分隔,保留长度≥2 的中文片段(如"鸿蒙"、"移植")
|
|
58
|
-
* 3. 标点/空格作为分隔符
|
|
59
|
-
*
|
|
60
|
-
* @param {string} text - 原始文本
|
|
61
|
-
* @param {object} [opts]
|
|
62
|
-
* @param {boolean} [opts.removeStopwords=true] - 是否移除英文停用词
|
|
63
|
-
* @returns {string[]} token 数组(小写)
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* tokenize("分析 libcurl 的鸿蒙化") → ["libcurl", "鸿蒙化"]
|
|
67
|
-
* tokenize("C++ code check") → ["code", "check"]
|
|
68
|
-
*/
|
|
69
|
-
export function tokenize(text, opts = {}) {
|
|
70
|
-
if (!text) return [];
|
|
71
|
-
const removeStopwords = opts.removeStopwords ?? true;
|
|
72
|
-
|
|
73
|
-
const lower = String(text).toLowerCase();
|
|
74
|
-
|
|
75
|
-
// 1. 提取英文 token(字母/下划线/数字,≥2 字符)
|
|
76
|
-
const englishTokens = lower.match(/[a-z_][a-z0-9_]{1,}/g) || [];
|
|
77
|
-
|
|
78
|
-
// 2. 提取中文 token(连续的中文字符,≥2 字符,按词组)
|
|
79
|
-
// 先提取连续中文 chunk,再用单字虚词进一步切分
|
|
80
|
-
// 例:"的鸿蒙化" → 去掉虚词"的" → "鸿蒙化"
|
|
81
|
-
const rawChineseChunks = lower.match(/[\u4e00-\u9fa5]+/g) || [];
|
|
82
|
-
const chineseChunks = [];
|
|
83
|
-
for (const chunk of rawChineseChunks) {
|
|
84
|
-
// 用单字虚词作为分隔,把黏连的 chunk 拆成实词
|
|
85
|
-
// 保留长度≥2 的片段(单字虚词本身丢弃)
|
|
86
|
-
const parts = chunk.split('').reduce((acc, ch) => {
|
|
87
|
-
if (CN_STOPCHARS.has(ch)) {
|
|
88
|
-
// 虚词作为分隔符,开始新片段
|
|
89
|
-
acc.push('');
|
|
90
|
-
} else {
|
|
91
|
-
if (acc.length === 0) acc.push('');
|
|
92
|
-
acc[acc.length - 1] += ch;
|
|
93
|
-
}
|
|
94
|
-
return acc;
|
|
95
|
-
}, []);
|
|
96
|
-
for (const p of parts) {
|
|
97
|
-
if (p && p.length >= MIN_TOKEN_LENGTH) {
|
|
98
|
-
chineseChunks.push(p);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 3. 合并,去停用词
|
|
104
|
-
const tokens = [];
|
|
105
|
-
for (const t of englishTokens) {
|
|
106
|
-
if (t.length < MIN_TOKEN_LENGTH) continue;
|
|
107
|
-
if (removeStopwords && STOPWORDS.has(t)) continue;
|
|
108
|
-
tokens.push(t);
|
|
109
|
-
}
|
|
110
|
-
for (const c of chineseChunks) {
|
|
111
|
-
if (c.length < MIN_TOKEN_LENGTH) continue;
|
|
112
|
-
tokens.push(c);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return tokens;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 规整字符串/数组为小写字符串数组(用于 keywords/techStack 等 LLM 输出)
|
|
120
|
-
* @param {string|Array<string>|null|undefined} v
|
|
121
|
-
* @returns {string[]}
|
|
122
|
-
*/
|
|
123
|
-
export function normalizeList(v) {
|
|
124
|
-
if (!v) return [];
|
|
125
|
-
if (Array.isArray(v)) {
|
|
126
|
-
return v.map((x) => String(x).toLowerCase().trim()).filter(Boolean);
|
|
127
|
-
}
|
|
128
|
-
return String(v)
|
|
129
|
-
.toLowerCase()
|
|
130
|
-
.split(/[\s,,、]+/)
|
|
131
|
-
.filter(Boolean);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
135
|
-
// 第 2 段:BM25(原 bm25.js)
|
|
136
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
137
|
-
|
|
138
|
-
// 默认 BM25 参数
|
|
139
|
-
const DEFAULT_K1 = 1.5;
|
|
140
|
-
const DEFAULT_B = 0.75;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* BM25 打分器(对一批文档预先建立索引,可对多次查询复用)
|
|
144
|
-
*
|
|
145
|
-
* 用法:
|
|
146
|
-
* const bm25 = new BM25(docs); // docs: string[] | string[][]
|
|
147
|
-
* const scores = bm25.scores(query); // query: string | string[]
|
|
148
|
-
*
|
|
149
|
-
* BM25 公式:
|
|
150
|
-
* score(q, d) = Σ_t∈q idf(t) · [ tf(t,d) · (k1+1) ] / [ tf(t,d) + k1·(1 − b + b·|d|/avgdl) ]
|
|
151
|
-
* idf(t) = ln( (N − df(t) + 0.5) / (df(t) + 0.5) + 1 ) (+1 平滑,保证非负)
|
|
152
|
-
*
|
|
153
|
-
* 参数(参考标准实现):
|
|
154
|
-
* k1 = 1.5 — 词频饱和控制
|
|
155
|
-
* b = 0.75 — 文档长度归一化强度
|
|
156
|
-
*/
|
|
157
|
-
export class BM25 {
|
|
158
|
-
/**
|
|
159
|
-
* @param {Array<string|Array<string>>} documents - 文档数组,每项是文本(会自动分词)或 token 数组
|
|
160
|
-
* @param {object} [opts]
|
|
161
|
-
* @param {number} [opts.k1=1.5]
|
|
162
|
-
* @param {number} [opts.b=0.75]
|
|
163
|
-
*/
|
|
164
|
-
constructor(documents, opts = {}) {
|
|
165
|
-
this.k1 = opts.k1 ?? DEFAULT_K1;
|
|
166
|
-
this.b = opts.b ?? DEFAULT_B;
|
|
167
|
-
|
|
168
|
-
// 统一转成 token 数组
|
|
169
|
-
this.docs = (documents || []).map((d) =>
|
|
170
|
-
Array.isArray(d) ? d : tokenize(d)
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
this.N = this.docs.length;
|
|
174
|
-
this.avgdl = this._avgDocLength();
|
|
175
|
-
|
|
176
|
-
// df: 每个 term 出现在多少篇文档中
|
|
177
|
-
this.df = new Map();
|
|
178
|
-
for (const tokens of this.docs) {
|
|
179
|
-
const seen = new Set(tokens);
|
|
180
|
-
for (const t of seen) {
|
|
181
|
-
this.df.set(t, (this.df.get(t) || 0) + 1);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* 计算单个查询对所有文档的 BM25 分数
|
|
188
|
-
* @param {string|Array<string>} query - 查询文本或 token 数组
|
|
189
|
-
* @returns {number[]} 每个文档的分数,顺序与构造时 documents 一致
|
|
190
|
-
*/
|
|
191
|
-
scores(query) {
|
|
192
|
-
const qTokens = Array.isArray(query) ? query : tokenize(query);
|
|
193
|
-
if (this.N === 0 || qTokens.length === 0) {
|
|
194
|
-
return new Array(this.N).fill(0);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 预计算 query 中每个 term 的 idf
|
|
198
|
-
const idfMap = new Map();
|
|
199
|
-
for (const t of new Set(qTokens)) {
|
|
200
|
-
idfMap.set(t, this._idf(t));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return this.docs.map((docTokens) => {
|
|
204
|
-
let score = 0;
|
|
205
|
-
// doc term frequency
|
|
206
|
-
const tfMap = this._tf(docTokens);
|
|
207
|
-
const dl = docTokens.length;
|
|
208
|
-
|
|
209
|
-
for (const t of qTokens) {
|
|
210
|
-
const tf = tfMap.get(t);
|
|
211
|
-
if (!tf) continue;
|
|
212
|
-
const idf = idfMap.get(t) || 0;
|
|
213
|
-
const denom = tf + this.k1 * (1 - this.b + (this.b * dl) / (this.avgdl || 1));
|
|
214
|
-
score += (idf * (tf * (this.k1 + 1))) / (denom || 1);
|
|
215
|
-
}
|
|
216
|
-
return score;
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 计算单个查询对单个文档的 BM25 分数
|
|
222
|
-
* @param {string|Array<string>} query
|
|
223
|
-
* @param {number} docIndex - 文档索引
|
|
224
|
-
* @returns {number}
|
|
225
|
-
*/
|
|
226
|
-
score(query, docIndex) {
|
|
227
|
-
return this.scores(query)[docIndex] || 0;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ─── 内部工具 ──────────────────────────────────────────────────────────────
|
|
231
|
-
_avgDocLength() {
|
|
232
|
-
if (this.N === 0) return 0;
|
|
233
|
-
let total = 0;
|
|
234
|
-
for (const t of this.docs) total += t.length;
|
|
235
|
-
return total / this.N;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** 逆文档频率(带 +1 平滑,保证非负) */
|
|
239
|
-
_idf(term) {
|
|
240
|
-
const df = this.df.get(term) || 0;
|
|
241
|
-
return Math.log((this.N - df + 0.5) / (df + 0.5) + 1);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** 文档词频 */
|
|
245
|
-
_tf(tokens) {
|
|
246
|
-
const m = new Map();
|
|
247
|
-
for (const t of tokens) {
|
|
248
|
-
m.set(t, (m.get(t) || 0) + 1);
|
|
249
|
-
}
|
|
250
|
-
return m;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* 一次性函数:对 documents 用 query 计算 BM25 分数(内部会建索引)
|
|
256
|
-
* @param {string|Array<string>} query
|
|
257
|
-
* @param {Array<string|Array<string>>} documents
|
|
258
|
-
* @param {object} [opts]
|
|
259
|
-
* @returns {number[]}
|
|
260
|
-
*/
|
|
261
|
-
export function bm25Scores(query, documents, opts = {}) {
|
|
262
|
-
return new BM25(documents, opts).scores(query);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
266
|
-
// 第 3 段:模糊匹配编排(原 fuzzyMatcher.js)
|
|
267
|
-
// ════════════════════════════════════════════════════════════════════════
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* 默认模糊匹配器
|
|
271
|
-
*
|
|
272
|
-
* 匹配策略(双通道 + 多路召回融合):
|
|
273
|
-
* - 路 A(动作通道):仅用 keywords + intent 中的「动作类 token」做 BM25 打分,
|
|
274
|
-
* 动作词加权只对真动作词生效(避免领域词混入动作词表稀释信号)。
|
|
275
|
-
* - 路 B(领域通道):用 keywords 中的「领域/产出物特征词」+ whenToUse 分词
|
|
276
|
-
* 做 BM25 打分,直接匹配文档侧已索引的 useWhen 与 searchableText 中的领域词。
|
|
277
|
-
* - 路 C(原文兜底路):用原始任务描述 task 全文分词做 BM25 打分,覆盖
|
|
278
|
-
* keywords 提取偏差导致的语义漂移。
|
|
279
|
-
* - 融合:score = wA·bm25A + wB·bm25B + wC·bm25C(归一化后加权),
|
|
280
|
-
* name 精确匹配 bonus 保留在所有路。
|
|
281
|
-
* - legacy fallback:融合后全零分时回退到子串匹配,保证不返回空。
|
|
282
|
-
*
|
|
283
|
-
* @param {object} analysis - Reason #1 产出的任务拆解
|
|
284
|
-
* @param {Array} skills - 候选 SkillMeta[]
|
|
285
|
-
* @param {object} [options]
|
|
286
|
-
* @param {number} [options.topK=5] - 召回数量
|
|
287
|
-
* @param {string} [options.task] - 原始任务描述(原文兜底路用)
|
|
288
|
-
* @param {object} [options.weights] - 融合权重 { action, domain, raw }
|
|
289
|
-
* @returns {Promise<Array>} 排序后的候选 SKILL(含 score / routes),长度 ≤ topK
|
|
290
|
-
*/
|
|
291
|
-
export async function fuzzyMatch(analysis, skills, options = {}) {
|
|
292
|
-
const topK = options.topK ?? 5;
|
|
293
|
-
if (!skills || skills.length === 0) return [];
|
|
294
|
-
|
|
295
|
-
const weights = {
|
|
296
|
-
action: options.weights?.action ?? 0.4,
|
|
297
|
-
domain: options.weights?.domain ?? 0.4,
|
|
298
|
-
raw: options.weights?.raw ?? 0.2,
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// ─── 准备数据 ──────────────────────────────────────────────────────────────
|
|
302
|
-
const keywords = normalizeList(analysis?.keywords);
|
|
303
|
-
const intentTokens = tokenize(analysis?.intent || '');
|
|
304
|
-
const whenToUseTokens = tokenize(analysis?.whenToUse || '');
|
|
305
|
-
const taskTokens = tokenize(options?.task || '');
|
|
306
|
-
|
|
307
|
-
// 把每个 SKILL 的 searchableText 分词,作为 BM25 的文档集
|
|
308
|
-
const docs = skills.map((s) => tokenize(s.searchableText || ''));
|
|
309
|
-
|
|
310
|
-
// ─── 路 A:动作通道 ──────────────────────────────────────────────────────────
|
|
311
|
-
// 仅保留动作类 token(keywords 中动作为主的词 + intent 中动作词),动作词加权。
|
|
312
|
-
const actionKeywords = keywords.filter((kw) => isActionWord(kw));
|
|
313
|
-
const actionIntentTokens = intentTokens.filter((t) => isActionWord(t));
|
|
314
|
-
const queryA = [];
|
|
315
|
-
const rawQueryATokens = actionKeywords.length + actionIntentTokens.length;
|
|
316
|
-
for (const kw of actionKeywords) {
|
|
317
|
-
queryA.push(kw);
|
|
318
|
-
for (let i = 0; i < ACTION_WORD_WEIGHT - 1; i++) queryA.push(kw);
|
|
319
|
-
}
|
|
320
|
-
for (const t of actionIntentTokens) {
|
|
321
|
-
queryA.push(t);
|
|
322
|
-
for (let i = 0; i < ACTION_WORD_WEIGHT - 1; i++) queryA.push(t);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ─── 路 B:领域通道 ──────────────────────────────────────────────────────────
|
|
326
|
-
// 领域/产出物特征词(keywords 中领域词 + whenToUse 分词)做查询,匹配文档侧领域词/useWhen。
|
|
327
|
-
const domainKeywords = keywords.filter((kw) => isDomainWord(kw));
|
|
328
|
-
const queryB = [...domainKeywords, ...whenToUseTokens];
|
|
329
|
-
|
|
330
|
-
// ─── 路 C:原文兜底通道 ──────────────────────────────────────────────────────
|
|
331
|
-
const queryC = taskTokens;
|
|
332
|
-
|
|
333
|
-
const bm25 = new BM25(docs);
|
|
334
|
-
const rawA = bm25.scores(queryA);
|
|
335
|
-
const rawB = bm25.scores(queryB);
|
|
336
|
-
const rawC = bm25.scores(queryC);
|
|
337
|
-
|
|
338
|
-
// 分数归一化:raw / max(3, 单路实际参与 token 数)
|
|
339
|
-
// 分母用各路的原始 token 数(非展开后),避免动作词展开/多路叠加导致分母虚高
|
|
340
|
-
const countA = Math.max(3, rawQueryATokens);
|
|
341
|
-
const countB = Math.max(3, queryB.length);
|
|
342
|
-
const countC = Math.max(3, queryC.length);
|
|
343
|
-
const normA = rawA.map((s) => s / countA);
|
|
344
|
-
const normB = rawB.map((s) => s / countB);
|
|
345
|
-
const normC = rawC.map((s) => s / countC);
|
|
346
|
-
|
|
347
|
-
// 融合
|
|
348
|
-
const fused = normA.map((_, i) => weights.action * normA[i] + weights.domain * normB[i] + weights.raw * normC[i]);
|
|
349
|
-
|
|
350
|
-
// 检查融合后是否全零(无任何 token 重叠)→ 回退到 legacy 子串匹配
|
|
351
|
-
const hasAnyScore = fused.some((s) => s > 0);
|
|
352
|
-
let candidates;
|
|
353
|
-
if (!hasAnyScore) {
|
|
354
|
-
candidates = _legacySubstringMatch(analysis, skills).slice(0, topK);
|
|
355
|
-
} else {
|
|
356
|
-
candidates = skills
|
|
357
|
-
.map((skill, i) => {
|
|
358
|
-
let score = fused[i] || 0;
|
|
359
|
-
// name 精确匹配 bonus:query keywords 中任一词与 skill.name 精确匹配时 ×1.15
|
|
360
|
-
const skillName = (skill.name || '').toLowerCase();
|
|
361
|
-
const hasNameMatch = keywords.some((kw) => kw && skillName === kw);
|
|
362
|
-
if (hasNameMatch && score > 0) {
|
|
363
|
-
score = score * 1.15;
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
skill,
|
|
367
|
-
score,
|
|
368
|
-
// 每路归一化分数留痕,便于定位偶发未命中
|
|
369
|
-
routes: { action: normA[i] || 0, domain: normB[i] || 0, raw: normC[i] || 0 },
|
|
370
|
-
};
|
|
371
|
-
})
|
|
372
|
-
.sort((a, b) => b.score - a.score)
|
|
373
|
-
.slice(0, topK);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return candidates;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// ─── Legacy fallback:子串匹配(BM25 全零分时启用)──────────────────────────────
|
|
380
|
-
/**
|
|
381
|
-
* 旧版子串匹配逻辑(作为 fallback)
|
|
382
|
-
* 保留原 fuzzyMatch 桩实现的策略,保证 BM25 无重叠时不返回空
|
|
383
|
-
*/
|
|
384
|
-
function _legacySubstringMatch(analysis, skills) {
|
|
385
|
-
const keywords = normalizeList(analysis?.keywords);
|
|
386
|
-
const techStack = normalizeList(analysis?.techStack);
|
|
387
|
-
const intentWords = _legacyTokenize(analysis?.intent);
|
|
388
|
-
|
|
389
|
-
const scored = skills.map((skill) => {
|
|
390
|
-
const text = skill.searchableText || '';
|
|
391
|
-
let score = 0;
|
|
392
|
-
|
|
393
|
-
for (const t of techStack) {
|
|
394
|
-
if (t && text.includes(t)) score += 10;
|
|
395
|
-
}
|
|
396
|
-
for (const kw of keywords) {
|
|
397
|
-
if (kw && text.includes(kw)) score += 6;
|
|
398
|
-
}
|
|
399
|
-
for (const w of intentWords) {
|
|
400
|
-
if (w && w.length > 1 && text.includes(w)) score += 2;
|
|
401
|
-
}
|
|
402
|
-
const name = (skill.name || '').toLowerCase();
|
|
403
|
-
for (const kw of keywords) {
|
|
404
|
-
if (kw && name === kw) score += 4;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return { skill, score, reasons: ['legacy fallback'] };
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
scored.sort((a, b) => b.score - a.score);
|
|
411
|
-
return scored;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** legacy 分词(保留原逻辑,不做停用词过滤) */
|
|
415
|
-
function _legacyTokenize(s) {
|
|
416
|
-
if (!s) return [];
|
|
417
|
-
return String(s)
|
|
418
|
-
.toLowerCase()
|
|
419
|
-
.split(/[\s,,。、;;::.()()\-_/]+/)
|
|
420
|
-
.filter((w) => w.length > 0);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// ─── 工厂:创建可替换的模糊匹配器 ──────────────────────────────────────────────
|
|
424
|
-
/**
|
|
425
|
-
* @param {(analysis, skills, options) => Promise<Array>} impl
|
|
426
|
-
* @returns {(analysis, skills, options) => Promise<Array>}
|
|
427
|
-
*/
|
|
428
|
-
export function createFuzzyMatcher(impl) {
|
|
429
|
-
if (typeof impl === 'function') return impl;
|
|
430
|
-
return fuzzyMatch;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
export default { tokenize, normalizeList, BM25, bm25Scores, fuzzyMatch, createFuzzyMatcher };
|
|
1
|
+
/**
|
|
2
|
+
* fuzzyMatcher.js — 模糊匹配工具(分词器 + BM25 + 召回编排)
|
|
3
|
+
*
|
|
4
|
+
* 合并自原 tokenizer.js / bm25.js / fuzzyMatcher.js 三文件,因三者强耦合
|
|
5
|
+
* (BM25 依赖分词器、编排依赖 BM25),且同属纯计算无外部 IO,合并为单文件
|
|
6
|
+
* 降低阅读跳转与维护成本。
|
|
7
|
+
*
|
|
8
|
+
* 职责:
|
|
9
|
+
* 1. 分词器:把文本切成小写 token 数组(英文停用词过滤 + 中文虚词切分)
|
|
10
|
+
* 2. BM25:纯 JS BM25 打分算法(k1=1.5, b=0.75),无 SQLite 依赖
|
|
11
|
+
* 3. 编排:根据 TaskAnalysis 在 SkillMeta[] 中召回 topK 候选(BM25 + legacy fallback)
|
|
12
|
+
*
|
|
13
|
+
* 设计原则:
|
|
14
|
+
* - 高内聚:分词 / BM25 / 编排集中于此
|
|
15
|
+
* - 低耦合:只依赖 JS 内置类型,不依赖 LLM/fs/Agent
|
|
16
|
+
* - 可替换:通过工厂函数 createFuzzyMatcher 暴露,便于注入真实算法
|
|
17
|
+
* - 纯函数:分词与 BM25 相同输入永远相同输出,无副作用,易单测
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
21
|
+
// 第 1 段:分词器(原 tokenizer.js)
|
|
22
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
import { isActionWord, isDomainWord, ACTION_WORD_WEIGHT } from '../utils/actionWords.js';
|
|
25
|
+
|
|
26
|
+
// ─── 英文停用词(参考 engine.py 的 _STOPWORDS)─────────────────────────────────
|
|
27
|
+
const STOPWORDS = new Set([
|
|
28
|
+
// engine.py 的停用词
|
|
29
|
+
'the', 'for', 'with', 'and', 'not', 'are', 'but', 'you', 'all',
|
|
30
|
+
'any', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'has',
|
|
31
|
+
'have', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old',
|
|
32
|
+
'see', 'them', 'then', 'this', 'will', 'your', 'from', 'that',
|
|
33
|
+
'make', 'error', 'warning', 'failed', 'file', 'line', 'into',
|
|
34
|
+
'such', 'than', 'were', 'when', 'what', 'been', 'some',
|
|
35
|
+
// 追加通用停用词
|
|
36
|
+
'of', 'to', 'in', 'is', 'it', 'on', 'or', 'an', 'as', 'by', 'at',
|
|
37
|
+
'be', 'no', 'so', 'if', 'we', 'they', 'there', 'here', 'about',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
// 最小 token 长度(参考 engine.py:长度 ≥ 2 的英文 token 才保留)
|
|
41
|
+
const MIN_TOKEN_LENGTH = 2;
|
|
42
|
+
|
|
43
|
+
// 中文单字虚词/停用词(用于切分黏连的中文 chunk)
|
|
44
|
+
// 如 "的鸿蒙化" → 切成 "鸿蒙化"(去掉虚词"的")
|
|
45
|
+
const CN_STOPCHARS = new Set([
|
|
46
|
+
'的', '了', '是', '在', '和', '与', '或', '及', '等', '把', '被',
|
|
47
|
+
'将', '对', '向', '为', '以', '于', '由', '从', '到', '给', '让',
|
|
48
|
+
'使', '这', '那', '其', '之', '而', '也', '都', '就', '还', '又',
|
|
49
|
+
'已', '能', '可', '会', '要', '应', '需', '该', '此', '个', '们',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 分词:把文本切成小写 token 数组
|
|
54
|
+
*
|
|
55
|
+
* 策略:
|
|
56
|
+
* 1. 英文:提取 [a-zA-Z_][a-zA-Z0-9_]{1,}(长度≥2),去停用词
|
|
57
|
+
* 2. 中文:按非字母数字字符分隔,保留长度≥2 的中文片段(如"鸿蒙"、"移植")
|
|
58
|
+
* 3. 标点/空格作为分隔符
|
|
59
|
+
*
|
|
60
|
+
* @param {string} text - 原始文本
|
|
61
|
+
* @param {object} [opts]
|
|
62
|
+
* @param {boolean} [opts.removeStopwords=true] - 是否移除英文停用词
|
|
63
|
+
* @returns {string[]} token 数组(小写)
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* tokenize("分析 libcurl 的鸿蒙化") → ["libcurl", "鸿蒙化"]
|
|
67
|
+
* tokenize("C++ code check") → ["code", "check"]
|
|
68
|
+
*/
|
|
69
|
+
export function tokenize(text, opts = {}) {
|
|
70
|
+
if (!text) return [];
|
|
71
|
+
const removeStopwords = opts.removeStopwords ?? true;
|
|
72
|
+
|
|
73
|
+
const lower = String(text).toLowerCase();
|
|
74
|
+
|
|
75
|
+
// 1. 提取英文 token(字母/下划线/数字,≥2 字符)
|
|
76
|
+
const englishTokens = lower.match(/[a-z_][a-z0-9_]{1,}/g) || [];
|
|
77
|
+
|
|
78
|
+
// 2. 提取中文 token(连续的中文字符,≥2 字符,按词组)
|
|
79
|
+
// 先提取连续中文 chunk,再用单字虚词进一步切分
|
|
80
|
+
// 例:"的鸿蒙化" → 去掉虚词"的" → "鸿蒙化"
|
|
81
|
+
const rawChineseChunks = lower.match(/[\u4e00-\u9fa5]+/g) || [];
|
|
82
|
+
const chineseChunks = [];
|
|
83
|
+
for (const chunk of rawChineseChunks) {
|
|
84
|
+
// 用单字虚词作为分隔,把黏连的 chunk 拆成实词
|
|
85
|
+
// 保留长度≥2 的片段(单字虚词本身丢弃)
|
|
86
|
+
const parts = chunk.split('').reduce((acc, ch) => {
|
|
87
|
+
if (CN_STOPCHARS.has(ch)) {
|
|
88
|
+
// 虚词作为分隔符,开始新片段
|
|
89
|
+
acc.push('');
|
|
90
|
+
} else {
|
|
91
|
+
if (acc.length === 0) acc.push('');
|
|
92
|
+
acc[acc.length - 1] += ch;
|
|
93
|
+
}
|
|
94
|
+
return acc;
|
|
95
|
+
}, []);
|
|
96
|
+
for (const p of parts) {
|
|
97
|
+
if (p && p.length >= MIN_TOKEN_LENGTH) {
|
|
98
|
+
chineseChunks.push(p);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. 合并,去停用词
|
|
104
|
+
const tokens = [];
|
|
105
|
+
for (const t of englishTokens) {
|
|
106
|
+
if (t.length < MIN_TOKEN_LENGTH) continue;
|
|
107
|
+
if (removeStopwords && STOPWORDS.has(t)) continue;
|
|
108
|
+
tokens.push(t);
|
|
109
|
+
}
|
|
110
|
+
for (const c of chineseChunks) {
|
|
111
|
+
if (c.length < MIN_TOKEN_LENGTH) continue;
|
|
112
|
+
tokens.push(c);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return tokens;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 规整字符串/数组为小写字符串数组(用于 keywords/techStack 等 LLM 输出)
|
|
120
|
+
* @param {string|Array<string>|null|undefined} v
|
|
121
|
+
* @returns {string[]}
|
|
122
|
+
*/
|
|
123
|
+
export function normalizeList(v) {
|
|
124
|
+
if (!v) return [];
|
|
125
|
+
if (Array.isArray(v)) {
|
|
126
|
+
return v.map((x) => String(x).toLowerCase().trim()).filter(Boolean);
|
|
127
|
+
}
|
|
128
|
+
return String(v)
|
|
129
|
+
.toLowerCase()
|
|
130
|
+
.split(/[\s,,、]+/)
|
|
131
|
+
.filter(Boolean);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
135
|
+
// 第 2 段:BM25(原 bm25.js)
|
|
136
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
137
|
+
|
|
138
|
+
// 默认 BM25 参数
|
|
139
|
+
const DEFAULT_K1 = 1.5;
|
|
140
|
+
const DEFAULT_B = 0.75;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* BM25 打分器(对一批文档预先建立索引,可对多次查询复用)
|
|
144
|
+
*
|
|
145
|
+
* 用法:
|
|
146
|
+
* const bm25 = new BM25(docs); // docs: string[] | string[][]
|
|
147
|
+
* const scores = bm25.scores(query); // query: string | string[]
|
|
148
|
+
*
|
|
149
|
+
* BM25 公式:
|
|
150
|
+
* score(q, d) = Σ_t∈q idf(t) · [ tf(t,d) · (k1+1) ] / [ tf(t,d) + k1·(1 − b + b·|d|/avgdl) ]
|
|
151
|
+
* idf(t) = ln( (N − df(t) + 0.5) / (df(t) + 0.5) + 1 ) (+1 平滑,保证非负)
|
|
152
|
+
*
|
|
153
|
+
* 参数(参考标准实现):
|
|
154
|
+
* k1 = 1.5 — 词频饱和控制
|
|
155
|
+
* b = 0.75 — 文档长度归一化强度
|
|
156
|
+
*/
|
|
157
|
+
export class BM25 {
|
|
158
|
+
/**
|
|
159
|
+
* @param {Array<string|Array<string>>} documents - 文档数组,每项是文本(会自动分词)或 token 数组
|
|
160
|
+
* @param {object} [opts]
|
|
161
|
+
* @param {number} [opts.k1=1.5]
|
|
162
|
+
* @param {number} [opts.b=0.75]
|
|
163
|
+
*/
|
|
164
|
+
constructor(documents, opts = {}) {
|
|
165
|
+
this.k1 = opts.k1 ?? DEFAULT_K1;
|
|
166
|
+
this.b = opts.b ?? DEFAULT_B;
|
|
167
|
+
|
|
168
|
+
// 统一转成 token 数组
|
|
169
|
+
this.docs = (documents || []).map((d) =>
|
|
170
|
+
Array.isArray(d) ? d : tokenize(d)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
this.N = this.docs.length;
|
|
174
|
+
this.avgdl = this._avgDocLength();
|
|
175
|
+
|
|
176
|
+
// df: 每个 term 出现在多少篇文档中
|
|
177
|
+
this.df = new Map();
|
|
178
|
+
for (const tokens of this.docs) {
|
|
179
|
+
const seen = new Set(tokens);
|
|
180
|
+
for (const t of seen) {
|
|
181
|
+
this.df.set(t, (this.df.get(t) || 0) + 1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 计算单个查询对所有文档的 BM25 分数
|
|
188
|
+
* @param {string|Array<string>} query - 查询文本或 token 数组
|
|
189
|
+
* @returns {number[]} 每个文档的分数,顺序与构造时 documents 一致
|
|
190
|
+
*/
|
|
191
|
+
scores(query) {
|
|
192
|
+
const qTokens = Array.isArray(query) ? query : tokenize(query);
|
|
193
|
+
if (this.N === 0 || qTokens.length === 0) {
|
|
194
|
+
return new Array(this.N).fill(0);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 预计算 query 中每个 term 的 idf
|
|
198
|
+
const idfMap = new Map();
|
|
199
|
+
for (const t of new Set(qTokens)) {
|
|
200
|
+
idfMap.set(t, this._idf(t));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return this.docs.map((docTokens) => {
|
|
204
|
+
let score = 0;
|
|
205
|
+
// doc term frequency
|
|
206
|
+
const tfMap = this._tf(docTokens);
|
|
207
|
+
const dl = docTokens.length;
|
|
208
|
+
|
|
209
|
+
for (const t of qTokens) {
|
|
210
|
+
const tf = tfMap.get(t);
|
|
211
|
+
if (!tf) continue;
|
|
212
|
+
const idf = idfMap.get(t) || 0;
|
|
213
|
+
const denom = tf + this.k1 * (1 - this.b + (this.b * dl) / (this.avgdl || 1));
|
|
214
|
+
score += (idf * (tf * (this.k1 + 1))) / (denom || 1);
|
|
215
|
+
}
|
|
216
|
+
return score;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 计算单个查询对单个文档的 BM25 分数
|
|
222
|
+
* @param {string|Array<string>} query
|
|
223
|
+
* @param {number} docIndex - 文档索引
|
|
224
|
+
* @returns {number}
|
|
225
|
+
*/
|
|
226
|
+
score(query, docIndex) {
|
|
227
|
+
return this.scores(query)[docIndex] || 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── 内部工具 ──────────────────────────────────────────────────────────────
|
|
231
|
+
_avgDocLength() {
|
|
232
|
+
if (this.N === 0) return 0;
|
|
233
|
+
let total = 0;
|
|
234
|
+
for (const t of this.docs) total += t.length;
|
|
235
|
+
return total / this.N;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** 逆文档频率(带 +1 平滑,保证非负) */
|
|
239
|
+
_idf(term) {
|
|
240
|
+
const df = this.df.get(term) || 0;
|
|
241
|
+
return Math.log((this.N - df + 0.5) / (df + 0.5) + 1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** 文档词频 */
|
|
245
|
+
_tf(tokens) {
|
|
246
|
+
const m = new Map();
|
|
247
|
+
for (const t of tokens) {
|
|
248
|
+
m.set(t, (m.get(t) || 0) + 1);
|
|
249
|
+
}
|
|
250
|
+
return m;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 一次性函数:对 documents 用 query 计算 BM25 分数(内部会建索引)
|
|
256
|
+
* @param {string|Array<string>} query
|
|
257
|
+
* @param {Array<string|Array<string>>} documents
|
|
258
|
+
* @param {object} [opts]
|
|
259
|
+
* @returns {number[]}
|
|
260
|
+
*/
|
|
261
|
+
export function bm25Scores(query, documents, opts = {}) {
|
|
262
|
+
return new BM25(documents, opts).scores(query);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
266
|
+
// 第 3 段:模糊匹配编排(原 fuzzyMatcher.js)
|
|
267
|
+
// ════════════════════════════════════════════════════════════════════════
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 默认模糊匹配器
|
|
271
|
+
*
|
|
272
|
+
* 匹配策略(双通道 + 多路召回融合):
|
|
273
|
+
* - 路 A(动作通道):仅用 keywords + intent 中的「动作类 token」做 BM25 打分,
|
|
274
|
+
* 动作词加权只对真动作词生效(避免领域词混入动作词表稀释信号)。
|
|
275
|
+
* - 路 B(领域通道):用 keywords 中的「领域/产出物特征词」+ whenToUse 分词
|
|
276
|
+
* 做 BM25 打分,直接匹配文档侧已索引的 useWhen 与 searchableText 中的领域词。
|
|
277
|
+
* - 路 C(原文兜底路):用原始任务描述 task 全文分词做 BM25 打分,覆盖
|
|
278
|
+
* keywords 提取偏差导致的语义漂移。
|
|
279
|
+
* - 融合:score = wA·bm25A + wB·bm25B + wC·bm25C(归一化后加权),
|
|
280
|
+
* name 精确匹配 bonus 保留在所有路。
|
|
281
|
+
* - legacy fallback:融合后全零分时回退到子串匹配,保证不返回空。
|
|
282
|
+
*
|
|
283
|
+
* @param {object} analysis - Reason #1 产出的任务拆解
|
|
284
|
+
* @param {Array} skills - 候选 SkillMeta[]
|
|
285
|
+
* @param {object} [options]
|
|
286
|
+
* @param {number} [options.topK=5] - 召回数量
|
|
287
|
+
* @param {string} [options.task] - 原始任务描述(原文兜底路用)
|
|
288
|
+
* @param {object} [options.weights] - 融合权重 { action, domain, raw }
|
|
289
|
+
* @returns {Promise<Array>} 排序后的候选 SKILL(含 score / routes),长度 ≤ topK
|
|
290
|
+
*/
|
|
291
|
+
export async function fuzzyMatch(analysis, skills, options = {}) {
|
|
292
|
+
const topK = options.topK ?? 5;
|
|
293
|
+
if (!skills || skills.length === 0) return [];
|
|
294
|
+
|
|
295
|
+
const weights = {
|
|
296
|
+
action: options.weights?.action ?? 0.4,
|
|
297
|
+
domain: options.weights?.domain ?? 0.4,
|
|
298
|
+
raw: options.weights?.raw ?? 0.2,
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// ─── 准备数据 ──────────────────────────────────────────────────────────────
|
|
302
|
+
const keywords = normalizeList(analysis?.keywords);
|
|
303
|
+
const intentTokens = tokenize(analysis?.intent || '');
|
|
304
|
+
const whenToUseTokens = tokenize(analysis?.whenToUse || '');
|
|
305
|
+
const taskTokens = tokenize(options?.task || '');
|
|
306
|
+
|
|
307
|
+
// 把每个 SKILL 的 searchableText 分词,作为 BM25 的文档集
|
|
308
|
+
const docs = skills.map((s) => tokenize(s.searchableText || ''));
|
|
309
|
+
|
|
310
|
+
// ─── 路 A:动作通道 ──────────────────────────────────────────────────────────
|
|
311
|
+
// 仅保留动作类 token(keywords 中动作为主的词 + intent 中动作词),动作词加权。
|
|
312
|
+
const actionKeywords = keywords.filter((kw) => isActionWord(kw));
|
|
313
|
+
const actionIntentTokens = intentTokens.filter((t) => isActionWord(t));
|
|
314
|
+
const queryA = [];
|
|
315
|
+
const rawQueryATokens = actionKeywords.length + actionIntentTokens.length;
|
|
316
|
+
for (const kw of actionKeywords) {
|
|
317
|
+
queryA.push(kw);
|
|
318
|
+
for (let i = 0; i < ACTION_WORD_WEIGHT - 1; i++) queryA.push(kw);
|
|
319
|
+
}
|
|
320
|
+
for (const t of actionIntentTokens) {
|
|
321
|
+
queryA.push(t);
|
|
322
|
+
for (let i = 0; i < ACTION_WORD_WEIGHT - 1; i++) queryA.push(t);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ─── 路 B:领域通道 ──────────────────────────────────────────────────────────
|
|
326
|
+
// 领域/产出物特征词(keywords 中领域词 + whenToUse 分词)做查询,匹配文档侧领域词/useWhen。
|
|
327
|
+
const domainKeywords = keywords.filter((kw) => isDomainWord(kw));
|
|
328
|
+
const queryB = [...domainKeywords, ...whenToUseTokens];
|
|
329
|
+
|
|
330
|
+
// ─── 路 C:原文兜底通道 ──────────────────────────────────────────────────────
|
|
331
|
+
const queryC = taskTokens;
|
|
332
|
+
|
|
333
|
+
const bm25 = new BM25(docs);
|
|
334
|
+
const rawA = bm25.scores(queryA);
|
|
335
|
+
const rawB = bm25.scores(queryB);
|
|
336
|
+
const rawC = bm25.scores(queryC);
|
|
337
|
+
|
|
338
|
+
// 分数归一化:raw / max(3, 单路实际参与 token 数)
|
|
339
|
+
// 分母用各路的原始 token 数(非展开后),避免动作词展开/多路叠加导致分母虚高
|
|
340
|
+
const countA = Math.max(3, rawQueryATokens);
|
|
341
|
+
const countB = Math.max(3, queryB.length);
|
|
342
|
+
const countC = Math.max(3, queryC.length);
|
|
343
|
+
const normA = rawA.map((s) => s / countA);
|
|
344
|
+
const normB = rawB.map((s) => s / countB);
|
|
345
|
+
const normC = rawC.map((s) => s / countC);
|
|
346
|
+
|
|
347
|
+
// 融合
|
|
348
|
+
const fused = normA.map((_, i) => weights.action * normA[i] + weights.domain * normB[i] + weights.raw * normC[i]);
|
|
349
|
+
|
|
350
|
+
// 检查融合后是否全零(无任何 token 重叠)→ 回退到 legacy 子串匹配
|
|
351
|
+
const hasAnyScore = fused.some((s) => s > 0);
|
|
352
|
+
let candidates;
|
|
353
|
+
if (!hasAnyScore) {
|
|
354
|
+
candidates = _legacySubstringMatch(analysis, skills).slice(0, topK);
|
|
355
|
+
} else {
|
|
356
|
+
candidates = skills
|
|
357
|
+
.map((skill, i) => {
|
|
358
|
+
let score = fused[i] || 0;
|
|
359
|
+
// name 精确匹配 bonus:query keywords 中任一词与 skill.name 精确匹配时 ×1.15
|
|
360
|
+
const skillName = (skill.name || '').toLowerCase();
|
|
361
|
+
const hasNameMatch = keywords.some((kw) => kw && skillName === kw);
|
|
362
|
+
if (hasNameMatch && score > 0) {
|
|
363
|
+
score = score * 1.15;
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
skill,
|
|
367
|
+
score,
|
|
368
|
+
// 每路归一化分数留痕,便于定位偶发未命中
|
|
369
|
+
routes: { action: normA[i] || 0, domain: normB[i] || 0, raw: normC[i] || 0 },
|
|
370
|
+
};
|
|
371
|
+
})
|
|
372
|
+
.sort((a, b) => b.score - a.score)
|
|
373
|
+
.slice(0, topK);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return candidates;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ─── Legacy fallback:子串匹配(BM25 全零分时启用)──────────────────────────────
|
|
380
|
+
/**
|
|
381
|
+
* 旧版子串匹配逻辑(作为 fallback)
|
|
382
|
+
* 保留原 fuzzyMatch 桩实现的策略,保证 BM25 无重叠时不返回空
|
|
383
|
+
*/
|
|
384
|
+
function _legacySubstringMatch(analysis, skills) {
|
|
385
|
+
const keywords = normalizeList(analysis?.keywords);
|
|
386
|
+
const techStack = normalizeList(analysis?.techStack);
|
|
387
|
+
const intentWords = _legacyTokenize(analysis?.intent);
|
|
388
|
+
|
|
389
|
+
const scored = skills.map((skill) => {
|
|
390
|
+
const text = skill.searchableText || '';
|
|
391
|
+
let score = 0;
|
|
392
|
+
|
|
393
|
+
for (const t of techStack) {
|
|
394
|
+
if (t && text.includes(t)) score += 10;
|
|
395
|
+
}
|
|
396
|
+
for (const kw of keywords) {
|
|
397
|
+
if (kw && text.includes(kw)) score += 6;
|
|
398
|
+
}
|
|
399
|
+
for (const w of intentWords) {
|
|
400
|
+
if (w && w.length > 1 && text.includes(w)) score += 2;
|
|
401
|
+
}
|
|
402
|
+
const name = (skill.name || '').toLowerCase();
|
|
403
|
+
for (const kw of keywords) {
|
|
404
|
+
if (kw && name === kw) score += 4;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return { skill, score, reasons: ['legacy fallback'] };
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
scored.sort((a, b) => b.score - a.score);
|
|
411
|
+
return scored;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** legacy 分词(保留原逻辑,不做停用词过滤) */
|
|
415
|
+
function _legacyTokenize(s) {
|
|
416
|
+
if (!s) return [];
|
|
417
|
+
return String(s)
|
|
418
|
+
.toLowerCase()
|
|
419
|
+
.split(/[\s,,。、;;::.()()\-_/]+/)
|
|
420
|
+
.filter((w) => w.length > 0);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── 工厂:创建可替换的模糊匹配器 ──────────────────────────────────────────────
|
|
424
|
+
/**
|
|
425
|
+
* @param {(analysis, skills, options) => Promise<Array>} impl
|
|
426
|
+
* @returns {(analysis, skills, options) => Promise<Array>}
|
|
427
|
+
*/
|
|
428
|
+
export function createFuzzyMatcher(impl) {
|
|
429
|
+
if (typeof impl === 'function') return impl;
|
|
430
|
+
return fuzzyMatch;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export default { tokenize, normalizeList, BM25, bm25Scores, fuzzyMatch, createFuzzyMatcher };
|