mocode-ai 1.6.1 → 1.6.2
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 +1 -1
- package/README.zh-CN.md +355 -355
- package/dist/agent/model-turn.js +7 -8
- package/dist/config/index.js +56 -10
- package/dist/repl/commands/system.js +29 -9
- package/dist/tools/builtins/ask-human.js +2 -3
- package/dist/tools/builtins/edit-file.js +5 -13
- package/dist/tools/builtins/glob.js +2 -2
- package/dist/tools/builtins/grep.js +3 -7
- package/dist/tools/builtins/note-append.js +4 -8
- package/dist/tools/builtins/plan-update.js +1 -6
- package/dist/tools/builtins/read-file.js +5 -12
- package/dist/tools/builtins/run-command.js +3 -9
- package/dist/tools/builtins/use-skill.js +2 -2
- package/dist/tools/builtins/web-fetch.js +1 -2
- package/dist/tools/builtins/web-search.js +1 -2
- package/dist/tools/builtins/write-file.js +2 -4
- package/dist/tools/policy.js +1 -1
- package/package.json +1 -1
package/dist/agent/model-turn.js
CHANGED
|
@@ -101,18 +101,17 @@ export async function runModelTurn(input) {
|
|
|
101
101
|
const ephemeralReminder = [
|
|
102
102
|
runPolicy.reminder,
|
|
103
103
|
!opts.suppressOpeningAnalysis && step === 0
|
|
104
|
-
? '## Opening analysis\
|
|
104
|
+
? '## Opening analysis\nStart your first response of this turn with a brief analysis of the request and approach (1-3 sentences, no filler), then start tool calls. This is the only expected pre-tool prose; afterwards work quietly, no narration between calls.'
|
|
105
105
|
: '',
|
|
106
106
|
historyRebuilt
|
|
107
|
-
? '## Post-compaction recovery\n' +
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'2. Read `## Session state` below (refreshed every step from notes.md / gui-actions.log): the active plan is authoritative — `[x]` steps are finished, resume from the first `[ ]`. A `## Compaction Snapshot` section there is the progress checkpoint written at this compaction. A `## GUI actions` section lists every GUI action already performed with its observed result: do not repeat an action that appears there, unless the latest screenshot contradicts it (then the screenshot wins — treat that line as attempted but unverified).\n' +
|
|
107
|
+
? '## Post-compaction recovery\nContext was compacted before this request. Recover first, in this order:\n' +
|
|
108
|
+
'1. Read the session summary at the top of the history: `## Completed` is done — do not redo or re-verify it; `## In Progress` / `## Next Steps` say where work stopped and what is next.\n' +
|
|
109
|
+
'2. Read `## Session state` below (refreshed from notes.md / gui-actions.log): the active plan is authoritative — `[x]` steps are done, resume from the first `[ ]`. `## Compaction Snapshot` is the checkpoint written at this compaction. `## GUI actions` lists every GUI action already performed with its result: do not repeat one that appears there, unless the latest screenshot contradicts it (the screenshot wins — treat that line as attempted but unverified).\n' +
|
|
111
110
|
(sessionStateText
|
|
112
111
|
? ''
|
|
113
|
-
: '(No active plan or snapshot
|
|
114
|
-
'3. Before any file edit, read_file the target fresh
|
|
115
|
-
'4. Before re-running a search/read you think you already did, check the summary and notes first
|
|
112
|
+
: '(No active plan or snapshot in notes.md — reconstruct progress from the summary; treat its `## Completed` as ground truth.)\n') +
|
|
113
|
+
'3. Before any file edit, read_file the target fresh for its current hash — never edit from pre-compaction memory.\n' +
|
|
114
|
+
'4. Before re-running a search/read you think you already did, check the summary and notes first; repeat only if the result is genuinely missing or the target changed.'
|
|
116
115
|
: '',
|
|
117
116
|
sessionStateText,
|
|
118
117
|
]
|
package/dist/config/index.js
CHANGED
|
@@ -322,7 +322,7 @@ export function buildSessionStateReminder(sessionId = getCurrentSessionId()) {
|
|
|
322
322
|
const sources = [notes || plan ? 'notes.md' : '', guiActions ? 'gui-actions.log' : ''].filter(Boolean).join(' + ');
|
|
323
323
|
const parts = [
|
|
324
324
|
`## Session state (current, from ${sources})`,
|
|
325
|
-
'
|
|
325
|
+
'Mirrors live session state, refreshed every step; authoritative — ignore older copies earlier in this conversation.',
|
|
326
326
|
...(plan ? [plan] : []),
|
|
327
327
|
...(notes ? [notes] : []),
|
|
328
328
|
...(guiActions ? [guiActions] : []),
|
|
@@ -331,23 +331,69 @@ export function buildSessionStateReminder(sessionId = getCurrentSessionId()) {
|
|
|
331
331
|
}
|
|
332
332
|
/** AGENTS.md 自动导入正文上限:system 位于 history[0] 且 compactHistory 不压缩 system,超长需截断防占窗口(见 memory/README.md)。 */
|
|
333
333
|
const MAX_AGENTS_IMPORT_CHARS = 20000;
|
|
334
|
+
/**
|
|
335
|
+
* 不常驻注入的章节(压成指针行,read_file 按需取全文):
|
|
336
|
+
* - 目录结构 / Directory structure / Project layout —— 架构探索任务用 codegraph/探查工具现查更准;
|
|
337
|
+
* - 扩展点 / Extension points —— 只在「加新工具/命令/模块」类任务才需要,恰好是 skill 的定义。
|
|
338
|
+
* 常驻价值密度最高的「项目/命令/约定」(市场实证 arXiv 2511.12884:build/run 62.3%、conventions 主流)全文保留。
|
|
339
|
+
* 章节标题大小写不敏感,兼容英文写法的 AGENTS.md。
|
|
340
|
+
*/
|
|
341
|
+
const AGENTS_INJECTION_INDEX_SECTIONS = [
|
|
342
|
+
'目录结构',
|
|
343
|
+
'扩展点',
|
|
344
|
+
'directory structure',
|
|
345
|
+
'project layout',
|
|
346
|
+
'extension points',
|
|
347
|
+
];
|
|
348
|
+
/**
|
|
349
|
+
* AGENTS.md 按章节过滤注入:命中 {@link AGENTS_INJECTION_INDEX_SECTIONS} 的 H2 章节整段压缩成一行指针,
|
|
350
|
+
* 其余章节(preamble、## 项目、## 命令、## 约定及未知章节)逐字保留。
|
|
351
|
+
* H1 及更深层级不动;空文件/无章节文件原样返回。导出供单测直接断言。
|
|
352
|
+
*/
|
|
353
|
+
export function filterAgentsSectionsForInjection(content) {
|
|
354
|
+
const lines = content.split('\n');
|
|
355
|
+
const out = [];
|
|
356
|
+
let inIndexedSection = false;
|
|
357
|
+
for (const line of lines) {
|
|
358
|
+
const h2 = /^##\s+(.*)$/.exec(line);
|
|
359
|
+
if (h2) {
|
|
360
|
+
const title = h2[1].trim().toLowerCase();
|
|
361
|
+
// 前缀匹配容忍「目录结构(monorepo)」「Directory Structure — monorepo」等后缀写法;
|
|
362
|
+
// 仅当后缀紧邻(空格/括号/冒号/破折号)时命中,避免误伤「约定与目录结构习惯」这类反向词序。
|
|
363
|
+
inIndexedSection = AGENTS_INJECTION_INDEX_SECTIONS.some((s) => title === s || new RegExp(`^${s}[\\s(:\\u2014\\uff08\\(]`).test(title));
|
|
364
|
+
if (inIndexedSection) {
|
|
365
|
+
out.push(`- ${h2[1].trim()}: (not injected — read_file AGENTS.md on demand)`);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (!inIndexedSection)
|
|
370
|
+
out.push(line);
|
|
371
|
+
}
|
|
372
|
+
return out.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
373
|
+
}
|
|
334
374
|
/**
|
|
335
375
|
* 工作区根 AGENTS.md 自动导入段:与 memory 开关完全无关——
|
|
336
376
|
* 只要 <cwd>/AGENTS.md 存在就把正文直接拼进 prompt(超 {@link MAX_AGENTS_IMPORT_CHARS} 截断+末尾提示),
|
|
337
377
|
* 不再只指路让模型按需 read_file。读失败静默跳过(返空串)。
|
|
378
|
+
*
|
|
379
|
+
* 瘦身(方案A):目录结构/扩展点两章节不常驻,压成指针行——模型真做架构/扩展任务时
|
|
380
|
+
* 一次 read_file 取全文(渐进披露,与 Skills 清单同构);截断上限作用于过滤后的正文。
|
|
338
381
|
*/
|
|
339
382
|
function buildAgentsImportSection() {
|
|
340
383
|
try {
|
|
341
384
|
const projectAgents = path.join(process.cwd(), 'AGENTS.md');
|
|
342
385
|
if (!fs.existsSync(projectAgents))
|
|
343
386
|
return '';
|
|
344
|
-
const
|
|
345
|
-
if (!
|
|
387
|
+
const raw = fs.readFileSync(projectAgents, 'utf8').trim();
|
|
388
|
+
if (!raw)
|
|
346
389
|
return '';
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
:
|
|
350
|
-
|
|
390
|
+
const filtered = filterAgentsSectionsForInjection(raw);
|
|
391
|
+
const body = filtered.length > MAX_AGENTS_IMPORT_CHARS
|
|
392
|
+
? `${filtered.slice(0, MAX_AGENTS_IMPORT_CHARS)}\n…[AGENTS.md truncated: first ${MAX_AGENTS_IMPORT_CHARS} characters injected]`
|
|
393
|
+
: filtered;
|
|
394
|
+
return (`\n## Project memory (AGENTS.md, auto-imported)\n${body}\n` +
|
|
395
|
+
'- AGENTS.md may be stale: current code and the user request override stale memory.\n' +
|
|
396
|
+
'- Discovered a stable, non-obvious project fact worth persisting? write_file(append=true) one line to `.mocode/agents-draft.md`; the user merges drafts into AGENTS.md via /init.');
|
|
351
397
|
}
|
|
352
398
|
catch {
|
|
353
399
|
return ''; // 读失败静默跳过:不让导入破坏 prompt 构建
|
|
@@ -398,14 +444,14 @@ export function buildBasePrompt(sessionId = getCurrentSessionId()) {
|
|
|
398
444
|
// 回复语言不写入提示词:模型按用户当轮提问语言自动识别(Voice 段的
|
|
399
445
|
// "Match the user's style and language" 已覆盖),/language 只切换终端 UI 文案。
|
|
400
446
|
const staticBody = `## Identity
|
|
401
|
-
You are mocode, a terminal coding agent.
|
|
447
|
+
You are mocode, a terminal coding agent created by Wan Engineer.
|
|
402
448
|
|
|
403
449
|
## Core behavior
|
|
404
450
|
Complete programming tasks through an "analyze → call tool → observe result → decide next step" loop until solved.
|
|
405
451
|
|
|
406
452
|
## Modes
|
|
407
|
-
- AUTO
|
|
408
|
-
- PLAN
|
|
453
|
+
- AUTO (default): complete tasks with the tools currently exposed.
|
|
454
|
+
- PLAN: read-only design; no changes until the user approves and switches back.
|
|
409
455
|
|
|
410
456
|
## Workflow
|
|
411
457
|
- Understand: use existing conversation and tool evidence before gathering more.
|
|
@@ -17,40 +17,60 @@ import { buildSlashCommands, slashHelpLines, HELP_GROUPS } from '../commands.js'
|
|
|
17
17
|
import { startRunningListener, stopRunningListener } from '../running-input.js';
|
|
18
18
|
import { checkVersion, fetchLatestVersion, getCurrentVersion, runUpgradeForeground } from '../../commands/upgrade.js';
|
|
19
19
|
import { unhandled, next, exit, forward } from './types.js';
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
20
22
|
/**
|
|
21
|
-
* /init 指令:发给 agent 扫描项目并生成 AGENTS.md
|
|
23
|
+
* /init 指令:发给 agent 扫描项目并生成 AGENTS.md。整体重写(非增量追加),预算分配制:
|
|
24
|
+
* 总长 ≤4000 字且 ≤ 旧文件字数(代码侧动态注入具体数字),装不下的高价值事实进
|
|
25
|
+
* .mocode/agents-draft.md 草稿而非硬塞,消除「删事实 vs 守预算」两难。写完供 memory 子系统下轮加载。
|
|
22
26
|
*
|
|
23
|
-
* 函数化(非 const):.codegraph/
|
|
27
|
+
* 函数化(非 const):.codegraph/ 索引与旧文件字数的探测放在调用瞬间,没索引时不提 codegraph,
|
|
24
28
|
* 避免 LLM 调出失败。/init 是冷启动动作,IO 开销可忽略。
|
|
25
29
|
*/
|
|
26
30
|
function buildInitPrompt() {
|
|
27
31
|
const cg = hasCodegraphIndex()
|
|
28
32
|
? '- 若有 .codegraph/:用 use_skill 加载 codegraph skill 后用 run_command 调 codegraph explore "<架构或入口符号>" 一次拿相关源码+调用路径,别逐文件读!!!\n'
|
|
29
33
|
: '';
|
|
34
|
+
// 动态注入旧文件字数:具体数字的遵循率远高于抽象原则(「≤4000」);首次生成时无旧文件则只约束绝对上限。
|
|
35
|
+
let oldSizeNote = '';
|
|
36
|
+
try {
|
|
37
|
+
const old = fs.readFileSync(path.join(process.cwd(), 'AGENTS.md'), 'utf8');
|
|
38
|
+
if (old.trim())
|
|
39
|
+
oldSizeNote = `旧 AGENTS.md 为 ${old.length} 字(新文件净字数必须 ≤ ${Math.min(4000, old.length)}:既守 4000 上限也守零和,超出即继续删低价值条目直到达标)。`;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// 无旧文件:首次生成,只受 4000 字绝对上限约束。
|
|
43
|
+
}
|
|
30
44
|
return `分析当前项目(process.cwd()),生成 AGENTS.md 项目记忆文件,供 mocode 后续会话自动加载——目标是让后续会话无需重新摸索就能上手。
|
|
31
45
|
|
|
32
46
|
先探查(尽量少调用拿全貌):
|
|
33
47
|
${cg}- read_file package.json(或 Cargo.toml/pyproject.toml/go.mod 等):scripts、依赖、入口、模块类型。
|
|
34
48
|
- glob 顶层目录;read_file 入口文件 + 各子系统 index.ts/README。
|
|
35
|
-
- 若 AGENTS.md 已存在:read_file
|
|
49
|
+
- 若 AGENTS.md 已存在:read_file 读它。**整体重写,不是增量追加**——不要把旧文当底稿做增量,从下面的预算出发重新分配每一条;以当前代码为准,过时/弱事实直接删,准确且仍高价值的可沿用。
|
|
50
|
+
- 若 .mocode/agents-draft.md 存在:read_file 读它,把草稿里的稳定事实合入对应章节,完成后用 write_file 覆盖为空(或删除该文件)。
|
|
36
51
|
|
|
37
52
|
AGENTS.md 按以下结构写(每节简短,只写稳定、非显然的事实):
|
|
38
53
|
## 项目
|
|
39
54
|
一两句:是什么、技术栈、运行环境。
|
|
40
55
|
## 命令
|
|
41
|
-
install / dev / build / test / typecheck / lint 等——从 package.json scripts 提炼,写原样命令行(如 \`npm run
|
|
56
|
+
install / dev / build / test / typecheck / lint 等——从 package.json scripts 提炼,写原样命令行(如 \`npm run build\`);没有的注明"无测试"/"无 lint"。
|
|
42
57
|
## 目录结构
|
|
43
|
-
|
|
58
|
+
顶层各目录与子系统职责,一句话/个;不逐文件列。此节系统提示不会常驻注入(按需 read_file),够用即可。
|
|
44
59
|
## 约定
|
|
45
60
|
从代码与现有文档提炼的硬约定:模块系统(ESM?)、命名、错误处理、工具/函数契约、易踩坑点。只写非显然、会让人踩坑的;不写"保持简洁"这种正确废话。
|
|
46
61
|
## 扩展点
|
|
47
|
-
加工具/命令/provider/模块的接缝(改哪个文件、加在哪)
|
|
62
|
+
加工具/命令/provider/模块的接缝(改哪个文件、加在哪)。此节同样不常驻注入,保持简短。
|
|
63
|
+
|
|
64
|
+
预算(硬上限,按字分配,参考):
|
|
65
|
+
- 总长 ≤ 4000 字${oldSizeNote ? `,且 ${oldSizeNote}` : '(首次生成)'}。参考分配:项目 ≤200、命令 ≤650、目录结构 ≤400、约定 ≤2000、扩展点 ≤400。
|
|
66
|
+
- **预算装不下但确有高价值的事实不丢**:write_file(append=true) 追加到 \`.mocode/agents-draft.md\`(一行一条,注明所属章节),下次 /init 再评估合入。禁止为了塞进预算牺牲准确性,也禁止超预算硬塞。
|
|
48
67
|
|
|
49
68
|
硬要求:
|
|
50
69
|
- 从实际代码提炼,引用具体文件名/命令/符号;不编造、不泛泛。
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
70
|
+
- 只写后续会话有用的稳定事实,不写易变项(当前 bug、临时文件、未决 TODO)。
|
|
71
|
+
- 写入前 run_command \`wc -m AGENTS.md\` 不可用(文件还没写);改用你草稿的字数估算,并按 ${oldSizeNote ? '上述净删目标' : '4000 字上限'}自查,超了先删再写。
|
|
72
|
+
- 用 write_file 写入项目根 AGENTS.md;**写完立即 read_file AGENTS.md 复查实际字数**,超限则编辑收窄后重写,直到达标。
|
|
73
|
+
- 最终简述必须含对账三件套(供用户验收,缺一不可):①新旧字数对比(旧 X → 新 Y,无旧文件写"首建 Y");②删掉的条目清单(标题级);③新增的条目清单(标题级)+ 从代码里发现的 2-3 条非显然关键约定。`;
|
|
54
74
|
}
|
|
55
75
|
export const systemCommands = [
|
|
56
76
|
(ctx) => {
|
|
@@ -111,10 +111,9 @@ export const askHumanTool = {
|
|
|
111
111
|
name: 'ask_human',
|
|
112
112
|
description: [
|
|
113
113
|
'Ask the user a question and wait for their response.',
|
|
114
|
-
' CHOICES: pass 2-4 concrete options via `options`, each as { label, description? }
|
|
114
|
+
' CHOICES: pass 2-4 concrete options via `options`, each as { label, description? }; every non-empty option requires a non-empty `label`.',
|
|
115
115
|
' FREE-TEXT: pass `options: []` when the answer cannot be reduced to choices (e.g. "paste the error message").',
|
|
116
|
-
'
|
|
117
|
-
' DO NOT call when the task is clear and you can pick a sensible default.',
|
|
116
|
+
' DO NOT call when the task is clear and a sensible default exists.',
|
|
118
117
|
].join(' '),
|
|
119
118
|
parameters: {
|
|
120
119
|
type: 'object',
|
|
@@ -13,23 +13,15 @@ function conflict(path, details) {
|
|
|
13
13
|
}
|
|
14
14
|
export const editFileTool = {
|
|
15
15
|
name: 'edit_file',
|
|
16
|
-
description: `Replace content in a file transactionally.
|
|
16
|
+
description: `Replace content in a file transactionally. Two modes:
|
|
17
17
|
|
|
18
|
-
**String replacement
|
|
18
|
+
**String replacement (default):** old_string must occur EXACTLY once, copied verbatim from a fresh read_file output — never reconstruct from memory, summaries, or grep output (whitespace/indentation/line-ending details are lost there).
|
|
19
19
|
|
|
20
|
-
**Line-range
|
|
20
|
+
**Line-range:** line_start/line_end (1-based, inclusive) instead of old_string — for large blocks, repeated patterns, or hard-to-reproduce text.
|
|
21
21
|
|
|
22
|
-
expected_hash
|
|
22
|
+
expected_hash (sha256 from read_file artifact header) is required and must match the current file; changed-since-read edits are rejected — re-read and retry with the new hash.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
- String replacement: small, unique text fragments (function signatures, config keys, error messages)
|
|
26
|
-
- Line-range: large blocks, repeated patterns, or when whitespace precision is critical
|
|
27
|
-
|
|
28
|
-
**Anti-patterns (will fail):**
|
|
29
|
-
- old_string reconstructed from memory or a summary
|
|
30
|
-
- old_string copied from a previous tool call that may be stale
|
|
31
|
-
- old_string that appears multiple times (add more context to make it unique)
|
|
32
|
-
- expected_hash from a different file or an old read_file call`,
|
|
24
|
+
Anti-patterns (will fail): old_string from memory/summary/stale call; multiple occurrences (add context to disambiguate); hash from another file or old read.`,
|
|
33
25
|
risk: 'confirm',
|
|
34
26
|
parameters: {
|
|
35
27
|
type: 'object',
|
|
@@ -4,8 +4,8 @@ import { getSandboxRoot, isInsideRoot } from '../../sandbox/index.js';
|
|
|
4
4
|
// ---------- glob ----------
|
|
5
5
|
export const globTool = {
|
|
6
6
|
name: 'glob',
|
|
7
|
-
description: 'Find files matching a glob pattern (e.g. **/*.ts). Auto-excludes node_modules/.git.' +
|
|
8
|
-
'
|
|
7
|
+
description: 'Find files matching a glob pattern (e.g. **/*.ts). Auto-excludes node_modules/.git. ' +
|
|
8
|
+
'For architecture or call chains, prefer the codegraph skill.',
|
|
9
9
|
parameters: {
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
@@ -60,13 +60,9 @@ export const grepTool = {
|
|
|
60
60
|
name: 'grep',
|
|
61
61
|
description: 'Search file contents by regex (recursive, excludes node_modules/.git/dist).\n' +
|
|
62
62
|
'Output: per-file header "<path>: N matches, lines [l1, l2, ...]" + matched lines with ORIGINAL INDENTATION kept.\n' +
|
|
63
|
-
'Pass context=2..5 to get neighbouring lines inline (like ripgrep -C) — use it INSTEAD of following every hit ' +
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'do NOT read entire files after grepping, and do NOT reconstruct an edit_file old_string from grep output ' +
|
|
67
|
-
'(long lines are clipped).\n' +
|
|
68
|
-
'Independent read_file/grep/glob calls may be issued in the same response and run concurrently. ' +
|
|
69
|
-
'For call chains across many files, prefer loading the `codegraph` skill (use_skill).',
|
|
63
|
+
'Pass context=2..5 to get neighbouring lines inline (like ripgrep -C) — use it INSTEAD of following every hit with a read_file round-trip.\n' +
|
|
64
|
+
'Still use read_file(offset=X, limit=Y) for a whole region or exact edit text — never reconstruct an edit_file old_string from grep output (long lines are clipped). ' +
|
|
65
|
+
'For call chains across many files, prefer the codegraph skill.',
|
|
70
66
|
parameters: {
|
|
71
67
|
type: 'object',
|
|
72
68
|
properties: {
|
|
@@ -36,14 +36,10 @@ function normalizeSection(raw) {
|
|
|
36
36
|
}
|
|
37
37
|
export const noteAppendTool = {
|
|
38
38
|
name: 'note_append',
|
|
39
|
-
description: 'Append
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'(that is the plan via `plan_update`) or for stable cross-session facts (that is `memory_save`). ' +
|
|
44
|
-
'Notes you write here persist across compaction within this session and are re-injected into the prompt ' +
|
|
45
|
-
'automatically, so the agent keeps remembering what it found/decided. Call it the moment you make the ' +
|
|
46
|
-
'discovery or decision — do not batch to the end.',
|
|
39
|
+
description: 'Append ONE decision-grade note (finding / decision / open question / risk) to the session notepad (notes.md); it survives compaction and is re-injected into the prompt automatically. ' +
|
|
40
|
+
'Only NON-OBVIOUS, lasting-value discoveries: subtle constraints, decisions with downstream impact, open questions blocking a choice, risks affecting later steps. ' +
|
|
41
|
+
'NOT routine progress (that is plan_update) and NOT stable cross-session facts (that is memory_save). ' +
|
|
42
|
+
'Call the moment you make the discovery or decision — do not batch to the end. One item per call.',
|
|
47
43
|
risk: 'safe',
|
|
48
44
|
parameters: {
|
|
49
45
|
type: 'object',
|
|
@@ -18,12 +18,7 @@ function normalizeStatus(raw) {
|
|
|
18
18
|
}
|
|
19
19
|
export const planUpdateTool = {
|
|
20
20
|
name: 'plan_update',
|
|
21
|
-
description: 'Record and update the session execution plan (the `## Plan:` block in
|
|
22
|
-
'Use for any task with 3+ steps or context-loss risk. This REPLACES the whole plan each call, so always pass the full steps array. ' +
|
|
23
|
-
'Rules: at most one step may be in_progress; mark a step completed as soon as its work is done — do not batch updates to end of turn. ' +
|
|
24
|
-
'Give every step a short `title` (≤20 chars, e.g. "编写测试" / "修 status bar") that shows in the status bar, ' +
|
|
25
|
-
'plus a `content` that is self-contained enough to survive context compaction: name the target file/symbol, the change, and how to verify. ' +
|
|
26
|
-
'When every step is completed the plan auto-settles to `## Done:`. Creates notes.md if missing. Safe to call in PLAN mode (writes only the session notepad, never project files).',
|
|
21
|
+
description: 'Record and update the session execution plan (the `## Plan:` block in notes.md). Use for any task with 3+ steps or context-loss risk. REPLACES the whole plan each call — always pass the full steps array. At most one step in_progress; mark a step completed as soon as its work is done, not batched to end of turn. Each step: short `title` (≤20 chars, shown in the status bar) + `content` self-contained enough to survive compaction (target file/symbol, change, verification). All steps completed → auto-settles to `## Done:`. Creates notes.md if missing. Safe in PLAN mode (writes only the session notepad).',
|
|
27
22
|
risk: 'safe',
|
|
28
23
|
parameters: {
|
|
29
24
|
type: 'object',
|
|
@@ -36,18 +36,11 @@ function failure(code, message) {
|
|
|
36
36
|
export const readFileTool = {
|
|
37
37
|
name: 'read_file',
|
|
38
38
|
description: 'Read a file: text with line numbers, images as visual model input.\n' +
|
|
39
|
-
'Text — read before editing.
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'For files ≤500 lines you may read the whole file in one call. Independent region reads ' +
|
|
45
|
-
'may be issued in the same response — they run concurrently, saving a round-trip each.\n' +
|
|
46
|
-
'Images — PNG/JPEG/GIF/WebP are detected by MAGIC BYTES (the extension does not matter) and attached as ' +
|
|
47
|
-
'visual model input; pass detail=low|high to control resolution, and oversized PNGs are downscaled automatically.\n' +
|
|
48
|
-
'Other binary files are REJECTED with an explanation instead of being dumped as garbled text. ' +
|
|
49
|
-
'If you truly need their content, use run_command with a proper tool (e.g. `file`, `strings`, a disassembler).\n' +
|
|
50
|
-
'For architecture or call-chain questions, prefer loading the `codegraph` skill (use_skill) over reading files one at a time.',
|
|
39
|
+
'Text — read before editing. Files >500 lines: grep first, then read_file with offset+limit (e.g. offset=350, limit=120); never read a whole large file in one call. ' +
|
|
40
|
+
'Need several regions of the SAME file? Issue those read_file calls together in one response (they run concurrently) instead of sequential offset+=limit walks. ' +
|
|
41
|
+
'Images — PNG/JPEG/GIF/WebP detected by MAGIC BYTES (extension ignored), attached as visual input; detail=low|high controls resolution, oversized PNGs downscale automatically. ' +
|
|
42
|
+
'Other binaries are REJECTED with an explanation — use run_command with a proper tool (`file`, `strings`, disassembler) if you need their content. ' +
|
|
43
|
+
'Architecture/call-chain questions: prefer the codegraph skill over reading files one at a time.',
|
|
51
44
|
parameters: {
|
|
52
45
|
type: 'object',
|
|
53
46
|
properties: {
|
|
@@ -164,15 +164,9 @@ function commandOutcome(result) {
|
|
|
164
164
|
// ---------- run_command ----------
|
|
165
165
|
export const runCommandTool = {
|
|
166
166
|
name: 'run_command',
|
|
167
|
-
description: 'Run a FOREGROUND shell command, merging stdout+stderr. Default timeout 120s, hard cap 10min. ' +
|
|
168
|
-
'
|
|
169
|
-
'
|
|
170
|
-
'pass shell=powershell (`Start-Sleep`) or shell=bash (`sleep`) when a wait is needed.\n' +
|
|
171
|
-
'Anything that must keep running after this call returns — dev server, inference/model service, watcher, ' +
|
|
172
|
-
'log tail — belongs to dev_server instead: it survives across tool calls and gives you an id for ' +
|
|
173
|
-
'incremental log reads and process-tree kill. Do NOT detach with `start /b`, `nohup`, `&` or similar here: ' +
|
|
174
|
-
'you lose both the logs and the handle.\n' +
|
|
175
|
-
"Multiple independent run_command calls may be issued in one response to save model round-trips; they execute serially, so do not depend one on another's output within the same message.",
|
|
167
|
+
description: 'Run a FOREGROUND shell command, merging stdout+stderr. Default timeout 120s, hard cap 10min; pass shell=cmd|powershell|bash to pick the interpreter (platform default and MOCODE_SHELL override are stated in the system prompt). Non-interactive cmd cannot run `timeout /t` — use shell=powershell (`Start-Sleep`) or shell=bash (`sleep`) for waits.\n' +
|
|
168
|
+
'Anything that must keep running after this call returns — dev server, model service, watcher, log tail — belongs to dev_server (survives across calls; gives an id for incremental logs and process-tree kill). Do NOT detach via `start /b`, `nohup`, `&`: you lose both the logs and the handle.\n' +
|
|
169
|
+
'Multiple independent calls may be issued in one response (they run serially, in order); do not depend one on another\'s output within the same message.',
|
|
176
170
|
risk: 'dangerous',
|
|
177
171
|
parameters: {
|
|
178
172
|
type: 'object',
|
|
@@ -13,9 +13,9 @@ import { activateSkill } from '../../skills/activation.js';
|
|
|
13
13
|
const MAX_SKILL_FILE = 200_000;
|
|
14
14
|
export const useSkillTool = {
|
|
15
15
|
name: 'use_skill',
|
|
16
|
-
description: 'Load the full SKILL.md instructions for a given skill
|
|
16
|
+
description: 'Load the full SKILL.md instructions for a given skill (see the skill list in the system prompt for when to use each). ' +
|
|
17
17
|
'Supports args (renders $ARGUMENTS / $1.. / ${SKILL_DIR}) and file (reads a bundled reference file). ' +
|
|
18
|
-
'
|
|
18
|
+
'Skills marked [fork] return a guide to call run_skill instead of loading the body inline.',
|
|
19
19
|
parameters: {
|
|
20
20
|
type: 'object',
|
|
21
21
|
properties: {
|
|
@@ -77,8 +77,7 @@ async function attemptFetch(target, via, signal) {
|
|
|
77
77
|
// ---------- web_fetch ----------
|
|
78
78
|
export const webFetchTool = {
|
|
79
79
|
name: 'web_fetch',
|
|
80
|
-
description: 'Fetch a URL and clean HTML to body text. Use to read a link from search results or a URL given by the user.',
|
|
81
|
-
parameters: {
|
|
80
|
+
description: 'Fetch a URL and clean HTML to body text. Use to read a link from search results or a URL given by the user.', parameters: {
|
|
82
81
|
type: 'object',
|
|
83
82
|
properties: {
|
|
84
83
|
url: { type: 'string', description: 'Full URL to fetch; must be http/https' },
|
|
@@ -6,8 +6,7 @@ const MAX_CONTENT_CHARS = 800;
|
|
|
6
6
|
// ---------- web_search ----------
|
|
7
7
|
export const webSearchTool = {
|
|
8
8
|
name: 'web_search',
|
|
9
|
-
description: 'Search the web (AnySearch). Returns title/url/snippet/body per result. Optional tag for sub-domain capability.',
|
|
10
|
-
parameters: {
|
|
9
|
+
description: 'Search the web (AnySearch). Returns title/url/snippet/body per result. Optional tag for sub-domain capability.', parameters: {
|
|
11
10
|
type: 'object',
|
|
12
11
|
properties: {
|
|
13
12
|
query: { type: 'string', description: 'Search query' },
|
|
@@ -28,10 +28,8 @@ function invalid(path, message) {
|
|
|
28
28
|
}
|
|
29
29
|
export const writeFileTool = {
|
|
30
30
|
name: 'write_file',
|
|
31
|
-
description: 'Create or replace one file transactionally. expected_hash may be omitted (or null) only for create-only writes to a path that must not exist; overwriting requires the hash from a fresh read_file
|
|
32
|
-
'To ADD to an existing file
|
|
33
|
-
'append needs NO expected_hash and NO prior read_file (concurrency is handled by the file lock); it creates the file when missing, and it appends bytes VERBATIM — ' +
|
|
34
|
-
'if the file does not end with a newline, start your content with "\\n" or the last line will merge with yours.',
|
|
31
|
+
description: 'Create or replace one file transactionally. expected_hash may be omitted (or null) only for create-only writes to a path that must not exist; overwriting requires the hash from a fresh read_file.\n' +
|
|
32
|
+
'To ADD to an existing file, pass append=true — only the new text goes in `content`; NO expected_hash and NO prior read_file needed (lock-protected; creates the file when missing). Appends VERBATIM: if the file does not end with a newline, start your content with "\\n" or the last line will merge with yours.',
|
|
35
33
|
risk: 'confirm',
|
|
36
34
|
parameters: {
|
|
37
35
|
type: 'object',
|
package/dist/tools/policy.js
CHANGED
|
@@ -237,7 +237,7 @@ export class ToolPolicyController {
|
|
|
237
237
|
'## Tool route (current turn)',
|
|
238
238
|
`Policy ${snapshot.id} v${snapshot.version}; active groups: ${active}.`,
|
|
239
239
|
`Router reason: ${snapshot.reason}`,
|
|
240
|
-
'Use only the tools
|
|
240
|
+
'Use only the exposed tools. Missing capability? Call add_tool_groups alone; dependent calls wait for the next step.',
|
|
241
241
|
];
|
|
242
242
|
if (remaining.length)
|
|
243
243
|
lines.push(`Groups still available: ${remaining.join(', ')}.`);
|