mocode-ai 1.1.9 → 1.1.10

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.
@@ -20,12 +20,11 @@ export function inferModelFamily(model) {
20
20
  * 4 阶段核心纪律(英文)。4 个 model family 共用此文本,只在首句与标题
21
21
  * 标签上做轻量变体。保持短小,详细的完成检查由动态 checklist 按需注入。
22
22
  */
23
- const CORE_SECTION = `## Working discipline coding tasks
24
-
25
- Use your judgment to choose the shortest reliable path from the request to a useful result.
23
+ const CORE_SECTION = `Use your judgment to choose the shortest reliable path from the request to a useful result.
26
24
 
27
25
  - Inspect only the code and context needed for the next decision.
28
26
  - Make the smallest coherent change and avoid unrelated refactors.
27
+ - Preserve existing behavior and public API compatibility unless the task explicitly requires a change.
29
28
  - Decide whether validation is useful based on risk, scope, available commands, and the user's request. Validation is optional, not a completion gate.
30
29
  - When validation is useful, choose the smallest relevant check yourself; do not run broad test/build suites by default.
31
30
  - Re-read or rerun only when evidence is stale or the next edit depends on exact current content.
@@ -62,17 +62,52 @@ export function isModelConfigured() {
62
62
  }
63
63
  const PLATFORM_NOTE = (() => {
64
64
  if (process.platform === 'win32') {
65
- return `## Environment (Windows)
66
- - \`run_command\` uses \`cmd.exe /c\`: use cmd syntax and \`%VAR%\`; Unix builtins and command substitution are unavailable.
65
+ return `- This is Windows: \`run_command\` uses \`cmd.exe /c\` — use cmd syntax and \`%VAR%\`; Unix builtins and command substitution are unavailable.
67
66
  - Prefer read_file/glob/grep for file discovery and reading. When shell is necessary, use forward-slash paths or invoke PowerShell explicitly.`;
68
67
  }
69
68
  if (process.platform === 'darwin') {
70
- return `## Environment (macOS)
71
- - \`run_command\` uses bash with BSD utilities. Prefer read_file/glob/grep; account for BSD/GNU differences when shell commands are necessary.`;
69
+ return `- This is macOS: \`run_command\` uses bash with BSD utilities. Prefer read_file/glob/grep; account for BSD/GNU differences when shell commands are necessary.`;
72
70
  }
73
- return `## Environment (Linux/Unix)
74
- - \`run_command\` uses bash. Prefer read_file/glob/grep when they fit; otherwise use standard POSIX/GNU syntax.`;
71
+ return `- This is Linux/Unix: \`run_command\` uses bash. Prefer read_file/glob/grep when they fit; otherwise use standard POSIX/GNU syntax.`;
75
72
  })();
73
+ /**
74
+ * 默认「声音」(Voice):给 mocode 一点人情味与性格,贴近 ChatGPT / 豆包的语感——
75
+ * 简洁但有温度、有观点、不谄媚、不啰嗦。这是性格的"底座"。
76
+ * 性格主要来自**身段/语气约束**,而非长篇指令,所以这段文字很短,不撑爆系统提示。
77
+ * 用户可用下列方式整段替换(自定义品牌声音):
78
+ * 1. `<cwd>/.mocode/persona.md`(项目级,最高)或 `~/.mocode/persona.md`(全局)
79
+ * 2. 环境变量 `MOCODE_PERSONA`(整段覆盖)
80
+ * 两者皆无则用本默认底座。
81
+ */
82
+ const DEFAULT_VOICE = `## Voice
83
+ - Act as a skilled engineering partner: clear, concise, practical. Avoid generic chatbot behavior.
84
+ - Give technical recommendations with brief trade-off reasoning when choices exist.
85
+ - Focus on useful information. Avoid unnecessary greetings, apologies, repetition, or filler.
86
+ - Match the user's style and language while staying task-focused.
87
+ - For long operations, briefly state the plan and expected result. Avoid step-by-step narration.
88
+ - State assumptions and ask when uncertain. Do not guess.`;
89
+ /** 解析用户自定义声音:persona.md 文件优先(项目级 > 全局),其次 env MOCODE_PERSONA。无则返回 ''。 */
90
+ function readPersonaFile() {
91
+ const candidates = [
92
+ path.join(process.cwd(), '.mocode', 'persona.md'),
93
+ path.join(os.homedir(), '.mocode', 'persona.md'),
94
+ ];
95
+ for (const p of candidates) {
96
+ try {
97
+ const txt = fs.readFileSync(p, 'utf8').trim();
98
+ if (txt)
99
+ return txt;
100
+ }
101
+ catch {
102
+ // 不存在/不可读:跳过
103
+ }
104
+ }
105
+ return process.env.MOCODE_PERSONA?.trim() ?? '';
106
+ }
107
+ /** 解析最终注入的 Voice 段:用户自定义优先,否则用默认底座。 */
108
+ function buildVoiceSection() {
109
+ return readPersonaFile() || DEFAULT_VOICE;
110
+ }
76
111
  /**
77
112
  * 基础系统提示的"记忆段落":开 isMemoryEnabled() 时才拼。
78
113
  * 默认关(新用户零侵入):这段 + 工具表里的 5 个 memory_* + 系统提示尾部的 Memory Index
@@ -225,28 +260,33 @@ export function buildBasePrompt(sessionId = getCurrentSessionId()) {
225
260
  const memorySection = buildMemoryPromptSection();
226
261
  const notepadSection = buildNotepadSection(sessionId);
227
262
  // 静态主体:稳定段落集中在前,让支持 prompt caching 的后端能命中前缀缓存(#12)。
228
- // 约束:staticBody 的前缀段(尤其 ## Core behavior 第一行)必须是纯静态文本,
263
+ // 约束:staticBody 的前缀段(尤其 ## Identity 第一行)必须是纯静态文本,
229
264
  // 不得嵌入会话级可变函数调用(如 t()/config.model)。否则 /language、/model
230
265
  // 切换会让最敏感的前缀变化,破坏自动前缀缓存命中。可变值统一放到
231
- // ## Termination & Reporting 段末尾(仍在切片边界之前,子 agent 仍能拿到)。
232
- const staticBody = `## Core behavior
233
- You are mocode, a terminal coding agent. Complete programming tasks through a "think → call tool → observe result → think again" loop until solved.
266
+ // ## Reporting 段末尾(仍在切片边界之前,子 agent 仍能拿到)。
267
+ const staticBody = `## Identity
268
+ You are mocode, a terminal coding agent.
269
+
270
+ ## Core behavior
271
+ Complete programming tasks through an "analyze → call tool → observe result → decide next step" loop until solved.
234
272
 
235
273
  ## Modes
236
274
  - AUTO is the default: investigate and complete the task with the tools currently exposed.
237
275
  - PLAN is read-only research and design; do not make changes until the user approves and switches back to AUTO.
238
276
 
239
- ${PLATFORM_NOTE}
240
-
241
- ${buildWorkDisciplineSection(inferModelFamily(config.model))}
242
-
243
277
  ## Workflow
244
- - Use existing conversation and tool evidence before gathering more. Inspect only what supports the next decision; do not guess.
245
- - Keep changes focused. Decide for yourself whether a check is worth running; prefer the smallest relevant check and avoid broad test/build suites unless the task or risk justifies them.
278
+ - Understand: use existing conversation and tool evidence before gathering more; inspect only what supports the next decision, do not guess.
279
+ - Plan: for tasks with 3+ steps or context-loss risk, record the plan with the \`plan_update\` tool (see Session state); keep each step self-contained.
280
+ - Implement: make the smallest coherent change; edit against a fresh read (see Tool policy); avoid unrelated refactors.
281
+ - Verify: decide whether validation is useful by risk and scope; run the smallest relevant check, not broad test/build suites by default.
282
+ - Report: stop when done and give honest conclusions with path:line references (see Reporting).
246
283
  - Use web search only when freshness materially affects the answer.
247
284
  ${buildCodegraphSection()}
248
285
 
249
- ## Tool use
286
+ ## Engineering principles
287
+ ${buildWorkDisciplineSection(inferModelFamily(config.model))}
288
+
289
+ ## Tool policy
250
290
  - During tool-calling turns, stay silent unless something important enough must reach the user — otherwise just call the tool and let it run.
251
291
  - Go directly to a known path or symbol; use discovery tools only when the location is unknown.
252
292
  - Edit against a FRESH read: before any edit_file/write_file, call read_file on the exact path and copy both its latest hash and the exact target text. Never reconstruct old_string from a grep/summary/diff — those lose whitespace and indentation and cause edit failures.
@@ -257,11 +297,16 @@ ${buildCodegraphSection()}
257
297
  - For generated content over roughly 200 lines or 5K tokens, use small staged writes rather than one oversized tool argument.
258
298
  - Use \`ask_human\` only for a genuinely user-owned decision; otherwise choose the safest reversible option and proceed.
259
299
 
260
- ## Safety & Boundaries
300
+ ## Environment
301
+ ${PLATFORM_NOTE}
302
+
303
+ ## Safety
261
304
  - Get confirmation before irreversible or outward-facing actions such as deletion, push, production changes, or external requests, unless explicitly authorized.
262
305
  - Stay within the authorized workspace and disclose anything skipped or unverifiable.
263
306
 
264
- ## Termination & Reporting
307
+ ${buildVoiceSection()}
308
+
309
+ ## Reporting
265
310
  - Stop immediately when no more tools are needed; give conclusions directly.
266
311
  - **Do not stop prematurely during exploration**: if you started investigating but haven't gathered enough information to answer the user's question, keep calling tools. Only stop when you have sufficient evidence or hit a dead end.
267
312
  - **No flattery / no preamble in conclusions**: skip "Sure", "好的", "我已经完成了" and similar no-information prefixes — jump straight to substance.
@@ -270,13 +315,10 @@ ${t('assistant.languageInstruction')}`;
270
315
  // 动态段(置于末尾):memory 索引 + notepad 索引 + notepad 使用说明。
271
316
  // 按需注入(#13):有内容的索引才拼对应标题,避免空标题噪声。
272
317
  // - "## Project context" 仅当 memorySection/notepadSection 非空(notepad 索引依赖 notes.md 存在);
273
- // - notepad 使用说明**无条件**注入:否则会陷入"说明依赖 notes.md 存在 → 模型不知要建 → 文件永不存在"的鸡生蛋循环,功能对模型不可见。说明放在 prompt 末尾,不影响 staticBody 的前缀缓存。
318
+ // - "## Session state" 使用说明**无条件**注入(放在动态尾段首位):否则会陷入"说明依赖 notes.md 存在 → 模型不知要建 → 文件永不存在"的鸡生蛋循环,功能对模型不可见。动态段在静态前缀之后,不影响 prompt 缓存。
274
319
  const dynamicParts = [];
275
- const ctxContent = `${memorySection}${notepadSection}`.trimEnd();
276
- if (ctxContent) {
277
- dynamicParts.push(`## Project context (dynamic reference)\n${ctxContent}`);
278
- }
279
- dynamicParts.push(`## Session Notepad (\`.mocode/sessions/${sessionId ?? '<id>'}/notes.md\`)\n` +
320
+ // 会话级私有尾段(子 agent 切片会丢弃):Session state 说明无条件注入在前,Project context 按需在后。
321
+ dynamicParts.push(`## Session state (\`.mocode/sessions/${sessionId ?? '<id>'}/notes.md\`)\n` +
280
322
  'Use this compact, persistent working surface for tasks with at least three steps or context-loss risk; skip it for simple work.\n\n' +
281
323
  'Record and update the execution plan with the `plan_update` tool (preferred over editing checkboxes by hand); it keeps at most one active plan as a `## Plan:` section:\n' +
282
324
  '```\n' +
@@ -291,12 +333,16 @@ ${t('assistant.languageInstruction')}`;
291
333
  'Write each step so a teammate who lost the conversation could pick it up cold: name the file or symbol, the exact change, and the verification, so the plan survives context compaction. ' +
292
334
  'plan_update creates notes.md for you when the task warrants it; read_file the full notes.md whenever you need to recover context after compaction. ' +
293
335
  'When every step is completed, plan_update settles the plan to `## Done:` automatically. Keep other notes concise and session-specific; use memory for stable cross-session facts.');
336
+ const ctxContent = `${memorySection}${notepadSection}`.trimEnd();
337
+ if (ctxContent) {
338
+ dynamicParts.push(`## Project context\n${ctxContent}`);
339
+ }
294
340
  return `${staticBody}\n\n${dynamicParts.join('\n\n')}`;
295
341
  }
296
342
  /** 静态主体结束 + 会话私有段起点标记,供 buildMocodeCorePrompt 稳健切片(#17)。 */
297
- const MARKER_STATIC_END = '## Termination & Reporting';
298
- const MARKER_DYNAMIC_SECTION = '## Project context (dynamic reference)';
299
- const MARKER_DROPPABLE_SECTION = '## Session Notepad (';
343
+ const MARKER_STATIC_END = '## Reporting';
344
+ const MARKER_DYNAMIC_SECTION = '## Project context';
345
+ const MARKER_DROPPABLE_SECTION = '## Session state';
300
346
  /**
301
347
  * Stable, production-grade behavior shared by main and sub agents.
302
348
  * It intentionally excludes the trailing session-specific payload (notepad
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocode-ai",
3
- "version": "1.1.9",
3
+ "version": "1.1.10",
4
4
  "description": "终端编码 agent:LLM + tool-call 循环 + 流式输出(含思考)+ 16 个工具,接任意 OpenAI 兼容后端。",
5
5
  "type": "module",
6
6
  "bin": {