@huicai/cli 2026.804.0 → 2026.1019.0
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/LICENSE +391 -32
- package/README.md +176 -10
- package/bin/huicai.js +166 -73
- package/package.json +27 -8
- package/skills/ab-testing/trigger-cases.json +30 -0
- package/skills/academic-writing/trigger-cases.json +30 -0
- package/skills/accounting-ops/trigger-cases.json +30 -0
- package/skills/ai-agent/trigger-cases.json +30 -0
- package/skills/ai-aiops/trigger-cases.json +30 -0
- package/skills/ai-coding/trigger-cases.json +30 -0
- package/skills/ai-compliance/trigger-cases.json +30 -0
- package/skills/ai-eval/trigger-cases.json +30 -0
- package/skills/ai-llm-app/trigger-cases.json +30 -0
- package/skills/ai-mlops/trigger-cases.json +30 -0
- package/skills/ai-multimodal/trigger-cases.json +30 -0
- package/skills/ai-prompt/trigger-cases.json +30 -0
- package/skills/ai-rag/trigger-cases.json +30 -0
- package/skills/anomaly-detection/trigger-cases.json +30 -0
- package/skills/api-design/trigger-cases.json +31 -0
- package/skills/api-integration/trigger-cases.json +31 -0
- package/skills/arch-design/trigger-cases.json +12 -0
- package/skills/arch-diagram/SKILL.md +118 -0
- package/skills/arch-diagram/trigger-cases.json +30 -0
- package/skills/arch-distributed/trigger-cases.json +30 -0
- package/skills/arch-multitenant/trigger-cases.json +30 -0
- package/skills/audio-brief/SKILL.md +65 -0
- package/skills/audio-brief/trigger-cases.json +30 -0
- package/skills/audit-assist/trigger-cases.json +30 -0
- package/skills/audit-chain/trigger-cases.json +31 -0
- package/skills/banking-credit/trigger-cases.json +30 -0
- package/skills/brand-design/trigger-cases.json +30 -0
- package/skills/business-plan/trigger-cases.json +30 -0
- package/skills/capital-ops/trigger-cases.json +30 -0
- package/skills/cicd-pipeline/trigger-cases.json +31 -0
- package/skills/code-archaeology/SKILL.md +55 -0
- package/skills/code-archaeology/trigger-cases.json +9 -0
- package/skills/code-quality/SKILL.md +138 -0
- package/skills/code-quality/references/languages.md +107 -0
- package/skills/code-quality/scripts/__pycache__/code_battery.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_check.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_check.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_csharp.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_csharp.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_fix.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_fix.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_java.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_java.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_jsts.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_jsts.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_lens.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_lens.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_python.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_python.cpython-313.pyc +0 -0
- package/skills/code-quality/scripts/__pycache__/code_testplan.cpython-312.pyc +0 -0
- package/skills/code-quality/scripts/code_battery.py +1072 -0
- package/skills/code-quality/scripts/code_check.py +1269 -0
- package/skills/code-quality/scripts/code_csharp.py +371 -0
- package/skills/code-quality/scripts/code_fix.py +201 -0
- package/skills/code-quality/scripts/code_java.py +695 -0
- package/skills/code-quality/scripts/code_jsts.py +313 -0
- package/skills/code-quality/scripts/code_lens.py +1079 -0
- package/skills/code-quality/scripts/code_python.py +536 -0
- package/skills/code-quality/scripts/code_testplan.py +239 -0
- package/skills/code-quality/trigger-cases.json +45 -0
- package/skills/code-review/SKILL.md +24 -0
- package/skills/code-review/trigger-cases.json +35 -0
- package/skills/commit-and-pr/trigger-cases.json +31 -0
- package/skills/content-creation/trigger-cases.json +30 -0
- package/skills/corporate-governance/trigger-cases.json +30 -0
- package/skills/crawl-chain/trigger-cases.json +31 -0
- package/skills/customer-service/trigger-cases.json +30 -0
- package/skills/data-analysis/trigger-cases.json +30 -0
- package/skills/data-chain/SKILL.md +1 -1
- package/skills/data-chain/trigger-cases.json +31 -0
- package/skills/data-governance/trigger-cases.json +30 -0
- package/skills/data-report-pro/trigger-cases.json +30 -0
- package/skills/data-streaming/trigger-cases.json +30 -0
- package/skills/data-wrangling/SKILL.md +1 -1
- package/skills/data-wrangling/trigger-cases.json +30 -0
- package/skills/db-es/trigger-cases.json +30 -0
- package/skills/db-migration/trigger-cases.json +30 -0
- package/skills/db-mysql/trigger-cases.json +30 -0
- package/skills/db-postgres/trigger-cases.json +30 -0
- package/skills/db-redis/trigger-cases.json +30 -0
- package/skills/debug-root-cause/trigger-cases.json +30 -0
- package/skills/dependency-audit/SKILL.md +56 -0
- package/skills/dependency-audit/trigger-cases.json +7 -0
- package/skills/dependency-upgrade/trigger-cases.json +31 -0
- package/skills/dev-cpp/trigger-cases.json +30 -0
- package/skills/dev-cross-platform/trigger-cases.json +30 -0
- package/skills/dev-csharp/SKILL.md +23 -0
- package/skills/dev-csharp/trigger-cases.json +30 -0
- package/skills/dev-frontend/SKILL.md +19 -0
- package/skills/dev-frontend/trigger-cases.json +30 -0
- package/skills/dev-go/trigger-cases.json +30 -0
- package/skills/dev-harmony/trigger-cases.json +30 -0
- package/skills/dev-java/SKILL.md +20 -0
- package/skills/dev-java/trigger-cases.json +30 -0
- package/skills/dev-mobile/trigger-cases.json +30 -0
- package/skills/dev-node/trigger-cases.json +30 -0
- package/skills/dev-php/trigger-cases.json +30 -0
- package/skills/dev-python/SKILL.md +21 -0
- package/skills/dev-python/trigger-cases.json +30 -0
- package/skills/dev-rust/trigger-cases.json +30 -0
- package/skills/dev-toolchain/trigger-cases.json +31 -0
- package/skills/doc-chain/trigger-cases.json +31 -0
- package/skills/doc-compare/trigger-cases.json +30 -0
- package/skills/docx-author/SKILL.md +127 -25
- package/skills/docx-author/scripts/__pycache__/docx_battery.cpython-312.pyc +0 -0
- package/skills/docx-author/scripts/__pycache__/docx_build.cpython-312.pyc +0 -0
- package/skills/docx-author/scripts/__pycache__/docx_check.cpython-312.pyc +0 -0
- package/skills/docx-author/scripts/__pycache__/docx_layout.cpython-312.pyc +0 -0
- package/skills/docx-author/scripts/__pycache__/zh_prose.cpython-312.pyc +0 -0
- package/skills/docx-author/scripts/docx_battery.py +586 -0
- package/skills/docx-author/scripts/docx_build.py +995 -0
- package/skills/docx-author/scripts/docx_check.py +1306 -0
- package/skills/docx-author/scripts/docx_doc.py +14 -154
- package/skills/docx-author/scripts/docx_layout.py +490 -0
- package/skills/docx-author/scripts/zh_prose.py +639 -0
- package/skills/docx-author/trigger-cases.json +58 -0
- package/skills/echo-brief/SKILL.md +84 -0
- package/skills/echo-brief/references/echo-format.md +68 -0
- package/skills/echo-brief/trigger-cases.json +40 -0
- package/skills/ecommerce-ops/trigger-cases.json +30 -0
- package/skills/education-training/trigger-cases.json +30 -0
- package/skills/ehs-safety/trigger-cases.json +30 -0
- package/skills/email-writing/trigger-cases.json +30 -0
- package/skills/eng-ai/trigger-cases.json +30 -0
- package/skills/eng-architecture/trigger-cases.json +31 -0
- package/skills/eng-automation/trigger-cases.json +31 -0
- package/skills/eng-collaboration/trigger-cases.json +30 -0
- package/skills/eng-complex-env/trigger-cases.json +30 -0
- package/skills/eng-data-platform/trigger-cases.json +31 -0
- package/skills/eng-database/trigger-cases.json +31 -0
- package/skills/eng-develop/trigger-cases.json +30 -0
- package/skills/eng-docs/SKILL.md +24 -0
- package/skills/eng-docs/trigger-cases.json +30 -0
- package/skills/eng-efficiency/trigger-cases.json +30 -0
- package/skills/eng-infra/trigger-cases.json +30 -0
- package/skills/eng-integration/trigger-cases.json +31 -0
- package/skills/eng-legacy/trigger-cases.json +31 -0
- package/skills/eng-observability/trigger-cases.json +31 -0
- package/skills/eng-ops/trigger-cases.json +31 -0
- package/skills/eng-performance/trigger-cases.json +31 -0
- package/skills/eng-planning/trigger-cases.json +31 -0
- package/skills/eng-quality/SKILL.md +8 -0
- package/skills/eng-quality/trigger-cases.json +31 -0
- package/skills/eng-release/trigger-cases.json +31 -0
- package/skills/eng-reliability/trigger-cases.json +31 -0
- package/skills/eng-requirements/trigger-cases.json +12 -0
- package/skills/eng-research/trigger-cases.json +30 -0
- package/skills/eng-security/trigger-cases.json +31 -0
- package/skills/eng-ux/trigger-cases.json +30 -0
- package/skills/engineering-chain/trigger-cases.json +30 -0
- package/skills/env-offline/trigger-cases.json +31 -0
- package/skills/env-private-deploy/trigger-cases.json +31 -0
- package/skills/env-xinchuang/trigger-cases.json +31 -0
- package/skills/equipment-tpm/trigger-cases.json +30 -0
- package/skills/esg-report/trigger-cases.json +30 -0
- package/skills/event-management/trigger-cases.json +30 -0
- package/skills/excel-analysis-chain/SKILL.md +17 -4
- package/skills/excel-analysis-chain/trigger-cases.json +31 -0
- package/skills/explain-codebase/trigger-cases.json +11 -0
- package/skills/file-ingest/trigger-cases.json +30 -0
- package/skills/finance-analysis/trigger-cases.json +31 -0
- package/skills/finance-chain/trigger-cases.json +31 -0
- package/skills/financial-modeling/trigger-cases.json +31 -0
- package/skills/find-simplifications/SKILL.md +43 -0
- package/skills/find-simplifications/trigger-cases.json +23 -0
- package/skills/foreign-trade/trigger-cases.json +30 -0
- package/skills/funnel-cohort/trigger-cases.json +30 -0
- package/skills/gov-document/trigger-cases.json +30 -0
- package/skills/gui-change-evidence/SKILL.md +47 -0
- package/skills/gui-change-evidence/trigger-cases.json +23 -0
- package/skills/hr-chain/trigger-cases.json +31 -0
- package/skills/hr-operations/trigger-cases.json +30 -0
- package/skills/info-disclosure/trigger-cases.json +31 -0
- package/skills/infra-gateway/trigger-cases.json +31 -0
- package/skills/infra-k8s/trigger-cases.json +31 -0
- package/skills/infra-mq/trigger-cases.json +30 -0
- package/skills/insurance-advisory/trigger-cases.json +30 -0
- package/skills/internal-audit/trigger-cases.json +30 -0
- package/skills/ip-management/trigger-cases.json +31 -0
- package/skills/ipo-listing/trigger-cases.json +31 -0
- package/skills/it-audit/trigger-cases.json +31 -0
- package/skills/itsm-ops/trigger-cases.json +31 -0
- package/skills/knowledge-sop/trigger-cases.json +31 -0
- package/skills/legal-chain/trigger-cases.json +31 -0
- package/skills/legal-contract/trigger-cases.json +30 -0
- package/skills/litigation-support/trigger-cases.json +31 -0
- package/skills/logistics-warehouse/trigger-cases.json +30 -0
- package/skills/long-doc-analysis/trigger-cases.json +31 -0
- package/skills/management-accounting/trigger-cases.json +30 -0
- package/skills/manufacturing-chain/trigger-cases.json +31 -0
- package/skills/manufacturing-quality/trigger-cases.json +30 -0
- package/skills/marketing-analytics/trigger-cases.json +31 -0
- package/skills/marketing-chain/trigger-cases.json +31 -0
- package/skills/meeting-minutes/trigger-cases.json +30 -0
- package/skills/metrics-system/trigger-cases.json +31 -0
- package/skills/negotiation-prep/trigger-cases.json +30 -0
- package/skills/obs-prometheus/trigger-cases.json +31 -0
- package/skills/office-automation/trigger-cases.json +31 -0
- package/skills/okr-review/trigger-cases.json +30 -0
- package/skills/ops-fleet/trigger-cases.json +31 -0
- package/skills/ops-linux/trigger-cases.json +31 -0
- package/skills/optimization-solver/trigger-cases.json +30 -0
- package/skills/patent-disclosure/SKILL.md +105 -0
- package/skills/patent-disclosure/references/figures.md +110 -0
- package/skills/patent-disclosure/references/outline.md +162 -0
- package/skills/patent-disclosure/scripts/patent_docx.py +230 -0
- package/skills/patent-disclosure/trigger-cases.json +31 -0
- package/skills/patent-drafting/SKILL.md +108 -0
- package/skills/patent-drafting/trigger-cases.json +31 -0
- package/skills/patent-portfolio/SKILL.md +81 -0
- package/skills/patent-portfolio/trigger-cases.json +31 -0
- package/skills/pdf-toolkit/trigger-cases.json +31 -0
- package/skills/perf-optimize/trigger-cases.json +31 -0
- package/skills/pivot-crosstab/trigger-cases.json +30 -0
- package/skills/policy-analysis/trigger-cases.json +31 -0
- package/skills/portfolio-analysis/trigger-cases.json +31 -0
- package/skills/ppt-chain/SKILL.md +14 -6
- package/skills/ppt-chain/trigger-cases.json +31 -0
- package/skills/pptx-author/SKILL.md +86 -33
- package/skills/pptx-author/scripts/__pycache__/pptx_check.cpython-312.pyc +0 -0
- package/skills/pptx-author/scripts/__pycache__/pptx_deck.cpython-312.pyc +0 -0
- package/skills/pptx-author/scripts/__pycache__/pptx_layout.cpython-312.pyc +0 -0
- package/skills/pptx-author/scripts/pptx_battery.py +178 -0
- package/skills/pptx-author/scripts/pptx_build.py +119 -0
- package/skills/pptx-author/scripts/pptx_check.py +1182 -0
- package/skills/pptx-author/scripts/pptx_deck.py +736 -145
- package/skills/pptx-author/scripts/pptx_layout.py +384 -0
- package/skills/pptx-author/trigger-cases.json +15 -0
- package/skills/pr-comms/trigger-cases.json +30 -0
- package/skills/procurement-supply/trigger-cases.json +31 -0
- package/skills/product-design/trigger-cases.json +31 -0
- package/skills/production-planning/trigger-cases.json +31 -0
- package/skills/project-management/trigger-cases.json +31 -0
- package/skills/project-proposal/trigger-cases.json +31 -0
- package/skills/prose-standard/SKILL.md +53 -0
- package/skills/prose-standard/trigger-cases.json +23 -0
- package/skills/qa-api-automation/trigger-cases.json +31 -0
- package/skills/qa-mobile-automation/trigger-cases.json +31 -0
- package/skills/qa-ui-automation/trigger-cases.json +31 -0
- package/skills/quant-analysis/trigger-cases.json +31 -0
- package/skills/rag-triage/SKILL.md +64 -0
- package/skills/rag-triage/evals.json +57 -0
- package/skills/rag-triage/references/triage-playbook.md +75 -0
- package/skills/rag-triage/trigger-cases.json +31 -0
- package/skills/real-estate-property/trigger-cases.json +30 -0
- package/skills/refactor-safely/SKILL.md +1 -1
- package/skills/refactor-safely/trigger-cases.json +30 -0
- package/skills/regression-forecast/trigger-cases.json +31 -0
- package/skills/release-checklist/trigger-cases.json +31 -0
- package/skills/report-craft/SKILL.md +76 -0
- package/skills/report-craft/references/feasibility.md +118 -0
- package/skills/report-craft/references/review-checklist.md +51 -0
- package/skills/report-craft/references/visuals.md +114 -0
- package/skills/report-craft/references/writing.md +80 -0
- package/skills/report-craft/scripts/__pycache__/chart_render.cpython-312.pyc +0 -0
- package/skills/report-craft/scripts/__pycache__/fin_model.cpython-312.pyc +0 -0
- package/skills/report-craft/scripts/__pycache__/project_facts.cpython-312.pyc +0 -0
- package/skills/report-craft/scripts/chart_render.py +1222 -0
- package/skills/report-craft/scripts/fin_model.py +602 -0
- package/skills/report-craft/scripts/project_facts.py +580 -0
- package/skills/report-craft/scripts/report_battery.py +349 -0
- package/skills/report-craft/trigger-cases.json +15 -0
- package/skills/retail-store-ops/trigger-cases.json +30 -0
- package/skills/risk-compliance/trigger-cases.json +31 -0
- package/skills/scope-creep/SKILL.md +45 -0
- package/skills/scope-creep/trigger-cases.json +7 -0
- package/skills/sec-auth/trigger-cases.json +31 -0
- package/skills/sec-compliance/trigger-cases.json +31 -0
- package/skills/securities-chain/trigger-cases.json +31 -0
- package/skills/securities-research/trigger-cases.json +31 -0
- package/skills/security-audit/trigger-cases.json +31 -0
- package/skills/slides-html/SKILL.md +25 -10
- package/skills/slides-html/trigger-cases.json +11 -0
- package/skills/sql-analysis/SKILL.md +110 -0
- package/skills/sql-analysis/trigger-cases.json +34 -0
- package/skills/sql-tuning/trigger-cases.json +31 -0
- package/skills/statistical-testing/trigger-cases.json +31 -0
- package/skills/strategy-planning/trigger-cases.json +30 -0
- package/skills/supply-chain/trigger-cases.json +31 -0
- package/skills/tax-compliance/trigger-cases.json +30 -0
- package/skills/tech-doc/SKILL.md +262 -0
- package/skills/tech-doc/scripts/__pycache__/archive_battery.cpython-312.pyc +0 -0
- package/skills/tech-doc/scripts/__pycache__/archive_build.cpython-312.pyc +0 -0
- package/skills/tech-doc/scripts/__pycache__/archive_check.cpython-312.pyc +0 -0
- package/skills/tech-doc/scripts/__pycache__/archive_spec.cpython-312.pyc +0 -0
- package/skills/tech-doc/scripts/__pycache__/doc_templates.cpython-312.pyc +0 -0
- package/skills/tech-doc/scripts/archive_battery.py +860 -0
- package/skills/tech-doc/scripts/archive_build.py +599 -0
- package/skills/tech-doc/scripts/archive_check.py +1307 -0
- package/skills/tech-doc/scripts/archive_spec.py +499 -0
- package/skills/tech-doc/scripts/doc_templates.py +624 -0
- package/skills/tech-doc/trigger-cases.json +57 -0
- package/skills/tech-research/trigger-cases.json +31 -0
- package/skills/tender-bidding/trigger-cases.json +31 -0
- package/skills/translation-pro/trigger-cases.json +30 -0
- package/skills/treasury-mgmt/trigger-cases.json +31 -0
- package/skills/trim-cot-leakage/SKILL.md +58 -0
- package/skills/trim-cot-leakage/trigger-cases.json +23 -0
- package/skills/web-craft/SKILL.md +103 -0
- package/skills/web-craft/references/craft-checklist.md +75 -0
- package/skills/web-craft/references/design-direction.md +122 -0
- package/skills/web-craft/references/h5-mobile.md +100 -0
- package/skills/web-craft/references/motion.md +136 -0
- package/skills/web-craft/references/recipes.md +74 -0
- package/skills/web-craft/templates/wedding-invitation.html +1180 -0
- package/skills/web-craft/trigger-cases.json +14 -0
- package/skills/web-scraping/trigger-cases.json +34 -0
- package/skills/web-search/SKILL.md +11 -10
- package/skills/web-search/trigger-cases.json +31 -0
- package/skills/weekly-report/SKILL.md +134 -0
- package/skills/weekly-report/trigger-cases.json +11 -0
- package/skills/write-tests/trigger-cases.json +31 -0
- package/skills/xlsx-analysis/SKILL.md +143 -77
- package/skills/xlsx-analysis/scripts/__pycache__/data_profile.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/__pycache__/xlsx_build.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/__pycache__/xlsx_check.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/__pycache__/xlsx_layout.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/__pycache__/xlsx_toolkit.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/data_profile.py +391 -0
- package/skills/xlsx-analysis/scripts/xlsx_battery.py +389 -0
- package/skills/xlsx-analysis/scripts/xlsx_build.py +522 -0
- package/skills/xlsx-analysis/scripts/xlsx_check.py +924 -0
- package/skills/xlsx-analysis/scripts/xlsx_layout.py +235 -0
- package/skills/xlsx-analysis/scripts/xlsx_style.py +28 -66
- package/skills/xlsx-analysis/scripts/xlsx_toolkit.py +61 -5
- package/skills/xlsx-analysis/trigger-cases.json +59 -0
- package/squads/hc-defense.md +47 -0
- package/squads/hc-rdarchive.md +110 -0
- package/squads/hc-redteam.md +50 -0
- package/squads/hc-weekly.md +125 -0
- package/src/account.js +1 -0
- package/src/acp.js +1 -0
- package/src/advisor.js +1 -1
- package/src/agent.js +1 -1
- package/src/api.js +1 -1
- package/src/archaeology.js +1 -0
- package/src/ask-ui.js +1 -0
- package/src/assets-import.js +1 -1
- package/src/autofix.js +1 -1
- package/src/automation-tools.js +1 -0
- package/src/automation.js +1 -1
- package/src/autorun.js +1 -1
- package/src/banner.js +1 -1
- package/src/capability-probe.js +1 -0
- package/src/checkpoints.js +1 -1
- package/src/code-tools.js +1 -0
- package/src/codeindex.js +1 -1
- package/src/codemode.js +1 -0
- package/src/command-bridge.js +1 -0
- package/src/command-ui.js +1 -0
- package/src/commands.js +1 -1
- package/src/compaction.js +1 -1
- package/src/computer.js +1 -0
- package/src/config.js +1 -1
- package/src/consult.js +1 -1
- package/src/context-report.js +1 -1
- package/src/context-window.js +1 -0
- package/src/context.js +1 -1
- package/src/crew.js +1 -0
- package/src/crush.js +1 -0
- package/src/daemon.js +1 -1
- package/src/decisions.js +1 -1
- package/src/depaudit.js +1 -0
- package/src/device.js +1 -1
- package/src/diagram-draw.js +1 -0
- package/src/diagram-layouts.js +1 -0
- package/src/diagram-raster.js +1 -0
- package/src/diagram.js +1 -0
- package/src/dictation.js +1 -0
- package/src/diffview.js +1 -1
- package/src/doctor.js +1 -1
- package/src/docx-tools.js +1 -0
- package/src/errkind.js +1 -0
- package/src/evidence.js +1 -0
- package/src/executors.js +1 -1
- package/src/expert-catalog.js +1 -1
- package/src/experts-store.js +1 -1
- package/src/experts.js +1 -1
- package/src/external-agents.js +1 -0
- package/src/forks.js +1 -1
- package/src/fs-observe.js +1 -0
- package/src/gitflow.js +1 -1
- package/src/goalround.js +1 -0
- package/src/hooks.js +1 -1
- package/src/input.js +1 -1
- package/src/insights.js +1 -1
- package/src/jobs.js +1 -0
- package/src/kbsync.js +1 -1
- package/src/lsp.js +1 -0
- package/src/mcp-apps.js +1 -0
- package/src/mcp.js +1 -1
- package/src/memory.js +1 -1
- package/src/notify.js +1 -0
- package/src/permissions.js +1 -1
- package/src/plan-limits.js +1 -0
- package/src/policy.js +1 -0
- package/src/pptx-tools.js +1 -0
- package/src/presence.js +1 -0
- package/src/privatedir.js +1 -0
- package/src/pyruntime.js +1 -1
- package/src/radar.js +1 -0
- package/src/render.js +1 -1
- package/src/repl.js +1 -1
- package/src/report-tools.js +1 -0
- package/src/routing.js +1 -0
- package/src/sandbox-probe.js +1 -0
- package/src/sandbox-win-launcher.mjs +156 -0
- package/src/sandbox.js +1 -0
- package/src/scan.js +1 -1
- package/src/schedule.js +1 -1
- package/src/scopecreep.js +1 -0
- package/src/sdk.js +1 -0
- package/src/selection.js +1 -0
- package/src/session-events.js +1 -0
- package/src/session.js +1 -1
- package/src/sessionindex.js +1 -0
- package/src/sessionlog.js +1 -0
- package/src/skill-catalog.js +1 -0
- package/src/skill-lint.js +1 -0
- package/src/skill-optimize.js +1 -0
- package/src/skill-scan.js +1 -0
- package/src/skills.js +1 -1
- package/src/spill.js +1 -0
- package/src/squad.js +1 -1
- package/src/subagent.js +1 -1
- package/src/surface-state.js +1 -0
- package/src/tapd.js +1 -0
- package/src/terminal-select.js +1 -0
- package/src/terminal.js +1 -0
- package/src/test-runner.js +1 -0
- package/src/todo.js +1 -1
- package/src/toolpipe.js +1 -0
- package/src/tools.js +1 -1
- package/src/toon.js +1 -0
- package/src/transcript.js +1 -1
- package/src/tui/app.js +1 -1
- package/src/tui/command-menu.js +1 -0
- package/src/tui/progress.js +1 -0
- package/src/tui/scroll.js +1 -0
- package/src/tui/welcome.js +1 -0
- package/src/tui/workbench.js +1 -0
- package/src/ui-spec.js +1 -0
- package/src/uitree.js +1 -0
- package/src/uninstall.js +1 -1
- package/src/vision.js +1 -0
- package/src/visual.js +1 -0
- package/src/voice-form.js +1 -0
- package/src/voice.js +1 -0
- package/src/watch.js +1 -1
- package/src/web-audit.js +1 -0
- package/src/web-cdp.js +1 -0
- package/src/web-inspect.js +1 -0
- package/src/websearch.js +1 -0
- package/src/weekly.js +1 -0
- package/src/workflow.js +1 -0
- package/src/worklog.js +1 -0
- package/src/xlsx-tools.js +1 -0
- package/ui/command-picker.js +79 -0
- package/skills/ab-testing/scripts/__pycache__/ab_test.cpython-312.pyc +0 -0
- package/skills/accounting-ops/scripts/__pycache__/acct_tools.cpython-312.pyc +0 -0
- package/skills/anomaly-detection/scripts/__pycache__/anomaly.cpython-312.pyc +0 -0
- package/skills/audit-assist/scripts/__pycache__/audit_sampling.cpython-312.pyc +0 -0
- package/skills/banking-credit/scripts/__pycache__/credit_metrics.cpython-312.pyc +0 -0
- package/skills/data-report-pro/scripts/__pycache__/bigdata.cpython-312.pyc +0 -0
- package/skills/data-wrangling/scripts/__pycache__/join_tools.cpython-312.pyc +0 -0
- package/skills/ecommerce-ops/scripts/__pycache__/gmv_decompose.cpython-312.pyc +0 -0
- package/skills/funnel-cohort/scripts/__pycache__/funnel.cpython-312.pyc +0 -0
- package/skills/management-accounting/scripts/__pycache__/costing.cpython-312.pyc +0 -0
- package/skills/manufacturing-quality/scripts/__pycache__/spc.cpython-312.pyc +0 -0
- package/skills/marketing-analytics/scripts/__pycache__/rfm.cpython-312.pyc +0 -0
- package/skills/optimization-solver/scripts/__pycache__/solver.cpython-312.pyc +0 -0
- package/skills/pivot-crosstab/scripts/__pycache__/pivot_tools.cpython-312.pyc +0 -0
- package/skills/portfolio-analysis/scripts/__pycache__/portfolio.cpython-312.pyc +0 -0
- package/skills/procurement-supply/scripts/__pycache__/supply.cpython-312.pyc +0 -0
- package/skills/production-planning/scripts/__pycache__/mrp_capacity.cpython-312.pyc +0 -0
- package/skills/project-management/scripts/__pycache__/cpm.cpython-312.pyc +0 -0
- package/skills/quant-analysis/scripts/__pycache__/quant.cpython-312.pyc +0 -0
- package/skills/real-estate-property/scripts/__pycache__/property_calc.cpython-312.pyc +0 -0
- package/skills/regression-forecast/scripts/__pycache__/regress.cpython-312.pyc +0 -0
- package/skills/securities-research/scripts/__pycache__/valuation.cpython-312.pyc +0 -0
- package/skills/statistical-testing/scripts/__pycache__/stat_tests.cpython-312.pyc +0 -0
- package/skills/treasury-mgmt/scripts/__pycache__/cashflow_forecast.cpython-312.pyc +0 -0
package/src/automation.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:fs";import e from"node:path";import{spawn as n}from"node:child_process";import{StringDecoder as o}from"node:string_decoder";import{runBash as r}from"./executors.js";const a=3e4,s=3e5;export const AUTOMATION_TOOLS=new Set(["install_packages","setup_environment","install_plugin","open_browser","open_path","open_app","auto_execute"]);export function automationActionLabel(t,e={}){switch(t){case"install_packages":return`安装 Node.js 包:${Array.isArray(e.packages)&&e.packages.length?e.packages.join(" "):"package.json 全部依赖"}${e.global?"(全局)":""}${e.dev?"(devDependencies)":""}`;case"setup_environment":return Array.isArray(e.commands)&&e.commands.length?`自动配置运行环境:执行 ${e.commands.length} 条配置命令`:"自动配置运行环境:检测项目技术栈并安装依赖";case"install_plugin":return`安装${"jetbrains"===e.target?"JetBrains 插件":"npm"===e.target?"npm 全局工具":"VS Code 扩展"}:${e.id??""}`;case"open_browser":return`打开浏览器访问:${e.url??""}`;case"open_path":return`在文件管理器中打开:${e.path??""}`;case"open_app":return`启动应用:${e.name??""}${e.target?"("+e.target+")":""}`;case"auto_execute":{const t=String(e.goal||"").replace(/\s+/g," ");return"自动执行已规划的任务"+(t?":"+(t.length>60?t.slice(0,60)+"…":t):"")}default:return t}}export function automationDetail(t,e={}){return"setup_environment"===t&&Array.isArray(e.commands)&&e.commands.length?e.commands.slice(0,12).map(t=>"$ "+String(t)).join("\n"):"open_browser"===t?String(e.url??""):"open_path"===t?String(e.path??""):"open_app"===t?String(e.target??""):""}export function which(n,{pathStr:o=process.env.PATH||"",platform:r=process.platform}={}){const a="win32"===r?";":":",s="win32"===r?[".cmd",".exe",".bat",""]:[""];for(const r of o.split(a))if(r)for(const o of s){const a=e.join(r,n+o);try{if(t.accessSync(a,t.constants.X_OK),t.statSync(a).isFile())return a}catch{}}return null}export function runArgv(t,e,{cwd:r,timeoutMs:s=3e5,winShell:i=!1}={}){const c="win32"===process.platform;return new Promise(p=>{let l,u="",d=!1;try{l=n(t,e,c?{cwd:r,shell:!!i}:{cwd:r,detached:!0})}catch(t){return void p({code:-1,killed:!1,output:`命令启动失败:${t.message}`})}const m=setTimeout(()=>{d=!0,(()=>{try{!c&&l.pid?process.kill(-l.pid,"SIGKILL"):l.kill("SIGKILL")}catch{try{l.kill("SIGKILL")}catch{}}})()},s),g=new o("utf8"),h=new o("utf8"),w=t=>{u.length<a&&(u+=t)};l.stdout?.on("data",t=>w(g.write(t))),l.stderr?.on("data",t=>w(h.write(t))),l.on("error",t=>{clearTimeout(m),p({code:-1,killed:!1,output:`命令启动失败:${t.message}`})}),l.on("close",t=>{clearTimeout(m),w(g.end()),w(h.end()),p({code:t,killed:d,output:u.length>a?u.slice(0,a)+"\n…(输出已截断)":u})})})}function i(t,e){return new Promise(o=>{let r,a="";try{r=n(t,e,{stdio:["ignore","pipe","pipe"]})}catch(t){return void o({code:-1,output:`命令启动失败:${t.message}`})}const s=setTimeout(()=>{try{r.unref()}catch{}o({code:0,output:a,lingering:!0})},8e3);r.stdout.on("data",t=>a+=t),r.stderr.on("data",t=>a+=t),r.on("error",t=>{clearTimeout(s),o({code:-1,output:`命令启动失败:${t.message}`})}),r.on("close",t=>{clearTimeout(s),o({code:t,output:a})})})}function c(t){if(!t||"string"!=typeof t)throw new Error("工作目录未设置:请在项目目录中运行(VS Code / IDEA 中需先「打开文件夹」)")}function p(t,e=1200){const n=String(t||"").trim();return n.length>e?"…"+n.slice(-e):n}export function detectManager(n){try{if(t.existsSync(e.join(n,"pnpm-lock.yaml")))return"pnpm";if(t.existsSync(e.join(n,"yarn.lock")))return"yarn"}catch{}return"npm"}export function validPackageToken(t){return!("string"!=typeof t||!t.trim()||/\s/.test(t)||t.startsWith("-"))&&/^(@[a-z0-9~][\w.-]*\/)?[a-z0-9~][\w.-]*(@[\w.^~=+*:-]+)?$/i.test(t)}export function buildInstallPlan(t,e={},n=process.platform){const o=["npm","pnpm","yarn"].includes(String(e.manager||"").toLowerCase())?String(e.manager).toLowerCase():detectManager(t),r=(Array.isArray(e.packages)?e.packages:[]).map(t=>String(t).trim()).filter(Boolean);for(const t of r)if(!validPackageToken(t))throw new Error(`包名不合法(仅支持 名字[@版本],不接受选项/空白/特殊字符):${t}`);const a=!!e.dev,s=!!e.global;if(s&&!r.length)throw new Error("全局安装(global=true)需要明确的 packages 列表");let i;i=r.length?"yarn"===o?s?["global","add",...r]:["add",...a?["-D"]:[],...r]:"pnpm"===o?["add",...s?["-g"]:[],...a?["-D"]:[],...r]:["install",...s?["-g"]:[],...a?["--save-dev"]:[],...r]:["install"];return{manager:o,bin:o,argv:i,winShell:"win32"===n,display:`${o} ${i.join(" ")}`}}export async function installPackages(t,e){c(t);const n=buildInstallPlan(t,e||{});if(!which(n.bin))return`未找到 ${n.bin}:请先安装 Node.js(https://nodejs.org,macOS 可 brew install node)`+("npm"!==n.manager?',或改用 manager:"npm"':"");const o=Number.isFinite(e?.timeout)?Math.min(e.timeout,6e5):s,r=await runArgv(n.bin,n.argv,{cwd:t,timeoutMs:o,winShell:n.winShell}),a=r.killed?"【超时被终止】":`退出码 ${r.code}`,i=`$ ${n.display}\n${a}`;return 0===r.code?`${i}\n已安装完成。${p(r.output,800)?"\n"+p(r.output,800):""}`:`${i}\n${p(r.output)||"(无输出)"}`}export function buildSetupSteps(n,o={},r={}){const a=r.platform||process.platform,s=r.pathStr,i=Array.isArray(o.commands)?o.commands.map(t=>String(t).trim()).filter(Boolean).slice(0,12):null;if(i&&i.length)return{mode:"commands",commands:i,detected:[]};const c=o=>{try{return t.existsSync(e.join(n,o))}catch{return!1}},p=[];c("package.json")&&p.push("node"),(c("requirements.txt")||c("pyproject.toml"))&&p.push("python"),c("go.mod")&&p.push("go"),c("Cargo.toml")&&p.push("rust"),c("pom.xml")&&p.push("maven"),(c("build.gradle")||c("build.gradle.kts"))&&p.push("gradle");const l=Array.isArray(o.stacks)&&o.stacks.length?p.filter(t=>o.stacks.includes(t)):p,u=[],d=[],m=(t,e)=>which(t,{pathStr:s,platform:a})?null:e;for(const t of l)if("node"===t){const e=detectManager(n),o=m(e,`未找到 ${e}:请先安装 Node.js(https://nodejs.org,macOS 可 brew install node)`);u.push(o?{stack:t,label:"安装 Node.js 依赖",missing:o}:{stack:t,label:`安装 Node.js 依赖(${e} install)`,bin:e,argv:["install"],winShell:!0})}else if("python"===t){const o="win32"===a?"python":"python3",r=m(o,`未找到 ${o}:macOS 可 brew install python,或从 python.org 安装`);if(r){u.push({stack:t,label:"配置 Python 环境",missing:r});continue}c(".venv")?d.push(".venv 已存在,跳过创建"):u.push({stack:t,label:"创建 Python 虚拟环境 .venv",bin:o,argv:["-m","venv",".venv"]});const s="win32"===a?".venv\\Scripts\\pip.exe":e.join(n,".venv/bin/pip");u.push(c("requirements.txt")?{stack:t,label:"安装 Python 依赖(requirements.txt)",bin:s,argv:["install","-r","requirements.txt"]}:{stack:t,label:"安装 Python 依赖(pyproject.toml)",bin:s,argv:["install","-e","."]})}else if("go"===t){const e=m("go","未找到 go:可从 https://go.dev/dl 安装(macOS 可 brew install go)");u.push(e?{stack:t,label:"下载 Go 依赖",missing:e}:{stack:t,label:"下载 Go 依赖(go mod download)",bin:"go",argv:["mod","download"]})}else if("rust"===t){const e=m("cargo","未找到 cargo:可用 rustup 安装(https://rustup.rs)");u.push(e?{stack:t,label:"拉取 Rust 依赖",missing:e}:{stack:t,label:"拉取 Rust 依赖(cargo fetch)",bin:"cargo",argv:["fetch"]})}else if("maven"===t){const e=m("mvn","未找到 mvn:macOS 可 brew install maven");u.push(e?{stack:t,label:"解析 Maven 依赖",missing:e}:{stack:t,label:"解析 Maven 依赖(mvn dependency:resolve)",bin:"mvn",argv:["-q","-DskipTests","dependency:resolve"],winShell:!0})}else"gradle"===t&&d.push("检测到 Gradle 项目:构建脚本可执行任意代码,出于安全未自动执行;可手动运行 ./gradlew build,或让我用 run_bash 执行");return{mode:"auto",detected:p,want:l,steps:u,notes:d}}export async function setupEnvironment(t,e){c(t);const n=e||{},o=buildSetupSteps(t,n),a=Number.isFinite(n.timeout)?Math.min(n.timeout,6e5):6e5;if("commands"===o.mode){const e=["环境配置(按给定命令逐条执行):"];for(const n of o.commands){const o=await r(t,{command:n,timeout:a});if(e.push(`$ ${n}\n${o}`),!/^退出码 0\b/.test(String(o))){e.push("(上一步未成功,中止后续配置命令)");break}}return e.join("\n\n")}if(!o.detected.length)return"未检测到已知技术栈(package.json / requirements.txt / pyproject.toml / go.mod / Cargo.toml / pom.xml / build.gradle)。可改用 commands 参数显式指定要执行的配置命令。";const s=[`检测到技术栈:${o.detected.join("、")}`+(o.want.length!==o.detected.length?`(本次仅处理:${o.want.join("、")||"无"})`:"")];for(const e of o.steps){if(e.missing){s.push(`✘ ${e.label}:${e.missing}`);continue}const n=await runArgv(e.bin,e.argv,{cwd:t,timeoutMs:a,winShell:e.winShell});if(0!==n.code){s.push(`✘ ${e.label} → ${n.killed?"超时被终止":"退出码 "+n.code}${p(n.output)?"\n"+p(n.output):""}`),s.push("(该步失败,停止后续步骤;可修正后重试,或用 commands 显式指定命令)");break}s.push(`✔ ${e.label} → 退出码 0`)}for(const t of o.notes||[])s.push("· "+t);return"环境配置报告:\n"+s.join("\n")}const l=["code","code-insiders","cursor","windsurf"],u=["idea","idea64","webstorm","pycharm","goland","rider","clion"];export function buildPluginPlan(t={},e={}){const n=e.platform||process.platform,o=e.pathStr,r=String(t.id||"").trim();if(!r||r.startsWith("-")||!/^[@\w./:-]+$/.test(r))throw new Error(`插件 id 不合法:${r||"(空)"}`);const a=String(t.target||"").trim().toLowerCase();if("vscode"===a){for(const t of l)if(which(t,{pathStr:o,platform:n}))return{kind:"vscode",bin:t,argv:["--install-extension",r],winShell:!0,note:`经 ${t} CLI 安装`};throw new Error("未找到 VS Code 命令行工具(code)。请在 VS Code 命令面板执行「Shell Command: Install 'code' command in PATH」后重试,或到扩展市场手动安装 "+r)}if("jetbrains"===a){for(const t of u)if(which(t,{pathStr:o,platform:n}))return{kind:"jetbrains",bin:t,argv:["installPlugins",r],note:"安装后需重启 IDE 生效"};throw new Error("未找到 JetBrains IDE 命令行启动器(idea 等)。可在 IDE 的 Tools → Create Command-line Launcher 生成,或到 Settings → Plugins 里搜索安装 "+r)}if("npm"===a){if(!which("npm",{pathStr:o,platform:n}))throw new Error("未找到 npm:请先安装 Node.js(https://nodejs.org)");return{kind:"npm",bin:"npm",argv:["install","-g",r],winShell:!0,note:"全局 CLI 工具"}}throw new Error("target 需为 vscode | jetbrains | npm")}export async function installPlugin(t,e){const n=buildPluginPlan(e||{}),o="jetbrains"===n.kind?18e4:s,r=await runArgv(n.bin,n.argv,{cwd:t||void 0,timeoutMs:o,winShell:n.winShell}),a=`${n.bin} ${n.argv.join(" ")}`;return 0===r.code?`$ ${a}\n退出码 0\n已安装 ${e?.id}${n.note?"("+n.note+")":""}${p(r.output,600)?"\n"+p(r.output,600):""}`:`$ ${a}\n${r.killed?"【超时被终止】":"退出码 "+r.code}\n${p(r.output)||"(无输出)"}`}export async function openBrowser(t,e){const n=String(e?.url||"").trim();if(!/^https?:\/\/\S+$/i.test(n))throw new Error("仅支持 http/https 网址");const o=process.platform,r="darwin"===o?await i("open",[n]):"win32"===o?await i("rundll32",["url.dll,FileProtocolHandler",n]):await i("xdg-open",[n]);return 0!==r.code?`打开浏览器失败(退出码 ${r.code}):${p(r.output)||"未知原因"}`:`已在系统默认浏览器打开:${n}`}export async function openPath(n,o){const r=String(o?.path||"").trim();if(!r)throw new Error("缺少路径 path");const a=e.resolve(n||".",r);if(!t.existsSync(a))throw new Error("路径不存在:"+a);const s=t.statSync(a).isDirectory(),c=null!=o?.reveal?!!o.reveal:!s,l=process.platform;let u;return"darwin"===l?u=await i("open",c&&!s?["-R",a]:[a]):"win32"===l?(u=await i("explorer.exe",c&&!s?["/select,"+a]:[a]),u={code:0,output:u.output}):u=await i("xdg-open",[c&&!s?e.dirname(a):a]),0!==u.code?`打开文件管理器失败(退出码 ${u.code}):${p(u.output)||"未知原因"}`:`已在文件管理器中${c&&!s?"定位":"打开"}:${a}`}export async function openApp(t,e){const o=String(e?.name||"").trim(),r=null!=e?.target?String(e.target).trim():"";if(!o)throw new Error("缺少应用名 name");if(o.length>120||/[\0-\x1f"'`$\\;|&<>{}[\]*?%]/.test(o))throw new Error("应用名含不允许的字符:"+o);if(r&&/[\0-\x1f"'`$\\;|&<>]/.test(r))throw new Error("target 含不允许的字符:"+r);const a=process.platform;if("darwin"===a){const t=await i("open",r?["-a",o,r]:["-a",o]);return 0!==t.code?`启动应用失败(退出码 ${t.code}):${p(t.output)||"未找到应用 "+o}`:`已启动应用:${o}${r?"(打开 "+r+")":""}`}if("win32"===a)return await new Promise(t=>{const e=`start "" "${o}"${r?` "${r}"`:""}`,a=n(process.env.COMSPEC||"cmd.exe",["/d","/s","/c",e],{windowsVerbatimArguments:!0});a.on("error",e=>t(`启动应用失败:${e.message}`)),a.on("close",e=>t(0===e?`已启动应用:${o}`:`启动应用失败(退出码 ${e})`))});if(!/^[\w./+-]+$/.test(o))throw new Error("Linux 下应用名需为可执行文件名(字母数字._/+-):"+o);return await new Promise(t=>{const e=n(o,r?[r]:[],{detached:!0,stdio:"ignore"});e.once("error",e=>t(`启动应用失败:${e.message}`)),e.once("spawn",()=>{e.unref(),t(`已启动应用:${o}${r?"(打开 "+r+")":""}`)})})}const d=(t,e)=>({type:"object",properties:t,required:e||[]}),m=t=>({type:"string",description:t}),g=t=>({type:"boolean",description:t}),h=t=>({type:"integer",description:t});export const AUTOMATION_TOOL_SPECS=[{type:"function",function:{name:"install_packages",description:"安装 Node.js 依赖包(npm/pnpm/yarn 按锁文件自动识别;执行前会征询用户同意)。省略 packages 时安装 package.json 的全部依赖。仅支持包名[@版本],不接受额外命令行选项。",parameters:d({packages:{type:"array",items:{type:"string"},description:'要安装的包(如 ["lodash","@types/node@^20"]);省略=安装全部依赖'},dev:g("可选:装入 devDependencies(默认否)"),global:g("可选:全局安装(需明确 packages;默认否)"),manager:m("可选:npm | pnpm | yarn(默认按锁文件自动识别)"),timeout:h("可选:超时毫秒(默认 300000,上限 600000)")},[])}},{type:"function",function:{name:"setup_environment",description:"自动配置项目运行环境(执行前会征询用户同意):检测技术栈(Node/Python/Go/Rust/Maven)→ 检查工具链是否安装 → 安装依赖(npm install、创建 .venv 并 pip install、go mod download 等)。也可用 commands 显式指定要执行的配置命令(逐条执行,失败即停)。",parameters:d({stacks:{type:"array",items:{type:"string"},description:"可选:只处理这些技术栈(node|python|go|rust|maven)"},commands:{type:"array",items:{type:"string"},description:"可选:显式配置命令列表(优先于自动检测,最多 12 条)"},timeout:h("可选:单步超时毫秒(默认 600000)")},[])}},{type:"function",function:{name:"install_plugin",description:"安装编辑器/IDE 插件(执行前会征询用户同意):VS Code 扩展(code --install-extension)、JetBrains 插件(idea installPlugins,装后需重启 IDE)、或 npm 全局 CLI 工具(npm install -g)。",parameters:d({target:m("vscode | jetbrains | npm"),id:m("插件标识:VS Code 扩展 id(如 dbaeumer.vscode-eslint)/ JetBrains 插件 id / npm 包名")},["target","id"])}},{type:"function",function:{name:"open_browser",description:"用系统默认浏览器打开一个 http/https 网址(执行前会征询用户同意)。适合把文档、本地预览页、报告直接呈现给用户。",parameters:d({url:m("完整网址(http/https)")},["url"])}},{type:"function",function:{name:"open_path",description:"在系统文件管理器(访达/资源管理器)中打开目录或定位文件(执行前会征询用户同意)。",parameters:d({path:m("目录或文件路径(相对项目根或绝对路径)"),reveal:g("可选:定位(选中)该文件而非打开;文件默认定位、目录默认打开")},["path"])}},{type:"function",function:{name:"open_app",description:'启动本机任意应用程序(执行前会征询用户同意),可附带要打开的文件或网址。macOS 用应用名(如 "Safari"、"Visual Studio Code"),Windows 用可启动名,Linux 用可执行文件名。',parameters:d({name:m("应用名/可执行名"),target:m("可选:让该应用打开的文件路径或 URL")},["name"])}}];export const AUTOMATION_EXECUTORS={install_packages:installPackages,setup_environment:setupEnvironment,install_plugin:installPlugin,open_browser:openBrowser,open_path:openPath,open_app:openApp};
|
|
1
|
+
import e from"node:fs";import t from"node:path";import{spawn as n}from"node:child_process";import{StringDecoder as r}from"node:string_decoder";import{runBash as o}from"./executors.js";const a=3e4,s=3e5;export const AUTOMATION_TOOLS=new Set(["install_packages","setup_environment","install_plugin","open_browser","open_path","open_app","auto_execute","computer_screenshot","computer_click","computer_move","computer_drag","computer_scroll","computer_type","computer_key","computer_focus_app","run_native_script","read_ui_tree","connect_mcp","set_wake_lock","schedule_followup","create_automation","update_automation","pause_automation","resume_automation","delete_automation","dispatch_external_agent","web_search"]);export function automationActionLabel(e,t={}){switch(e){case"install_packages":return`安装Node.js包:${Array.isArray(t.packages)&&t.packages.length?t.packages.join(" "):"package.json全部依赖"}${t.global?"(全局)":""}${t.dev?"(devDependencies)":""}`;case"setup_environment":return Array.isArray(t.commands)&&t.commands.length?`自动配置运行环境:执行 ${t.commands.length} 条配置命令`:"自动配置运行环境:检测项目技术栈并安装依赖";case"install_plugin":return`安装${"jetbrains"===t.target?"JetBrains插件":"npm"===t.target?"npm全局工具":"VS Code扩展"}:${t.id??""}`;case"open_browser":return`打开浏览器访问:${t.url??""}`;case"open_path":return`在文件管理器中打开:${t.path??""}`;case"open_app":return`启动应用:${t.name??""}${t.target?"("+t.target+")":""}`;case"auto_execute":{const e=String(t.goal||"").replace(/\s+/g," ");return"自动执行已规划的任务"+(e?":"+(e.length>60?e.slice(0,60)+"…":e):"")}case"computer_screenshot":return"对屏幕截图"+(t.app?`(先切到 ${t.app})`:"(整屏)");case"read_ui_tree":return(t.menu?"读取菜单快捷键清单":"读取界面结构(控件名称与坐标)")+(t.app?`:${t.app}`:":当前前台应用");case"computer_click":return`在屏幕 (${t.x??"?"}, ${t.y??"?"}) 处${Number(t.count)>=2?"双击":"点击"}`+(t.app?`(${t.app})`:"");case"computer_move":return`移动鼠标到 (${t.x??"?"}, ${t.y??"?"})`;case"computer_drag":return`拖拽 (${t.from_x??"?"}, ${t.from_y??"?"}) → (${t.to_x??"?"}, ${t.to_y??"?"})`;case"computer_scroll":return`滚动屏幕内容(${t.direction||"down"} × ${t.amount??3})`;case"computer_type":{const e=String(t.text||"").replace(/\s+/g," ");return"向当前窗口键入文本:"+(e.length>50?e.slice(0,50)+"…":e)}case"computer_key":return"按下按键:"+[...Array.isArray(t.modifiers)?t.modifiers:[],t.key].filter(Boolean).join("+");case"computer_focus_app":return`把应用切到前台:${t.name??""}`;case"run_native_script":return"执行系统原生自动化脚本"+(t.app?`(操作 ${t.app})`:"");case"connect_mcp":{const e=t.url?`远程 ${t.url}`:t.argument?`(${t.argument})`:"";return`连接MCP服务:${t.name??""}${e?" "+e:""}`}case"set_wake_lock":return!1===t.on?"释放防休眠锁":"获取防休眠锁(任务期间电脑不休眠)";case"schedule_followup":return`安排后续自动化任务(${t.when??""}):${String(t.task||"").slice(0,40)}`;case"create_automation":return`新建自动化任务「${t.name||String(t.task||"").slice(0,20)}」(${t.when??""})`;case"update_automation":return`修改自动化任务:${t.id||t.match||""}`;case"pause_automation":return`暂停自动化任务:${t.id||t.match||""}`;case"resume_automation":return`启用自动化任务:${t.id||t.match||""}`;case"delete_automation":return`删除自动化任务:${t.id||t.match||""}(不可恢复)`;case"web_search":return`联网搜索「${String(t.query||"").slice(0,40)}」(查询词会经平台发送给搜索提供商)`;case"dispatch_external_agent":return`调用外部智能体 ${t.agent||""} 执行:${String(t.objective||"").slice(0,50)}(它会在本机自行读写文件、执行命令)`;default:return e}}export function automationDetail(e,t={}){return"setup_environment"===e&&Array.isArray(t.commands)&&t.commands.length?t.commands.slice(0,12).map(e=>"$ "+String(e)).join("\n"):"open_browser"===e?String(t.url??""):"open_path"===e?String(t.path??""):"open_app"===e?String(t.target??""):"run_native_script"===e?String(t.script??"").split("\n").slice(0,12).join("\n"):"computer_type"===e?String(t.text??"").split("\n").slice(0,8).join("\n"):"schedule_followup"===e||"create_automation"===e?String(t.task??""):"update_automation"===e?[t.when?"新频率:"+t.when:"",t.task?"新提示词:"+String(t.task).slice(0,200):"",t.permission?"新权限:"+t.permission:""].filter(Boolean).join("\n"):""}export function which(n,{pathStr:r=process.env.PATH||"",platform:o=process.platform}={}){const a="win32"===o?";":":",s="win32"===o?[".cmd",".exe",".bat",""]:[""];for(const o of r.split(a))if(o)for(const r of s){const a=t.join(o,n+r);try{if(e.accessSync(a,e.constants.X_OK),e.statSync(a).isFile())return a}catch{}}return null}export function runArgv(e,t,{cwd:o,timeoutMs:s=3e5,winShell:i=!1}={}){const c="win32"===process.platform;return new Promise(p=>{let l,u="",m=!1;try{l=n(e,t,c?{cwd:o,shell:!!i}:{cwd:o,detached:!0})}catch(e){return void p({code:-1,killed:!1,output:`命令启动失败:${e.message}`})}const d=setTimeout(()=>{m=!0,(()=>{try{!c&&l.pid?process.kill(-l.pid,"SIGKILL"):l.kill("SIGKILL")}catch{try{l.kill("SIGKILL")}catch{}}})()},s),g=new r("utf8"),h=new r("utf8"),w=e=>{u.length<a&&(u+=e)};l.stdout?.on("data",e=>w(g.write(e))),l.stderr?.on("data",e=>w(h.write(e))),l.on("error",e=>{clearTimeout(d),p({code:-1,killed:!1,output:`命令启动失败:${e.message}`})}),l.on("close",e=>{clearTimeout(d),w(g.end()),w(h.end()),p({code:e,killed:m,output:u.length>a?u.slice(0,a)+"\n…(输出已截断)":u})})})}function i(e,t){return new Promise(r=>{let o,a="";try{o=n(e,t,{stdio:["ignore","pipe","pipe"]})}catch(e){return void r({code:-1,output:`命令启动失败:${e.message}`})}const s=setTimeout(()=>{try{o.unref()}catch{}r({code:0,output:a,lingering:!0})},8e3);o.stdout.on("data",e=>a+=e),o.stderr.on("data",e=>a+=e),o.on("error",e=>{clearTimeout(s),r({code:-1,output:`命令启动失败:${e.message}`})}),o.on("close",e=>{clearTimeout(s),r({code:e,output:a})})})}function c(e){if(!e||"string"!=typeof e)throw new Error("工作目录未设置:请在项目目录中运行(VS Code / IDEA中需先「打开文件夹」)")}function p(e,t=1200){const n=String(e||"").trim();return n.length>t?"…"+n.slice(-t):n}export function detectManager(n){try{if(e.existsSync(t.join(n,"pnpm-lock.yaml")))return"pnpm";if(e.existsSync(t.join(n,"yarn.lock")))return"yarn"}catch{}return"npm"}export function validPackageToken(e){return!("string"!=typeof e||!e.trim()||/\s/.test(e)||e.startsWith("-"))&&/^(@[a-z0-9~][\w.-]*\/)?[a-z0-9~][\w.-]*(@[\w.^~=+*:-]+)?$/i.test(e)}export function buildInstallPlan(e,t={},n=process.platform){const r=["npm","pnpm","yarn"].includes(String(t.manager||"").toLowerCase())?String(t.manager).toLowerCase():detectManager(e),o=(Array.isArray(t.packages)?t.packages:[]).map(e=>String(e).trim()).filter(Boolean);for(const e of o)if(!validPackageToken(e))throw new Error(`包名不合法(仅支持 名字[@版本],不接受选项/空白/特殊字符):${e}`);const a=!!t.dev,s=!!t.global;if(s&&!o.length)throw new Error("全局安装(global=true)需要明确的packages列表");let i;i=o.length?"yarn"===r?s?["global","add",...o]:["add",...a?["-D"]:[],...o]:"pnpm"===r?["add",...s?["-g"]:[],...a?["-D"]:[],...o]:["install",...s?["-g"]:[],...a?["--save-dev"]:[],...o]:["install"];return{manager:r,bin:r,argv:i,winShell:"win32"===n,display:`${r} ${i.join(" ")}`}}export async function installPackages(e,t){c(e);const n=buildInstallPlan(e,t||{});if(!which(n.bin))return`未找到${n.bin}:请先安装Node.js(https://nodejs.org,macOS可brew install node)`+("npm"!==n.manager?',或改用manager:"npm"':"");const r=Number.isFinite(t?.timeout)?Math.min(t.timeout,6e5):s,o=await runArgv(n.bin,n.argv,{cwd:e,timeoutMs:r,winShell:n.winShell}),a=o.killed?"【超时被终止】":`退出码 ${o.code}`,i=`$ ${n.display}\n${a}`;return 0===o.code?`${i}\n已安装完成。${p(o.output,800)?"\n"+p(o.output,800):""}`:`${i}\n${p(o.output)||"(无输出)"}`}export function buildSetupSteps(n,r={},o={}){const a=o.platform||process.platform,s=o.pathStr,i=Array.isArray(r.commands)?r.commands.map(e=>String(e).trim()).filter(Boolean).slice(0,12):null;if(i&&i.length)return{mode:"commands",commands:i,detected:[]};const c=r=>{try{return e.existsSync(t.join(n,r))}catch{return!1}},p=[];c("package.json")&&p.push("node"),(c("requirements.txt")||c("pyproject.toml"))&&p.push("python"),c("go.mod")&&p.push("go"),c("Cargo.toml")&&p.push("rust"),c("pom.xml")&&p.push("maven"),(c("build.gradle")||c("build.gradle.kts"))&&p.push("gradle");const l=Array.isArray(r.stacks)&&r.stacks.length?p.filter(e=>r.stacks.includes(e)):p,u=[],m=[],d=(e,t)=>which(e,{pathStr:s,platform:a})?null:t;for(const e of l)if("node"===e){const t=detectManager(n),r=d(t,`未找到${t}:请先安装Node.js(https://nodejs.org,macOS可brew install node)`);u.push(r?{stack:e,label:"安装Node.js依赖",missing:r}:{stack:e,label:`安装Node.js依赖(${t} install)`,bin:t,argv:["install"],winShell:!0})}else if("python"===e){const r="win32"===a?"python":"python3",o=d(r,`未找到${r}:macOS可brew install python,或从python.org安装`);if(o){u.push({stack:e,label:"配置Python环境",missing:o});continue}c(".venv")?m.push(".venv已存在,跳过创建"):u.push({stack:e,label:"创建Python虚拟环境 .venv",bin:r,argv:["-m","venv",".venv"]});const s="win32"===a?".venv\\Scripts\\pip.exe":t.join(n,".venv/bin/pip");u.push(c("requirements.txt")?{stack:e,label:"安装Python依赖(requirements.txt)",bin:s,argv:["install","-r","requirements.txt"]}:{stack:e,label:"安装Python依赖(pyproject.toml)",bin:s,argv:["install","-e","."]})}else if("go"===e){const t=d("go","未找到go:可从https://go.dev/dl安装(macOS可brew install go)");u.push(t?{stack:e,label:"下载Go依赖",missing:t}:{stack:e,label:"下载Go依赖(go mod download)",bin:"go",argv:["mod","download"]})}else if("rust"===e){const t=d("cargo","未找到cargo:可用rustup安装(https://rustup.rs)");u.push(t?{stack:e,label:"拉取Rust依赖",missing:t}:{stack:e,label:"拉取Rust依赖(cargo fetch)",bin:"cargo",argv:["fetch"]})}else if("maven"===e){const t=d("mvn","未找到mvn:macOS可brew install maven");u.push(t?{stack:e,label:"解析Maven依赖",missing:t}:{stack:e,label:"解析Maven依赖(mvn dependency:resolve)",bin:"mvn",argv:["-q","-DskipTests","dependency:resolve"],winShell:!0})}else"gradle"===e&&m.push("检测到Gradle项目:构建脚本可执行任意代码,出于安全未自动执行;可手动运行 ./gradlew build,或让我用run_bash执行");return{mode:"auto",detected:p,want:l,steps:u,notes:m}}export async function setupEnvironment(e,t){c(e);const n=t||{},r=buildSetupSteps(e,n),a=Number.isFinite(n.timeout)?Math.min(n.timeout,6e5):6e5;if("commands"===r.mode){const t=["环境配置(按给定命令逐条执行):"];for(const n of r.commands){const r=await o(e,{command:n,timeout:a});if(t.push(`$ ${n}\n${r}`),!/^退出码 0\b/.test(String(r))){t.push("(上一步未成功,中止后续配置命令)");break}}return t.join("\n\n")}if(!r.detected.length)return"未检测到已知技术栈(package.json / requirements.txt / pyproject.toml / go.mod / Cargo.toml / pom.xml / build.gradle)。可改用commands参数显式指定要执行的配置命令。";const s=[`检测到技术栈:${r.detected.join("、")}`+(r.want.length!==r.detected.length?`(本次仅处理:${r.want.join("、")||"无"})`:"")];for(const t of r.steps){if(t.missing){s.push(`✘ ${t.label}:${t.missing}`);continue}const n=await runArgv(t.bin,t.argv,{cwd:e,timeoutMs:a,winShell:t.winShell});if(0!==n.code){s.push(`✘ ${t.label} → ${n.killed?"超时被终止":"退出码 "+n.code}${p(n.output)?"\n"+p(n.output):""}`),s.push("(该步失败,停止后续步骤;可修正后重试,或用commands显式指定命令)");break}s.push(`✔ ${t.label} → 退出码 0`)}for(const e of r.notes||[])s.push("· "+e);return"环境配置报告:\n"+s.join("\n")}const l=["code","code-insiders","cursor","windsurf"],u=["idea","idea64","webstorm","pycharm","goland","rider","clion"];export function buildPluginPlan(e={},t={}){const n=t.platform||process.platform,r=t.pathStr,o=String(e.id||"").trim();if(!o||o.startsWith("-")||!/^[@\w./:-]+$/.test(o))throw new Error(`插件id不合法:${o||"(空)"}`);const a=String(e.target||"").trim().toLowerCase();if("vscode"===a){for(const e of l)if(which(e,{pathStr:r,platform:n}))return{kind:"vscode",bin:e,argv:["--install-extension",o],winShell:!0,note:`经 ${e} CLI安装`};throw new Error("未找到VS Code命令行工具(code)。请在VS Code命令面板执行「Shell Command: Install 'code' command in PATH」后重试,或到扩展市场手动安装 "+o)}if("jetbrains"===a){for(const e of u)if(which(e,{pathStr:r,platform:n}))return{kind:"jetbrains",bin:e,argv:["installPlugins",o],note:"安装后需重启IDE生效"};throw new Error("未找到JetBrains IDE命令行启动器(idea等)。可在IDE的Tools → Create Command-line Launcher生成,或到Settings → Plugins里搜索安装 "+o)}if("npm"===a){if(!which("npm",{pathStr:r,platform:n}))throw new Error("未找到npm:请先安装Node.js(https://nodejs.org)");return{kind:"npm",bin:"npm",argv:["install","-g",o],winShell:!0,note:"全局CLI工具"}}throw new Error("target需为vscode | jetbrains | npm")}export async function installPlugin(e,t){const n=buildPluginPlan(t||{}),r="jetbrains"===n.kind?18e4:s,o=await runArgv(n.bin,n.argv,{cwd:e||void 0,timeoutMs:r,winShell:n.winShell}),a=`${n.bin} ${n.argv.join(" ")}`;return 0===o.code?`$ ${a}\n退出码 0\n已安装 ${t?.id}${n.note?"("+n.note+")":""}${p(o.output,600)?"\n"+p(o.output,600):""}`:`$ ${a}\n${o.killed?"【超时被终止】":"退出码 "+o.code}\n${p(o.output)||"(无输出)"}`}export async function openBrowser(e,t){const n=String(t?.url||"").trim();if(!/^https?:\/\/\S+$/i.test(n))throw new Error("仅支持http/https网址");const r=process.platform,o="darwin"===r?await i("open",[n]):"win32"===r?await i("rundll32",["url.dll,FileProtocolHandler",n]):await i("xdg-open",[n]);return 0!==o.code?`打开浏览器失败(退出码 ${o.code}):${p(o.output)||"未知原因"}`:`已在系统默认浏览器打开:${n}`}export async function openPath(n,r){const o=String(r?.path||"").trim();if(!o)throw new Error("缺少路径path");const a=t.resolve(n||".",o);if(!e.existsSync(a))throw new Error("路径不存在:"+a);const s=e.statSync(a).isDirectory(),c=null!=r?.reveal?!!r.reveal:!s,l=process.platform;let u;return"darwin"===l?u=await i("open",c&&!s?["-R",a]:[a]):"win32"===l?(u=await i("explorer.exe",c&&!s?["/select,"+a]:[a]),u={code:0,output:u.output}):u=await i("xdg-open",[c&&!s?t.dirname(a):a]),0!==u.code?`打开文件管理器失败(退出码 ${u.code}):${p(u.output)||"未知原因"}`:`已在文件管理器中${c&&!s?"定位":"打开"}:${a}`}export async function openApp(e,t){const r=String(t?.name||"").trim(),o=null!=t?.target?String(t.target).trim():"";if(!r)throw new Error("缺少应用名name");if(r.length>120||/[\0-\x1f"'`$\\;|&<>{}[\]*?%]/.test(r))throw new Error("应用名含不允许的字符:"+r);if(o&&/[\0-\x1f"'`$\\;|&<>]/.test(o))throw new Error("target含不允许的字符:"+o);const a=process.platform;if("darwin"===a){const e=await i("open",o?["-a",r,o]:["-a",r]);return 0!==e.code?`启动应用失败(退出码 ${e.code}):${p(e.output)||"未找到应用 "+r}`:`已启动应用:${r}${o?"(打开 "+o+")":""}`}if("win32"===a)return await new Promise(e=>{const t=`start "" "${r}"${o?` "${o}"`:""}`,a=n(process.env.COMSPEC||"cmd.exe",["/d","/s","/c",t],{windowsVerbatimArguments:!0});a.on("error",t=>e(`启动应用失败:${t.message}`)),a.on("close",t=>e(0===t?`已启动应用:${r}`:`启动应用失败(退出码 ${t})`))});if(!/^[\w./+-]+$/.test(r))throw new Error("Linux下应用名需为可执行文件名(字母数字._/+-):"+r);return await new Promise(e=>{const t=n(r,o?[o]:[],{detached:!0,stdio:"ignore"});t.once("error",t=>e(`启动应用失败:${t.message}`)),t.once("spawn",()=>{t.unref(),e(`已启动应用:${r}${o?"(打开 "+o+")":""}`)})})}const m=(e,t)=>({type:"object",properties:e,required:t||[]}),d=e=>({type:"string",description:e}),g=e=>({type:"boolean",description:e}),h=e=>({type:"integer",description:e});export const AUTOMATION_TOOL_SPECS=[{type:"function",function:{name:"install_packages",description:"安装Node.js依赖包(npm/pnpm/yarn按锁文件自动识别;执行前会征询用户同意)。省略packages时安装package.json的全部依赖。仅支持包名[@版本],不接受额外命令行选项。",parameters:m({packages:{type:"array",items:{type:"string"},description:'要安装的包(如 ["lodash","@types/node@^20"]);省略=安装全部依赖'},dev:g("可选:装入devDependencies(默认否)"),global:g("可选:全局安装(需明确packages;默认否)"),manager:d("可选:npm | pnpm | yarn(默认按锁文件自动识别)"),timeout:h("可选:超时毫秒(默认 300000,上限 600000)")},[])}},{type:"function",function:{name:"setup_environment",description:"自动配置项目运行环境(执行前会征询用户同意):检测技术栈(Node/Python/Go/Rust/Maven)→ 检查工具链是否安装 → 安装依赖(npm install、创建 .venv并pip install、go mod download等)。也可用commands显式指定要执行的配置命令(逐条执行,失败即停)。",parameters:m({stacks:{type:"array",items:{type:"string"},description:"可选:只处理这些技术栈(node|python|go|rust|maven)"},commands:{type:"array",items:{type:"string"},description:"可选:显式配置命令列表(优先于自动检测,最多 12 条)"},timeout:h("可选:单步超时毫秒(默认 600000)")},[])}},{type:"function",function:{name:"install_plugin",description:"安装编辑器/IDE插件(执行前会征询用户同意):VS Code扩展(code --install-extension)、JetBrains插件(idea installPlugins,装后需重启IDE)、或npm全局CLI工具(npm install -g)。",parameters:m({target:d("vscode | jetbrains | npm"),id:d("插件标识:VS Code扩展id(如dbaeumer.vscode-eslint)/ JetBrains插件id / npm包名")},["target","id"])}},{type:"function",function:{name:"open_browser",description:"用系统默认浏览器打开一个http/https网址(执行前会征询用户同意)。适合把文档、本地预览页、报告直接呈现给用户。",parameters:m({url:d("完整网址(http/https)")},["url"])}},{type:"function",function:{name:"open_path",description:"在系统文件管理器(访达/资源管理器)中打开目录或定位文件(执行前会征询用户同意)。",parameters:m({path:d("目录或文件路径(相对项目根或绝对路径)"),reveal:g("可选:定位(选中)该文件而非打开;文件默认定位、目录默认打开")},["path"])}},{type:"function",function:{name:"open_app",description:'启动本机任意应用程序(执行前会征询用户同意),可附带要打开的文件或网址。macOS用应用名(如 "Safari"、"Visual Studio Code"),Windows用可启动名,Linux用可执行文件名。',parameters:m({name:d("应用名/可执行名"),target:d("可选:让该应用打开的文件路径或URL")},["name"])}}];export const AUTOMATION_EXECUTORS={install_packages:installPackages,setup_environment:setupEnvironment,install_plugin:installPlugin,open_browser:openBrowser,open_path:openPath,open_app:openApp};
|
package/src/autorun.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export function planPrompt(e){return`目标:${e}\n\n请先自动规划(不要执行任何改动):\n1) 用只读工具(read_file/glob/grep/search_code/list_files)充分调研现状;\n2) 把达成目标的完整步骤用
|
|
1
|
+
export function planPrompt(e){return`目标:${e}\n\n请先自动规划(不要执行任何改动):\n1) 用只读工具(read_file/glob/grep/search_code/list_files)充分调研现状;\n2) 把达成目标的完整步骤用todo_write列成分步计划(涉及哪些文件、每步改动要点、如何验证、风险点);\n3) 用一段话概述方案要点,然后停下——等待用户确认后才会开始执行。`}export function execPrompt(){return"计划已获用户确认。现在开始自动逐步执行你制定的计划:每完成一步用todo_write更新状态并简要自检;遇到与计划不符的情况及时调整并说明;改动后尽量运行测试/构建自检,失败要定位修复。全部完成后总结改了什么、如何验证。"}export async function runAutoFlow({goal:e,config:o,permissions:n,cwd:t,runTurnOnce:a,emit:r=()=>{}}){const l=n.isPlanMode?!!n.isPlanMode():!!o.planMode;r("自动规划中:先只读调研并产出分步计划…"),o.planMode=!0,n.setPlanMode?.(!0);try{await a(planPrompt(e))}finally{o.planMode=l,n.setPlanMode?.(l)}const p=await n.request("auto_execute",{goal:e},t,!1);return p.allow?(o.planMode=!1,n.setPlanMode?.(!1),r("已确认,开始自动执行计划…"),await a(execPrompt()),{approved:!0}):(r("已取消自动执行:计划已生成但未执行"+(p.reason?`(${p.reason})`:"")+"。可补充要求后重新 /auto,或 /plan approve按现有计划执行。"),{approved:!1})}
|
package/src/banner.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as o,colorEnabled as t}from"./colors.js";const n={H:["█ █","█ █","█████","█ █","█ █"],U:["█ █","█ █","█ █","█ █","█████"],I:["█████"," █ "," █ "," █ ","█████"],C:["█████","█ ","█ ","█ ","█████"],A:["█████","█ █","█████","█ █","█ █"]," ":[" "," "," "," "," "]};const r=t&&/truecolor|24bit/i.test(process.env.COLORTERM||""),s=(o,t,n)=>Math.round(o+(t-o)*n);function e(o,n,e){if(!t)return o;const u=[...o],c=u.length;let a="";for(let o=0;o<c;o++){const t=u[o];if(" "===t){a+=" ";continue}if(!r){a+=`[36m${t}`;continue}const l=c<=1?0:o/(c-1);a+=`[38;2;${s(n[0],e[0],l)};${s(n[1],e[1],l)};${s(n[2],e[2],l)}m${t}`}return a+"[0m"}const u=" ",c=[0,229,255],a=[168,85,247];export function renderWelcome({model:r,cwd:s,version:l,platform:i,yolo:h,consult:p,ink:y}={}){const f=process.stdout.columns||60,g=o.gray("─".repeat(Math.min(Math.max(f-2,24),50))),m=[""];if(t)for(const o of function(o){const t=["","","","",""];return[...o].forEach((o,r)=>{const s=n[o]||n[" "];for(let o=0;o<5;o++)t[o]+=(r?" ":"")+s[o]}),t}("HUICAI"))m.push(u+e(o,c,a));else m.push(" H U I C A I");return m.push(""),m.push(u+o.bold("HuiCai AI")+o.gray(" · 终端智能体")+(l?o.gray(" v"+l):"")+(y?o.gray(" · ink"):"")),m.push(u+g),m.push(u+o.gray(i?"目标 ":"模型 ")+o.cyan(r||o.gray("(未设置,/model
|
|
1
|
+
import{c as o,colorEnabled as t}from"./colors.js";const n={H:["█ █","█ █","█████","█ █","█ █"],U:["█ █","█ █","█ █","█ █","█████"],I:["█████"," █ "," █ "," █ ","█████"],C:["█████","█ ","█ ","█ ","█████"],A:["█████","█ █","█████","█ █","█ █"]," ":[" "," "," "," "," "]};const r=t&&/truecolor|24bit/i.test(process.env.COLORTERM||""),s=(o,t,n)=>Math.round(o+(t-o)*n);function e(o,n,e){if(!t)return o;const u=[...o],c=u.length;let a="";for(let o=0;o<c;o++){const t=u[o];if(" "===t){a+=" ";continue}if(!r){a+=`[36m${t}`;continue}const l=c<=1?0:o/(c-1);a+=`[38;2;${s(n[0],e[0],l)};${s(n[1],e[1],l)};${s(n[2],e[2],l)}m${t}`}return a+"[0m"}const u=" ",c=[0,229,255],a=[168,85,247];export function renderWelcome({model:r,cwd:s,version:l,platform:i,yolo:h,consult:p,ink:y}={}){const f=process.stdout.columns||60,g=o.gray("─".repeat(Math.min(Math.max(f-2,24),50))),m=[""];if(t)for(const o of function(o){const t=["","","","",""];return[...o].forEach((o,r)=>{const s=n[o]||n[" "];for(let o=0;o<5;o++)t[o]+=(r?" ":"")+s[o]}),t}("HUICAI"))m.push(u+e(o,c,a));else m.push(" H U I C A I");return m.push(""),m.push(u+o.bold("HuiCai AI")+o.gray(" · 终端智能体")+(l?o.gray(" v"+l):"")+(y?o.gray(" · ink"):"")),m.push(u+g),m.push(u+o.gray(i?"目标 ":"模型 ")+o.cyan(r||o.gray("(未设置,/model设置)"))+(i?o.gray(" · 平台编排(无本机工具)"):"")),m.push(u+o.gray("目录 ")+s),p&&m.push(u+o.gray("咨询 ")+o.magenta(p)+o.gray(" · 混合模式(本机工具 + 平台咨询)")),h&&m.push(u+o.yellow("⚠ 自动放行(yolo) 已开:写文件/执行命令不再逐次确认")),m.push(""),m.push(u+o.magenta("✦ ")+o.gray("/help命令 · @文件 引用 · !命令 直通shell · Ctrl+C中断")),m.join("\n")}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TINY_PNG="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFklEQVQIW2NkYGD4z0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC";const e=3e4;export const VERDICT={OK:"ok",UNSUPPORTED:"unsupported",NOT_CONFIGURED:"not_configured",ERROR:"error",SKIPPED:"skipped"};export function classifyFailure(e,t){const n=String(t||"").toLowerCase();return/unknown variant|unsupported|not supported|invalid.*type|无法识别|不支持/.test(n)?{verdict:VERDICT.UNSUPPORTED,next:"上游渠道不认识这个请求形状——需要渠道/采购换一条真正具备该能力的上游。"}:404===e||/model.*not found|no such model|未配置|模型不存在/.test(n)?{verdict:VERDICT.NOT_CONFIGURED,next:"平台没有这个模型——去模型广场/系统配置里上架并指向可用渠道。"}:401===e||403===e?{verdict:VERDICT.ERROR,next:"鉴权失败——检查登录状态与API Key权限。"}:402===e||/quota|insufficient|余额|额度/.test(n)?{verdict:VERDICT.ERROR,next:"额度或余额问题——这不是能力问题,充值后重测。"}:502===e||503===e||504===e?{verdict:VERDICT.UNSUPPORTED,next:"网关到上游这一段失败——多半是上游不接受该请求,需渠道侧核实。"}:{verdict:VERDICT.ERROR,next:"未归类的失败——把下面的原始响应交给服务端排查。"}}const t=e=>({Authorization:`Bearer ${e}`,"Content-Type":"application/json"});async function n(e){try{return(await e.text()).slice(0,400)}catch{return""}}export async function probeMultimodalEmbedding(i,a={}){const r="跨模态嵌入(C1/C2 解除条件)";if(!i?.baseUrl||!i?.apiKey)return{name:r,verdict:VERDICT.SKIPPED,detail:"未登录平台",next:"先huicai login再测。"};const o=String(i.embedModel||"").trim();if(!o)return{name:r,verdict:VERDICT.SKIPPED,detail:"未指定嵌入模型",next:"带上知识库实际使用的嵌入模型再测:--embed-model <模型名>。用别的模型测通了也不算数——入库与检索必须同一个模型同一个维度。"};const s=a.fetchImpl||fetch;try{const a=await s(`${i.baseUrl}/v1/embeddings`,{method:"POST",headers:t(i.apiKey),body:JSON.stringify({model:o,input:[],images:[TINY_PNG],input_type:"search_document"}),signal:AbortSignal.timeout(e)});if(!a.ok){const e=await n(a);return{name:r,...classifyFailure(a.status,e),detail:`HTTP ${a.status} ${e}`,model:o}}const l=await a.json(),c=l?.data?.[0]?.embedding;return Array.isArray(c)&&c.length?{name:r,verdict:VERDICT.OK,model:o,dims:c.length,detail:`通了,维度 ${c.length}`,next:`可以把知识库的embed-route切到native了。切之前确认既有库的向量维度也是 ${c.length}——维度不一致的库必须重建,否则检索结果会毫无规律地不准。`}:{name:r,verdict:VERDICT.ERROR,model:o,detail:"返回 200 但没有向量",next:"上游返回了成功但内容不对——这比明确报错更危险,务必让服务端确认它到底做了什么。"}}catch(e){return{name:r,verdict:VERDICT.ERROR,model:o,detail:String(e?.message||e),next:"请求没发出去或超时——先确认平台可达。"}}}export async function probeVisionChat(i,a={}){const r="视觉对话(image_url是否被接受)";if(!i?.baseUrl||!i?.apiKey)return{name:r,verdict:VERDICT.SKIPPED,detail:"未登录平台",next:"先huicai login再测。"};const o=String(i.visionModel||i.model||"").trim();if(!o)return{name:r,verdict:VERDICT.SKIPPED,detail:"未指定模型",next:"带上 --vision-model <模型名> 再测。"};const s=a.fetchImpl||fetch;try{const a=await s(`${i.baseUrl}/v1/chat/completions`,{method:"POST",headers:t(i.apiKey),body:JSON.stringify({model:o,max_tokens:8,messages:[{role:"user",content:[{type:"text",text:"这是什么颜色?一个词回答。"},{type:"image_url",image_url:{url:TINY_PNG}}]}]}),signal:AbortSignal.timeout(e)});if(!a.ok){const e=await n(a),t=classifyFailure(a.status,e),i=/unknown variant/.test(e.toLowerCase())&&/image_url/.test(e.toLowerCase());return{name:r,...t,model:o,detail:`HTTP ${a.status} ${e}`,next:i?"正是历史上那个「502 unknown variant image_url」——上游根本不认识image_url这种内容块。这在供应商文档里查不到,只能靠换渠道解决。":t.next}}return{name:r,verdict:VERDICT.OK,model:o,detail:"通了",next:"视觉对话可用;多模态入库的described路径前提已满足。"}}catch(e){return{name:r,verdict:VERDICT.ERROR,model:o,detail:String(e?.message||e),next:"请求没发出去或超时。"}}}export async function probeRealtimeVoice(t,i={}){const a="实时语音双向流(C3 解除条件)";if(!t?.baseUrl||!t?.apiKey)return{name:a,verdict:VERDICT.SKIPPED,detail:"未登录平台",next:"先huicai login再测。"};const r=i.fetchImpl||fetch;try{const i=await r(`${t.baseUrl}/v1/models`,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(e)});if(!i.ok){const e=await n(i);return{name:a,...classifyFailure(i.status,e),detail:`HTTP ${i.status} ${e}`}}const o=await i.json(),s=(Array.isArray(o?.data)?o.data:[]).map(e=>String(e?.id||"")).filter(Boolean),l=s.filter(e=>/realtime|live|流式|duplex/i.test(e));return l.length?{name:a,verdict:VERDICT.OK,detail:`发现候选:${l.join("、")}`,next:"清单里有候选,但**上架 ≠ 能用**:接下来必须真建一条WebSocket跑一轮音频再下结论——这正是上次栽跟头的地方。"}:{name:a,verdict:VERDICT.NOT_CONFIGURED,detail:`模型清单 ${s.length} 个,没有实时语音类`,next:"需要渠道上架一个支持WebSocket双向流的实时语音模型。在此之前C3 只能走分段流式(已可用,见voice.transcribeStream)。"}}catch(e){return{name:a,verdict:VERDICT.ERROR,detail:String(e?.message||e),next:"请求没发出去或超时。"}}}export async function probeAll(e,t={}){return[await probeVisionChat(e,t),await probeMultimodalEmbedding(e,t),await probeRealtimeVoice(e,t)]}const i={[VERDICT.OK]:"✅",[VERDICT.UNSUPPORTED]:"❌",[VERDICT.NOT_CONFIGURED]:"⚠️",[VERDICT.ERROR]:"❌",[VERDICT.SKIPPED]:"·"};export function formatProbes(e){const t=["渠道能力实测(docs/29 C级三项的解除条件)",""];for(const n of e||[])t.push(`${i[n.verdict]||"?"} ${n.name}${n.model?` [${n.model}]`:""}`),n.detail&&t.push(` ${n.detail}`),n.next&&t.push(` 下一步:${n.next}`),t.push("");const n=e||[],a=n.filter(e=>e.verdict===VERDICT.SKIPPED),r=n.filter(e=>e.verdict===VERDICT.UNSUPPORTED||e.verdict===VERDICT.NOT_CONFIGURED);return a.length===n.length?t.push("结论:一项都没能测——上面每项都写了为什么。没测过的不叫没问题。"):t.push(r.length?`结论:${r.length} 项仍被阻塞——工程侧已就位,等的是渠道。`:`结论:已测的 ${n.length-a.length} 项均未发现阻塞`+(a.length?`(另有 ${a.length} 项没测)`:"")+"。切换配置前请先读各项的「下一步」。"),t.push("注意:本探针只测「能不能调通」,不测质量。调通之后仍要用真实语料验一遍检索效果。"),t.join("\n")}
|
package/src/checkpoints.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:fs";import
|
|
1
|
+
import e from"node:fs";import n from"node:path";function t(e){try{return e?JSON.parse(JSON.stringify(e)):{}}catch{return{}}}function s(e){const n=String(e||"").replace(/\s+/g," ").trim();return n?n.length>60?n.slice(0,60)+"…":n:"(空)"}export function createCheckpoints(){const o=[];let r=0;function i(e){const n=new Map;for(let t=e;t<o.length;t++)for(const e of o[t].journal)e&&e.path&&!n.has(e.path)&&n.set(e.path,e);return n}return{list:o,snapshot:function({session:e,label:n}){const i={id:++r,time:Date.now(),label:s(n),messagesLen:e.messages.length,compaction:t(e.compaction),journal:[]};return o.push(i),o.length>100&&o.splice(0,o.length-100),i.journal},restore:function(s,r,{session:c,cwd:a}){const l=o.findIndex(e=>e.id===s);if(l<0)return{ok:!1,error:`检查点 ${s} 不存在(/rewind查看可用检查点)`};const f=o[l],p={ok:!0,id:s,mode:r,conversation:!1,filesRestored:[],filesDeleted:[],filesFailed:[],filesSkipped:[]};if("code"===r||"both"===r)for(const[t,s]of i(l)){if(s.tooLarge){p.filesSkipped.push(t);continue}const o=n.resolve(a,t);try{s.existed?(e.mkdirSync(n.dirname(o),{recursive:!0}),"base64"===s.encoding?e.writeFileSync(o,Buffer.from(s.before,"base64")):e.writeFileSync(o,s.before,"utf8"),p.filesRestored.push(t)):(e.rmSync(o,{force:!0}),p.filesDeleted.push(t))}catch(e){p.filesFailed.push(`${t}:${e.message}`)}}return"chat"!==r&&"both"!==r||(f.messagesLen<=c.messages.length&&(c.messages.length=f.messagesLen),c.compaction=t(f.compaction),c.lastPromptTokens=0,p.conversation=!0,o.length=l),p},changedFilesSince:i}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:fs";import e from"node:path";import{execFileSync as n}from"node:child_process";import{BUNDLED_SKILLS_DIR as r}from"./skills.js";import{ensureRuntime as s,execFileCapture as i,runtimeStatus as o,venvPython as a}from"./pyruntime.js";export function engineDir(){return e.join(r,"code-quality","scripts")}let c=null;export function probeSystemPython(){if(null!==c)return c;const t="win32"===process.platform?["python","py"]:["python3","python"];for(const e of t)try{const t=n(e,["-c","import sys;print(sys.version_info[0]*100+sys.version_info[1])"],{encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]});if(Number(String(t).trim())>=308)return c=e,e}catch{}return c="",""}async function p(n,r,c,{timeoutMs:p=12e4,config:u={}}={}){const l=e.join(engineDir(),r);if(!t.existsSync(l))throw new Error("代码体检引擎缺失:"+l);const h=(o(u).usable&&t.existsSync(a())?a():"")||probeSystemPython();if(h){const t=await i(h,["-X","utf8",l,...c],{cwd:n,timeoutMs:p,env:{PYTHONIOENCODING:"utf-8"}});if(t.killed)throw new Error("体检超时被终止(目标太大时用path缩小范围)");return t.output||""}await s({log:()=>{},config:u});const f=a();if(!t.existsSync(f))throw new Error("本机没有可用的Python,代码体检无法运行");return(await i(f,["-X","utf8",l,...c],{cwd:n,timeoutMs:p,env:{PYTHONIOENCODING:"utf-8"}})).output||""}const u=new Set([".js",".mjs",".cjs",".jsx",".ts",".tsx",".mts",".cts",".vue",".py",".java",".go",".rs",".cs",".php",".c",".h",".cc",".cpp",".hpp",".rb",".kt",".swift",".scala"]);export function isCodeFile(t){const n=String(t||"").toLowerCase();return!/\.(min\.js|min\.css|bundle\.js|d\.ts)$/.test(n)&&u.has(e.extname(n))}function l(t){const e=String(t||""),n=e.match(/\{[\s\S]*\}\s*$/);if(!n)throw new Error("引擎输出异常:"+e.slice(-400));return JSON.parse(n[0])}function h(n,r){const s=e.resolve(n,String(r||".").trim()||".");if(s!==n&&!s.startsWith(n+e.sep))throw new Error("路径越出工作目录:"+r);if(!t.existsSync(s))throw new Error("路径不存在:"+r);return s}export function makeCodeTools({cwd:t,config:n={}}){return[{readOnly:!0,spec:{type:"function",function:{name:"code_inspect",description:"给代码做体检——**写完或改完代码后用它自查**,不要凭肉眼扫一遍就说「已检查」。\n覆盖八类共 143 条规则(含Java 33、Python 30、JS/TS/Vue 20、C# 20 条专属):\n· 逻辑:空捕获吞异常、恒真条件、条件里写了赋值、自我比较、死代码、Python可变默认参数、循环内await串行、Java用 == 比字符串、浮点比相等;\n· 注释:**文档写的形参函数里根本没有**、文档说有返回值却从不返回、文档名字是从别的函数复制来的、被注释掉的死代码、无主TODO、长函数零注释;\n· 可读性:函数过长、嵌套过深、圈复杂度、参数过多、魔法数字;\n· 结构:重复代码块、硬编码本机路径、调试输出残留;\n· 测试:`.only` 残留(会让CI只跑一个用例)、恒真断言、没有断言的测试、公共函数无覆盖;\n· 安全:硬编码密钥、SQL拼接、命令注入、eval、弱随机数做令牌;\n· 一致性:同名函数在多处签名漂移;\n· **框架用法(仅Java)**:@Transactional落在private/final上(代理包不住,注解完全不生效)、抛受检异常却没配rollbackFor(Spring默认只回滚RuntimeException)、事务方法内catch住异常不再抛(事务不回滚)、事务内起新线程、**同类内自调用 @Transactional/@Async/@Cacheable**(走this不走代理,注解等于没写)、字段注入、单例Bean可变状态、MyBatis ${} 拼接;\n· **Java语言陷阱**:new BigDecimal(double) 精度、BigDecimal用equals、装箱类型 == 比较、static SimpleDateFormat非线程安全、ThreadLocal无remove、Executors快捷工厂、`YYYY` 周年、Arrays.asList后修改、for-each里增删、循环内String +=、整数除法赋给double、finally里return、equals/hashCode不成对;\n· **Python专属(走真AST,判得最准)**:类属性是list/dict(所有实例共享同一个)、闭包捕获循环变量(全拿到最后一次的值)、**except顺序错导致子类分支永不执行**、遍历时增删容器、`is` 比字面量、生产代码用assert(-O会删掉)、`open()` 不写encoding(Windows是GBK)、naive datetime、`Decimal(浮点)`、`os.path.join` 后段绝对路径吞前缀、async里混同步库;\n· **pandas(事故不改外观只改数字)**:链式赋值df[a][b]=x改到副本、读表不指定dtype(前导零丢失、长编号变科学计数法且精度已丢)、df.append(2.0 已移除)、iterrows、inplace=True;\n· **JS/TS专属**:`.forEach(async …)`(forEach不等Promise,循环瞬间跑完)、`a?.b.c`(可选链只防了第一层,后半段照样崩)、`x === NaN`(永远false)、`for...in` 遍历数组、排数字忘了比较函数、`delete arr[i]`、`JSON.parse` 无防护;TS类型逃逸:`as any`、`@ts-ignore` 无理由、函数返回any;\n· **Vue 3(含 <template>)**:v-for无 :key、:key用index、**v-if与v-for写在同一元素**(Vue 3 里v-if先执行,循环变量还不存在)、v-html、直接修改props、解构reactive(响应性断掉);\n· **C# 专属**:`async void`(异常无人能接、直接终结进程)、`.Result`/`.Wait()`(有同步上下文时必然死锁,而控制台与单测里一切正常)、**`throw ex;`**(堆栈从这一行重新开始,原始出错位置丢失;正确写法是 `throw;`)、`DateTime.Now`、`double` 算金额、`lock(this)`、`static Random`、LINQ延迟查询被多次枚举、资源不走using、循环里查库(N+1)、DbContext注册成Singleton、`FromSqlRaw` 拼接、手new HttpClient、`async void` 测试方法(框架不等它,断言没跑用例就绿了)。\n深度支持JS/TS/Vue、Python、Java;其它语言跑语言无关的那部分并在报告里标明是降级检查。\n\n**必修项清零之前不要对用户说「已完成」**。\nfix=true只会自动修五类改法唯一、不改变行为的问题(.only残留、行尾空白、被注释掉的死代码、调试输出、空finally);逻辑、注释与实现不符、缺测试、安全这些**必须你自己改**——工具不会假装能修它修不了的东西。",parameters:{type:"object",properties:{path:{type:"string",description:"要体检的文件或目录(相对工作目录)。不给则整个工作目录。"},fix:{type:"boolean",description:"是否自动修那五类可确定性修复的问题(默认否,只报不改)"},only:{type:"string",description:"只看某几类,逗号分隔。可选:logic逻辑 / comment注释 / readability可读性 / structure结构 / test测试 / security安全 / consistency一致性 / framework框架用法(Spring/MyBatis,仅Java)"},skip:{type:"string",description:"排除某几类,逗号分隔(同上取值)"},strict:{type:"boolean",description:"严格模式:把「建议」提升为「必修」(交付前把关用)"},max:{type:"number",description:"最多展开几条明细,默认 40"}},required:[]}}},run:async r=>{let s;try{s=h(t,r?.path)}catch(t){return t.message}const i=["--json"];let o;r?.only&&i.push(`--only=${String(r.only).replace(/[^a-z,]/g,"")}`),r?.skip&&i.push(`--skip=${String(r.skip).replace(/[^a-z,]/g,"")}`),r?.strict&&i.push("--strict");try{o=l(await p(t,"code_check.py",[s,...i],{config:n}))}catch(t){return"体检未能运行:"+t.message}if(o.error)return o.error;const a=[],c=Number(r?.max)>0?Math.min(Number(r.max),200):40,u=(o.issues||[]).slice(0,c),f=[];for(const t of(o.report||"").split("\n")){if(t.startsWith("──"))break;t.trim()&&f.push(t)}if(a.push(f.join("\n")),u.length){a.push("");for(const n of u){const r=e.relative(t,n.path)||n.path,s={error:"必修",warn:"建议",info:"提示"}[n.level]||n.level;a.push(`[${s}][${n.rule}] ${r}:${n.line}${n.unit?` 〔${n.unit}〕`:""}`),n.excerpt&&a.push(` │ ${n.excerpt}`),a.push(` ${n.msg}`),n.hint&&a.push(` → ${n.hint}`)}(o.issues||[]).length>c&&a.push(`…… 还有 ${o.issues.length-c} 条(用only= 分类逐类处理,或把path缩到具体文件)`)}if(r?.fix)try{const r=l(await p(t,"code_fix.py",[s,"--json","--write"],{config:n})),i=r.files||[],o=i.reduce((t,e)=>t+(e.edits||[]).length,0);if(a.push(""),o){a.push(`【自动修复】已改 ${o} 处,涉及 ${i.length} 个文件:`);for(const n of i.slice(0,10)){const r=e.relative(t,n.path)||n.path,s=[...new Set((n.edits||[]).map(t=>t.rule))].join("、");a.push(` · ${r}(${n.edits.length} 处:${s})`),n.aborted?a.push(` ⚠ ${n.aborted}`):(n.remaining||[]).length&&a.push(` ⚠ 复检仍在响:${n.remaining.join("、")},请人工确认`)}a.push("剩下的问题(逻辑、注释与实现不符、缺测试、安全)工具修不了,需要你逐条改。")}else a.push("【自动修复】没有可自动修的问题。可自动修的只有:"+(r.fixable_rules||[]).join("、")+";上面报出的其它问题需要你自己判断怎么改。")}catch(t){a.push("【自动修复】未能运行:"+t.message)}return a.filter(Boolean).join("\n")}},{readOnly:!0,spec:{type:"function",function:{name:"test_plan",description:"在动手写单元测试之前用它算清**该测什么**。\n直接开写的产出通常是一堆「调一次、断言不为null」的用例:覆盖率上去了,缺陷一个也拦不住。\n这件工具给出:哪些对外函数测试里从没出现过(按圈复杂度排序,分支多的先补)、每个函数至少需要几个用例才能覆盖各分支、每个参数该试哪些边界值(按名字与用法推断)、有没有异常路径/异步失败路径/外部依赖需要替身、以及**这个项目的测试框架、放在哪个目录、怎么命名、参照哪个现成文件**——照着既有约定写,不要自创一套。\n写完用例后再跑一次code_inspect:恒真断言、没有断言的用例、`.only` 残留由那边的规则管。",parameters:{type:"object",properties:{path:{type:"string",description:"要补测试的文件或目录(相对工作目录)"}},required:["path"]}}},run:async e=>{let r;try{r=h(t,e?.path)}catch(t){return t.message}try{return(await p(t,"code_testplan.py",[r,`--root=${t}`],{config:n})).trim()||"未能生成测试计划(目标里没有对外可见的函数)。"}catch(t){return"测试计划未能生成:"+t.message}}}]}export async function inspectForTurn(e,n,r={}){const s=(n||[]).filter(e=>{try{return t.existsSync(e)&&t.statSync(e).isFile()}catch{return!1}});if(!s.length)return null;try{const t=l(await p(e,"code_check.py",[...s,"--json"],{config:r,timeoutMs:45e3})),n=(t.issues||[]).filter(t=>"error"===t.level);return{counts:t.counts||{},issues:n,stats:t.stats||{}}}catch{return null}}
|
package/src/codeindex.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:fs";import t from"node:path";import n from"node:zlib";import s from"node:crypto";import{execFileSync as r}from"node:child_process";import{CONFIG_DIR as o}from"./config.js";import{embed as c}from"./api.js";export const IGNORE=new Set([".git","node_modules","dist","build","target","out",".next",".nuxt",".venv","venv","__pycache__",".idea",".gradle",".mvn","coverage",".cache","vendor","bower_components","Pods","DerivedData",".turbo",".parcel-cache",".terraform",".pytest_cache",".mypy_cache",".tox",".angular",".svelte-kit",".output",".serverless",".svn",".hg"]);const i=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".java",".kt",".py",".go",".rs",".rb",".php",".c",".h",".cpp",".hpp",".cs",".swift",".scala",".sql",".vue",".svelte",".sh",".css",".scss",".html",".md",".yaml",".yml",".json",".toml",".xml",".properties",".gradle",".groovy",".proto",".graphql",".gql",".tf",".ini",".cfg",".conf",".dart",".lua",".clj",".ex",".exs",".erl",".hs",".jl",".pl",".pm",".r",".dockerfile"]),a=/(?:\.min\.(?:js|css)$)|(?:\.bundle\.js$)|(?:\.map$)|(?:(?:^|\/)(?:package-lock\.json|pnpm-lock\.yaml|npm-shrinkwrap\.json)$)/i;export const MAX_FILE_BYTES=524288;const l=t.join(o,"cache","index"),u=new Map;export function tokenize(e){const t=String(e),n=t.replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().split(/[^a-z0-9_]+/).filter(e=>e.length>=2&&e.length<=40),s=t.match(/[一-鿿]/g);if(s)for(const e of s)n.push(e);const r=t.match(/[一-鿿]{2,}/g);if(r)for(const e of r)for(let t=0;t<e.length-1;t++)n.push(e.slice(t,t+2));return n}function*f(n,s=0,r=14){let o;try{o=e.readdirSync(n,{withFileTypes:!0})}catch{return}for(const e of o){if(IGNORE.has(e.name)||e.name.startsWith("."))continue;const o=t.join(n,e.name);e.isDirectory()?s<r&&(yield*f(o,s+1,r)):e.isFile()&&i.has(t.extname(e.name).toLowerCase())&&(yield o)}}let p=null;export function ignoreGlobs(){return[...IGNORE].flatMap(e=>["-g",`!**/${e}/**`,"-g",`!${e}/**`])}export function listSourceFiles(e){let n=null;if(function(){if(null!==p)return p;try{r("rg",["--version"],{stdio:"ignore"}),p=!0}catch{p=!1}return p}())try{n=r("rg",["--files",...ignoreGlobs()],{cwd:e,encoding:"utf8",maxBuffer:64<<20,stdio:["ignore","pipe","ignore"]}).split("\n").map(e=>e.replace(/\\/g,"/")).filter(Boolean)}catch{n=null}if(!n){n=[];for(const s of f(e))n.push(t.relative(e,s).split(t.sep).join("/"))}const s=[];for(const r of n)i.has(t.extname(r).toLowerCase())&&(a.test(r)||s.push({rel:r,abs:t.join(e,r)}));return s}const h=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".java",".kt",".py",".go",".rs",".rb",".php",".c",".h",".cpp",".hpp",".cs",".swift",".scala",".vue",".svelte",".dart",".lua",".clj",".ex",".exs",".erl",".hs",".jl",".groovy"]),d=/(?:^|\/)(?:tests?|__tests__|spec|specs|fixtures?|mocks?|__mocks__|generated|gen|snapshots?|migrations?|node_modules|vendor)(?:\/|$)/i;export function indexPriority(e){const n=e.toLowerCase();let s=0;return h.has(t.extname(n))?s-=100:s+=25,d.test(n)&&(s+=60),s+=2*(n.split("/").length-1),s}const m=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".java",".kt",".go",".rs",".c",".h",".cpp",".hpp",".cc",".cs",".swift",".scala",".php",".dart",".groovy"]);const g=/^(?:(?:export|default|public|private|protected|internal|static|final|abstract|sealed|open|override|async|suspend|inline|const|unsafe|extern|virtual|partial|pub|data|companion)\s+)*(?:function|func|fn|def|fun|class|interface|enum|record|struct|trait|impl|module|mod|namespace|protocol|extension|object|actor|type)\b(?:\s+([A-Za-z_]\w*))?/,y=/^(?:@[\w.]+(?:\([^)]*\))?\s+)*(?:(?:public|private|protected|internal|static|final|abstract|synchronized|native|override|open|suspend|inline|virtual|sealed|async|unsafe|extern)\s+)+[\w<>\[\],.?&:*\s]*?\b([A-Za-z_]\w*)\s*\([^;{]*\)[^;]*?(?:\{|$)/,x=/^(?!(?:if|for|while|switch|catch|return|do|else|try|with|function|class|new|await|yield|throw|case|default|synchronized)\b)(?:static\s+|async\s+|get\s+|set\s+|readonly\s+|public\s+|private\s+|protected\s+|\*\s*)*([A-Za-z_$][\w$]*)\s*\([^;]*\)\s*(?::\s*[\w<>\[\].|&, ]+)?\s*\{/,w=/^(?:export\s+)?(?:default\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:function\b|\([^)]*\)\s*(?::\s*[\w<>\[\].|&, ]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,b=/^(?:async\s+)?(?:def|class)\s+([A-Za-z_]\w*)/,z=/^(?:def|class|module)\s+([A-Za-z_][\w:]*)/;function j(e,t){if("py"===e){const e=b.exec(t);return e?{name:e[1]}:null}if("ruby"===e){const e=z.exec(t);return e?{name:e[1]}:null}let n=g.exec(t);return n?{name:n[1]||""}:(n=y.exec(t),n?{name:n[1]}:(n=x.exec(t),n?{name:n[1]}:(n=w.exec(t),n?{name:n[1]}:null)))}function v(e,t=0){const n=[];for(let s=0;s<e.length;s+=40){const r=Math.min(e.length,s+50),o=e.slice(s,r).join("\n"),c=tokenize(o);if(c.length){const e=new Map;for(const t of c)e.set(t,(e.get(t)||0)+1);n.push({start:t+s+1,end:t+r,len:c.length,text:o,tf:[...e]})}if(r>=e.length)break}return n}function k(e,t){const n=function(e){return m.has(e)?"brace":".py"===e?"py":".rb"===e?"ruby":null}(e);if(!n)return null;const s=[];for(let e=0;e<t.length;e++){const r=t[e].trim();if(!r)continue;const o=j(n,r);o&&s.push({line:e,sym:o.name||""})}if(!s.length)return null;for(let e=0;e<s.length;e++){let n=s[e].line;const r=e>0?s[e-1].line+1:0;for(;n>r&&/^@[\w.]/.test(t[n-1].trim());)n--;s[e].start=n}const r=[];s[0].start>0&&r.push({start:0,sym:""});for(const e of s)r.push({start:e.start,sym:e.sym});const o=[];for(let e=0;e<r.length;e++){const n=r[e].start,s=e+1<r.length?r[e+1].start:t.length;if(s<=n)continue;const c=t.slice(n,s);if(c.length>160){const t=v(c,n);t.length&&r[e].sym&&(t[0].sym=r[e].sym),o.push(...t);continue}const i=c.join("\n"),a=tokenize(i);if(!a.length)continue;const l=new Map;for(const e of a)l.set(e,(l.get(e)||0)+1);const u={start:n+1,end:s,len:a.length,text:i,tf:[...l]};r[e].sym&&(u.sym=r[e].sym),o.push(u)}return o.length?o:null}export function chunkText(e,n=""){const s=e.split("\n"),r=n?t.extname(n).toLowerCase():"";return r&&k(r,s)||v(s,0)}export function buildIndexCore(t,n){const s=Date.now()+2500,r=listSourceFiles(t).map(t=>{let n=1/0,s=0;try{const r=e.statSync(t.abs);n=r.size,s=r.mtimeMs}catch{}return{...t,size:n,mtimeMs:s,prio:indexPriority(t.rel)}}).filter(e=>e.size<=524288).sort((e,t)=>e.prio-t.prio||e.size-t.size),o=new Set(r.map(e=>e.rel)),c=[],i=new Map,a={};let l=0,u=0,f=0,p=0,h=!1;for(const t of r){if(c.length>=12e3||Date.now()>s){h=!0;break}const r=n&&n[t.rel];let o;if(r&&r.mtimeMs===t.mtimeMs&&r.size===t.size&&Array.isArray(r.chunks))o=r.chunks,f++;else{let n;try{const s=e.readFileSync(t.abs);if(s.includes(0))continue;n=s.toString("utf8")}catch{continue}o=chunkText(n,t.rel),p++}u++,a[t.rel]={mtimeMs:t.mtimeMs,size:t.size,chunks:o};const d=new Set(tokenize(t.rel));for(const e of o){const n=new Map(e.tf);for(const[t]of e.tf)i.set(t,(i.get(t)||0)+1);c.push({file:t.rel,start:e.start,end:e.end,len:e.len,text:e.text,tf:n,pathTokens:d,sym:e.sym}),l+=e.len}}if(h&&n)for(const e in n)!a[e]&&o.has(e)&&(a[e]=n[e]);const d=n?Object.keys(n).length:-1,m=p>0||Object.keys(a).length!==d;return{index:{chunks:c,df:i,N:c.length,avgLen:c.length?l/c.length:1,truncated:h,indexedFiles:u,totalFiles:r.length,reused:f,rechunked:p},filesMap:a,changed:m}}export function buildIndex(e){return buildIndexCore(e,null).index}function _(e){const n=s.createHash("sha1").update(t.resolve(e)).digest("hex").slice(0,16);return t.join(l,n+".json.gz")}function S(s,r){try{e.mkdirSync(l,{recursive:!0});const o=_(s),c=n.gzipSync(JSON.stringify({v:2,cwd:t.resolve(s),at:Date.now(),files:r}));e.writeFileSync(o+".tmp",c),e.renameSync(o+".tmp",o),function(){try{const n=e.readdirSync(l).filter(e=>e.endsWith(".json.gz"));if(n.length<=24)return;const s=n.map(n=>{try{return{f:n,t:e.statSync(t.join(l,n)).mtimeMs}}catch{return null}}).filter(Boolean).sort((e,t)=>e.t-t.t);for(const{f:n}of s.slice(0,s.length-24))try{e.unlinkSync(t.join(l,n))}catch{}}catch{}}()}catch{}}export function getIndex(s){const r=u.get(s);if(r&&Date.now()-r.builtAt<3e5)return r.index;const{index:o,filesMap:c,changed:i}=buildIndexCore(s,function(s){try{const r=_(s),o=e.statSync(r);if(Date.now()-o.mtimeMs>12096e5)return null;const c=JSON.parse(n.gunzipSync(e.readFileSync(r)).toString("utf8"));if(!c||2!==c.v||c.cwd!==t.resolve(s)||!c.files)return null;const i=new Date;try{e.utimesSync(r,i,i)}catch{}return c.files}catch{return null}}(s));return i&&S(s,c),u.set(s,{index:o,builtAt:Date.now()}),o}export function invalidate(e){u.delete(e)}export function clearIndexCache(){u.clear();try{e.rmSync(l,{recursive:!0,force:!0})}catch{}}export function search(e,t,n=8){const s=[...new Set(tokenize(t))];if(!s.length||!e.N)return[];const{chunks:r,df:o,N:c,avgLen:i}=e,a=new Map;for(const e of s){const t=o.get(e)||1;a.set(e,Math.log(1+(c-t+.5)/(t+.5)))}const l=[];for(const e of r){let t=0;for(const n of s){const s=e.tf.get(n);s&&(t+=a.get(n)*(2.5*s/(s+1.5*(.25+e.len/i*.75))))}if(t>0){if(e.pathTokens)for(const n of s)e.pathTokens.has(n)&&(t+=.6*a.get(n));l.push({file:e.file,start:e.start,end:e.end,text:e.text,score:t,sym:e.sym})}}return l.sort((e,t)=>t.score-e.score),l.slice(0,n)}export function cosine(e,t){if(!e||!t)return 0;let n=0,s=0,r=0;const o=Math.min(e.length,t.length);for(let c=0;c<o;c++)n+=e[c]*t[c],s+=e[c]*e[c],r+=t[c]*t[c];return s&&r?n/(Math.sqrt(s)*Math.sqrt(r)):0}export async function searchCode(e,t,n){const s=String(t?.query||"").trim();if(!s)return"缺少 query(要检索的概念/功能描述)";const r=Number.isFinite(t?.k)&&t.k>0?Math.min(t.k,20):8,o=getIndex(e);if(!o.N)return"(当前目录未索引到代码文件)";const i=!!(n&&n.embeddingModel&&n.baseUrl&&n.apiKey);let a=search(o,s,i?Math.min(30,Math.max(3*r,15)):r);if(!a.length)return`(未找到与「${s}」明显相关的代码;可换关键词,或用 grep 精确匹配)`;let l=!1;if(i&&a.length>1)try{const e=a.map(e=>e.text.slice(0,2e3)),t=await c(n.baseUrl,n.apiKey,n.embeddingModel,[s,...e]);if(t&&t.length===e.length+1&&t[0]){const e=t[0];a=a.map((n,s)=>({...n,score:cosine(e,t[s+1])})).sort((e,t)=>t.score-e.score),l=!0}}catch{}let u=l?"(向量语义重排)\n":"";return o.truncated&&(u+=`(大型仓库:已索引最相关的约 ${o.indexedFiles}/${o.totalFiles} 个文件;如需穷尽匹配请配合 grep)\n`),u+a.slice(0,r).map(e=>{const t=e.text.length>1200?e.text.slice(0,1200)+"\n…(本块过长已截断)":e.text;return`${`${e.file}:${e.start}-${e.end}${e.sym?` ▸ ${e.sym}`:""}`}\n${t}`}).join("\n\n─────\n\n")}function M(e){const t=new Set,n=[/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_]\w*)/g,/\bclass\s+([A-Za-z_]\w*)/g,/\bdef\s+([A-Za-z_]\w*)/g,/\b(?:export\s+)?const\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?(?:function|\([^)]*\)\s*=>)/g,/@(?:Get|Post|Put|Delete|Request)Mapping\(\s*"([^"]+)"/g,/\bpublic\s+[\w<>[\]]+\s+([A-Za-z_]\w*)\s*\(/g,/\b(?:export\s+)?(?:interface|type|enum)\s+([A-Za-z_]\w*)/g,/\b(?:func|fn|fun)\s+([A-Za-z_]\w*)/g,/\b(?:struct|trait|impl)\s+([A-Za-z_]\w*)/g];for(const s of n){let n;for(;(n=s.exec(e))&&t.size<12;)t.add(n[1])}return[...t]}export function buildRepoMap(t,{maxFiles:n=40,maxChars:s=2500}={}){const r=[];for(const n of listSourceFiles(t))try{const t=e.statSync(n.abs);if(t.size>524288)continue;r.push({abs:n.abs,size:t.size,rel:n.rel,prio:indexPriority(n.rel)})}catch{}if(!r.length)return"";r.sort((e,t)=>e.prio-t.prio||t.size-e.size);const o=[];let c=0;for(const t of r.slice(0,n)){let n=[];try{n=M(e.readFileSync(t.abs,"utf8").slice(0,4e4))}catch{}const r=`- ${t.rel}${n.length?":"+n.slice(0,8).join(", "):""}`;if(c+r.length>s)break;o.push(r),c+=r.length+1}return o.join("\n")}
|
|
1
|
+
import e from"node:fs";import t from"node:path";import n from"node:zlib";import s from"node:crypto";import{execFileSync as r}from"node:child_process";import{CONFIG_DIR as o}from"./config.js";import{embed as c}from"./api.js";export const IGNORE=new Set([".git","node_modules","dist","build","target","out",".next",".nuxt",".venv","venv","__pycache__",".idea",".gradle",".mvn","coverage",".cache","vendor","bower_components","Pods","DerivedData",".turbo",".parcel-cache",".terraform",".pytest_cache",".mypy_cache",".tox",".angular",".svelte-kit",".output",".serverless",".svn",".hg"]);const i=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".java",".kt",".py",".go",".rs",".rb",".php",".c",".h",".cpp",".hpp",".cs",".swift",".scala",".sql",".vue",".svelte",".sh",".css",".scss",".html",".md",".yaml",".yml",".json",".toml",".xml",".properties",".gradle",".groovy",".proto",".graphql",".gql",".tf",".ini",".cfg",".conf",".dart",".lua",".clj",".ex",".exs",".erl",".hs",".jl",".pl",".pm",".r",".dockerfile"]),a=/(?:\.min\.(?:js|css)$)|(?:\.bundle\.js$)|(?:\.map$)|(?:(?:^|\/)(?:package-lock\.json|pnpm-lock\.yaml|npm-shrinkwrap\.json)$)/i;export const MAX_FILE_BYTES=524288;const l=t.join(o,"cache","index"),u=new Map;export function tokenize(e){const t=String(e),n=t.replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().split(/[^a-z0-9_]+/).filter(e=>e.length>=2&&e.length<=40),s=t.match(/[一-鿿]/g);if(s)for(const e of s)n.push(e);const r=t.match(/[一-鿿]{2,}/g);if(r)for(const e of r)for(let t=0;t<e.length-1;t++)n.push(e.slice(t,t+2));return n}function*f(n,s=0,r=14){let o;try{o=e.readdirSync(n,{withFileTypes:!0})}catch{return}for(const e of o){if(IGNORE.has(e.name)||e.name.startsWith("."))continue;const o=t.join(n,e.name);e.isDirectory()?s<r&&(yield*f(o,s+1,r)):e.isFile()&&i.has(t.extname(e.name).toLowerCase())&&(yield o)}}let p=null;export function ignoreGlobs(){return[...IGNORE].flatMap(e=>["-g",`!**/${e}/**`,"-g",`!${e}/**`])}export function listSourceFiles(e){let n=null;if(function(){if(null!==p)return p;try{r("rg",["--version"],{stdio:"ignore"}),p=!0}catch{p=!1}return p}())try{n=r("rg",["--files",...ignoreGlobs()],{cwd:e,encoding:"utf8",maxBuffer:64<<20,stdio:["ignore","pipe","ignore"]}).split("\n").map(e=>e.replace(/\\/g,"/")).filter(Boolean)}catch{n=null}if(!n){n=[];for(const s of f(e))n.push(t.relative(e,s).split(t.sep).join("/"))}const s=[];for(const r of n)i.has(t.extname(r).toLowerCase())&&(a.test(r)||s.push({rel:r,abs:t.join(e,r)}));return s}const h=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".java",".kt",".py",".go",".rs",".rb",".php",".c",".h",".cpp",".hpp",".cs",".swift",".scala",".vue",".svelte",".dart",".lua",".clj",".ex",".exs",".erl",".hs",".jl",".groovy"]),d=/(?:^|\/)(?:tests?|__tests__|spec|specs|fixtures?|mocks?|__mocks__|generated|gen|snapshots?|migrations?|node_modules|vendor)(?:\/|$)/i;export function indexPriority(e){const n=e.toLowerCase();let s=0;return h.has(t.extname(n))?s-=100:s+=25,d.test(n)&&(s+=60),s+=2*(n.split("/").length-1),s}const m=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".java",".kt",".go",".rs",".c",".h",".cpp",".hpp",".cc",".cs",".swift",".scala",".php",".dart",".groovy"]);const g=/^(?:(?:export|default|public|private|protected|internal|static|final|abstract|sealed|open|override|async|suspend|inline|const|unsafe|extern|virtual|partial|pub|data|companion)\s+)*(?:function|func|fn|def|fun|class|interface|enum|record|struct|trait|impl|module|mod|namespace|protocol|extension|object|actor|type)\b(?:\s+([A-Za-z_]\w*))?/,y=/^(?:@[\w.]+(?:\([^)]*\))?\s+)*(?:(?:public|private|protected|internal|static|final|abstract|synchronized|native|override|open|suspend|inline|virtual|sealed|async|unsafe|extern)\s+)+[\w<>\[\],.?&:*\s]*?\b([A-Za-z_]\w*)\s*\([^;{]*\)[^;]*?(?:\{|$)/,x=/^(?!(?:if|for|while|switch|catch|return|do|else|try|with|function|class|new|await|yield|throw|case|default|synchronized)\b)(?:static\s+|async\s+|get\s+|set\s+|readonly\s+|public\s+|private\s+|protected\s+|\*\s*)*([A-Za-z_$][\w$]*)\s*\([^;]*\)\s*(?::\s*[\w<>\[\].|&, ]+)?\s*\{/,w=/^(?:export\s+)?(?:default\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:function\b|\([^)]*\)\s*(?::\s*[\w<>\[\].|&, ]+)?\s*=>|[A-Za-z_$][\w$]*\s*=>)/,b=/^(?:async\s+)?(?:def|class)\s+([A-Za-z_]\w*)/,z=/^(?:def|class|module)\s+([A-Za-z_][\w:]*)/;function j(e,t){if("py"===e){const e=b.exec(t);return e?{name:e[1]}:null}if("ruby"===e){const e=z.exec(t);return e?{name:e[1]}:null}let n=g.exec(t);return n?{name:n[1]||""}:(n=y.exec(t),n?{name:n[1]}:(n=x.exec(t),n?{name:n[1]}:(n=w.exec(t),n?{name:n[1]}:null)))}function v(e,t=0){const n=[];for(let s=0;s<e.length;s+=40){const r=Math.min(e.length,s+50),o=e.slice(s,r).join("\n"),c=tokenize(o);if(c.length){const e=new Map;for(const t of c)e.set(t,(e.get(t)||0)+1);n.push({start:t+s+1,end:t+r,len:c.length,text:o,tf:[...e]})}if(r>=e.length)break}return n}function k(e,t){const n=function(e){return m.has(e)?"brace":".py"===e?"py":".rb"===e?"ruby":null}(e);if(!n)return null;const s=[];for(let e=0;e<t.length;e++){const r=t[e].trim();if(!r)continue;const o=j(n,r);o&&s.push({line:e,sym:o.name||""})}if(!s.length)return null;for(let e=0;e<s.length;e++){let n=s[e].line;const r=e>0?s[e-1].line+1:0;for(;n>r&&/^@[\w.]/.test(t[n-1].trim());)n--;s[e].start=n}const r=[];s[0].start>0&&r.push({start:0,sym:""});for(const e of s)r.push({start:e.start,sym:e.sym});const o=[];for(let e=0;e<r.length;e++){const n=r[e].start,s=e+1<r.length?r[e+1].start:t.length;if(s<=n)continue;const c=t.slice(n,s);if(c.length>160){const t=v(c,n);t.length&&r[e].sym&&(t[0].sym=r[e].sym),o.push(...t);continue}const i=c.join("\n"),a=tokenize(i);if(!a.length)continue;const l=new Map;for(const e of a)l.set(e,(l.get(e)||0)+1);const u={start:n+1,end:s,len:a.length,text:i,tf:[...l]};r[e].sym&&(u.sym=r[e].sym),o.push(u)}return o.length?o:null}export function chunkText(e,n=""){const s=e.split("\n"),r=n?t.extname(n).toLowerCase():"";return r&&k(r,s)||v(s,0)}export function buildIndexCore(t,n){const s=Date.now()+2500,r=listSourceFiles(t).map(t=>{let n=1/0,s=0;try{const r=e.statSync(t.abs);n=r.size,s=r.mtimeMs}catch{}return{...t,size:n,mtimeMs:s,prio:indexPriority(t.rel)}}).filter(e=>e.size<=524288).sort((e,t)=>e.prio-t.prio||e.size-t.size),o=new Set(r.map(e=>e.rel)),c=[],i=new Map,a={};let l=0,u=0,f=0,p=0,h=!1;for(const t of r){if(c.length>=12e3||Date.now()>s){h=!0;break}const r=n&&n[t.rel];let o;if(r&&r.mtimeMs===t.mtimeMs&&r.size===t.size&&Array.isArray(r.chunks))o=r.chunks,f++;else{let n;try{const s=e.readFileSync(t.abs);if(s.includes(0))continue;n=s.toString("utf8")}catch{continue}o=chunkText(n,t.rel),p++}u++,a[t.rel]={mtimeMs:t.mtimeMs,size:t.size,chunks:o};const d=new Set(tokenize(t.rel));for(const e of o){const n=new Map(e.tf);for(const[t]of e.tf)i.set(t,(i.get(t)||0)+1);c.push({file:t.rel,start:e.start,end:e.end,len:e.len,text:e.text,tf:n,pathTokens:d,sym:e.sym}),l+=e.len}}if(h&&n)for(const e in n)!a[e]&&o.has(e)&&(a[e]=n[e]);const d=n?Object.keys(n).length:-1,m=p>0||Object.keys(a).length!==d;return{index:{chunks:c,df:i,N:c.length,avgLen:c.length?l/c.length:1,truncated:h,indexedFiles:u,totalFiles:r.length,reused:f,rechunked:p},filesMap:a,changed:m}}export function buildIndex(e){return buildIndexCore(e,null).index}function _(e){const n=s.createHash("sha1").update(t.resolve(e)).digest("hex").slice(0,16);return t.join(l,n+".json.gz")}function S(s,r){try{e.mkdirSync(l,{recursive:!0});const o=_(s),c=n.gzipSync(JSON.stringify({v:2,cwd:t.resolve(s),at:Date.now(),files:r}));e.writeFileSync(o+".tmp",c),e.renameSync(o+".tmp",o),function(){try{const n=e.readdirSync(l).filter(e=>e.endsWith(".json.gz"));if(n.length<=24)return;const s=n.map(n=>{try{return{f:n,t:e.statSync(t.join(l,n)).mtimeMs}}catch{return null}}).filter(Boolean).sort((e,t)=>e.t-t.t);for(const{f:n}of s.slice(0,s.length-24))try{e.unlinkSync(t.join(l,n))}catch{}}catch{}}()}catch{}}export function getIndex(s){const r=u.get(s);if(r&&Date.now()-r.builtAt<3e5)return r.index;const{index:o,filesMap:c,changed:i}=buildIndexCore(s,function(s){try{const r=_(s),o=e.statSync(r);if(Date.now()-o.mtimeMs>12096e5)return null;const c=JSON.parse(n.gunzipSync(e.readFileSync(r)).toString("utf8"));if(!c||2!==c.v||c.cwd!==t.resolve(s)||!c.files)return null;const i=new Date;try{e.utimesSync(r,i,i)}catch{}return c.files}catch{return null}}(s));return i&&S(s,c),u.set(s,{index:o,builtAt:Date.now()}),o}export function invalidate(e){u.delete(e)}export function clearIndexCache(){u.clear();try{e.rmSync(l,{recursive:!0,force:!0})}catch{}}export function search(e,t,n=8){const s=[...new Set(tokenize(t))];if(!s.length||!e.N)return[];const{chunks:r,df:o,N:c,avgLen:i}=e,a=new Map;for(const e of s){const t=o.get(e)||1;a.set(e,Math.log(1+(c-t+.5)/(t+.5)))}const l=[];for(const e of r){let t=0;for(const n of s){const s=e.tf.get(n);s&&(t+=a.get(n)*(2.5*s/(s+1.5*(.25+e.len/i*.75))))}if(t>0){if(e.pathTokens)for(const n of s)e.pathTokens.has(n)&&(t+=.6*a.get(n));l.push({file:e.file,start:e.start,end:e.end,text:e.text,score:t,sym:e.sym})}}return l.sort((e,t)=>t.score-e.score),l.slice(0,n)}export function cosine(e,t){if(!e||!t)return 0;let n=0,s=0,r=0;const o=Math.min(e.length,t.length);for(let c=0;c<o;c++)n+=e[c]*t[c],s+=e[c]*e[c],r+=t[c]*t[c];return s&&r?n/(Math.sqrt(s)*Math.sqrt(r)):0}export async function searchCode(e,t,n){const s=String(t?.query||"").trim();if(!s)return"缺少query(要检索的概念/功能描述)";const r=Number.isFinite(t?.k)&&t.k>0?Math.min(t.k,20):8,o=getIndex(e);if(!o.N)return"(当前目录未索引到代码文件)";const i=!!(n&&n.embeddingModel&&n.baseUrl&&n.apiKey);let a=search(o,s,i?Math.min(30,Math.max(3*r,15)):r);if(!a.length)return`(未找到与「${s}」明显相关的代码;可换关键词,或用grep精确匹配)`;let l=!1;if(i&&a.length>1)try{const e=a.map(e=>e.text.slice(0,2e3)),t=await c(n.baseUrl,n.apiKey,n.embeddingModel,[s,...e]);if(t&&t.length===e.length+1&&t[0]){const e=t[0];a=a.map((n,s)=>({...n,score:cosine(e,t[s+1])})).sort((e,t)=>t.score-e.score),l=!0}}catch{}let u=l?"(向量语义重排)\n":"";return o.truncated&&(u+=`(大型仓库:已索引最相关的约 ${o.indexedFiles}/${o.totalFiles} 个文件;如需穷尽匹配请配合grep)\n`),u+a.slice(0,r).map(e=>{const t=e.text.length>1200?e.text.slice(0,1200)+"\n…(本块过长已截断)":e.text;return`${`${e.file}:${e.start}-${e.end}${e.sym?` ▸ ${e.sym}`:""}`}\n${t}`}).join("\n\n─────\n\n")}function M(e){const t=new Set,n=[/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_]\w*)/g,/\bclass\s+([A-Za-z_]\w*)/g,/\bdef\s+([A-Za-z_]\w*)/g,/\b(?:export\s+)?const\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?(?:function|\([^)]*\)\s*=>)/g,/@(?:Get|Post|Put|Delete|Request)Mapping\(\s*"([^"]+)"/g,/\bpublic\s+[\w<>[\]]+\s+([A-Za-z_]\w*)\s*\(/g,/\b(?:export\s+)?(?:interface|type|enum)\s+([A-Za-z_]\w*)/g,/\b(?:func|fn|fun)\s+([A-Za-z_]\w*)/g,/\b(?:struct|trait|impl)\s+([A-Za-z_]\w*)/g];for(const s of n){let n;for(;(n=s.exec(e))&&t.size<12;)t.add(n[1])}return[...t]}export function buildRepoMap(t,{maxFiles:n=40,maxChars:s=2500}={}){const r=[];for(const n of listSourceFiles(t))try{const t=e.statSync(n.abs);if(t.size>524288)continue;r.push({abs:n.abs,size:t.size,rel:n.rel,prio:indexPriority(n.rel)})}catch{}if(!r.length)return"";r.sort((e,t)=>e.prio-t.prio||t.size-e.size);const o=[];let c=0;for(const t of r.slice(0,n)){let n=[];try{n=M(e.readFileSync(t.abs,"utf8").slice(0,4e4))}catch{}const r=`- ${t.rel}${n.length?":"+n.slice(0,8).join(", "):""}`;if(c+r.length>s)break;o.push(r),c+=r.length+1}return o.join("\n")}
|
package/src/codemode.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Worker as e}from"node:worker_threads";import{tryEncode as r}from"./toon.js";export const DEFAULT_COMPUTE_MS=3e4;export const DEFAULT_WALL_MS=3e5;export const DEFAULT_OUTPUT_BYTES=6e4;export const CODE_MODE_HIDDEN=new Set(["read_file","write_file","edit_file","grep","glob","list_files","search_code"]);export function runProgram({code:r,toolNames:n,dispatch:t,isReadOnly:o,signal:s,budgets:l={}}={}){const a=Number(l.computeMs)||3e4,i=Number(l.wallMs)||3e5,u=Number(l.outputBytes)||6e4,c=new Set(n||[]);return new Promise(n=>{let l=null,p=!1,d=0,g=0,m=0,f=0;const y=[];let v=0,h=!1;const k=Date.now(),w=e=>{if(!p){p=!0,clearInterval(_),clearTimeout(b),s?.removeEventListener?.("abort",S);try{l?.terminate()}catch{}n(e)}},_=setInterval(()=>{Date.now()-k-f-(g>0?Date.now()-m:0)>a&&w({ok:!1,kind:"compute-timeout",calls:d,logs:[],value:null,error:`程序自身运算超过 ${Math.round(a/1e3)} 秒(不含等待工具的时间)。请拆小或减少循环。`})},250),b=setTimeout(()=>{w({ok:!1,kind:"wall-timeout",calls:d,logs:[],value:null,error:`程序总耗时超过 ${Math.round(i/1e3)} 秒。请减少工具调用次数,或分几次跑。`})},i),S=()=>w({ok:!1,kind:"abort",calls:d,logs:[],value:null,error:"已中断"});if(s){if(s.aborted)return S();s.addEventListener("abort",S,{once:!0})}const j=()=>{for(;y.length&&!p;){const e=y[0],r=!!o&&!!o(e.name);if(h)return;if(r){if(v>=8)return}else{if(v>0)return;h=!0}y.shift(),v++,(async()=>{let n;try{const r=await t(e.name,e.args);n=r.ok?{reply:e.id,ok:!0,result:r.result}:{reply:e.id,ok:!1,error:r.denied||"调用被拒绝"}}catch(r){n={reply:e.id,ok:!1,error:r.message}}if(v--,r||(h=!1),!p){g--,0===g&&(f+=Date.now()-m);try{l.postMessage(n)}catch{}}j()})()}};try{l=new e("\nconst { parentPort, workerData } = require('node:worker_threads');\nconst pending = new Map();\nlet nextId = 0;\nconst logs = [];\nconst cap = (v) => { try { return typeof v === 'string' ? v : JSON.stringify(v); } catch { return String(v); } };\nconst mkConsole = () => {\n const push = (...a) => { logs.push(a.map(cap).join(' ')); };\n return { log: push, info: push, warn: push, error: push, debug: push };\n};\n// 空原型 + defineProperty:工具名叫 __proto__ / constructor也只是普通属性,不会污染原型链\nconst tools = Object.create(null);\nfor (const name of workerData.toolNames) {\n Object.defineProperty(tools, name, {\n enumerable: true,\n value: (args) => new Promise((resolve, reject) => {\n const id = ++nextId;\n pending.set(id, { resolve, reject });\n parentPort.postMessage({ call: id, name, args: args === undefined ? {} : args });\n }),\n });\n}\nparentPort.on('message', (m) => {\n if (!m || typeof m.reply !== 'number') return;\n const p = pending.get(m.reply);\n if (!p) return; // 未知/重复的回包:忽略(对端可能是敌意的,宁可丢也不乱派发)\n pending.delete(m.reply);\n if (m.ok) p.resolve(m.result);\n else p.reject(new Error(m.error || '工具调用失败'));\n});\n(async () => {\n const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;\n try {\n const fn = new AsyncFunction('tools', 'console', workerData.code);\n const value = await fn(tools, mkConsole());\n parentPort.postMessage({ done: true, value: value === undefined ? null : value, logs });\n } catch (e) {\n parentPort.postMessage({ done: true, error: (e && e.message) ? e.message : String(e), logs });\n }\n})();\n",{eval:!0,workerData:{code:String(r||""),toolNames:[...c]},env:{},resourceLimits:{maxOldGenerationSizeMb:256},stdout:!0,stderr:!0})}catch(e){return w({ok:!1,kind:"exception",calls:d,logs:[],value:null,error:"无法启动程序运行时:"+e.message})}l.on("message",e=>{if(!p&&e&&"object"==typeof e){if(e.done){const r=Array.isArray(e.logs)?e.logs.map(String):[];if(e.error)return w({ok:!1,kind:"exception",calls:d,logs:r,value:null,error:e.error});const n=function(e){if(null==e)return null;try{return JSON.stringify(e),e}catch{return"(程序的返回值不是可序列化的JSON数据,已忽略;请改为return普通对象/数组/字符串)"}}(e.value),t=Buffer.byteLength(JSON.stringify({logs:r,value:n})||"","utf8");return w(t>u?{ok:!1,kind:"output-limit",calls:d,logs:[],value:null,error:`程序回传内容 ${t} 字节,超过 ${u} 上限。只print/return你真正需要的部分(这正是Code Mode的意义)。`}:{ok:!0,calls:d,logs:r,value:n})}if("number"==typeof e.call)if(c.has(e.name)){if(++d>200)return w({ok:!1,kind:"too-many-calls",calls:d,logs:[],value:null,error:"程序发起的工具调用超过 200 次上限,已中止(疑似失控循环)。"});0===g&&(m=Date.now()),g++,y.push({id:e.call,name:e.name,args:e.args&&"object"==typeof e.args?e.args:{}}),j()}else try{l.postMessage({reply:e.call,ok:!1,error:`未知或不可用的工具:${e.name}`})}catch{}}}),l.on("error",e=>w({ok:!1,kind:"exception",calls:d,logs:[],value:null,error:e.message})),l.on("exit",e=>{p||w({ok:!1,kind:"worker-exit",calls:d,logs:[],value:null,error:`程序运行时意外退出(code=${e}),可能是内存超限。`})})})}export function renderSdk(e){const r=["你可以用run_program写一段JavaScript程序来编排下列工具。程序是一个异步函数体,","可用顶层await与return。每个工具都返回字符串:","","```js","declare const tools: {"];for(const n of[...e||[]].sort((e,r)=>e.function.name.localeCompare(r.function.name))){const e=n.function,t=Object.keys(e.parameters?.properties||{}),o=new Set(e.parameters?.required||[]),s=t.map(e=>`${e}${o.has(e)?"":"?"}`).join(", ");r.push(` /** ${String(e.description||"").split("。")[0]}。 */`),r.push(` ${e.name}(args: { ${s} }): Promise<string>;`)}return r.push("};","```","","要点:","· 独立的只读调用可以Promise.all并发;写类调用会自动串行。","· 只print/return你真正需要的内容——中间结果留在程序里不进上下文,这正是它比逐个调用省的地方。","· **工具返回的就是你平时看到的那份文本**,不是结构化数据:read_file每行带 `行号→` 前缀、grep是 `文件:行号:内容`、list_files是清单。写正则或做解析前,先print一份样本看清真实格式——格式猜错不会报错,只会安静地统计出一堆 0,比直接失败更难发现。","· 改文件一律用edit_file(按old_string→new_string精确替换),"," **不要**「read_file拿到全文 → 字符串替换 → write_file写回」:read_file的内容带行号前缀,原样写回会把行号写进文件,把文件毁掉。确需全文时先 `t.replace(/^\\s*\\d+→/gm, '')` 去掉前缀。"),r.join("\n")}export function makeCodeModeTool(){return{readOnly:!1,timeoutMs:33e4,spec:{type:"function",function:{name:"run_program",description:"写一段JavaScript程序来**编排工具调用**,只有你print或return的内容会回到对话里。适合「把N个文件挨个改一遍」「扫全仓统计后聚合」「先查再按结果分支处理」这类需要循环/分支/汇总的多步工具工作——比一次一个工具调用快得多,也不会把几十份中间结果塞进上下文。\n注意区分:跑数据分析/处理Excel用run_python(它不能调工具);本工具的价值是**编排工具**。\n程序里用await tools.工具名({参数}) 调用工具,每个都返回字符串。独立的只读调用可以Promise.all并发;写类调用会自动串行。\n每次工具调用都照常走权限审批与钩子——程序不是绕过审批的通道。",parameters:{type:"object",properties:{code:{type:"string",description:"程序体(异步函数体;可用顶层await与return)"},description:{type:"string",description:"一句话说明这段程序做什么(5-15 字,展示用)"}},required:["code","description"]}}},runWithEnv:async(e,n,t)=>{const o=String(e?.code||"").trim();if(!o)return"缺少code(要执行的程序体)。";const{makeDispatch:s}=await import("./toolpipe.js"),l=(n.listTools?n.listTools():[]).map(e=>e.function.name).filter(e=>"run_program"!==e),a=await runProgram({code:o,toolNames:l,dispatch:s(n),isReadOnly:e=>!!n.resolveTool?.(e)?.readOnly,signal:t}),i=`【程序执行${a.ok?"完成":"失败"}】共发起 ${a.calls} 次工具调用`;if(!a.ok)return`${i}\n${a.error}`;const u=[i];return a.logs.length&&u.push("输出:\n"+a.logs.join("\n")),null!==a.value&&void 0!==a.value&&u.push("返回值:\n"+function(e){if("string"==typeof e)return e;let n;try{n=JSON.stringify(e,null,2)}catch{return String(e)}const t=r(e,{name:"result",json:n});return t?t.text:n}(a.value)),a.logs.length||null!==a.value&&void 0!==a.value||u.push("(程序没有print也没有return任何内容——结果不会自动回传,记得输出你需要的部分。)"),u.join("\n")}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomUUID as e}from"node:crypto";export function createCommandBridge(n){const o=e();let l=null,t=0;const r=e=>{const o=l;o&&(l=null,o.signal?.removeEventListener("abort",o.abort),n({type:"commandMenu",id:o.id,menu:null}),o.resolve(e))};return{select:(e,u)=>u?.aborted?Promise.resolve(null):(r(null),new Promise(a=>{const m=`command-${o}-${++t}`,s=()=>r(null);l={id:m,menu:e,signal:u,abort:s,resolve:a},u?.addEventListener("abort",s,{once:!0}),n({type:"commandMenu",id:m,menu:e})})),respond:e=>!(!l||e.id!==l.id)&&(!(null!==e.value&&!l.menu.options.some(n=>n.value===e.value))&&(r(e.value),!0)),cancel(){r(null)}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ACCENTS as o}from"./colors.js";import{PERMISSION_MODES as t,MODE_LABEL as e,MODE_DESC as n}from"./permissions.js";import{isWebSearchOn as r}from"./config.js";export const COMMAND_ALIASES=Object.freeze({quit:"exit",new:"clear",reset:"clear",settings:"config",approvals:"permissions","auto-compact":"autocompact","exec-sandbox":"execsandbox","tool-mode":"toolmode","auto-model":"automodel","web-search":"websearch",reasoning:"effort"});const i={"常用":"help model permissions config status clear compact","会话":"sessions resume history rename fork branch rewind copy export recap distill context memory decisions decision btw","模型与显示":"models effort advisor automodel embed theme color tui markdown autocompact budget","权限与工具":"mode yolo plan sandbox execsandbox toolmode websearch skills reload-skills mcp hooks add-dir","工程与自动化":"init diff pr review security-review scan cover verify inspect simplify auto goal agents orchestrate squads squad squad-init experts batch tasks schedule loop background crew","系统":"usage cost login logout cwd cd doctor debug insights ide statusline exit"};export function commandCategory(o){return Object.entries(i).find(([,t])=>t.split(" ").includes(o))?.[0]||"扩展"}export function commandAliases(o){return Object.entries(COMMAND_ALIASES).filter(([,t])=>t===o).map(([o])=>o)}export function matchCommands(o,t=""){const e=t.toLowerCase().trim(),n=o=>o.name===e?5:o.aliases?.includes(e)?4:o.name.startsWith(e)?3:o.aliases?.some(o=>o.startsWith(e))?2:1;return o.filter(o=>!e||[o.name,...o.aliases||[],o.desc,o.category].join(" ").toLowerCase().includes(e)).sort((o,t)=>n(t)-n(o))}export const option=(o,t=o,e="")=>({value:o,label:t,description:e});export const modeOptions=()=>t.map(o=>option(o,`${e[o]} (${o})`,"auto"===o?"文件、命令、联网免审批;拒绝规则与策略仍生效":n[o]));export function settingMenu(t,e){const n=e.config,i=e.permissions?.getMode?.()||(e.permissions?.isYolo?.()?"auto":"manual"),s=(o,t,e="")=>({title:o,layout:"segments",current:t?"on":"off",options:[option("on","开启",e),option("off","关闭")]});switch(t){case"mode":return{title:"对话权限模式",current:i,options:modeOptions()};case"permissions":return{title:"权限与审批",current:i,options:[...modeOptions(),option("rules","查看权限规则","全局 / 项目规则与策略"),option("allow","添加允许规则","选择工具;仅写入当前项目"),option("deny","添加拒绝规则","拒绝规则优先于允许规则和自动模式"),option("remove","移除项目规则"),option("execsandbox","设置执行沙箱","操作系统层面的文件访问限制")]};case"effort":return{title:"推理强度",layout:"segments",current:n.reasoningEffort||"off",options:[option("off","默认","由模型决定推理强度"),option("minimal","最低","减少推理开销,适合简单任务"),option("low","低","较少推理,偏向响应速度"),option("medium","中","平衡推理深度与响应速度"),option("high","高","更多推理,适合复杂任务;模型不支持时忽略")]};case"theme":return{layout:"segments",title:"输出主题",current:n.theme||"default",options:[option("default","彩色"),option("mono","无色")]};case"color":return{layout:"segments",title:"强调色",current:n.accentColor||"cyan",options:o.map(o=>option(o))};case"tui":return{layout:"segments",title:"终端界面 · 下次启动生效",current:n.tui||"auto",options:[option("auto","自动选择"),option("rich","全屏终端"),option("plain","普通终端")]};case"toolmode":return{title:"工具调用方式",current:n.toolMode||"native",options:[option("native","原生工具调用","逐个调用工具"),option("both","自动编排","模型可选择原生调用或程序编排"),option("code","程序编排","读写与检索通过 run_program 调用,仍受权限约束")]};case"execsandbox":return{title:"执行沙箱",current:n.execSandbox||"off",options:[option("off","关闭"),option("workspace-write","工作区可写","限制命令写入工作区与临时目录"),option("read-only","只读","禁止命令写入文件")]};case"debug":return s("调试输出",!!n.debug);case"markdown":return s("Markdown 渲染",!1!==n.markdown);case"websearch":return s("联网搜索",r(),"由平台提供搜索服务");case"sandbox":return s("工具沙箱模式","sandbox"===i,"禁用命令和联网,仍可读写本地文件");case"yolo":return{title:"自动执行权限",current:"auto"===i?"auto":"manual",options:modeOptions().filter(o=>["manual","auto"].includes(o.value))};case"plan":return{...s("计划模式","plan"===i,"只读调研与制定方案"),layout:"list",options:[option("on","进入计划模式"),option("off","返回审批模式"),option("approve","批准并执行已有计划")]};case"autocompact":return{title:"自动压缩阈值 · 也可直接输入任意 ≥1000 的值",current:String(n.autoCompactTokens<0?"off":n.autoCompactTokens||"default"),options:[option("default","默认阈值"),...[16e3,32e3,64e3,128e3].map(o=>option(String(o),`${o.toLocaleString()} tokens`)),option("off","关闭自动压缩")]};case"budget":return{title:"Token 预算 · 软提醒",current:String(n.budgetTokens||"off"),options:[option("off","不设预算"),...[1e4,5e4,1e5,5e5].map(o=>option(String(o),`${o.toLocaleString()} tokens`))]};default:return null}}export async function choose(o,t){if(o.signal?.aborted)return null;if(!t.options.length)return o.print("暂无可选项。"),null;const e=await o.select(t);return!o.signal?.aborted&&t.options.some(o=>o.value===e)?e:null}export async function commandRequest(o,t){if(!t)return o;let e;try{return await Promise.race([o,new Promise((o,n)=>{e=()=>n(new Error("操作已取消")),t.addEventListener("abort",e,{once:!0}),t.aborted&&e()})])}finally{t.removeEventListener("abort",e)}}export function createTextSelector(o,t){return async e=>{for(t(e.title+"\n"+e.options.map((o,t)=>` ${t+1}. ${o.label}${o.value===e.current?" ✓ 当前":""}${o.description?" — "+o.description:""}`).join("\n"));;){let n;try{n=(await o("选择编号(回车 / q 取消):")).trim()}catch{return null}if(!n||"q"===n||""===n)return null;if(/^\d+$/.test(n)&&e.options[Number(n)-1])return e.options[Number(n)-1].value;t("请输入列表中的编号,或直接回车取消。")}}}
|