@huicai/cli 2026.714.0 → 2026.721.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 +2 -1
- 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/agent.js +1 -1
- package/src/api.js +1 -1
- package/src/codeindex.js +1 -1
- package/src/commands.js +1 -1
- package/src/config.js +1 -1
- package/src/context.js +1 -1
- package/src/doccache.js +1 -0
- package/src/doctor.js +1 -1
- package/src/executors.js +1 -1
- package/src/input.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/tools.js +1 -1
- package/src/tui/app.js +1 -1
package/src/scan.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import e from"node:fs";import t from"node:path";import{execSync as n,execFileSync as r}from"node:child_process";import{captureCommand as a}from"./autofix.js";function o(n,r){try{return e.existsSync(t.join(n,r))}catch{return!1}}function c(e){try{return n("win32"===process.platform?`where ${e}`:`command -v ${e}`,{stdio:"ignore"}),!0}catch{return!1}}export function detectScanners(e){const t=[];return o(e,"package.json")&&c("npm")&&t.push({name:"npm audit",command:"npm audit --audit-level=high",why:"Node 依赖漏洞"}),(o(e,"requirements.txt")||o(e,"pyproject.toml"))&&c("pip-audit")&&t.push({name:"pip-audit",command:"pip-audit",why:"Python 依赖漏洞"}),o(e,"go.mod")&&c("govulncheck")&&t.push({name:"govulncheck",command:"govulncheck ./...",why:"Go 漏洞"}),o(e,"Cargo.toml")&&c("cargo-audit")&&t.push({name:"cargo audit",command:"cargo audit",why:"Rust 依赖漏洞"}),c("semgrep")&&t.push({name:"semgrep",command:"semgrep --config auto --error -q .",why:"代码静态安全(SAST)"}),c("gitleaks")&&t.push({name:"gitleaks",command:"gitleaks detect --no-banner -r /dev/stdout",why:"密钥泄漏"}),c("trivy")&&t.push({name:"trivy fs",command:"trivy fs --scanners vuln,secret -q .",why:"依赖漏洞+密钥"}),t}export async function runScanners(e,t){const n=[];for(const r of t){const{code:t,output:o}=await a(e,r.command,18e4);n.push({name:r.name,why:r.why,command:r.command,code:t,output:o})}return n}export const HEALTH_RULES=[{id:"dynamic-eval",severity:"high",why:"动态执行 eval / new Function",pattern:"\\beval\\(|new Function\\("},{id:"hardcoded-secret",severity:"high",why:"疑似硬编码密钥/口令",pattern:"(password|passwd|secret|api[_-]?key|access[_-]?key)\\s*[:=]\\s*[\"'][^\"']{6,}"},{id:"insecure-random",severity:"high",why:"安全场景疑用不安全随机源",pattern:"Math\\.random\\(\\)[^\\n]*(token|secret|key|nonce|salt|otp|passw)"},{id:"print-stack-trace",severity:"warn",why:"Java 直接打印堆栈(应走日志框架)",pattern:"printStackTrace\\("},{id:"stdout-print",severity:"warn",why:"裸 System.out/err 输出(应走日志)",pattern:"System\\.(out|err)\\.print"},{id:"empty-catch",severity:"warn",why:"空 catch 吞异常",pattern:"catch\\s*(\\([^)]*\\))?\\s*\\{\\s*\\}",multiline:!0},{id:"debugger",severity:"warn",why:"遗留 debugger 断点",pattern:"\\bdebugger\\b"},{id:"todo-fixme",severity:"info",why:"未完成标记 TODO/FIXME/HACK",pattern:"\\b(TODO|FIXME|HACK)\\b"}];const s=["!**/node_modules/**","!**/dist/**","!**/build/**","!**/target/**","!**/out/**","!**/.git/**","!**/*.min.js","!**/vendor/**"],i={high:0,warn:1,info:2};function u(e,t){if(!c("rg"))return{count:-1,sample:""};const n=s.flatMap(e=>["-g",e]),a=e.multiline?["-lU"]:["-l"];try{const o=r("rg",[...a,...n,"-e",e.pattern,"."],{cwd:t,encoding:"utf8",maxBuffer:16<<20,stdio:["ignore","pipe","ignore"]}).split("\n").filter(Boolean);return{count:o.length,sample:o[0]||""}}catch{return{count:0,sample:""}}}export function runHealthScan(e,t=u){const n=[];for(const r of HEALTH_RULES){const{count:a,sample:o}=t(r,e);a>0&&n.push({id:r.id,severity:r.severity,why:r.why,count:a,sample:o})}return n.sort((e,t)=>i[e.severity]-i[t.severity]||t.count-e.count),n}export function formatHealthScan(e){return e.length?["发现以下高信号项:",...e.map(e=>`- [${e.severity}] ${e.why}:命中 ${e.count} 文件${e.sample?`(如 ${e.sample})`:""}`)].join("\n"):"未发现高信号问题(eval/硬编码密钥/不安全随机/printStackTrace/空catch/debugger 等)。"}export function makeScanTool({cwd:e}){return{readOnly:!0,spec:{type:"function",function:{name:"security_scan",description:"运行内置代码健康自检(eval/硬编码密钥/不安全随机/printStackTrace/空catch/debugger 等高信号规则,无需外部工具)并自动检测运行本项目适用的外部扫描器(npm audit / semgrep / trivy / gitleaks,仅跑本机已装的),返回发现的问题。用于安全审查或提交前自检;发现问题后你应逐条修复再重扫确认。",parameters:{type:"object",properties:{},required:[]}}},run:async()=>{const t=[];t.push("## 代码健康自检(内置)\n"+formatHealthScan(runHealthScan(e)));const n=detectScanners(e);if(n.length){const r=await runScanners(e,n);t.push(...r.map(e=>`## ${e.name}(${0===e.code?"无高危":"有发现"})\n${e.output.slice(0,4e3)}`))}else t.push("(未检测到外部扫描器:可装 semgrep/trivy/gitleaks 获取更深的依赖漏洞/SAST/密钥扫描。)");return t.join("\n\n")}}}
|
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)中执行代码,用于数据分析与 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){const i=c[e];if(!i)return`未知工具:${e}`;try{return await i(r,t||{},n)}catch(e){return`工具执行失败:${e.message}`}}
|
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 c}from"react";import{execSync as s}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 d}from"../api.js";import{toolLabel as g}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 M,fmtReset as P}from"../usage.js";import{expandAtMentions as A,buildUserContent as S,runBangCommand as I,loadHistory as D,appendHistory as L}from"../input.js";import{loadAllowRules as _,loadDenyRules as C,matchAllowRules as U,suggestRule as O,addProjectAllowRule as R,riskWarning as W}from"../settings.js";import{PLAN_DENY_REASON as F,SANDBOX_DENY_REASON as K,sandboxBlocked as Y,AUTOMATION_TOOLS as q,automationActionLabel as B,automationDetail as G,PERMISSION_MODES as J,MODE_ICON as N,MODE_LABEL as Q}from"../permissions.js";import{runVerifyLoop as X}from"../autofix.js";import{runAutoFlow as z}from"../autorun.js";const H=r.createElement,V=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 rr(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const tr=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:l,session:x,extraTools:nr,mcp:or,yolo:cr,initialPrompt:sr,version:lr,onExit:ur}){const ir=p(),[ar,mr]=t(l),pr=e(nr),fr=e([]),dr=e(0),gr=e(""),hr=e(x),wr=e(null),xr=e(null),yr=e(new Set),br=e(_(ar)),vr=e(C(ar)),kr=e(!1),Tr=e(!1),$r=e(!1),jr=e(v()),Er=e(!!cr),Mr=e(!1),Pr=e("idle"),Ar=e([]),Sr=e({list:[]}),Ir=e(D(ar)),Dr=e(null),Lr=e(""),[,_r]=n(r=>r+1,0),[Cr,Ur]=n(r=>r+1,0),[Or,Rr]=t(0),[Wr,Fr]=t("idle"),[Kr,Yr]=t({text:"",cursor:0}),[qr,Br]=t(null),[Gr,Jr]=t({prompt:0,completion:0}),[Nr,Qr]=t(0),[Xr,zr]=t([]),[Hr,Vr]=t(0),[Zr,rt]=t(cr?"auto":"manual"),tt=()=>kr.current?"plan":Tr.current?"sandbox":Er.current?"auto":$r.current?"acceptEdits":"manual",[et,nt]=t(""),ot=e(null),[ct,st]=t(0),[lt,ut]=t(null),it=e(null),[at,mt]=t(0),[pt,ft]=t(!1),dt=c(()=>E(ar),[ar]),gt=(r,t)=>{fr.current.push({id:dr.current++,kind:r,text:t}),Ur()},ht=()=>{if(gr.current){const t=gr.current,e=!1!==r.markdown&&w(t);gt("assistant",e?h(t):t),gr.current=""}},wt=r=>{Pr.current=r?"busy":"idle",Fr(Pr.current)};o(()=>{if("busy"!==Wr)return;const r=setInterval(()=>st(r=>(r+1)%tr.length),80);return()=>clearInterval(r)},[Wr]);const xt=async()=>{if(r.apiKey)try{const t=await d(r.baseUrl,r.apiKey);it.current=t,ut(t)}catch{}};o(()=>{xt();const r=setInterval(()=>{xt()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void nt("");const t=()=>{try{const t=s(r.statusLine,{cwd:ar,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);nt(t)}catch{nt("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{ot.current&&clearInterval(ot.current)},[]);const yt=e(""),bt={onDelta:r=>{yt.current&&(yt.current=""),gr.current+=r,_r()},onReasoning:r=>{yt.current=(yt.current+r).slice(-400),_r()},onToolStart:(r,t)=>{yt.current="",ht(),gt("tool",g(r,t))},onToolResult:(r,t)=>gt("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>gt("diff",r),onSkip:(r,t)=>gt("skip",g(r,t)+" — 已跳过"),onInfo:r=>{ht(),gt("info",r)},onTodo:r=>zr(r),onTurnEnd:()=>{yt.current="",ht()}},vt={request:(r,t,e,n)=>{if(vr.current.length&&U(vr.current,r,t))return Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"});if(kr.current&&!n)return Promise.resolve({allow:!1,reason:F});if(Tr.current&&Y(r))return Promise.resolve({allow:!1,reason:K});if(n||Er.current||yr.current.has(r)||U(br.current,r,t))return Promise.resolve({allow:!0});if($r.current&&("write_file"===r||"edit_file"===r))return Promise.resolve({allow:!0});const o=q.has(r);return new Promise(e=>{xr.current={name:r,args:t,resolve:e,consent:o},Br({label:o?B(r,t):g(r,t),detail:o?G(r,t):"",name:r,args:t,consent:o,risk:W(r,t)})})},isYolo:()=>Er.current,setYolo:r=>{Er.current=!!r,rt(tt())},setPlanMode:r=>{kr.current=!!r,rt(tt())},isPlanMode:()=>kr.current,setSandbox:r=>{Tr.current=!!r,rt(tt())},isSandbox:()=>Tr.current,setAcceptEdits:r=>{$r.current=!!r,rt(tt())},isAcceptEdits:()=>$r.current,setMode:r=>{Er.current="auto"===r,$r.current="acceptEdits"===r,kr.current="plan"===r,Tr.current="sandbox"===r,rt(r)},getMode:()=>tt(),reloadRules:()=>{br.current=_(ar),vr.current=C(ar)}},kt=()=>{const r=Ar.current.shift();return Vr(Ar.current.length),null==r?null:r},Tt=async t=>{const e=M(it.current);if(e&&"exhausted"===e.level)return void gt("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${P(e.resetAtEpochMs)}`);const{text:n,images:o}=await A(t,ar,r),c=jr.current.snapshot({session:hr.current,label:t});hr.current.messages.push({role:"user",content:S(n,o)}),wt(!0),wr.current=new AbortController,hr.current.compaction||(hr.current.compaction={});const s=await f({config:r,messages:hr.current.messages,permissions:vt,cwd:ar,extraTools:pr.current,events:bt,signal:wr.current.signal,todoState:Sr.current,compaction:hr.current.compaction,pullQueued:kt,fileJournal:c});return wr.current=null,ht(),s?.usage&&Jr(r=>({prompt:r.prompt+s.usage.prompt,completion:r.completion+s.usage.completion})),s?.lastPromptTokens&&(Qr(s.lastPromptTokens),hr.current.lastPromptTokens=s.lastPromptTokens),b(hr.current),(s?.aborted||s?.error)&&Ar.current.length&&(Ar.current=[],Vr(0),gt("info","已清空排队消息")),wt(!1),xt(),s},$t=()=>{ur?.(),ir.exit()},jt=async t=>{const e=t.trim();if(Yr({text:"",cursor:0}),Dr.current=null,e){if(L(ar,e),Ir.current[Ir.current.length-1]!==e&&Ir.current.push(e),e.startsWith("/"))return(async t=>{const e={version:lr,config:r,cwd:ar,usage:Gr,permissions:vt,extraTools:pr.current,session:hr.current,checkpoints:jr.current,print:r=>gt("info",String(r))},n=await j(t,e);if("exit"===n.action)return $t();if("clear"===n.action&&(hr.current=y({cwd:ar,model:r.model,messages:[hr.current.messages[0]]}),Sr.current.list=[],jr.current.list.length=0,zr([]),Qr(0),fr.current=[],gr.current="",Rr(r=>r+1),gt("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(Sr.current.list=[],zr([]),Qr(0)),"autofix"===n.action&&await X({command:n.command,maxIters:n.maxIters,cwd:ar,runTurnOnce:r=>Tt(r),emit:r=>gt("info",r)}),"autorun"===n.action&&await z({goal:n.goal,config:r,permissions:vt,cwd:ar,runTurnOnce:r=>Tt(r),emit:r=>gt("info",r)}),"cd"===n.action){br.current=_(n.cwd),vr.current=C(n.cwd),$(or?.clients);const t=await k(r,n.cwd);pr.current=t.extraTools,or&&(or.clients=t.clients),hr.current=y({cwd:n.cwd,model:r.model,messages:[{role:"system",content:T(n.cwd,{memory:!1!==r.memoryEnabled})}]}),r.sessionId=hr.current.id,Sr.current.list=[],jr.current.list.length=0,zr([]),Qr(0),fr.current=[],gr.current="",Rr(r=>r+1),mr(n.cwd),gt("info","已切换到 "+n.cwd+"(新会话,旧会话已保存)")}"loop"===n.action&&(ot.current&&(clearInterval(ot.current),ot.current=null),n.stop?gt("info","已停止循环。"):(gt("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),ot.current=setInterval(()=>{"busy"!==Pr.current&&(gt("user",n.prompt),Tt(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===Pr.current?(Ar.current.push(n.prompt),Vr(Ar.current.length)):(gt("user",n.prompt),await Tt(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return(async r=>{if("busy"===Pr.current)return void gt("info","正在处理上一条,! 命令请稍候再试");gt("user","! "+r);const{output:t,record:e}=await I(r,ar);gt("toolresult",Z(t)),hr.current.messages.push({role:"user",content:e}),b(hr.current)})(e.slice(1).trim());if("busy"===Pr.current)return gt("user",e),Ar.current.push(e),void Vr(Ar.current.length);gt("user",e),await Tt(e)}};o(()=>{!Mr.current&&sr&&(Mr.current=!0,jt(sr))},[]);const Et=/^\/(\S*)$/.exec(Kr.text),Mt=Et?Et[1]:null,Pt=null==Mt?[]:dt.filter(r=>r.name.toLowerCase().startsWith(Mt.toLowerCase())),At=Pt.length>0&&!pt,St=Pt.length<=V?0:Math.max(0,Math.min(at-Math.floor(5),Pt.length-V)),It=Pt.slice(St,St+V);o(()=>{mt(0),ft(!1)},[Mt]),m((t,e)=>{if(xr.current){const r=xr.current,n=t=>{xr.current=null,Br(null),r.resolve(t)};if("y"===t||"1"===t)return n({allow:!0});if("a"===t||"2"===t)return yr.current.add(r.name),n({allow:!0});if("3"===t)return n({allow:!1,reason:"用户选择了「否」,该操作未执行。"});if("A"===t){yr.current.add(r.name);const t=O(r.name,r.args);try{R(ar,t),br.current=_(ar),gt("info",`已写入允许规则:${t} → .huicai/settings.json`)}catch(r){gt("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===t||e.escape||e.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(e.tab&&e.shift){const t=J[(J.indexOf(vt.getMode())+1)%J.length];return vt.setMode(n=t),r.planMode="plan"===n,r.sandbox="sandbox"===n,void gt("info",`对话模式 → ${N[n]} ${Q[n]}`+("auto"===n?"(已开放所有权限,任何命令免审批)":""))}var n;if(At){const r=()=>Pt[Math.min(at,Pt.length-1)];if(e.upArrow)return mt(r=>(r-1+Pt.length)%Pt.length);if(e.downArrow)return mt(r=>(r+1)%Pt.length);if(e.pageUp)return mt(r=>Math.max(0,r-V));if(e.pageDown)return mt(r=>Math.min(Pt.length-1,r+V));if(e.tab){const t=r().name;return Yr({text:"/"+t+" ",cursor:t.length+2})}if(e.return)return void jt("/"+r().name);if(e.escape)return ft(!0)}if(e.escape)"busy"===Pr.current&&wr.current&&(wr.current.abort(),gt("info","已请求中断(Esc)"));else if(e.ctrl&&"c"===t)"busy"===Pr.current&&wr.current?(wr.current.abort(),gt("info","已请求中断")):$t();else if(e.return)jt(Kr.text);else{if(e.upArrow)return(()=>{const r=Ir.current;if(!r.length)return;null==Dr.current?(Lr.current=Kr.text,Dr.current=r.length-1):Dr.current>0&&Dr.current--;const t=r[Dr.current]??"";Yr({text:t,cursor:t.length})})();if(e.downArrow)return(()=>{if(null==Dr.current)return;const r=Ir.current;if(Dr.current<r.length-1){Dr.current++;const t=r[Dr.current];Yr({text:t,cursor:t.length})}else Dr.current=null,Yr({text:Lr.current,cursor:Lr.current.length})})();if(e.leftArrow)return Yr(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(e.rightArrow)return Yr(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(e.ctrl&&"a"===t)return Yr(r=>({...r,cursor:0}));if(e.ctrl&&"e"===t)return Yr(r=>({...r,cursor:r.text.length}));if(e.ctrl&&"u"===t)return Yr(r=>({text:r.text.slice(r.cursor),cursor:0}));if(e.ctrl&&"k"===t)return Yr(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(e.ctrl&&"w"===t)return Yr(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 Dr.current=null,Yr(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){Dr.current=null;const r=t.replace(/\r\n?/g,"\n");Yr(t=>({text:t.text.slice(0,t.cursor)+r+t.text.slice(t.cursor),cursor:t.cursor+r.length}))}}});const Dt=c(()=>fr.current.slice(),[Cr]),Lt=ar.split("/").filter(Boolean).pop()||ar,_t=Kr.text.slice(0,Kr.cursor),Ct=Kr.text[Kr.cursor]??" ",Ut=Kr.text.slice(Kr.cursor+1),Ot=Math.max(8,process.stdout.columns||80),Rt=M(lt),Wt=Rt?"exhausted"===Rt.level?`⛔ ${Rt.label}用量已用尽${P(Rt.resetAtEpochMs)?" · "+P(Rt.resetAtEpochMs):""}`:`⚠ ${Rt.label}用量 ${Math.round(100*Rt.pct)}%(${rr(Rt.used)}/${rr(Rt.limit)})`:"";return H(u,{flexDirection:"column"},H(a,{key:Or,items:Dt},r=>H(er,{key:r.id,item:r})),yt.current?H(u,null,H(i,{color:"gray",italic:!0},"✦ 思考 "+yt.current.replace(/\n/g," ").slice(-160))):null,gr.current?H(u,{marginTop:0},H(i,null,gr.current)):null,Xr.length?H(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Xr.map((r,t)=>H(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"!==Wr||gr.current?null:H(u,null,H(i,{color:"cyan"},tr[ct]+" 思考中…"),H(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),qr?H(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},H(i,{color:"yellow",bold:!0},qr.consent?"● 是否继续?"+qr.label:"● 需要授权:"+qr.label),qr.risk?H(i,{color:"red",bold:!0},"⚠ "+qr.risk):null,qr.consent&&qr.detail?H(i,{color:"gray"},Z(qr.detail,6)):null,"run_bash"===qr.name?H(i,{color:"gray"},"$ "+(qr.args.command||"")):null,"fetch_url"===qr.name?H(i,{color:"gray"},"GET "+(qr.args.url||"")):null,H(i,null,qr.consent?"[1] 是 · [2] 是,并允许在当前对话中后续相同操作不再询问 · [3/Esc] 否":"[y] 允许一次 · [a] 本会话 · [A] 永久(写入项目设置) · [n/Esc] 拒绝")):null,At?H(u,{flexDirection:"column",marginTop:1},H(i,{color:"gray"},Pt.length>V?` 斜杠命令 ${St+1}-${St+It.length}/${Pt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...It.map((r,t)=>{const e=St+t;return H(u,{key:r.name},H(i,{color:"cyan",bold:e===at},(e===at?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),H(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,H(u,{marginTop:At?0:1},H(i,{color:"blue"},"─".repeat(Ot))),H(u,null,H(i,{color:"busy"===Wr?"yellow":"cyan"},"busy"===Wr?"⧗ ":"› "),H(i,null,_t),H(i,{inverse:!0},Ct),H(i,null,Ut)),H(i,{color:"blue"},"─".repeat(Ot)),H(u,{justifyContent:"space-between"},H(i,{color:"gray"},(et?et+" · ":"")+`${Lt} · ${r.model} · ${N[Zr]} ${Q[Zr]}`+(Hr?` · 排队 ${Hr}`:"")+" · ⇧Tab 模式 · /help"),Wt?H(i,{color:"exhausted"===Rt.level?"red":"yellow",bold:!0},Wt):H(i,null,"")))}function er({item:r}){const{kind:t,text:e}=r;if("user"===t)return H(u,{marginTop:1},H(i,{color:"cyan",bold:!0},"› "),H(i,{bold:!0},e));if("assistant"===t)return H(u,null,H(i,null,e));if("diff"===t)return H(u,{marginLeft:2},H(i,null,e));if("tool"===t)return H(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return H(i,{color:"gray"}," "+e);if("skip"===t)return H(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return H(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return H(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:c}){process.stdout.write(x({model:r.model,cwd:t,version:c,platform:/^(agent|workflow|datahuman)-/.test(String(r.model||"")),yolo:n,consult:r.consult?r.consultLabel||r.consult:null,ink:!0})+"\n");const{extraTools:s,clients:u}=await k(r,t),i={clients:u},{waitUntilExit:a}=l(H(App,{config:r,cwd:t,session:e,extraTools:s,mcp:i,yolo:n,initialPrompt:o,version:c,onExit:null}));await a(),$(i.clients)}
|