Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions configs/executor.yaml

This file was deleted.

12 changes: 5 additions & 7 deletions configs/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,19 @@ func LoadWithEnv(path string) (*Config, error) {
}

// FindConfigFile searches for config file in standard locations.
// Priority: MSCLI_CONFIG > ./.mscli/config.yaml > ~/.config/mscli/config.yaml > ~/.mscli/config.yaml
func FindConfigFile() string {
// Check environment variable
if path := os.Getenv("MSCLI_CONFIG"); path != "" {
return path
}

// Check current directory
if _, err := os.Stat("mscli.yaml"); err == nil {
return "mscli.yaml"
}
if _, err := os.Stat("configs/mscli.yaml"); err == nil {
return "configs/mscli.yaml"
// Check project-level config
if _, err := os.Stat(".mscli/config.yaml"); err == nil {
return ".mscli/config.yaml"
}

// Check config directories
// Check user-level config
home, err := os.UserHomeDir()
if err == nil {
paths := []string{
Expand Down
99 changes: 99 additions & 0 deletions configs/mscli.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# ms-cli 配置示例文件
#
# 使用方法:
# 1. 复制此文件到 .mscli/config.yaml(推荐,已被 .gitignore 忽略,不会提交到 git)
# cp configs/mscli.example.yaml .mscli/config.yaml
#
# 2. 或者复制到用户级配置目录(全局生效)
# cp configs/mscli.example.yaml ~/.mscli/config.yaml
# # 或遵循 XDG 规范
# mkdir -p ~/.config/mscli
# cp configs/mscli.example.yaml ~/.config/mscli/config.yaml
#
# 3. 根据你的实际情况修改配置项
#
# 配置加载优先级(从高到低):
# 1. 命令行参数 (--url, --model, --api-key)
# 2. 环境变量 (MSCLI_* / OPENAI_*)
# 3. 配置文件(依次搜索直至找到第一个存在的文件):
# - MSCLI_CONFIG 环境变量指定的文件
# - ./.mscli/config.yaml(项目级)
# - ~/.config/mscli/config.yaml(用户级)
# - ~/.mscli/config.yaml(用户级备选)
# 4. 内置默认值
#
# 注意:API key 建议通过环境变量设置,避免提交到版本控制
# export MSCLI_API_KEY="your-api-key"

model:
# OpenAI-compatible API base URL
# 环境变量覆盖:MSCLI_BASE_URL(优先)或 OPENAI_BASE_URL
url: https://api.openai.com/v1

# 模型名称
# 环境变量覆盖:MSCLI_MODEL(优先)或 OPENAI_MODEL
model: gpt-4o-mini

# API 密钥
# 强烈建议通过环境变量设置:MSCLI_API_KEY 或 OPENAI_API_KEY
# 如果在此处设置,请确保 .mscli/ 目录已被 .gitignore 忽略
key: ""

# 温度参数 (0-2),控制输出的随机性
# 环境变量覆盖:MSCLI_TEMPERATURE
temperature: 0.7

# 最大生成 token 数
# 环境变量覆盖:MSCLI_MAX_TOKENS
max_tokens: 4096

# 请求超时时间(秒)
# 环境变量覆盖:MSCLI_TIMEOUT
timeout_sec: 180

budget:
# 单次会话最大 token 数
max_tokens: 32768

# 单次会话最大成本(美元)
max_cost_usd: 10

ui:
# 是否启用 TUI 界面
enabled: true

# 是否显示 token 使用条
show_token_bar: true

permissions:
# 是否跳过权限请求(自动允许所有操作,谨慎使用)
skip_requests: false

# 默认权限级别:ask(询问)/ allow(允许)/ deny(拒绝)
default_level: ask

# 允许使用的工具列表(为空表示允许所有)
allowed_tools: []

context:
# 上下文最大 token 数
max_tokens: 24000

# 预留 token 数(用于生成回复)
reserve_tokens: 4000

# 上下文压缩阈值(当使用量超过此比例时触发压缩)
compaction_threshold: 0.85

memory:
# 是否启用记忆系统
enabled: true

# 最大记忆条目数
max_items: 200

# 最大记忆大小(字节)
max_bytes: 2097152

# 记忆 TTL(小时)
ttl_hours: 168
24 changes: 0 additions & 24 deletions configs/mscli.yaml

This file was deleted.

6 changes: 0 additions & 6 deletions configs/skills.yaml

This file was deleted.

16 changes: 16 additions & 0 deletions configs/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Config struct {
Memory MemoryConfig `yaml:"memory"`
Skills SkillsConfig `yaml:"skills"`
Execution ExecutionConfig `yaml:"execution"`
SSH SSHConfig `yaml:"ssh,omitempty"`
}

// ModelConfig holds the LLM model configuration.
Expand Down Expand Up @@ -94,6 +95,21 @@ type DockerConfig struct {
Env map[string]string `yaml:"env,omitempty"`
}

// SSHConfig holds the SSH remote execution configuration.
type SSHConfig struct {
Hosts map[string]HostConfig `yaml:"hosts,omitempty"` // 预配置的主机别名
DefaultTimeout int `yaml:"default_timeout"` // 默认超时秒数
}

// HostConfig holds the configuration for a single SSH host.
type HostConfig struct {
Address string `yaml:"address"` // 主机地址(IP 或域名)
User string `yaml:"user"` // 用户名
KeyPath string `yaml:"key_path,omitempty"` // SSH 私钥路径(优先)
Password string `yaml:"password,omitempty"` // SSH 密码(备选)
Port int `yaml:"port,omitempty"` // 端口(默认 22)
}

// DefaultConfig returns a configuration with default values.
func DefaultConfig() *Config {
return &Config{
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
Expand All @@ -30,8 +31,10 @@ require (
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pkg/sftp v1.13.10 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand All @@ -40,18 +42,26 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
21 changes: 19 additions & 2 deletions internal/app/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import (
itrain "github.com/vigo999/ms-cli/internal/train"
"github.com/vigo999/ms-cli/permission"
rshell "github.com/vigo999/ms-cli/runtime/shell"
rssh "github.com/vigo999/ms-cli/runtime/ssh"
"github.com/vigo999/ms-cli/tools"
"github.com/vigo999/ms-cli/tools/fs"
"github.com/vigo999/ms-cli/tools/remote"
"github.com/vigo999/ms-cli/tools/shell"
"github.com/vigo999/ms-cli/trace"
"github.com/vigo999/ms-cli/ui/model"
Expand All @@ -48,6 +50,7 @@ type Application struct {
permService permission.PermissionService
stateManager *configs.StateManager
traceWriter trace.Writer
sshPool *rssh.Pool

// Train mode state
trainMode bool
Expand Down Expand Up @@ -130,7 +133,10 @@ func Wire(cfg BootstrapConfig) (*Application, error) {
}
}

toolRegistry := initTools(config, workDir)
// Initialize SSH pool for remote execution
sshPool := rssh.NewPool(config.SSH)

toolRegistry := initTools(config, workDir, sshPool)

ctxManager := agentctx.NewManager(agentctx.ManagerConfig{
MaxTokens: config.Context.MaxTokens,
Expand Down Expand Up @@ -184,6 +190,7 @@ func Wire(cfg BootstrapConfig) (*Application, error) {
permService: permService,
stateManager: stateManager,
traceWriter: traceWriter,
sshPool: sshPool,
llmReady: llmReady,
}, nil
}
Expand Down Expand Up @@ -287,7 +294,7 @@ func initProvider(cfg configs.ModelConfig) (llm.Provider, error) {
return client, nil
}

func initTools(cfg *configs.Config, workDir string) *tools.Registry {
func initTools(cfg *configs.Config, workDir string, sshPool *rssh.Pool) *tools.Registry {
registry := tools.NewRegistry()

registry.MustRegister(fs.NewReadTool(workDir))
Expand All @@ -305,5 +312,15 @@ func initTools(cfg *configs.Config, workDir string) *tools.Registry {
})
registry.MustRegister(shell.NewShellTool(shellRunner))

// Register remote SSH tools
if sshPool != nil {
registry.MustRegister(remote.NewShellTool(sshPool))
registry.MustRegister(remote.NewReadTool(sshPool))
registry.MustRegister(remote.NewWriteTool(sshPool))
registry.MustRegister(remote.NewEditTool(sshPool))
registry.MustRegister(remote.NewGlobTool(sshPool))
registry.MustRegister(remote.NewGrepTool(sshPool))
}

return registry
}
Loading