@huicai/cli 2026.714.0 → 2026.804.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/README.md +13 -5
- package/bin/huicai.js +83 -0
- package/package.json +2 -2
- package/skills/ab-testing/SKILL.md +58 -0
- package/skills/ab-testing/scripts/__pycache__/ab_test.cpython-312.pyc +0 -0
- package/skills/ab-testing/scripts/ab_test.py +71 -0
- package/skills/academic-writing/SKILL.md +54 -0
- package/skills/accounting-ops/SKILL.md +68 -0
- package/skills/accounting-ops/scripts/__pycache__/acct_tools.cpython-312.pyc +0 -0
- package/skills/accounting-ops/scripts/acct_tools.py +89 -0
- package/skills/ai-agent/SKILL.md +85 -0
- package/skills/ai-agent/references/tool-design-checklist.md +51 -0
- package/skills/ai-aiops/SKILL.md +73 -0
- package/skills/ai-aiops/references/aiops-rollout-checklist.md +36 -0
- package/skills/ai-coding/SKILL.md +89 -0
- package/skills/ai-coding/references/task-brief-template.md +48 -0
- package/skills/ai-compliance/SKILL.md +74 -0
- package/skills/ai-compliance/references/genai-compliance-checklist.md +40 -0
- package/skills/ai-eval/SKILL.md +80 -0
- package/skills/ai-eval/scripts/eval-runner.py +150 -0
- package/skills/ai-llm-app/SKILL.md +90 -0
- package/skills/ai-llm-app/references/llm-integration-checklist.md +39 -0
- package/skills/ai-mlops/SKILL.md +72 -0
- package/skills/ai-mlops/scripts/token-cost-calc.py +108 -0
- package/skills/ai-multimodal/SKILL.md +86 -0
- package/skills/ai-multimodal/scripts/field-validate.py +182 -0
- package/skills/ai-prompt/SKILL.md +75 -0
- package/skills/ai-prompt/references/prompt-template.md +46 -0
- package/skills/ai-rag/SKILL.md +76 -0
- package/skills/ai-rag/scripts/chunk-inspect.py +129 -0
- package/skills/anomaly-detection/SKILL.md +54 -0
- package/skills/anomaly-detection/scripts/__pycache__/anomaly.cpython-312.pyc +0 -0
- package/skills/anomaly-detection/scripts/anomaly.py +99 -0
- package/skills/api-integration/SKILL.md +74 -0
- package/skills/api-integration/references/signing-recipes.md +141 -0
- package/skills/arch-design/SKILL.md +57 -0
- package/skills/arch-distributed/SKILL.md +81 -0
- package/skills/arch-distributed/references/idempotency-review.md +43 -0
- package/skills/arch-multitenant/SKILL.md +73 -0
- package/skills/arch-multitenant/references/tenant-isolation-checklist.md +34 -0
- package/skills/audit-assist/SKILL.md +63 -0
- package/skills/audit-assist/scripts/__pycache__/audit_sampling.cpython-312.pyc +0 -0
- package/skills/audit-assist/scripts/audit_sampling.py +93 -0
- package/skills/audit-chain/SKILL.md +60 -0
- package/skills/banking-credit/SKILL.md +59 -0
- package/skills/banking-credit/scripts/__pycache__/credit_metrics.cpython-312.pyc +0 -0
- package/skills/banking-credit/scripts/credit_metrics.py +53 -0
- package/skills/brand-design/SKILL.md +56 -0
- package/skills/business-plan/SKILL.md +56 -0
- package/skills/capital-ops/SKILL.md +52 -0
- package/skills/cicd-pipeline/SKILL.md +85 -0
- package/skills/cicd-pipeline/references/pipeline-review-checklist.md +39 -0
- package/skills/content-creation/SKILL.md +56 -0
- package/skills/corporate-governance/SKILL.md +56 -0
- package/skills/crawl-chain/SKILL.md +61 -0
- package/skills/customer-service/SKILL.md +51 -0
- package/skills/data-analysis/SKILL.md +46 -0
- package/skills/data-analysis/scripts/cjk_font.py +56 -0
- package/skills/data-chain/SKILL.md +54 -0
- package/skills/data-governance/SKILL.md +54 -0
- package/skills/data-report-pro/SKILL.md +65 -0
- package/skills/data-report-pro/scripts/__pycache__/bigdata.cpython-312.pyc +0 -0
- package/skills/data-report-pro/scripts/bigdata.py +79 -0
- package/skills/data-streaming/SKILL.md +87 -0
- package/skills/data-streaming/references/job-checklist.md +36 -0
- package/skills/data-wrangling/SKILL.md +52 -0
- package/skills/data-wrangling/scripts/__pycache__/join_tools.cpython-312.pyc +0 -0
- package/skills/data-wrangling/scripts/join_tools.py +99 -0
- package/skills/db-es/SKILL.md +68 -0
- package/skills/db-es/scripts/es-health.sh +72 -0
- package/skills/db-migration/SKILL.md +72 -0
- package/skills/db-migration/scripts/checksum-mysql.sql +46 -0
- package/skills/db-migration/scripts/checksum-postgres.sql +39 -0
- package/skills/db-migration/scripts/compare-reports.py +98 -0
- package/skills/db-mysql/SKILL.md +78 -0
- package/skills/db-mysql/scripts/mysql-health.sql +80 -0
- package/skills/db-postgres/SKILL.md +86 -0
- package/skills/db-postgres/scripts/pg-health.sql +67 -0
- package/skills/db-redis/SKILL.md +83 -0
- package/skills/db-redis/scripts/redis-health.sh +92 -0
- package/skills/dev-cpp/SKILL.md +94 -0
- package/skills/dev-cpp/references/cmake-sanitizer-snippets.md +56 -0
- package/skills/dev-cross-platform/SKILL.md +73 -0
- package/skills/dev-cross-platform/references/capability-matrix-template.md +39 -0
- package/skills/dev-csharp/SKILL.md +96 -0
- package/skills/dev-csharp/references/review-checklist.md +37 -0
- package/skills/dev-frontend/SKILL.md +91 -0
- package/skills/dev-frontend/scripts/bundle-budget.py +106 -0
- package/skills/dev-go/SKILL.md +88 -0
- package/skills/dev-go/scripts/go-triage.sh +54 -0
- package/skills/dev-harmony/SKILL.md +64 -0
- package/skills/dev-harmony/references/harmony-checklist.md +40 -0
- package/skills/dev-java/SKILL.md +77 -0
- package/skills/dev-java/scripts/jvm-triage.sh +84 -0
- package/skills/dev-mobile/SKILL.md +82 -0
- package/skills/dev-mobile/references/store-compliance-checklist.md +33 -0
- package/skills/dev-node/SKILL.md +82 -0
- package/skills/dev-node/scripts/node-triage.sh +78 -0
- package/skills/dev-php/SKILL.md +86 -0
- package/skills/dev-php/scripts/fpm-calc.py +48 -0
- package/skills/dev-python/SKILL.md +81 -0
- package/skills/dev-python/scripts/venv-doctor.sh +75 -0
- package/skills/dev-rust/SKILL.md +84 -0
- package/skills/dev-rust/references/cargo-ci-snippets.md +50 -0
- package/skills/dev-toolchain/SKILL.md +78 -0
- package/skills/dev-toolchain/scripts/secret-scan.sh +83 -0
- package/skills/doc-chain/SKILL.md +58 -0
- package/skills/doc-compare/SKILL.md +44 -0
- package/skills/doc-compare/scripts/compare.py +93 -0
- package/skills/docx-author/SKILL.md +39 -0
- package/skills/docx-author/scripts/docx_doc.py +161 -0
- package/skills/ecommerce-ops/SKILL.md +54 -0
- package/skills/ecommerce-ops/scripts/__pycache__/gmv_decompose.cpython-312.pyc +0 -0
- package/skills/ecommerce-ops/scripts/gmv_decompose.py +61 -0
- package/skills/education-training/SKILL.md +53 -0
- package/skills/ehs-safety/SKILL.md +63 -0
- package/skills/email-writing/SKILL.md +52 -0
- package/skills/eng-ai/SKILL.md +74 -0
- package/skills/eng-ai/references/ai-feature-checklist.md +36 -0
- package/skills/eng-architecture/SKILL.md +85 -0
- package/skills/eng-architecture/scripts/capacity-calc.py +100 -0
- package/skills/eng-automation/SKILL.md +84 -0
- package/skills/eng-automation/references/automation-inventory.md +38 -0
- package/skills/eng-automation/references/routine-presets.md +73 -0
- package/skills/eng-collaboration/SKILL.md +76 -0
- package/skills/eng-collaboration/references/collab-templates.md +43 -0
- package/skills/eng-complex-env/SKILL.md +84 -0
- package/skills/eng-complex-env/references/env-dossier-template.md +52 -0
- package/skills/eng-complex-env/scripts/env-recon.sh +152 -0
- package/skills/eng-data-platform/SKILL.md +81 -0
- package/skills/eng-data-platform/references/table-spec-template.md +39 -0
- package/skills/eng-database/SKILL.md +79 -0
- package/skills/eng-database/references/schema-review-checklist.md +37 -0
- package/skills/eng-develop/SKILL.md +76 -0
- package/skills/eng-develop/references/dod-checklist.md +43 -0
- package/skills/eng-docs/SKILL.md +71 -0
- package/skills/eng-docs/references/runbook-template.md +57 -0
- package/skills/eng-efficiency/SKILL.md +75 -0
- package/skills/eng-efficiency/scripts/dora-calc.py +104 -0
- package/skills/eng-infra/SKILL.md +81 -0
- package/skills/eng-infra/references/production-readiness.md +46 -0
- package/skills/eng-integration/SKILL.md +82 -0
- package/skills/eng-integration/references/integration-dossier-template.md +38 -0
- package/skills/eng-legacy/SKILL.md +81 -0
- package/skills/eng-legacy/references/modernization-workbook.md +46 -0
- package/skills/eng-observability/SKILL.md +78 -0
- package/skills/eng-observability/scripts/slo-calc.py +82 -0
- package/skills/eng-ops/SKILL.md +74 -0
- package/skills/eng-ops/references/postmortem-template.md +67 -0
- package/skills/eng-performance/SKILL.md +73 -0
- package/skills/eng-performance/scripts/perf-baseline.py +110 -0
- package/skills/eng-planning/SKILL.md +79 -0
- package/skills/eng-planning/scripts/schedule-calc.py +101 -0
- package/skills/eng-quality/SKILL.md +74 -0
- package/skills/eng-quality/references/test-plan-templates.md +46 -0
- package/skills/eng-release/SKILL.md +78 -0
- package/skills/eng-release/references/release-plan-template.md +36 -0
- package/skills/eng-reliability/SKILL.md +72 -0
- package/skills/eng-reliability/references/resilience-review.md +43 -0
- package/skills/eng-requirements/SKILL.md +86 -0
- package/skills/eng-requirements/references/story-templates.md +47 -0
- package/skills/eng-research/SKILL.md +73 -0
- package/skills/eng-research/references/research-templates.md +42 -0
- package/skills/eng-security/SKILL.md +73 -0
- package/skills/eng-security/references/security-review-checklist.md +33 -0
- package/skills/eng-ux/SKILL.md +85 -0
- package/skills/eng-ux/references/ux-review-checklist.md +36 -0
- package/skills/engineering-chain/SKILL.md +101 -0
- package/skills/engineering-chain/references/eng-health-review.md +37 -0
- package/skills/env-offline/SKILL.md +81 -0
- package/skills/env-offline/scripts/bundle-manifest.sh +94 -0
- package/skills/env-private-deploy/SKILL.md +72 -0
- package/skills/env-private-deploy/references/delivery-templates.md +35 -0
- package/skills/env-xinchuang/SKILL.md +73 -0
- package/skills/env-xinchuang/references/adaptation-matrix-template.md +41 -0
- package/skills/equipment-tpm/SKILL.md +56 -0
- package/skills/equipment-tpm/scripts/oee.py +58 -0
- package/skills/esg-report/SKILL.md +58 -0
- package/skills/esg-report/scripts/carbon_calc.py +61 -0
- package/skills/event-management/SKILL.md +51 -0
- package/skills/excel-analysis-chain/SKILL.md +73 -0
- package/skills/file-ingest/SKILL.md +51 -0
- package/skills/file-ingest/scripts/__pycache__/file_toolkit.cpython-312.pyc +0 -0
- package/skills/file-ingest/scripts/file_toolkit.py +404 -0
- package/skills/finance-analysis/SKILL.md +60 -0
- package/skills/finance-analysis/references/ratios.md +54 -0
- package/skills/finance-analysis/scripts/fin_ratios.py +132 -0
- package/skills/finance-chain/SKILL.md +59 -0
- package/skills/financial-modeling/SKILL.md +58 -0
- package/skills/financial-modeling/scripts/fin_model.py +123 -0
- package/skills/foreign-trade/SKILL.md +54 -0
- package/skills/funnel-cohort/SKILL.md +53 -0
- package/skills/funnel-cohort/scripts/__pycache__/funnel.cpython-312.pyc +0 -0
- package/skills/funnel-cohort/scripts/funnel.py +105 -0
- package/skills/gov-document/SKILL.md +57 -0
- package/skills/hr-chain/SKILL.md +51 -0
- package/skills/hr-operations/SKILL.md +53 -0
- package/skills/info-disclosure/SKILL.md +56 -0
- package/skills/infra-gateway/SKILL.md +85 -0
- package/skills/infra-gateway/references/golive-checklist.md +36 -0
- package/skills/infra-k8s/SKILL.md +78 -0
- package/skills/infra-k8s/scripts/k8s-triage.sh +84 -0
- package/skills/infra-mq/SKILL.md +87 -0
- package/skills/infra-mq/references/mq-review-checklist.md +36 -0
- package/skills/insurance-advisory/SKILL.md +57 -0
- package/skills/internal-audit/SKILL.md +66 -0
- package/skills/ip-management/SKILL.md +54 -0
- package/skills/ipo-listing/SKILL.md +60 -0
- package/skills/it-audit/SKILL.md +56 -0
- package/skills/itsm-ops/SKILL.md +55 -0
- package/skills/knowledge-sop/SKILL.md +51 -0
- package/skills/legal-chain/SKILL.md +55 -0
- package/skills/legal-contract/SKILL.md +58 -0
- package/skills/legal-contract/references/contract-checklists.md +59 -0
- package/skills/litigation-support/SKILL.md +53 -0
- package/skills/logistics-warehouse/SKILL.md +53 -0
- package/skills/logistics-warehouse/scripts/freight_check.py +71 -0
- package/skills/long-doc-analysis/SKILL.md +52 -0
- package/skills/management-accounting/SKILL.md +57 -0
- package/skills/management-accounting/scripts/__pycache__/costing.cpython-312.pyc +0 -0
- package/skills/management-accounting/scripts/costing.py +90 -0
- package/skills/manufacturing-chain/SKILL.md +57 -0
- package/skills/manufacturing-quality/SKILL.md +57 -0
- package/skills/manufacturing-quality/scripts/__pycache__/spc.cpython-312.pyc +0 -0
- package/skills/manufacturing-quality/scripts/spc.py +86 -0
- package/skills/marketing-analytics/SKILL.md +59 -0
- package/skills/marketing-analytics/scripts/__pycache__/rfm.cpython-312.pyc +0 -0
- package/skills/marketing-analytics/scripts/rfm.py +60 -0
- package/skills/marketing-chain/SKILL.md +57 -0
- package/skills/meeting-minutes/SKILL.md +55 -0
- package/skills/metrics-system/SKILL.md +55 -0
- package/skills/negotiation-prep/SKILL.md +56 -0
- package/skills/obs-prometheus/SKILL.md +74 -0
- package/skills/obs-prometheus/references/promql-cheatsheet.md +78 -0
- package/skills/office-automation/SKILL.md +54 -0
- package/skills/okr-review/SKILL.md +44 -0
- package/skills/ops-fleet/SKILL.md +69 -0
- package/skills/ops-fleet/scripts/batch-exec-guard.sh +119 -0
- package/skills/ops-linux/SKILL.md +69 -0
- package/skills/ops-linux/scripts/quick-triage.sh +138 -0
- package/skills/optimization-solver/SKILL.md +53 -0
- package/skills/optimization-solver/scripts/__pycache__/solver.cpython-312.pyc +0 -0
- package/skills/optimization-solver/scripts/solver.py +107 -0
- package/skills/pdf-toolkit/SKILL.md +43 -0
- package/skills/pdf-toolkit/scripts/pdf_ops.py +137 -0
- package/skills/pivot-crosstab/SKILL.md +44 -0
- package/skills/pivot-crosstab/scripts/__pycache__/pivot_tools.cpython-312.pyc +0 -0
- package/skills/pivot-crosstab/scripts/pivot_tools.py +90 -0
- package/skills/policy-analysis/SKILL.md +47 -0
- package/skills/portfolio-analysis/SKILL.md +56 -0
- package/skills/portfolio-analysis/scripts/__pycache__/portfolio.cpython-312.pyc +0 -0
- package/skills/portfolio-analysis/scripts/portfolio.py +78 -0
- package/skills/ppt-chain/SKILL.md +61 -0
- package/skills/pptx-author/SKILL.md +48 -0
- package/skills/pptx-author/scripts/pptx_deck.py +193 -0
- package/skills/pr-comms/SKILL.md +58 -0
- package/skills/pr-comms/references/pr-templates.md +54 -0
- package/skills/procurement-supply/SKILL.md +47 -0
- package/skills/procurement-supply/scripts/__pycache__/supply.cpython-312.pyc +0 -0
- package/skills/procurement-supply/scripts/supply.py +71 -0
- package/skills/product-design/SKILL.md +66 -0
- package/skills/product-design/references/prd-framework.md +60 -0
- package/skills/production-planning/SKILL.md +56 -0
- package/skills/production-planning/scripts/__pycache__/mrp_capacity.cpython-312.pyc +0 -0
- package/skills/production-planning/scripts/mrp_capacity.py +70 -0
- package/skills/project-management/SKILL.md +53 -0
- package/skills/project-management/scripts/__pycache__/cpm.cpython-312.pyc +0 -0
- package/skills/project-management/scripts/cpm.py +84 -0
- package/skills/project-proposal/SKILL.md +54 -0
- package/skills/project-proposal/scripts/budget_sheet.py +90 -0
- package/skills/qa-api-automation/SKILL.md +67 -0
- package/skills/qa-api-automation/references/three-layer-skeleton.md +61 -0
- package/skills/qa-mobile-automation/SKILL.md +70 -0
- package/skills/qa-mobile-automation/references/device-matrix-template.md +34 -0
- package/skills/qa-ui-automation/SKILL.md +80 -0
- package/skills/qa-ui-automation/references/stability-conventions.md +41 -0
- package/skills/quant-analysis/SKILL.md +60 -0
- package/skills/quant-analysis/scripts/__pycache__/quant.cpython-312.pyc +0 -0
- package/skills/quant-analysis/scripts/quant.py +102 -0
- package/skills/real-estate-property/SKILL.md +51 -0
- package/skills/real-estate-property/scripts/__pycache__/property_calc.cpython-312.pyc +0 -0
- package/skills/real-estate-property/scripts/property_calc.py +106 -0
- package/skills/regression-forecast/SKILL.md +52 -0
- package/skills/regression-forecast/scripts/__pycache__/regress.cpython-312.pyc +0 -0
- package/skills/regression-forecast/scripts/regress.py +106 -0
- package/skills/retail-store-ops/SKILL.md +57 -0
- package/skills/retail-store-ops/scripts/store_ops.py +57 -0
- package/skills/risk-compliance/SKILL.md +51 -0
- package/skills/sec-auth/SKILL.md +89 -0
- package/skills/sec-auth/references/auth-review-checklist.md +38 -0
- package/skills/sec-compliance/SKILL.md +74 -0
- package/skills/sec-compliance/references/dengbao-checklist.md +43 -0
- package/skills/securities-chain/SKILL.md +69 -0
- package/skills/securities-research/SKILL.md +58 -0
- package/skills/securities-research/scripts/__pycache__/valuation.cpython-312.pyc +0 -0
- package/skills/securities-research/scripts/valuation.py +93 -0
- package/skills/slides-html/SKILL.md +50 -0
- package/skills/slides-html/scripts/template.html +117 -0
- package/skills/statistical-testing/SKILL.md +51 -0
- package/skills/statistical-testing/scripts/__pycache__/stat_tests.cpython-312.pyc +0 -0
- package/skills/statistical-testing/scripts/stat_tests.py +131 -0
- package/skills/strategy-planning/SKILL.md +53 -0
- package/skills/supply-chain/SKILL.md +53 -0
- package/skills/tax-compliance/SKILL.md +57 -0
- package/skills/tax-compliance/scripts/tax_calc.py +90 -0
- package/skills/tech-research/SKILL.md +55 -0
- package/skills/tech-research/references/research-framework.md +50 -0
- package/skills/tender-bidding/SKILL.md +65 -0
- package/skills/tender-bidding/references/bid-checklist.md +44 -0
- package/skills/translation-pro/SKILL.md +44 -0
- package/skills/treasury-mgmt/SKILL.md +56 -0
- package/skills/treasury-mgmt/scripts/__pycache__/cashflow_forecast.cpython-312.pyc +0 -0
- package/skills/treasury-mgmt/scripts/cashflow_forecast.py +51 -0
- package/skills/web-scraping/SKILL.md +81 -0
- package/skills/web-search/SKILL.md +67 -0
- package/skills/xlsx-analysis/SKILL.md +96 -0
- package/skills/xlsx-analysis/scripts/__pycache__/xlsx_toolkit.cpython-312.pyc +0 -0
- package/skills/xlsx-analysis/scripts/xlsx_style.py +71 -0
- package/skills/xlsx-analysis/scripts/xlsx_toolkit.py +335 -0
- package/src/advisor.js +1 -1
- package/src/agent.js +1 -1
- package/src/api.js +1 -1
- package/src/assets-import.js +1 -0
- package/src/autofix.js +1 -1
- package/src/automation.js +1 -1
- package/src/codeindex.js +1 -1
- package/src/commands.js +1 -1
- package/src/compaction.js +1 -1
- package/src/config.js +1 -1
- package/src/context-report.js +1 -1
- package/src/context.js +1 -1
- package/src/decisions.js +1 -0
- package/src/doccache.js +1 -0
- package/src/doctor.js +1 -1
- package/src/executors.js +1 -1
- package/src/expert-catalog.js +1 -0
- package/src/experts-store.js +1 -0
- package/src/gitflow.js +1 -1
- package/src/input.js +1 -1
- package/src/mcp.js +1 -1
- package/src/permissions.js +1 -1
- package/src/pyruntime.js +1 -0
- package/src/repl.js +1 -1
- package/src/scan.js +1 -1
- package/src/schedule-freq.js +1 -0
- package/src/schedule.js +1 -1
- package/src/session.js +1 -1
- package/src/skills.js +1 -1
- package/src/squad.js +1 -1
- package/src/subagent.js +1 -1
- package/src/title.js +1 -0
- package/src/tools.js +1 -1
- package/src/transcript.js +1 -1
- package/src/tui/app.js +1 -1
package/src/tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"./executors.js";import{searchCode as t}from"./codeindex.js";import{AUTOMATION_TOOL_SPECS as r,AUTOMATION_EXECUTORS as n}from"./automation.js";const
|
|
1
|
+
import*as e from"./executors.js";import{searchCode as t}from"./codeindex.js";import{AUTOMATION_TOOL_SPECS as r,AUTOMATION_EXECUTORS as n}from"./automation.js";import{runPython as i}from"./pyruntime.js";const o=(e,t)=>({type:"object",properties:e,required:t||[]}),p=e=>({type:"string",description:e}),a=e=>({type:"integer",description:e}),s=e=>({type:"boolean",description:e});export const TOOL_SPECS=[{type:"function",function:{name:"read_file",description:"读取文本文件内容,每行带「行号→」前缀(行号仅供定位,不属于文件内容,编辑时不要包含它)。默认最多 2000 行,可用 offset(起始行,1基)/limit(行数) 分段读取大文件。",parameters:o({path:p("相对项目根的文件路径"),offset:a("可选:起始行(1基)"),limit:a("可选:读取行数")},["path"])}},{type:"function",function:{name:"list_files",description:"列出目录下的文件树(跳过 .git/node_modules 等)。可选 path 指定子目录(默认项目根)。",parameters:o({path:p("可选:子目录路径,默认当前项目根")},[])}},{type:"function",function:{name:"glob",description:'按 glob 模式查找文件(支持 ** 与 *),如 "**/*.ts"、"src/**/*.java"。',parameters:o({pattern:p("glob 模式")},["pattern"])}},{type:"function",function:{name:"search_code",description:"按「概念/功能」检索最相关的代码块(BM25 相关性排序,比 grep 更懂「哪段代码在做这件事」)。适合「登录逻辑在哪」「哪里处理支付回调」这类找相关实现;要找精确字符串仍用 grep。返回 文件:行范围 + 代码块。",parameters:o({query:p("要找的功能/概念(自然语言或关键词组合,多写几个相关词更准)"),k:a("可选:返回块数,默认 8,上限 20")},["query"])}},{type:"function",function:{name:"grep",description:"在文件内容中按正则搜索(底层用 ripgrep,大型仓库快、自动尊重 .gitignore、不受单文件大小限制→大文件也能命中)。返回 匹配文件:行号: 该行。可选 path 限定目录、glob 限定文件名模式、ignore_case 忽略大小写、files_only 只列出命中文件、max_results 上限。",parameters:o({pattern:p("正则表达式"),path:p("可选:搜索目录,默认项目根"),glob:p("可选:文件名 glob 过滤"),ignore_case:s("可选:忽略大小写(默认否)"),files_only:s("可选:只返回命中文件列表,不含匹配行(默认否)"),max_results:a("可选:最多返回条数(默认 200,上限 500)")},["pattern"])}},{type:"function",function:{name:"read_document",description:"读取 PDF/Word/Excel/PPT/RTF 等文档的纯文本内容(经平台解析,read_file 读不了这类二进制文档)。大型文档分段通读:段末标注「全文共N字符/本段区间」,继续调用并传 offset 读取后续,直到「已到结尾」。对话附件若标注「已截断」,用本工具读取被省略的部分。",parameters:o({path:p("相对项目根的文档路径"),offset:a("可选:窗口起始字符位置(0 基,默认 0,从头顺序读取)"),max_chars:a("可选:本段最大字符数(默认 16000)")},["path"])}},{type:"function",function:{name:"write_file",description:"把文本写入文件(覆盖式,自动建父目录)。仅限当前项目目录内。仅用于新建文件或全量重写;小改动优先 edit_file。生成超长文件(长文档/PPT/大代码文件)时分段产出:先写首段,后续每段设 append=true 追加到文件末尾,避免单次内容过长被截断。",parameters:o({path:p("相对项目根的文件路径"),content:p("本次写入的内容(append=true 时为追加的续段)"),append:s("可选:true=追加到文件末尾(分段产出大文件);默认覆盖写")},["path","content"])}},{type:"function",function:{name:"edit_file",description:"精确替换文件中的片段:old_string 必须是文件原文(不含 read_file 的行号前缀)且在文件中唯一(除非 replace_all=true)。仅限项目目录内。",parameters:o({path:p("相对项目根的文件路径"),old_string:p("被替换的原文(需在文件中唯一,除非 replace_all;不要包含行号前缀)"),new_string:p("替换为的新内容"),replace_all:s("是否替换全部匹配(默认否)")},["path","old_string","new_string"])}},{type:"function",function:{name:"run_bash",description:"在项目目录下执行一条 shell 命令并返回输出(stdout+stderr+退出码)。用于构建、测试、git、脚手架等。默认超时 120s(可用 timeout 调整)。避免用它读文件/搜索(用 read_file/grep 更高效)。注意:以当前用户的完整权限运行、无沙箱隔离(可访问项目目录之外),删除/覆盖/联网等破坏性或外发操作须先说明意图再执行。",parameters:o({command:p("要执行的 shell 命令"),timeout:a("可选:超时毫秒")},["command"])}},{type:"function",function:{name:"run_python",description:"在内置托管 Python(含 pandas/openpyxl/python-docx/python-pptx/pypdf/matplotlib/tabulate/requests)中执行代码,用于数据分析与 Excel/Word/PPT/PDF 文档处理(读写原始文档优先用它,勿依赖 read_document 的纯文本摘要做精确分析)。首次调用会自动准备运行时(可能需数分钟,进度随结果返回)。结果必须 print 输出;图表用 matplotlib savefig 落盘(Agg 无窗口后端)。缺库时用 packages 按需安装。",parameters:o({code:p("内联 Python 代码(与 script 二选一)"),script:p("脚本路径(相对项目根,与 code 二选一)"),args:{type:"array",items:{type:"string"},description:"可选:传给脚本的命令行参数"},packages:{type:"array",items:{type:"string"},description:"可选:额外 PyPI 包(如 reportlab、xlsxwriter),自动按需安装"},timeout:a("可选:超时毫秒,默认 120000,上限 600000")},[])}},{type:"function",function:{name:"fetch_url",description:"抓取一个 http/https 网页或接口(GET),HTML 自动转为纯文本(按页面字符集解码,附标题)。用于查在线文档、API 说明、Issue、公开数据页等。需要顺着页面继续跳转/翻页采集时传 links=true 获取「锚文本 → 绝对URL」清单。结果可能很长,尽量带着明确目的使用。",parameters:o({url:p("完整 URL(http/https)"),timeout:a("可选:超时毫秒,默认 30000"),links:s("可选:附带页面链接清单(继续跳转/翻页采集用),默认不附")},["url"])}},...r].filter(e=>!("1"===process.env.HUICAI_PYTHON_DISABLED&&"run_python"===e.function.name));export const READONLY_TOOLS=new Set(["read_file","list_files","glob","grep","search_code","read_document"]);const c={read_file:e.readFile,read_document:e.readDocument,list_files:e.listFiles,glob:e.glob,grep:e.grep,search_code:t,write_file:e.writeFile,edit_file:e.editFile,run_bash:e.runBash,run_python:i,fetch_url:e.fetchUrl,...n};export async function runTool(e,t,r,n,i){const o=c[e];if(!o)return`未知工具:${e}`;try{return await o(r,t||{},n,i)}catch(e){return`工具执行失败:${e.message}`}}
|
package/src/transcript.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman|squad)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,s,r=""){const i=n.test(String(s.model||""))?s.compactModel||e:s.model,l=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,600)}`).join("\n").slice(0,16e3);if(!l.trim())return[];const c=(r?"【已有记忆(勿重复)】\n"+r+"\n\n":"")+"【本次记录】\n"+l,a=((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:i,messages:[{role:"system",content:"你是项目知识提炼器。从下面这段对话/执行记录中,提炼出**值得跨会话长期记住**的稳定事实/约定/结论/踩坑(如构建与测试命令、架构决定、代码约定、易错点、外部依赖要点)。要求:每条一行,以「- 」开头,简洁具体;只输出真正长期有用的,不要一次性细节、不要大段代码;若与「已有记忆」重复则跳过;最多 5 条;没有则输出「(无)」。"},{role:"user",content:c}]})).content||"").trim();return!a||/^[((]无[))]$/.test(a)?[]:a.split("\n").map(t=>t.replace(/^[-*]\s*/,"").trim()).filter(t=>t&&!/^[((]无[))]$/.test(t)).slice(0,5)}export async function recapConversation(o,s){const r=n.test(String(s.model||""))?s.compactModel||e:s.model,i=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,500)}`).join("\n").slice(0,12e3);if(!i.trim())return"(对话尚无内容)";return((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:r,messages:[{role:"system",content:"用一句话(不超过40个汉字)概括这段对话到目前为止在做什么、进展如何。只输出这一句话,不要引号、不要多余说明。"},{role:"user",content:i}]})).content||"").trim().split("\n")[0]||"(无法生成小结)"}
|
|
1
|
+
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman|squad)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,s,r=""){const i=n.test(String(s.model||""))?s.compactModel||e:s.model,l=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,600)}`).join("\n").slice(0,16e3);if(!l.trim())return[];const c=(r?"【已有记忆(勿重复)】\n"+r+"\n\n":"")+"【本次记录】\n"+l,a=((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:i,client:s.client,aux:!0,messages:[{role:"system",content:"你是项目知识提炼器。从下面这段对话/执行记录中,提炼出**值得跨会话长期记住**的稳定事实/约定/结论/踩坑(如构建与测试命令、架构决定、代码约定、易错点、外部依赖要点)。要求:每条一行,以「- 」开头,简洁具体;只输出真正长期有用的,不要一次性细节、不要大段代码;若与「已有记忆」重复则跳过;最多 5 条;没有则输出「(无)」。"},{role:"user",content:c}]})).content||"").trim();return!a||/^[((]无[))]$/.test(a)?[]:a.split("\n").map(t=>t.replace(/^[-*]\s*/,"").trim()).filter(t=>t&&!/^[((]无[))]$/.test(t)).slice(0,5)}export async function recapConversation(o,s){const r=n.test(String(s.model||""))?s.compactModel||e:s.model,i=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,500)}`).join("\n").slice(0,12e3);if(!i.trim())return"(对话尚无内容)";return((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:r,client:s.client,aux:!0,messages:[{role:"system",content:"用一句话(不超过40个汉字)概括这段对话到目前为止在做什么、进展如何。只输出这一句话,不要引号、不要多余说明。"},{role:"user",content:i}]})).content||"").trim().split("\n")[0]||"(无法生成小结)"}
|
package/src/tui/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import r,{useState as t,useRef as e,useReducer as n,useEffect as o,useMemo as s}from"react";import{execSync as c}from"node:child_process";import{render as l,Box as u,Text as i,Static as a,useInput as m,useApp as p}from"ink";import{runTurn as f}from"../agent.js";import{fetchUsage as g}from"../api.js";import{toolLabel as d}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as w}from"../markdown.js";import{renderWelcome as x}from"../banner.js";import{createSession as y,saveSession as b}from"../session.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as k}from"../repl.js";import{buildSystemPrompt as T}from"../context.js";import{closeMcp as $}from"../mcp.js";import{runSlash as j,listCommands as E}from"../commands.js";import{usageStatus as P,fmtReset as S}from"../usage.js";import{expandAtMentions as M,buildUserContent as A,runBangCommand as I,loadHistory as D,appendHistory as L}from"../input.js";import{loadAllowRules as C,loadDenyRules as _,matchAllowRules as U,suggestRule as R,addProjectAllowRule as W,riskWarning as F}from"../settings.js";import{PLAN_DENY_REASON as K,SANDBOX_DENY_REASON as O,sandboxBlocked as Y,AUTOMATION_TOOLS as q,automationActionLabel as B,automationDetail as G}from"../permissions.js";import{runVerifyLoop as J}from"../autofix.js";import{runAutoFlow as N}from"../autorun.js";const Q=r.createElement,X=10;function z(r,t=12){const e=String(r).split("\n");return e.length<=t?r:e.slice(0,t).join("\n")+`\n…(共 ${e.length} 行)`}function H(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const V=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:l,session:x,extraTools:rr,mcp:tr,yolo:er,initialPrompt:nr,version:or,onExit:sr}){const cr=p(),[lr,ur]=t(l),ir=e(rr),ar=e([]),mr=e(0),pr=e(""),fr=e(x),gr=e(null),dr=e(null),hr=e(new Set),wr=e(C(lr)),xr=e(_(lr)),yr=e(!1),br=e(!1),vr=e(v()),kr=e(!!er),Tr=e(!1),$r=e("idle"),jr=e([]),Er=e({list:[]}),Pr=e(D(lr)),Sr=e(null),Mr=e(""),[,Ar]=n(r=>r+1,0),[Ir,Dr]=n(r=>r+1,0),[Lr,Cr]=t(0),[_r,Ur]=t("idle"),[Rr,Wr]=t({text:"",cursor:0}),[Fr,Kr]=t(null),[Or,Yr]=t({prompt:0,completion:0}),[qr,Br]=t(0),[Gr,Jr]=t([]),[Nr,Qr]=t(0),[Xr,zr]=t(!1),[Hr,Vr]=t(""),Zr=e(null),[rt,tt]=t(0),[et,nt]=t(null),ot=e(null),[st,ct]=t(0),[lt,ut]=t(!1),it=s(()=>E(lr),[lr]),at=(r,t)=>{ar.current.push({id:mr.current++,kind:r,text:t}),Dr()},mt=()=>{if(pr.current){const t=pr.current,e=!1!==r.markdown&&w(t);at("assistant",e?h(t):t),pr.current=""}},pt=r=>{$r.current=r?"busy":"idle",Ur($r.current)};o(()=>{if("busy"!==_r)return;const r=setInterval(()=>tt(r=>(r+1)%V.length),80);return()=>clearInterval(r)},[_r]);const ft=async()=>{if(r.apiKey)try{const t=await g(r.baseUrl,r.apiKey);ot.current=t,nt(t)}catch{}};o(()=>{ft();const r=setInterval(()=>{ft()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void Vr("");const t=()=>{try{const t=c(r.statusLine,{cwd:lr,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);Vr(t)}catch{Vr("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{Zr.current&&clearInterval(Zr.current)},[]);const gt=e(""),dt={onDelta:r=>{gt.current&&(gt.current=""),pr.current+=r,Ar()},onReasoning:r=>{gt.current=(gt.current+r).slice(-400),Ar()},onToolStart:(r,t)=>{gt.current="",mt(),at("tool",d(r,t))},onToolResult:(r,t)=>at("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>at("diff",r),onSkip:(r,t)=>at("skip",d(r,t)+" — 已跳过"),onInfo:r=>{mt(),at("info",r)},onTodo:r=>Jr(r),onTurnEnd:()=>{gt.current="",mt()}},ht={request:(r,t,e,n)=>{if(xr.current.length&&U(xr.current,r,t))return Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"});if(yr.current&&!n)return Promise.resolve({allow:!1,reason:K});if(br.current&&Y(r))return Promise.resolve({allow:!1,reason:O});if(n||kr.current||hr.current.has(r)||U(wr.current,r,t))return Promise.resolve({allow:!0});const o=q.has(r);return new Promise(e=>{dr.current={name:r,args:t,resolve:e,consent:o},Kr({label:o?B(r,t):d(r,t),detail:o?G(r,t):"",name:r,args:t,consent:o,risk:F(r,t)})})},isYolo:()=>kr.current,setYolo:r=>{kr.current=!!r},setPlanMode:r=>{yr.current=!!r,zr(!!r)},isPlanMode:()=>yr.current,setSandbox:r=>{br.current=!!r},isSandbox:()=>br.current,reloadRules:()=>{wr.current=C(lr),xr.current=_(lr)}},wt=()=>{const r=jr.current.shift();return Qr(jr.current.length),null==r?null:r},xt=async t=>{const e=P(ot.current);if(e&&"exhausted"===e.level)return void at("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${S(e.resetAtEpochMs)}`);const{text:n,images:o}=await M(t,lr,r),s=vr.current.snapshot({session:fr.current,label:t});fr.current.messages.push({role:"user",content:A(n,o)}),pt(!0),gr.current=new AbortController,fr.current.compaction||(fr.current.compaction={});const c=await f({config:r,messages:fr.current.messages,permissions:ht,cwd:lr,extraTools:ir.current,events:dt,signal:gr.current.signal,todoState:Er.current,compaction:fr.current.compaction,pullQueued:wt,fileJournal:s});return gr.current=null,mt(),c?.usage&&Yr(r=>({prompt:r.prompt+c.usage.prompt,completion:r.completion+c.usage.completion})),c?.lastPromptTokens&&(Br(c.lastPromptTokens),fr.current.lastPromptTokens=c.lastPromptTokens),b(fr.current),(c?.aborted||c?.error)&&jr.current.length&&(jr.current=[],Qr(0),at("info","已清空排队消息")),pt(!1),ft(),c},yt=()=>{sr?.(),cr.exit()},bt=async t=>{const e=t.trim();if(Wr({text:"",cursor:0}),Sr.current=null,e){if(L(lr,e),Pr.current[Pr.current.length-1]!==e&&Pr.current.push(e),e.startsWith("/"))return(async t=>{const e={version:or,config:r,cwd:lr,usage:Or,permissions:ht,extraTools:ir.current,session:fr.current,checkpoints:vr.current,print:r=>at("info",String(r))},n=await j(t,e);if("exit"===n.action)return yt();if("clear"===n.action&&(fr.current=y({cwd:lr,model:r.model,messages:[fr.current.messages[0]]}),Er.current.list=[],vr.current.list.length=0,Jr([]),Br(0),ar.current=[],pr.current="",Cr(r=>r+1),at("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(Er.current.list=[],Jr([]),Br(0)),"autofix"===n.action&&await J({command:n.command,maxIters:n.maxIters,cwd:lr,runTurnOnce:r=>xt(r),emit:r=>at("info",r)}),"autorun"===n.action&&await N({goal:n.goal,config:r,permissions:ht,cwd:lr,runTurnOnce:r=>xt(r),emit:r=>at("info",r)}),"cd"===n.action){wr.current=C(n.cwd),xr.current=_(n.cwd),$(tr?.clients);const t=await k(r,n.cwd);ir.current=t.extraTools,tr&&(tr.clients=t.clients),fr.current=y({cwd:n.cwd,model:r.model,messages:[{role:"system",content:T(n.cwd,{memory:!1!==r.memoryEnabled})}]}),r.sessionId=fr.current.id,Er.current.list=[],vr.current.list.length=0,Jr([]),Br(0),ar.current=[],pr.current="",Cr(r=>r+1),ur(n.cwd),at("info","已切换到 "+n.cwd+"(新会话,旧会话已保存)")}"loop"===n.action&&(Zr.current&&(clearInterval(Zr.current),Zr.current=null),n.stop?at("info","已停止循环。"):(at("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),Zr.current=setInterval(()=>{"busy"!==$r.current&&(at("user",n.prompt),xt(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===$r.current?(jr.current.push(n.prompt),Qr(jr.current.length)):(at("user",n.prompt),await xt(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return(async r=>{if("busy"===$r.current)return void at("info","正在处理上一条,! 命令请稍候再试");at("user","! "+r);const{output:t,record:e}=await I(r,lr);at("toolresult",z(t)),fr.current.messages.push({role:"user",content:e}),b(fr.current)})(e.slice(1).trim());if("busy"===$r.current)return at("user",e),jr.current.push(e),void Qr(jr.current.length);at("user",e),await xt(e)}};o(()=>{!Tr.current&&nr&&(Tr.current=!0,bt(nr))},[]);const vt=/^\/(\S*)$/.exec(Rr.text),kt=vt?vt[1]:null,Tt=null==kt?[]:it.filter(r=>r.name.toLowerCase().startsWith(kt.toLowerCase())),$t=Tt.length>0&&!lt,jt=Tt.length<=X?0:Math.max(0,Math.min(st-Math.floor(5),Tt.length-X)),Et=Tt.slice(jt,jt+X);o(()=>{ct(0),ut(!1)},[kt]),m((r,t)=>{if(dr.current){const e=dr.current,n=r=>{dr.current=null,Kr(null),e.resolve(r)};if("y"===r||"1"===r)return n({allow:!0});if("a"===r||"2"===r)return hr.current.add(e.name),n({allow:!0});if("3"===r)return n({allow:!1,reason:"用户选择了「否」,该操作未执行。"});if("A"===r){hr.current.add(e.name);const r=R(e.name,e.args);try{W(lr,r),wr.current=C(lr),at("info",`已写入允许规则:${r} → .huicai/settings.json`)}catch(r){at("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===r||t.escape||t.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if($t){const r=()=>Tt[Math.min(st,Tt.length-1)];if(t.upArrow)return ct(r=>(r-1+Tt.length)%Tt.length);if(t.downArrow)return ct(r=>(r+1)%Tt.length);if(t.pageUp)return ct(r=>Math.max(0,r-X));if(t.pageDown)return ct(r=>Math.min(Tt.length-1,r+X));if(t.tab){const t=r().name;return Wr({text:"/"+t+" ",cursor:t.length+2})}if(t.return)return void bt("/"+r().name);if(t.escape)return ut(!0)}if(t.escape)"busy"===$r.current&&gr.current&&(gr.current.abort(),at("info","已请求中断(Esc)"));else if(t.ctrl&&"c"===r)"busy"===$r.current&&gr.current?(gr.current.abort(),at("info","已请求中断")):yt();else if(t.return)bt(Rr.text);else{if(t.upArrow)return(()=>{const r=Pr.current;if(!r.length)return;null==Sr.current?(Mr.current=Rr.text,Sr.current=r.length-1):Sr.current>0&&Sr.current--;const t=r[Sr.current]??"";Wr({text:t,cursor:t.length})})();if(t.downArrow)return(()=>{if(null==Sr.current)return;const r=Pr.current;if(Sr.current<r.length-1){Sr.current++;const t=r[Sr.current];Wr({text:t,cursor:t.length})}else Sr.current=null,Wr({text:Mr.current,cursor:Mr.current.length})})();if(t.leftArrow)return Wr(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(t.rightArrow)return Wr(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(t.ctrl&&"a"===r)return Wr(r=>({...r,cursor:0}));if(t.ctrl&&"e"===r)return Wr(r=>({...r,cursor:r.text.length}));if(t.ctrl&&"u"===r)return Wr(r=>({text:r.text.slice(r.cursor),cursor:0}));if(t.ctrl&&"k"===r)return Wr(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(t.ctrl&&"w"===r)return Wr(r=>{const t=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:t+r.text.slice(r.cursor),cursor:t.length}});if(t.backspace||t.delete)return Sr.current=null,Wr(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(r&&!t.ctrl&&!t.meta){Sr.current=null;const t=r.replace(/\r\n?/g,"\n");Wr(r=>({text:r.text.slice(0,r.cursor)+t+r.text.slice(r.cursor),cursor:r.cursor+t.length}))}}});const Pt=s(()=>ar.current.slice(),[Ir]),St=lr.split("/").filter(Boolean).pop()||lr,Mt=Rr.text.slice(0,Rr.cursor),At=Rr.text[Rr.cursor]??" ",It=Rr.text.slice(Rr.cursor+1),Dt=Math.max(8,process.stdout.columns||80),Lt=P(et),Ct=Lt?"exhausted"===Lt.level?`⛔ ${Lt.label}用量已用尽${S(Lt.resetAtEpochMs)?" · "+S(Lt.resetAtEpochMs):""}`:`⚠ ${Lt.label}用量 ${Math.round(100*Lt.pct)}%(${H(Lt.used)}/${H(Lt.limit)})`:"";return Q(u,{flexDirection:"column"},Q(a,{key:Lr,items:Pt},r=>Q(Z,{key:r.id,item:r})),gt.current?Q(u,null,Q(i,{color:"gray",italic:!0},"✦ 思考 "+gt.current.replace(/\n/g," ").slice(-160))):null,pr.current?Q(u,{marginTop:0},Q(i,null,pr.current)):null,Gr.length?Q(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Gr.map((r,t)=>Q(i,{key:t,color:"completed"===r.status?"green":"in_progress"===r.status?"cyan":"gray",dimColor:"completed"===r.status,bold:"in_progress"===r.status},("completed"===r.status?"☑ ":"in_progress"===r.status?"◐ ":"☐ ")+r.content))):null,"busy"!==_r||pr.current?null:Q(u,null,Q(i,{color:"cyan"},V[rt]+" 思考中…"),Q(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),Fr?Q(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},Q(i,{color:"yellow",bold:!0},Fr.consent?"● 是否继续?"+Fr.label:"● 需要授权:"+Fr.label),Fr.risk?Q(i,{color:"red",bold:!0},"⚠ "+Fr.risk):null,Fr.consent&&Fr.detail?Q(i,{color:"gray"},z(Fr.detail,6)):null,"run_bash"===Fr.name?Q(i,{color:"gray"},"$ "+(Fr.args.command||"")):null,"fetch_url"===Fr.name?Q(i,{color:"gray"},"GET "+(Fr.args.url||"")):null,Q(i,null,Fr.consent?"[1] 是 · [2] 是,并允许在当前对话中后续相同操作不再询问 · [3/Esc] 否":"[y] 允许一次 · [a] 本会话 · [A] 永久(写入项目设置) · [n/Esc] 拒绝")):null,$t?Q(u,{flexDirection:"column",marginTop:1},Q(i,{color:"gray"},Tt.length>X?` 斜杠命令 ${jt+1}-${jt+Et.length}/${Tt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...Et.map((r,t)=>{const e=jt+t;return Q(u,{key:r.name},Q(i,{color:"cyan",bold:e===st},(e===st?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),Q(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,Q(u,{marginTop:$t?0:1},Q(i,{color:"blue"},"─".repeat(Dt))),Q(u,null,Q(i,{color:"busy"===_r?"yellow":"cyan"},"busy"===_r?"⧗ ":"› "),Q(i,null,Mt),Q(i,{inverse:!0},At),Q(i,null,It)),Q(i,{color:"blue"},"─".repeat(Dt)),Q(u,{justifyContent:"space-between"},Q(i,{color:"gray"},(Hr?Hr+" · ":"")+`${St} · ${r.model}${kr.current?" · ⚠ yolo":""}${Xr?" · 📋 计划":""}`+(Nr?` · 排队 ${Nr}`:"")+" · /help"),Ct?Q(i,{color:"exhausted"===Lt.level?"red":"yellow",bold:!0},Ct):Q(i,null,"")))}function Z({item:r}){const{kind:t,text:e}=r;if("user"===t)return Q(u,{marginTop:1},Q(i,{color:"cyan",bold:!0},"› "),Q(i,{bold:!0},e));if("assistant"===t)return Q(u,null,Q(i,null,e));if("diff"===t)return Q(u,{marginLeft:2},Q(i,null,e));if("tool"===t)return Q(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return Q(i,{color:"gray"}," "+e);if("skip"===t)return Q(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return Q(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return Q(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:s}){process.stdout.write(x({model:r.model,cwd:t,version:s,platform:/^(agent|workflow|datahuman)-/.test(String(r.model||"")),yolo:n,consult:r.consult?r.consultLabel||r.consult:null,ink:!0})+"\n");const{extraTools:c,clients:u}=await k(r,t),i={clients:u},{waitUntilExit:a}=l(Q(App,{config:r,cwd:t,session:e,extraTools:c,mcp:i,yolo:n,initialPrompt:o,version:s,onExit:null}));await a(),$(i.clients)}
|
|
1
|
+
import r,{useState as t,useRef as e,useReducer as n,useEffect as o,useMemo as l}from"react";import{execSync as c}from"node:child_process";import{render as s,Box as u,Text as i,Static as a,useInput as m,useApp as p}from"ink";import{runTurn as g}from"../agent.js";import{fetchUsage as f}from"../api.js";import{toolLabel as d}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as x}from"../markdown.js";import{renderWelcome as w}from"../banner.js";import{createSession as y,saveSession as b}from"../session.js";import{ensureSessionTitle as k}from"../title.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as $}from"../repl.js";import{buildSystemPrompt as M}from"../context.js";import{closeMcp as T}from"../mcp.js";import{runSlash as E,listCommands as j}from"../commands.js";import{usageStatus as P,fmtReset as A}from"../usage.js";import{expandAtMentions as S,buildUserContent as D,runBangCommand as I,loadHistory as L,appendHistory as C}from"../input.js";import{listWorkspaceFiles as U}from"../executors.js";import{loadAllowRules as _,loadDenyRules as W,matchAllowRules as O,suggestRule as R,addProjectAllowRule as F,riskWarning as K}from"../settings.js";import{PLAN_DENY_REASON as Y,SANDBOX_DENY_REASON as q,sandboxBlocked as B,AUTOMATION_TOOLS as G,automationActionLabel as J,automationDetail as N,PERMISSION_MODES as Q,MODE_ICON as X,MODE_LABEL as z}from"../permissions.js";import{runVerifyLoop as H}from"../autofix.js";import{runAutoFlow as V}from"../autorun.js";const Z=r.createElement,rr=10;function tr(r,t=12){const e=String(r).split("\n");return e.length<=t?r:e.slice(0,t).join("\n")+`\n…(共 ${e.length} 行)`}function er(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const nr=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:s,session:w,extraTools:lr,mcp:cr,yolo:sr,initialPrompt:ur,version:ir,onExit:ar}){const mr=p(),[pr,gr]=t(s),fr=e(lr),dr=e([]),hr=e(0),xr=e(""),wr=e(w),yr=e(null),br=e(null),kr=e(new Set),vr=e(_(pr)),$r=e(W(pr)),Mr=e(!1),Tr=e(!1),Er=e(!1),jr=e(v()),Pr=e(!!sr),Ar=e(!1),Sr=e("idle"),Dr=e([]),Ir=e({list:[]}),Lr=e(L(pr)),Cr=e(null),Ur=e(""),[,_r]=n(r=>r+1,0),[Wr,Or]=n(r=>r+1,0),[Rr,Fr]=t(0),[Kr,Yr]=t("idle"),[qr,Br]=t({text:"",cursor:0}),[Gr,Jr]=t(null),[Nr,Qr]=t({prompt:0,completion:0}),[Xr,zr]=t(0),[Hr,Vr]=t([]),[Zr,rt]=t(0),[tt,et]=t(sr?"auto":"manual"),nt=()=>Mr.current?"plan":Tr.current?"sandbox":Pr.current?"auto":Er.current?"acceptEdits":"manual",[ot,lt]=t(""),ct=e(null),[st,ut]=t(0),[it,at]=t(null),mt=e(null),[pt,gt]=t(0),[ft,dt]=t(!1),ht=l(()=>j(pr),[pr]),[xt,wt]=t(0),[yt,bt]=t(!1),kt=l(()=>{try{return U(pr)}catch{return[]}},[pr]),[vt,$t]=t(!1),[Mt,Tt]=t(0),Et=e(0),jt=(r,t)=>{dr.current.push({id:hr.current++,kind:r,text:t}),Or()},Pt=()=>{if(xr.current){const t=xr.current,e=!1!==r.markdown&&x(t);jt("assistant",e?h(t):t),xr.current=""}},At=r=>{Sr.current=r?"busy":"idle",Yr(Sr.current)};o(()=>{if("busy"!==Kr)return;const r=setInterval(()=>ut(r=>(r+1)%nr.length),80);return()=>clearInterval(r)},[Kr]);const St=async()=>{if(r.apiKey)try{const t=await f(r.baseUrl,r.apiKey);mt.current=t,at(t)}catch{}};o(()=>{St();const r=setInterval(()=>{St()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void lt("");const t=()=>{try{const t=c(r.statusLine,{cwd:pr,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);lt(t)}catch{lt("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{ct.current&&clearInterval(ct.current)},[]);const Dt=e(""),It={onDelta:r=>{Dt.current&&(Dt.current=""),xr.current+=r,_r()},onReasoning:r=>{Dt.current=(Dt.current+r).slice(-400),_r()},onToolStart:(r,t)=>{Dt.current="",Pt(),jt("tool",d(r,t))},onToolResult:(r,t)=>jt("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>jt("diff",r),onSkip:(r,t)=>jt("skip",d(r,t)+" — 已跳过"),onInfo:r=>{Pt(),jt("info",r)},onTodo:r=>Vr(r),onDecision:r=>{Pt();const t=(r.alternatives||[]).length?` · ${r.alternatives.length} 备选`:"",e="draft"===r.status?"(待确认草稿)":"";jt("info",`◆ 已记录决策:${r.title}${t}${e}`)},onTurnEnd:()=>{Dt.current="",Pt()}},Lt={request:(r,t,e,n)=>{if($r.current.length&&O($r.current,r,t))return Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"});if(Mr.current&&!n)return Promise.resolve({allow:!1,reason:Y});if(Tr.current&&B(r))return Promise.resolve({allow:!1,reason:q});if(n||Pr.current||kr.current.has(r)||O(vr.current,r,t))return Promise.resolve({allow:!0});if(Er.current&&("write_file"===r||"edit_file"===r))return Promise.resolve({allow:!0});const o=G.has(r);return new Promise(e=>{br.current={name:r,args:t,resolve:e,consent:o},Jr({label:o?J(r,t):d(r,t),detail:o?N(r,t):"",name:r,args:t,consent:o,risk:K(r,t)})})},isYolo:()=>Pr.current,setYolo:r=>{Pr.current=!!r,et(nt())},setPlanMode:r=>{Mr.current=!!r,et(nt())},isPlanMode:()=>Mr.current,setSandbox:r=>{Tr.current=!!r,et(nt())},isSandbox:()=>Tr.current,setAcceptEdits:r=>{Er.current=!!r,et(nt())},isAcceptEdits:()=>Er.current,setMode:r=>{Pr.current="auto"===r,Er.current="acceptEdits"===r,Mr.current="plan"===r,Tr.current="sandbox"===r,et(r)},getMode:()=>nt(),reloadRules:()=>{vr.current=_(pr),$r.current=W(pr)}},Ct=()=>{const r=Dr.current.shift();return rt(Dr.current.length),null==r?null:r},Ut=()=>{k(wr.current,r).then(r=>{r&&b(wr.current)}).catch(()=>{})},_t=async t=>{const e=P(mt.current);if(e&&"exhausted"===e.level)return void jt("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${A(e.resetAtEpochMs)}`);const{text:n,images:o}=await S(t,pr,r),l=jr.current.snapshot({session:wr.current,label:t});wr.current.messages.push({role:"user",content:D(n,o)}),Ut(),At(!0),yr.current=new AbortController,wr.current.compaction||(wr.current.compaction={});const c=await g({config:r,messages:wr.current.messages,permissions:Lt,cwd:pr,extraTools:fr.current,events:It,signal:yr.current.signal,todoState:Ir.current,compaction:wr.current.compaction,pullQueued:Ct,fileJournal:l});return yr.current=null,Pt(),c?.usage&&Qr(r=>({prompt:r.prompt+c.usage.prompt,completion:r.completion+c.usage.completion})),c?.lastPromptTokens&&(zr(c.lastPromptTokens),wr.current.lastPromptTokens=c.lastPromptTokens),b(wr.current),Ut(),(c?.aborted||c?.error)&&Dr.current.length&&(Dr.current=[],rt(0),jt("info","已清空排队消息")),At(!1),St(),c},Wt=()=>{ar?.(),mr.exit()},Ot=async t=>{const e=t.trim();if(Br({text:"",cursor:0}),Cr.current=null,e){if(C(pr,e),Lr.current[Lr.current.length-1]!==e&&Lr.current.push(e),e.startsWith("/"))return(async t=>{const e={version:ir,config:r,cwd:pr,usage:Nr,permissions:Lt,extraTools:fr.current,session:wr.current,checkpoints:jr.current,print:r=>jt("info",String(r))},n=await E(t,e);if("exit"===n.action)return Wt();if("clear"===n.action&&(wr.current=y({cwd:pr,model:r.model,messages:[wr.current.messages[0]]}),Ir.current.list=[],jr.current.list.length=0,Vr([]),zr(0),dr.current=[],xr.current="",Fr(r=>r+1),jt("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(Ir.current.list=[],Vr([]),zr(0)),"autofix"===n.action&&await H({command:n.command,maxIters:n.maxIters,cwd:pr,runTurnOnce:r=>_t(r),emit:r=>jt("info",r)}),"autorun"===n.action&&await V({goal:n.goal,config:r,permissions:Lt,cwd:pr,runTurnOnce:r=>_t(r),emit:r=>jt("info",r)}),"cd"===n.action){vr.current=_(n.cwd),$r.current=W(n.cwd),T(cr?.clients);const t=await $(r,n.cwd);fr.current=t.extraTools,cr&&(cr.clients=t.clients),wr.current=y({cwd:n.cwd,model:r.model,messages:[{role:"system",content:M(n.cwd,{memory:!1!==r.memoryEnabled})}]}),r.sessionId=wr.current.id,Ir.current.list=[],jr.current.list.length=0,Vr([]),zr(0),dr.current=[],xr.current="",Fr(r=>r+1),gr(n.cwd),jt("info","已切换到 "+n.cwd+"(新会话,旧会话已保存)")}"loop"===n.action&&(ct.current&&(clearInterval(ct.current),ct.current=null),n.stop?jt("info","已停止循环。"):(jt("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),ct.current=setInterval(()=>{"busy"!==Sr.current&&(jt("user",n.prompt),_t(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===Sr.current?(Dr.current.push(n.prompt),rt(Dr.current.length)):(jt("user",n.prompt),await _t(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return(async r=>{if("busy"===Sr.current)return void jt("info","正在处理上一条,! 命令请稍候再试");jt("user","! "+r);const{output:t,record:e}=await I(r,pr);jt("toolresult",tr(t)),wr.current.messages.push({role:"user",content:e}),b(wr.current)})(e.slice(1).trim());if("busy"===Sr.current)return jt("user",e),Dr.current.push(e),void rt(Dr.current.length);jt("user",e),await _t(e)}};o(()=>{!Ar.current&&ur&&(Ar.current=!0,Ot(ur))},[]);const Rt=/^\/(\S*)$/.exec(qr.text),Ft=Rt?Rt[1]:null,Kt=null==Ft?[]:ht.filter(r=>r.name.toLowerCase().startsWith(Ft.toLowerCase())),Yt=Kt.length>0&&!ft,qt=Kt.length<=rr?0:Math.max(0,Math.min(pt-Math.floor(5),Kt.length-rr)),Bt=Kt.slice(qt,qt+rr);o(()=>{gt(0),dt(!1)},[Ft]);const Gt=null==Ft?/(?:^|\s)@([^\s@]*)$/.exec(qr.text):null,Jt=Gt?Gt[1]:null,Nt=null==Jt?[]:(()=>{const r=Jt.toLowerCase(),t=[];for(const e of kt){const n=e.toLowerCase(),o=n.split("/").pop();let l=-1;if(r?o.startsWith(r)?l=3:o.includes(r)?l=2:n.includes(r)&&(l=1):l=0,l>=0&&t.push({f:e,rank:l}),!r&&t.length>=50)break}return t.sort((r,t)=>t.rank-r.rank||r.f.length-t.f.length||r.f.localeCompare(t.f)),t.slice(0,50).map(r=>r.f)})(),Qt=Nt.length>0&&!yt,Xt=Nt.length<=rr?0:Math.max(0,Math.min(xt-Math.floor(5),Nt.length-rr)),zt=Nt.slice(Xt,Xt+rr);o(()=>{wt(0),bt(!1)},[Jt]);const Ht=l(()=>{const r=new Set,t=[];for(let e=Lr.current.length-1;e>=0&&t.length<50;e--){const n=Lr.current[e];n&&!r.has(n)&&(r.add(n),t.push(n))}return t},[vt]),Vt=Ht.length<=rr?0:Math.max(0,Math.min(Mt-Math.floor(5),Ht.length-rr)),Zt=Ht.slice(Vt,Vt+rr);m((t,e)=>{if(br.current){const r=br.current,n=t=>{br.current=null,Jr(null),r.resolve(t)};if("y"===t||"1"===t)return n({allow:!0});if("a"===t||"2"===t)return kr.current.add(r.name),n({allow:!0});if("3"===t)return n({allow:!1,reason:"用户选择了「否」,该操作未执行。"});if("A"===t){kr.current.add(r.name);const t=R(r.name,r.args);try{F(pr,t),vr.current=_(pr),jt("info",`已写入允许规则:${t} → .huicai/settings.json`)}catch(r){jt("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===t||e.escape||e.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(vt){if(!Ht.length)return void $t(!1);if(e.upArrow)return Tt(r=>(r-1+Ht.length)%Ht.length);if(e.downArrow)return Tt(r=>(r+1)%Ht.length);if(e.pageUp)return Tt(r=>Math.max(0,r-rr));if(e.pageDown)return Tt(r=>Math.min(Ht.length-1,r+rr));if(e.return){const r=Ht[Math.min(Mt,Ht.length-1)]||"";return $t(!1),Cr.current=null,Br({text:r,cursor:r.length})}return e.escape?$t(!1):void 0}if(e.tab&&e.shift){const t=Q[(Q.indexOf(Lt.getMode())+1)%Q.length];return Lt.setMode(n=t),r.planMode="plan"===n,r.sandbox="sandbox"===n,void jt("info",`对话模式 → ${X[n]} ${z[n]}`+("auto"===n?"(已开放所有权限,任何命令免审批)":""))}var n;if(Yt){const r=()=>Kt[Math.min(pt,Kt.length-1)];if(e.upArrow)return gt(r=>(r-1+Kt.length)%Kt.length);if(e.downArrow)return gt(r=>(r+1)%Kt.length);if(e.pageUp)return gt(r=>Math.max(0,r-rr));if(e.pageDown)return gt(r=>Math.min(Kt.length-1,r+rr));if(e.tab){const t=r().name;return Br({text:"/"+t+" ",cursor:t.length+2})}if(e.return)return void Ot("/"+r().name);if(e.escape)return dt(!0)}if(Qt){const r=()=>Nt[Math.min(xt,Nt.length-1)];if(e.upArrow)return wt(r=>(r-1+Nt.length)%Nt.length);if(e.downArrow)return wt(r=>(r+1)%Nt.length);if(e.pageUp)return wt(r=>Math.max(0,r-rr));if(e.pageDown)return wt(r=>Math.min(Nt.length-1,r+rr));if(e.tab||e.return){const t=r(),e=qr.text.length-(Jt?Jt.length:0)-1,n=qr.text.slice(0,e+1)+t+" ";return Br({text:n,cursor:n.length})}if(e.escape)return bt(!0)}if(e.escape){if("busy"===Sr.current&&yr.current)return yr.current.abort(),void jt("info","已请求中断(Esc)");const r=Date.now();return r-Et.current<600&&Lr.current.length?(Et.current=0,Tt(0),void $t(!0)):void(Et.current=r)}if(e.ctrl&&"c"===t)"busy"===Sr.current&&yr.current?(yr.current.abort(),jt("info","已请求中断")):Wt();else if(e.return)Ot(qr.text);else{if(e.upArrow)return(()=>{const r=Lr.current;if(!r.length)return;null==Cr.current?(Ur.current=qr.text,Cr.current=r.length-1):Cr.current>0&&Cr.current--;const t=r[Cr.current]??"";Br({text:t,cursor:t.length})})();if(e.downArrow)return(()=>{if(null==Cr.current)return;const r=Lr.current;if(Cr.current<r.length-1){Cr.current++;const t=r[Cr.current];Br({text:t,cursor:t.length})}else Cr.current=null,Br({text:Ur.current,cursor:Ur.current.length})})();if(e.leftArrow)return Br(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(e.rightArrow)return Br(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(e.ctrl&&"a"===t)return Br(r=>({...r,cursor:0}));if(e.ctrl&&"e"===t)return Br(r=>({...r,cursor:r.text.length}));if(e.ctrl&&"u"===t)return Br(r=>({text:r.text.slice(r.cursor),cursor:0}));if(e.ctrl&&"k"===t)return Br(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(e.ctrl&&"w"===t)return Br(r=>{const t=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:t+r.text.slice(r.cursor),cursor:t.length}});if(e.backspace||e.delete)return Cr.current=null,Br(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(t&&!e.ctrl&&!e.meta){Cr.current=null;const r=t.replace(/\r\n?/g,"\n");Br(t=>({text:t.text.slice(0,t.cursor)+r+t.text.slice(t.cursor),cursor:t.cursor+r.length}))}}});const re=l(()=>dr.current.slice(),[Wr]),te=pr.split("/").filter(Boolean).pop()||pr,ee=qr.text.slice(0,qr.cursor),ne=qr.text[qr.cursor]??" ",oe=qr.text.slice(qr.cursor+1),le=Math.max(8,process.stdout.columns||80),ce=P(it),se=ce?"exhausted"===ce.level?`⛔ ${ce.label}用量已用尽${A(ce.resetAtEpochMs)?" · "+A(ce.resetAtEpochMs):""}`:`⚠ ${ce.label}用量 ${Math.round(100*ce.pct)}%(${er(ce.used)}/${er(ce.limit)})`:"";return Z(u,{flexDirection:"column"},Z(a,{key:Rr,items:re},r=>Z(or,{key:r.id,item:r})),Dt.current?Z(u,null,Z(i,{color:"gray",italic:!0},"✦ 思考 "+Dt.current.replace(/\n/g," ").slice(-160))):null,xr.current?Z(u,{marginTop:0},Z(i,null,xr.current)):null,Hr.length?Z(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Hr.map((r,t)=>Z(i,{key:t,color:"completed"===r.status?"green":"in_progress"===r.status?"cyan":"gray",dimColor:"completed"===r.status,bold:"in_progress"===r.status},("completed"===r.status?"☑ ":"in_progress"===r.status?"◐ ":"☐ ")+r.content))):null,"busy"!==Kr||xr.current?null:Z(u,null,Z(i,{color:"cyan"},nr[st]+" 思考中…"),Z(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),Gr?Z(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},Z(i,{color:"yellow",bold:!0},Gr.consent?"● 是否继续?"+Gr.label:"● 需要授权:"+Gr.label),Gr.risk?Z(i,{color:"red",bold:!0},"⚠ "+Gr.risk):null,Gr.consent&&Gr.detail?Z(i,{color:"gray"},tr(Gr.detail,6)):null,"run_bash"===Gr.name?Z(i,{color:"gray"},"$ "+(Gr.args.command||"")):null,"fetch_url"===Gr.name?Z(i,{color:"gray"},"GET "+(Gr.args.url||"")):null,Z(u,{marginTop:1}),...Gr.consent?[Z(u,{key:"c1"},Z(i,{color:"green",bold:!0}," [1] "),Z(i,null,"是,继续")),Z(u,{key:"c2"},Z(i,{color:"cyan",bold:!0}," [2] "),Z(i,null,"是,并在本次对话中后续相同操作不再询问")),Z(u,{key:"c3"},Z(i,{color:"gray",bold:!0}," [3] "),Z(i,{color:"gray"},"否(或按 Esc / 回车)"))]:[Z(u,{key:"a1"},Z(i,{color:"green",bold:!0}," [y] "),Z(i,null,"允许一次")),Z(u,{key:"a2"},Z(i,{color:"cyan",bold:!0}," [a] "),Z(i,null,"本次对话都允许")),Z(u,{key:"a3"},Z(i,{color:"blue",bold:!0}," [A] "),Z(i,null,"永久允许(写入项目设置)")),Z(u,{key:"a4"},Z(i,{color:"gray",bold:!0}," [n] "),Z(i,{color:"gray"},"拒绝(或按 Esc / 回车)"))]):null,Yt?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Kt.length>rr?` 斜杠命令 ${qt+1}-${qt+Bt.length}/${Kt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...Bt.map((r,t)=>{const e=qt+t;return Z(u,{key:r.name},Z(i,{color:"cyan",bold:e===pt},(e===pt?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),Z(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,Qt?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Nt.length>rr?` @ 引用文件 ${Xt+1}-${Xt+zt.length}/${Nt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab/⏎ 插入 · Esc 关闭`:" @ 引用文件 · ↑↓ 选择 · Tab/⏎ 插入 · Esc 关闭"),...zt.map((r,t)=>{const e=Xt+t;return Z(u,{key:r},Z(i,{color:"cyan",bold:e===xt},(e===xt?" ❯ ":" ")+"@"+r))})):null,vt&&Ht.length?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Ht.length>rr?` 历史记录 ${Vt+1}-${Vt+Zt.length}/${Ht.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · ⏎ 填入 · Esc 关闭`:" 历史记录 · ↑↓ 选择 · ⏎ 填入 · Esc 关闭"),...Zt.map((r,t)=>{const e=Vt+t;return Z(u,{key:e},Z(i,{color:"cyan",bold:e===Mt},(e===Mt?" ❯ ":" ")+r.replace(/\s+/g," ").slice(0,le-6)))})):null,Z(u,{marginTop:Yt||Qt||vt?0:1},Z(i,{color:"blue"},"─".repeat(le))),Z(u,null,Z(i,{color:"busy"===Kr?"yellow":"cyan"},"busy"===Kr?"⧗ ":"› "),Z(i,null,ee),Z(i,{inverse:!0},ne),Z(i,null,oe)),Z(i,{color:"blue"},"─".repeat(le)),Z(u,{justifyContent:"space-between"},Z(i,{color:"gray"},(ot?ot+" · ":"")+`${te} · ${r.model} · ${X[tt]} ${z[tt]}`+(Zr?` · 排队 ${Zr}`:"")+" · ⇧Tab 模式 · /help"),se?Z(i,{color:"exhausted"===ce.level?"red":"yellow",bold:!0},se):Z(i,null,"")))}function or({item:r}){const{kind:t,text:e}=r;if("user"===t)return Z(u,{marginTop:1},Z(i,{color:"cyan",bold:!0},"› "),Z(i,{bold:!0},e));if("assistant"===t)return Z(u,null,Z(i,null,e));if("diff"===t)return Z(u,{marginLeft:2},Z(i,null,e));if("tool"===t)return Z(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return Z(i,{color:"gray"}," "+e);if("skip"===t)return Z(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return Z(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return Z(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:l}){process.stdout.write(w({model:r.model,cwd:t,version:l,platform:/^(agent|workflow|datahuman)-/.test(String(r.model||"")),yolo:n,consult:r.consult?r.consultLabel||r.consult:null,ink:!0})+"\n");const{extraTools:c,clients:u}=await $(r,t),i={clients:u},{waitUntilExit:a}=s(Z(App,{config:r,cwd:t,session:e,extraTools:c,mcp:i,yolo:n,initialPrompt:o,version:l,onExit:null}));await a(),T(i.clients)}
|