mocode-ai 0.6.7 → 0.6.9
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 +9 -4
- package/README.zh-CN.md +5 -0
- package/dist/agent/core.js +5 -5
- package/dist/agent/index.js +10 -8
- package/dist/agent/spawn.js +1 -5
- package/dist/commands/config.js +9 -7
- package/dist/config/index.js +49 -41
- package/dist/context/budget.js +2 -3
- package/dist/i18n/index.js +450 -0
- package/dist/index.js +8 -4
- package/dist/llm/index.js +19 -16
- package/dist/mcp/client.js +389 -0
- package/dist/mcp/config.js +128 -0
- package/dist/mcp/index.js +105 -0
- package/dist/mcp/registry.js +5 -0
- package/dist/mcp/types.js +1 -0
- package/dist/permissions/index.js +16 -12
- package/dist/pet/state.js +2 -1
- package/dist/repl/index.js +210 -102
- package/dist/rollback/index.js +311 -119
- package/dist/session/persist.js +11 -5
- package/dist/tools/builtins/ask-human.js +4 -3
- package/dist/tools/builtins/run-command.js +6 -5
- package/dist/tools/builtins/task.js +5 -4
- package/dist/tools/registry.js +58 -22
- package/dist/tools/result.js +7 -0
- package/dist/ui/intervention.js +12 -7
- package/dist/ui/layout.js +4 -3
- package/dist/ui/prompt.js +83 -28
- package/dist/ui/render.js +15 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,11 @@ MoCode isn't a chat box with a coat of paint — it's an agent that actually get
|
|
|
32
32
|
- **Optional desktop pet** — A small floating window (`/pet`) shows a stateful character that mirrors agent activity (idle / thinking / tool running / waiting for human). Works as a separate process over WebSocket; quit it with `/pet quit`. Sits beside the terminal, never blocks it.
|
|
33
33
|
- **Slash commands** — `/exit` `/clear` `/context` `/skills` `/compact` `/resume` `/rollback` `/memory` `/reflect` `/init` `/theme` `/model` `/plan` `/auto` `/pet`, with dropdown filtering as you type.
|
|
34
34
|
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
- [中文使用指南](./docs/usage.md) — 菜单式快速上手、命令速查、模式、会话、项目上下文与排障。
|
|
38
|
+
- [Project context](./docs/USAGE_SNAPSHOT_SKILL.md) — Snapshot and Project Skill reference.
|
|
39
|
+
|
|
35
40
|
## Installation
|
|
36
41
|
|
|
37
42
|
Requires Node.js ≥ 18.
|
|
@@ -208,14 +213,14 @@ A skill's `description` is injected into the system prompt (progressive disclosu
|
|
|
208
213
|
|
|
209
214
|
MoCode uses two complementary systems to help the agent understand your project:
|
|
210
215
|
|
|
211
|
-
- **Project Snapshot** (automatic + LLM-enhanced): Scans your project files and generates a structured summary including project description, tech stack, key commands, module responsibilities, and directory tree.
|
|
212
|
-
- **Project Skill** (manual + AI-assisted): A knowledge base
|
|
216
|
+
- **Project Snapshot** (automatic + LLM-enhanced, enabled by default): Scans your project files and generates a structured summary including project description, tech stack, key commands, module responsibilities, and directory tree. Stored in `.mocode/snapshot.json` and reused across sessions. Use `/snapshot` to toggle it and `/snapshot_refresh` after major changes.
|
|
217
|
+
- **Project Skill** (manual + AI-assisted, disabled by default): A knowledge base for design decisions, architectural patterns, pitfalls, conventions, and workflow notes. Enable it with `/project_skill on` (or `MOCODE_PROJECT_SKILL=true`), generate an initial draft with `/project_skill init`, then refine `.mocode/project-skill.md`. The agent can update it through `project_skill_update` during conversations.
|
|
213
218
|
|
|
214
219
|
**Complementary principle**: Snapshot provides *what/where* (facts: files, structure, commands), Skill provides *why/how* (insights: decisions, behaviors, gotchas). No duplication, ~46% token savings compared to the previous approach.
|
|
215
220
|
|
|
216
|
-
|
|
221
|
+
Control via environment variables:
|
|
217
222
|
- `MOCODE_PROJECT_SNAPSHOT=false` — disable snapshot
|
|
218
|
-
- `MOCODE_PROJECT_SKILL=
|
|
223
|
+
- `MOCODE_PROJECT_SKILL=true` — enable Project Skill
|
|
219
224
|
|
|
220
225
|
See [docs/USAGE_SNAPSHOT_SKILL.md](./docs/USAGE_SNAPSHOT_SKILL.md) for detailed usage.
|
|
221
226
|
|
package/README.zh-CN.md
CHANGED
|
@@ -31,6 +31,11 @@ mocode 不是一个套壳聊天框,而是一个能真正动手干活的 agent:
|
|
|
31
31
|
- **可选桌宠** — 独立悬浮窗(`/pet`)显示一个小角色,镜像 agent 活动(空闲 / 思考 / 跑工具 / 等人工),独立进程走 WebSocket,`/pet quit` 完全关闭。挂在终端外,绝不挡终端。
|
|
32
32
|
- **斜杠命令** — `/exit` `/clear` `/context` `/skills` `/compact` `/resume` `/rollback` `/memory` `/reflect` `/init` `/theme` `/model` `/plan` `/auto` `/pet`,输入时下拉过滤
|
|
33
33
|
|
|
34
|
+
## 使用文档
|
|
35
|
+
|
|
36
|
+
- [菜单式使用指南](./docs/usage.md) — 快速上手、命令速查、模式、会话、项目上下文与排障。
|
|
37
|
+
- [项目上下文:Snapshot 与 Project Skill](./docs/USAGE_SNAPSHOT_SKILL.md)
|
|
38
|
+
|
|
34
39
|
## 安装
|
|
35
40
|
|
|
36
41
|
要求 Node.js ≥ 18。
|
package/dist/agent/core.js
CHANGED
|
@@ -150,7 +150,7 @@ function pushToolResult(history, tc, output, pruner, lifecycle, _scheduler, runt
|
|
|
150
150
|
* 这些是纯字符串格式化,无副作用,共享安全。
|
|
151
151
|
*/
|
|
152
152
|
export async function runAgentCore(opts) {
|
|
153
|
-
const { history, userInput, signal, onContextUpdate, hooks
|
|
153
|
+
const { history, userInput, signal, onContextUpdate, hooks } = opts;
|
|
154
154
|
const runtimeContextState = opts.contextState ?? contextState;
|
|
155
155
|
const maxSteps = opts.maxSteps ?? config.maxSteps;
|
|
156
156
|
// 中断还原:LLM 中途可能调 switch_mode 切了模式,abort 时连同模式一起还原回轮首。
|
|
@@ -191,7 +191,7 @@ export async function runAgentCore(opts) {
|
|
|
191
191
|
let savedHistory = history.slice();
|
|
192
192
|
// drop_context 工具的上下文剔除回调:闭包捕获 history,原地剔除无关旧 tool 结果。
|
|
193
193
|
// 保护由 dropContextFromHistory 内部保证:history[0](system)+ 当前轮(最后 user 及其后)永不剔除。
|
|
194
|
-
// 子 agent 也在自己的 history 上操作(子 agent 独立 history)
|
|
194
|
+
// 子 agent 也在自己的 history 上操作(子 agent 独立 history),文件回滚事务则与主轮共享。
|
|
195
195
|
const dropContext = (filter) => dropContextFromHistory(history, filter);
|
|
196
196
|
// 相关性裁剪 pruner:每个 runAgentCore 实例一个,纯静态、不调 LLM、自动判定 read_file 失效。
|
|
197
197
|
// 开关关闭时为 null,所有 pushToolResult 调用走无 pruner 路径(零行为变化)。
|
|
@@ -352,7 +352,7 @@ export async function runAgentCore(opts) {
|
|
|
352
352
|
for (const tc of batch)
|
|
353
353
|
hooks.onToolHeader?.(tc);
|
|
354
354
|
hooks.onToolStart?.(batch[0].name);
|
|
355
|
-
const started = batch.map((tc) => executeTool(tc.name, tc.arguments, signal, {
|
|
355
|
+
const started = batch.map((tc) => executeTool(tc.name, tc.arguments, signal, { dropContext }));
|
|
356
356
|
for (let k = 0; k < batch.length; k++) {
|
|
357
357
|
const tc = batch[k];
|
|
358
358
|
const output = await started[k];
|
|
@@ -413,7 +413,7 @@ export async function runAgentCore(opts) {
|
|
|
413
413
|
i = j;
|
|
414
414
|
continue; // 全部被拒绝,跳过执行
|
|
415
415
|
}
|
|
416
|
-
const started = allowedBatch.map((tc) => executeTool(tc.name, tc.arguments, signal, {
|
|
416
|
+
const started = allowedBatch.map((tc) => executeTool(tc.name, tc.arguments, signal, { dropContext }));
|
|
417
417
|
// 先批量打印所有头 + 启 spinner(多 task 并发,spinner 只显一个,但 ● 头都打出来)
|
|
418
418
|
for (const tc of allowedBatch) {
|
|
419
419
|
hooks.onToolHeader?.(tc);
|
|
@@ -468,7 +468,7 @@ export async function runAgentCore(opts) {
|
|
|
468
468
|
: null;
|
|
469
469
|
const { preWriteOld, editStartLine } = readDiffContext(tc, mutationParsed);
|
|
470
470
|
hooks.onToolStart?.(tc.name);
|
|
471
|
-
const output = await executeTool(tc.name, tc.arguments, signal, {
|
|
471
|
+
const output = await executeTool(tc.name, tc.arguments, signal, { dropContext });
|
|
472
472
|
hooks.onToolDone?.();
|
|
473
473
|
hooks.onToolResult?.(tc, output, mutationParsed, preWriteOld, editStartLine);
|
|
474
474
|
// Thrashing:同上(history 附 hint,UI 干净)
|
package/dist/agent/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import { beginTurn } from '../rollback/index.js';
|
|
|
12
12
|
import { config } from '../config/index.js';
|
|
13
13
|
import { runAgentCore, isMutationTool, } from './core.js';
|
|
14
14
|
import { createPetHooks } from '../pet/state.js';
|
|
15
|
+
import { t } from '../i18n/index.js';
|
|
16
|
+
import { isToolErrorOutput } from '../tools/result.js';
|
|
15
17
|
/** 当前 turn 的 batch id(runAgent 内闭包变量;一条 turn 一轮 tool batch 结束即清空)。 */
|
|
16
18
|
let currentBatchId = null;
|
|
17
19
|
/** 取 userInput 的首行:字符串直接 split;多模态 parts 找首个 text part 再 split。 */
|
|
@@ -40,7 +42,7 @@ function writeToolResult(tc, output, parsed, preWriteOld, editStartLine) {
|
|
|
40
42
|
if (!currentBatchId)
|
|
41
43
|
return;
|
|
42
44
|
let diff = null;
|
|
43
|
-
if (isMutationTool(tc.name) && parsed && !output
|
|
45
|
+
if (isMutationTool(tc.name) && parsed && !isToolErrorOutput(output)) {
|
|
44
46
|
diff = renderFileChange({
|
|
45
47
|
path: String(parsed.path ?? ''),
|
|
46
48
|
kind: tc.name === 'edit_file' ? 'edit' : 'write',
|
|
@@ -143,9 +145,9 @@ onContextUpdate) {
|
|
|
143
145
|
hasPendingTextBoundary = false;
|
|
144
146
|
}
|
|
145
147
|
if (name)
|
|
146
|
-
spinner.start(
|
|
148
|
+
spinner.start(t('agent.generating', { tool: name }));
|
|
147
149
|
},
|
|
148
|
-
onStepStart: () => spinner.start('
|
|
150
|
+
onStepStart: () => spinner.start(t('agent.thinking')),
|
|
149
151
|
onChatDone: () => spinner.stop(),
|
|
150
152
|
onTextEnd: () => {
|
|
151
153
|
if (lastChar && lastChar !== '\n') {
|
|
@@ -164,7 +166,7 @@ onContextUpdate) {
|
|
|
164
166
|
toolBatchFollowsText = false;
|
|
165
167
|
writeToolHeader(tc);
|
|
166
168
|
},
|
|
167
|
-
onToolStart: (name) => spinner.start(
|
|
169
|
+
onToolStart: (name) => spinner.start(t('agent.executing', { tool: name })),
|
|
168
170
|
onToolDone: () => spinner.stop(),
|
|
169
171
|
onToolResult: (tc, output, parsed, preWriteOld, editStartLine) => writeToolResult(tc, output, parsed, preWriteOld, editStartLine),
|
|
170
172
|
onToolBatchEnd: () => {
|
|
@@ -172,23 +174,23 @@ onContextUpdate) {
|
|
|
172
174
|
},
|
|
173
175
|
onNoReply: () => {
|
|
174
176
|
flushToolBatch();
|
|
175
|
-
layout.contentWrite(`${ui.dim}(
|
|
177
|
+
layout.contentWrite(`${ui.dim}${t('agent.noReply')}${ui.reset}\n`);
|
|
176
178
|
},
|
|
177
179
|
onMaxSteps: () => {
|
|
178
180
|
flushToolBatch();
|
|
179
|
-
layout.contentWrite(` ${ui.yellow}●${ui.reset} ${ui.yellow}
|
|
181
|
+
layout.contentWrite(` ${ui.yellow}●${ui.reset} ${ui.yellow}${t('agent.maxSteps', { count: config.maxSteps })}${ui.reset}\n`);
|
|
180
182
|
},
|
|
181
183
|
onAbort: () => {
|
|
182
184
|
spinner.stop();
|
|
183
185
|
if (lastChar && lastChar !== '\n')
|
|
184
186
|
layout.contentWrite('\n');
|
|
185
187
|
flushToolBatch();
|
|
186
|
-
layout.contentWrite(`${ui.dim}(
|
|
188
|
+
layout.contentWrite(`${ui.dim}${t('agent.aborted')}${ui.reset}\n`);
|
|
187
189
|
},
|
|
188
190
|
onDone: (elapsedMs, usage) => {
|
|
189
191
|
flushToolBatch();
|
|
190
192
|
const tok = formatTurnTokens(usage);
|
|
191
|
-
layout.contentWrite(` ${ui.dim}✻
|
|
193
|
+
layout.contentWrite(` ${ui.dim}✻ ${t('agent.workedFor', { elapsed: fmtElapsed(elapsedMs) })}${tok}${ui.reset}\n`);
|
|
192
194
|
// 内容区触底时,DECSTBM 增量滚屏可能只推进物理终端,未把 Worked 前已在
|
|
193
195
|
// buffer 中的空行完整画出来;用户滚动/点击触发 repaint 后才“突然”出现。
|
|
194
196
|
// 轮次收尾立即按 buffer 原子重画,使未满屏与触底滚屏的布局一致。
|
package/dist/agent/spawn.js
CHANGED
|
@@ -7,13 +7,10 @@
|
|
|
7
7
|
// - 独立 history:不共享主对话,避免子任务的工具噪声污染主上下文。
|
|
8
8
|
// - 系统提示复用主 agent 组装链(config.systemPrompt + memory 段 + skills 段)+ 子 agent 角色后缀。
|
|
9
9
|
// - 工具子集:按白名单从 chatTools 过滤;无白名单 = 全量(但 task 工具调用方通常会限定只读)。
|
|
10
|
-
// - 不调 beginTurn
|
|
11
|
-
// 子 agent 的改动不进主回滚快照链(靠 git 兜底,见下方 skipRollback 注释)。
|
|
10
|
+
// - 不调 beginTurn:子 agent 共享主 agent 当前轮次;其文件修改进入同一回滚事务。
|
|
12
11
|
// - 步数上限默认更低(config.subAgentMaxSteps ?? 50),防子任务失控耗尽配额。
|
|
13
12
|
// - 中断透传:opts.signal(主 agent 的 abort signal)透传给 runAgentCore → chat/executeTool,
|
|
14
13
|
// 主 Ctrl+C 树杀子 agent(chat 流式 abort + run_command/web_fetch 即时取消)。
|
|
15
|
-
// - 逻辑隔离(回滚):skipRollback=true,子 agent 的 write_file/edit_file 改动不进主回滚快照链,
|
|
16
|
-
// 主 /rollback 不撤销子 agent 改动(靠 git 兜底)。子 agent 与主 agent 共享 cwd(文件改动可见)。
|
|
17
14
|
import { chatTools } from '../llm/index.js';
|
|
18
15
|
import { config, isMemoryEnabled } from '../config/index.js';
|
|
19
16
|
import { effectiveSystemPrompt } from '../skills/index.js';
|
|
@@ -126,7 +123,6 @@ export async function spawnAgent(opts) {
|
|
|
126
123
|
hooks,
|
|
127
124
|
maxSteps,
|
|
128
125
|
toolsOverride,
|
|
129
|
-
skipRollback: true, // 逻辑隔离:子 agent 文件改动不进主回滚快照链,主 /rollback 不撤销(靠 git 兜底)
|
|
130
126
|
contextState: localContextState,
|
|
131
127
|
});
|
|
132
128
|
return {
|
package/dist/commands/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as readline from 'node:readline';
|
|
2
2
|
import { CONFIG_PATH, readConfigFile, writeConfigKeys } from '../config/file.js';
|
|
3
|
+
import { detectLanguage, setLanguage, t } from '../i18n/index.js';
|
|
3
4
|
function ask(rl, q) {
|
|
4
5
|
return new Promise((resolve) => {
|
|
5
6
|
rl.question(q, (ans) => resolve(ans.trim()));
|
|
@@ -14,23 +15,24 @@ function ask(rl, q) {
|
|
|
14
15
|
*/
|
|
15
16
|
export async function runConfigWizard() {
|
|
16
17
|
const cur = readConfigFile();
|
|
18
|
+
setLanguage(detectLanguage(process.env.MOCODE_LANGUAGE ?? cur.MOCODE_LANGUAGE));
|
|
17
19
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
18
|
-
console.log(
|
|
19
|
-
const baseURLIn = await ask(rl, `LLM_BASE_URL${cur.LLM_BASE_URL ? ` [${cur.LLM_BASE_URL}]` : ''}(
|
|
20
|
+
console.log(`${t('config.title', { path: CONFIG_PATH })}\n`);
|
|
21
|
+
const baseURLIn = await ask(rl, `LLM_BASE_URL${cur.LLM_BASE_URL ? ` [${cur.LLM_BASE_URL}]` : ''}${t('config.example')}: `);
|
|
20
22
|
const baseURL = baseURLIn || cur.LLM_BASE_URL || '';
|
|
21
23
|
if (!baseURL) {
|
|
22
|
-
console.error('
|
|
24
|
+
console.error(t('config.required', { key: 'LLM_BASE_URL' }));
|
|
23
25
|
rl.close();
|
|
24
26
|
process.exit(1);
|
|
25
27
|
}
|
|
26
|
-
const apiKeyIn = await ask(rl, `LLM_API_KEY${cur.LLM_API_KEY ? '
|
|
28
|
+
const apiKeyIn = await ask(rl, `LLM_API_KEY${cur.LLM_API_KEY ? t('config.keySet') : ''}: `);
|
|
27
29
|
const apiKey = apiKeyIn || cur.LLM_API_KEY || '';
|
|
28
30
|
if (!apiKey) {
|
|
29
|
-
console.error('
|
|
31
|
+
console.error(t('config.required', { key: 'LLM_API_KEY' }));
|
|
30
32
|
rl.close();
|
|
31
33
|
process.exit(1);
|
|
32
34
|
}
|
|
33
|
-
const modelIn = await ask(rl, `LLM_MODEL${cur.LLM_MODEL ? ` [${cur.LLM_MODEL}]` : ''}(
|
|
35
|
+
const modelIn = await ask(rl, `LLM_MODEL${cur.LLM_MODEL ? ` [${cur.LLM_MODEL}]` : ''}${t('config.modelDefault')}: `);
|
|
34
36
|
const model = modelIn || cur.LLM_MODEL || 'gpt-4o-mini';
|
|
35
37
|
rl.close();
|
|
36
38
|
// 合并:保留其它键,只覆盖三键。
|
|
@@ -39,5 +41,5 @@ export async function runConfigWizard() {
|
|
|
39
41
|
LLM_API_KEY: apiKey,
|
|
40
42
|
LLM_MODEL: model,
|
|
41
43
|
});
|
|
42
|
-
console.log(
|
|
44
|
+
console.log(t('config.done', { path: CONFIG_PATH }));
|
|
43
45
|
}
|
package/dist/config/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { loadSnapshot } from '../project-snapshot/index.js';
|
|
|
6
6
|
import { buildProjectSkillSection } from '../project-skill/index.js';
|
|
7
7
|
import { getSandboxRoot } from '../sandbox/root.js';
|
|
8
8
|
import { getCurrentSessionId } from '../session/state.js';
|
|
9
|
+
import { detectLanguage, setLanguage, t, } from '../i18n/index.js';
|
|
9
10
|
/**
|
|
10
11
|
* 按优先级加载配置文件并回填 process.env:
|
|
11
12
|
* 候选(后者覆盖前者,优先级升序):<cwd>/.env(兼容旧用法,最低)→ ~/.mocode/config(全局)→ <cwd>/.mocode/config(项目级覆盖,最高)。
|
|
@@ -34,14 +35,16 @@ function loadEnvFiles() {
|
|
|
34
35
|
process.env[k] = v;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
// 在 loadEnvFiles 回填前捕获:MOCODE_THEME 是否由 shell
|
|
38
|
+
// 在 loadEnvFiles 回填前捕获:MOCODE_THEME / MOCODE_LANGUAGE 是否由 shell 设置。
|
|
38
39
|
const themeFromShell = process.env.MOCODE_THEME !== undefined;
|
|
40
|
+
export const languageFromShell = process.env.MOCODE_LANGUAGE !== undefined;
|
|
39
41
|
// 在 loadEnvFiles 回填前捕获:哪些 LLM 键由 shell 设置(决定 /model 写文件是否下次启动生效)。
|
|
40
42
|
// 仿 themeFromShell 模式:shell export 的环境变量在 loadEnvFiles 中不被回填(优先级最高),
|
|
41
43
|
// 故 /model 写入 ~/.mocode/config 的同名键下次启动会被 shell 值覆盖——据此给 dim 警告。
|
|
42
44
|
const LLM_ENV_KEYS = ['LLM_BASE_URL', 'LLM_API_KEY', 'LLM_MODEL', 'CONTEXT_WINDOW_TOKENS'];
|
|
43
45
|
const llmKeysFromShell = LLM_ENV_KEYS.filter((k) => process.env[k] !== undefined);
|
|
44
46
|
loadEnvFiles();
|
|
47
|
+
setLanguage(detectLanguage(process.env.MOCODE_LANGUAGE));
|
|
45
48
|
/**
|
|
46
49
|
* 取环境变量;缺则返回空字符串(不退出)。
|
|
47
50
|
* 历史上缺 LLM_BASE_URL/LLM_API_KEY 会 process.exit(1),但 /model 命令已能在 REPL 内配置模型,
|
|
@@ -163,14 +166,14 @@ const SYSTEM_PROMPT_MEMORY_SECTION = `
|
|
|
163
166
|
* 句都不出现,且 read-only 列表里的 memory_search/memory_list 也移除——避免提示词里出现
|
|
164
167
|
* 根本不存在的工具名引起 LLM 调不到。
|
|
165
168
|
*/
|
|
166
|
-
const PLAN_RESEARCH_RULES = `
|
|
167
|
-
- Research enough to locate relevant code, trace call paths, and understand existing conventions. Use the codegraph-first Workflow, but do not repeat information already retrieved in this session.
|
|
168
|
-
- Produce an actionable plan: files and reasons, ordered steps, edge cases, and verification (typecheck / tests / build).
|
|
169
|
-
- When ready, MUST call the \`ask_human\` tool with a concise summary and exactly these options:
|
|
170
|
-
1. "按计划执行 (switch to auto and implement)" — call \`switch_mode("auto")\` in the same turn, then implement.
|
|
171
|
-
2. "继续细化方案 (stay in plan, refine)" — remain in plan and refine.
|
|
172
|
-
3. "取消 / 暂不执行 (abort)" — stop without switching mode.
|
|
173
|
-
- Never silently switch or stop. Do not ask approval in plain text; \`ask_human\` is the approval channel.
|
|
169
|
+
const PLAN_RESEARCH_RULES = `
|
|
170
|
+
- Research enough to locate relevant code, trace call paths, and understand existing conventions. Use the codegraph-first Workflow, but do not repeat information already retrieved in this session.
|
|
171
|
+
- Produce an actionable plan: files and reasons, ordered steps, edge cases, and verification (typecheck / tests / build).
|
|
172
|
+
- When ready, MUST call the \`ask_human\` tool with a concise summary and exactly these options:
|
|
173
|
+
1. "按计划执行 (switch to auto and implement)" — call \`switch_mode("auto")\` in the same turn, then implement.
|
|
174
|
+
2. "继续细化方案 (stay in plan, refine)" — remain in plan and refine.
|
|
175
|
+
3. "取消 / 暂不执行 (abort)" — stop without switching mode.
|
|
176
|
+
- Never silently switch or stop. Do not ask approval in plain text; \`ask_human\` is the approval channel.
|
|
174
177
|
- The REPL approval prompt is only a fallback; do not rely on it.`;
|
|
175
178
|
function buildPlanModeSuffix() {
|
|
176
179
|
const memoryTools = isMemoryEnabled()
|
|
@@ -182,11 +185,11 @@ function buildPlanModeSuffix() {
|
|
|
182
185
|
const removed = ['write_file', 'edit_file', 'run_command', memoryTools]
|
|
183
186
|
.filter(Boolean)
|
|
184
187
|
.join(', ');
|
|
185
|
-
return `
|
|
186
|
-
|
|
187
|
-
## ⛯ PLAN MODE (active now)
|
|
188
|
-
You are in PLAN mode: investigate and design only — do NOT execute or change anything.
|
|
189
|
-
- Removed from your tool list: ${removed}. Use only these read-only tools: ${readOnlyTools}.
|
|
188
|
+
return `
|
|
189
|
+
|
|
190
|
+
## ⛯ PLAN MODE (active now)
|
|
191
|
+
You are in PLAN mode: investigate and design only — do NOT execute or change anything.
|
|
192
|
+
- Removed from your tool list: ${removed}. Use only these read-only tools: ${readOnlyTools}.
|
|
190
193
|
${PLAN_RESEARCH_RULES}`;
|
|
191
194
|
}
|
|
192
195
|
/** 兼容旧名字:repl 的 buildSystemMessage 仍引 PLAN_MODE_SUFFIX(变量)。运行时按需现拼。 */
|
|
@@ -198,8 +201,8 @@ export function buildBasePrompt() {
|
|
|
198
201
|
const planLine = isMemoryEnabled()
|
|
199
202
|
? '- For complex or multi-step tasks, the user may switch to PLAN mode (Shift+Tab): your editing/command/memory-write tools are then removed from your tool list, and you must research with read-only tools only and produce a step-by-step plan (no execution). On approval the session returns to auto mode to execute the plan.'
|
|
200
203
|
: '- For complex or multi-step tasks, the user may switch to PLAN mode (Shift+Tab): your editing/command tools are then removed from your tool list, and you must research with read-only tools only and produce a step-by-step plan (no execution). On approval the session returns to auto mode to execute the plan.';
|
|
201
|
-
return `## Core behavior
|
|
202
|
-
You are mocode, a terminal coding agent. Complete programming tasks through a "think → call tool → observe result → think again" loop until solved.
|
|
204
|
+
return `## Core behavior
|
|
205
|
+
You are mocode, a terminal coding agent. Complete programming tasks through a "think → call tool → observe result → think again" loop until solved. ${t('assistant.languageInstruction')}
|
|
203
206
|
|
|
204
207
|
## 模式 (Modes)
|
|
205
208
|
${autoAllToolsLine}
|
|
@@ -207,31 +210,31 @@ ${planLine}
|
|
|
207
210
|
|
|
208
211
|
${PLATFORM_NOTE}
|
|
209
212
|
|
|
210
|
-
## Tool details
|
|
211
|
-
### Token-efficient execution
|
|
212
|
-
- First check whether the answer is already in this conversation or a previous tool result. If yes, answer directly; do not re-run tools "to be safe".
|
|
213
|
-
- Plan the complete sub-task before calling tools. Batch independent reads in one turn. Because tool calls in one response execute without intermediate model reasoning, never batch a read with an edit that depends on its result.
|
|
214
|
-
- Do not read "just to see". Read only what supports the next decision. Re-read after a change, compaction, stale state, or uncertain line context.
|
|
215
|
-
- Prefer one precise call over overlapping searches. If a call fails, inspect the error and change the approach instead of repeating it unchanged.
|
|
216
|
-
- Batch only independent read-only calls. After their results arrive, make the dependent edit in the next turn; then batch independent edits and one final verification when their exact inputs are already known.
|
|
217
|
-
- Read only what supports the next decision; verify once after a related edit set, not after every edit.
|
|
218
|
-
- Do not repeat an unchanged failing call; after three unproductive attempts, change tools or ask for the missing decision.
|
|
219
|
-
- For \`edit_file\`, derive \`old_string\` by copying the exact relevant lines from the latest successful \`read_file\` of that same path; never reconstruct it from memory, a summary, grep output, or a previous diff. That read becomes stale after any edit/write to the path, compaction/resume, or a possible external change. On an \`old_string\` mismatch, re-read the exact region and retry once with the newly returned text; never retry identical arguments.
|
|
213
|
+
## Tool details
|
|
214
|
+
### Token-efficient execution
|
|
215
|
+
- First check whether the answer is already in this conversation or a previous tool result. If yes, answer directly; do not re-run tools "to be safe".
|
|
216
|
+
- Plan the complete sub-task before calling tools. Batch independent reads in one turn. Because tool calls in one response execute without intermediate model reasoning, never batch a read with an edit that depends on its result.
|
|
217
|
+
- Do not read "just to see". Read only what supports the next decision. Re-read after a change, compaction, stale state, or uncertain line context.
|
|
218
|
+
- Prefer one precise call over overlapping searches. If a call fails, inspect the error and change the approach instead of repeating it unchanged.
|
|
219
|
+
- Batch only independent read-only calls. After their results arrive, make the dependent edit in the next turn; then batch independent edits and one final verification when their exact inputs are already known.
|
|
220
|
+
- Read only what supports the next decision; verify once after a related edit set, not after every edit.
|
|
221
|
+
- Do not repeat an unchanged failing call; after three unproductive attempts, change tools or ask for the missing decision.
|
|
222
|
+
- For \`edit_file\`, derive \`old_string\` by copying the exact relevant lines from the latest successful \`read_file\` of that same path; never reconstruct it from memory, a summary, grep output, or a previous diff. That read becomes stale after any edit/write to the path, compaction/resume, or a possible external change. On an \`old_string\` mismatch, re-read the exact region and retry once with the newly returned text; never retry identical arguments.
|
|
220
223
|
|
|
221
|
-
## Workflow
|
|
222
|
-
- Understand requirements and current code before acting; do not guess.
|
|
223
|
-
- If \`.codegraph/\` exists, use \`codegraph\` first for unfamiliar code questions. Use direct reads for known or recently changed files.
|
|
224
|
-
- After modifications, run the smallest relevant verification, then typecheck/build when appropriate. Never claim success without evidence.
|
|
225
|
-
- Use web search only when freshness materially affects the answer (new APIs, versions, security, current UI conventions).
|
|
224
|
+
## Workflow
|
|
225
|
+
- Understand requirements and current code before acting; do not guess.
|
|
226
|
+
- If \`.codegraph/\` exists, use \`codegraph\` first for unfamiliar code questions. Use direct reads for known or recently changed files.
|
|
227
|
+
- After modifications, run the smallest relevant verification, then typecheck/build when appropriate. Never claim success without evidence.
|
|
228
|
+
- Use web search only when freshness materially affects the answer (new APIs, versions, security, current UI conventions).
|
|
226
229
|
|
|
227
|
-
## Tool rules
|
|
228
|
-
- Precise path/symbol → go directly to \`read_file\` or \`codegraph node\`; use \`glob\`/\`grep\` only for discovery.
|
|
229
|
-
- Before editing, read the exact target region and use its verbatim text as \`old_string\`. Use \`edit_file\` for unique local replacements and \`write_file\` for new/full files.
|
|
230
|
-
- Local edits require an exact unique match; use \`write_file\` for new/full files.
|
|
231
|
-
- Use \`glob\`/\`grep\` for discovery and \`run_command\` for execution or verification, not file existence checks. State intent before side effects.
|
|
232
|
-
- Call \`ask_human\` only when a real user decision is required; otherwise decide and proceed.
|
|
233
|
-
- Drop stale tool output when it no longer supports the current sub-task. Keep only evidence needed for the next decision.
|
|
234
|
-
- Batch independent writes only when each input is already known and their order does not matter. Keep dependent mutations sequential. Combine a clear shell workflow in one command; follow up when its result creates a decision.
|
|
230
|
+
## Tool rules
|
|
231
|
+
- Precise path/symbol → go directly to \`read_file\` or \`codegraph node\`; use \`glob\`/\`grep\` only for discovery.
|
|
232
|
+
- Before editing, read the exact target region and use its verbatim text as \`old_string\`. Use \`edit_file\` for unique local replacements and \`write_file\` for new/full files.
|
|
233
|
+
- Local edits require an exact unique match; use \`write_file\` for new/full files.
|
|
234
|
+
- Use \`glob\`/\`grep\` for discovery and \`run_command\` for execution or verification, not file existence checks. State intent before side effects.
|
|
235
|
+
- Call \`ask_human\` only when a real user decision is required; otherwise decide and proceed.
|
|
236
|
+
- Drop stale tool output when it no longer supports the current sub-task. Keep only evidence needed for the next decision.
|
|
237
|
+
- Batch independent writes only when each input is already known and their order does not matter. Keep dependent mutations sequential. Combine a clear shell workflow in one command; follow up when its result creates a decision.
|
|
235
238
|
|
|
236
239
|
## Large file writes (avoid token-cap truncation)
|
|
237
240
|
- \`write_file\` / \`edit_file\` arguments are part of the model's JSON output — a single tool call's content > ~5K tokens risks mid-stream truncation when the model's max output (default 8K–16K tokens) is exceeded, producing a "arguments 不是合法 JSON" error. Even with \`MAX_TOKENS=32000\` set, huge files still risk truncation.
|
|
@@ -248,8 +251,8 @@ ${PLATFORM_NOTE}
|
|
|
248
251
|
- Confirm with the user before irreversible or outward-facing operations (delete, overwrite existing files, push, request external services), unless explicitly authorized.
|
|
249
252
|
- Operate only within authorized scope; when unsure, ask — don't guess.
|
|
250
253
|
|
|
251
|
-
## Project context (dynamic reference)
|
|
252
|
-
${buildSnapshotSection()}${config.projectSkillEnabled ? buildProjectSkillSection() : ''}${memorySection}${buildNotepadSection()}
|
|
254
|
+
## Project context (dynamic reference)
|
|
255
|
+
${buildSnapshotSection()}${config.projectSkillEnabled ? buildProjectSkillSection() : ''}${memorySection}${buildNotepadSection()}
|
|
253
256
|
|
|
254
257
|
## Session Notepad — working notes file
|
|
255
258
|
${getCurrentSessionId()
|
|
@@ -445,3 +448,8 @@ export function updateSnapshotConfig(enabled) {
|
|
|
445
448
|
config.projectSnapshotEnabled = enabled;
|
|
446
449
|
process.env.MOCODE_PROJECT_SNAPSHOT = enabled ? 'true' : 'false';
|
|
447
450
|
}
|
|
451
|
+
/** 切换界面与模型回复语言;持久化由 REPL 调用 config/file.ts 完成。 */
|
|
452
|
+
export function updateLanguageConfig(language) {
|
|
453
|
+
setLanguage(language);
|
|
454
|
+
process.env.MOCODE_LANGUAGE = language;
|
|
455
|
+
}
|
package/dist/context/budget.js
CHANGED
|
@@ -151,9 +151,8 @@ export function scheduleActions(report) {
|
|
|
151
151
|
actions.push({
|
|
152
152
|
kind: 'warn',
|
|
153
153
|
layer: 'system',
|
|
154
|
-
reason:
|
|
155
|
-
+
|
|
156
|
-
+ '系统提示偏高时检查 MOCODE.md;工具 schema 偏高时减少可用工具;CONTEXT_WINDOW_TOKENS 应匹配模型真实窗口。',
|
|
154
|
+
reason: `固定开销 ${actual}/${budget} (+${excess}, ${percent}%);`
|
|
155
|
+
+ `提示 ${prompt} + 工具 ${toolSchemas},×${report.correction.toFixed(2)}。`,
|
|
157
156
|
});
|
|
158
157
|
}
|
|
159
158
|
if ((layers.history.overBudget || totalOver)
|