1 Commits

Author SHA1 Message Date
ageerle
0efb8a2c39 feat: improve thinking mode portability and add continuous runner
Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
2026-05-04 14:44:23 +00:00
3 changed files with 101 additions and 4 deletions

32
.monkeycode/MEMORY.md Normal file
View File

@@ -0,0 +1,32 @@
# 用户指令记忆
本文件记录了用户的指令、偏好和教导,用于在未来的交互中提供参考。
## 格式
### 用户指令条目
用户指令条目应遵循以下格式:
[用户指令摘要]
- Date: [YYYY-MM-DD]
- Context: [提及的场景或时间]
- Instructions:
- [用户教导或指示的内容,逐行描述]
### 项目知识条目
Agent 在任务执行过程中发现的条目应遵循以下格式:
[项目知识摘要]
- Date: [YYYY-MM-DD]
- Context: Agent 在执行 [具体任务描述] 时发现
- Category: [代码结构|代码模式|代码生成|构建方法|测试方法|依赖关系|环境配置]
- Instructions:
- [具体的知识点,逐行描述]
## 去重策略
- 添加新条目前,检查是否存在相似或相同的指令
- 若发现重复,跳过新条目或与已有条目合并
- 合并时,更新上下文或日期信息
- 这有助于避免冗余条目,保持记忆文件整洁
## 条目

View File

@@ -219,6 +219,8 @@ public class ChatServiceFacade implements IChatService {
*/ */
private SseEmitter handleThinkingMode(ChatRequest chatRequest) { private SseEmitter handleThinkingMode(ChatRequest chatRequest) {
String npxCommand = resolveNpxCommand();
// 配置监督者模型 // 配置监督者模型
OpenAiChatModel plannerModel = OpenAiChatModel.builder() OpenAiChatModel plannerModel = OpenAiChatModel.builder()
.baseUrl(chatRequest.getChatModelVo().getApiHost()) .baseUrl(chatRequest.getChatModelVo().getApiHost())
@@ -228,7 +230,7 @@ public class ChatServiceFacade implements IChatService {
// Bing 搜索 MCP 客户端 // Bing 搜索 MCP 客户端
McpTransport bingTransport = new StdioMcpTransport.Builder() McpTransport bingTransport = new StdioMcpTransport.Builder()
.command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "bing-cn-mcp")) .command(List.of(npxCommand, "-y", "bing-cn-mcp"))
.logEvents(true) .logEvents(true)
.build(); .build();
@@ -240,7 +242,7 @@ public class ChatServiceFacade implements IChatService {
// Playwright MCP 客户端 - 浏览器自动化工具 // Playwright MCP 客户端 - 浏览器自动化工具
McpTransport playwrightTransport = new StdioMcpTransport.Builder() McpTransport playwrightTransport = new StdioMcpTransport.Builder()
.command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "@playwright/mcp@latest")) .command(List.of(npxCommand, "-y", "@playwright/mcp@latest"))
.logEvents(true) .logEvents(true)
.build(); .build();
@@ -253,7 +255,7 @@ public class ChatServiceFacade implements IChatService {
// 允许 AI 读取、写入、搜索文件(基于当前项目根目录) // 允许 AI 读取、写入、搜索文件(基于当前项目根目录)
String userDir = System.getProperty("user.dir"); String userDir = System.getProperty("user.dir");
McpTransport filesystemTransport = new StdioMcpTransport.Builder() McpTransport filesystemTransport = new StdioMcpTransport.Builder()
.command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", .command(List.of(npxCommand, "-y",
"@modelcontextprotocol/server-filesystem", userDir)) "@modelcontextprotocol/server-filesystem", userDir))
.logEvents(true) .logEvents(true)
@@ -344,6 +346,18 @@ public class ChatServiceFacade implements IChatService {
return chatRequest.getEmitter(); return chatRequest.getEmitter();
} }
/**
* 根据运行平台选择 npx 命令,避免 Linux/macOS 环境下固定使用 npx.cmd 导致失败。
*/
private String resolveNpxCommand() {
String customNpxCommand = System.getenv("MCP_NPX_COMMAND");
if (StringUtils.isNotBlank(customNpxCommand)) {
return customNpxCommand;
}
String osName = System.getProperty("os.name", "").toLowerCase();
return osName.contains("win") ? "npx.cmd" : "npx";
}
/** /**
* 支持外部 handler 的对话接口(跨模块调用) * 支持外部 handler 的对话接口(跨模块调用)
* 同时发送到 SSE 和外部 handler * 同时发送到 SSE 和外部 handler
@@ -618,4 +632,3 @@ public class ChatServiceFacade implements IChatService {
}; };
} }
} }

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Run a shell command continuously for a fixed duration.
Example:
python3 scripts/continuous_runner.py --cmd "mvn -pl ruoyi-modules/ruoyi-chat -DskipTests compile"
"""
from __future__ import annotations
import argparse
import subprocess
import time
from datetime import datetime, timedelta
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Run command continuously with retry")
parser.add_argument("--cmd", required=True, help="Command to run each round")
parser.add_argument("--minutes", type=int, default=30, help="Total running minutes")
parser.add_argument("--interval", type=int, default=5, help="Seconds between rounds")
parser.add_argument("--stop-on-success", action="store_true", help="Stop after first success")
return parser.parse_args()
def main() -> int:
args = parse_args()
deadline = datetime.now() + timedelta(minutes=args.minutes)
print(f"start={datetime.now().isoformat(timespec='seconds')}")
print(f"end={deadline.isoformat(timespec='seconds')}")
print(f"cmd={args.cmd}")
round_no = 0
while datetime.now() < deadline:
round_no += 1
print(f"\n[{datetime.now().isoformat(timespec='seconds')}] round={round_no}")
result = subprocess.run(args.cmd, shell=True)
print(f"exit_code={result.returncode}")
if result.returncode == 0 and args.stop_on_success:
print("stop_on_success=true, exiting")
return 0
if datetime.now() < deadline:
time.sleep(max(0, args.interval))
print(f"done={datetime.now().isoformat(timespec='seconds')}")
return 0
if __name__ == "__main__":
raise SystemExit(main())