mocode-ai 1.2.7 → 1.2.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/dist/agent/core.js +126 -43
- package/dist/config/index.js +31 -6
- package/dist/config/presets.js +36 -1
- package/dist/context/budget.js +5 -16
- package/dist/llm/index.js +42 -4
- package/dist/repl/index.js +57 -13
- package/dist/session/compact.js +329 -40
- package/dist/session/notes.js +63 -0
- package/dist/session/scheduler.js +5 -9
- package/dist/tools/constants.js +21 -0
- package/package.json +1 -1
- package/dist/__trace_manual_test__.js +0 -1
- package/dist/agent/core.d.ts +0 -112
- package/dist/agent/index.d.ts +0 -19
- package/dist/agent/mode.d.ts +0 -29
- package/dist/agent/retry-classifier.js +0 -64
- package/dist/agent/spawn.d.ts +0 -36
- package/dist/attachments/image.d.ts +0 -22
- package/dist/commands/config.d.ts +0 -8
- package/dist/commands/skill.js +0 -230
- package/dist/config/file.d.ts +0 -23
- package/dist/config/index.d.ts +0 -114
- package/dist/config/presets.d.ts +0 -57
- package/dist/context/budget.d.ts +0 -79
- package/dist/context/classifier.d.ts +0 -14
- package/dist/context/encoders/_util.d.ts +0 -11
- package/dist/context/encoders/code.d.ts +0 -2
- package/dist/context/encoders/doc.d.ts +0 -14
- package/dist/context/encoders/graph.d.ts +0 -11
- package/dist/context/encoders/graph.js +0 -81
- package/dist/context/encoders/index.d.ts +0 -2
- package/dist/context/encoders/log.d.ts +0 -2
- package/dist/context/encoders/log.js +0 -2
- package/dist/context/encoders/memory.d.ts +0 -2
- package/dist/context/encoders/passthrough.d.ts +0 -11
- package/dist/context/encoders/search.d.ts +0 -2
- package/dist/context/encoders/summary.d.ts +0 -12
- package/dist/context/encoders/table.d.ts +0 -2
- package/dist/context/encoders/tree.d.ts +0 -2
- package/dist/context/index.d.ts +0 -6
- package/dist/context/lifecycle.d.ts +0 -51
- package/dist/context/pipeline.d.ts +0 -9
- package/dist/context/registry.d.ts +0 -9
- package/dist/context/relevance.d.ts +0 -65
- package/dist/context/types.d.ts +0 -36
- package/dist/foo.js +0 -28
- package/dist/foo.test.js +0 -12
- package/dist/index.d.ts +0 -1
- package/dist/llm/capabilities.d.ts +0 -19
- package/dist/llm/index.d.ts +0 -111
- package/dist/memory/discover.d.ts +0 -15
- package/dist/memory/index.d.ts +0 -13
- package/dist/memory/reflect.d.ts +0 -34
- package/dist/memory/store.d.ts +0 -124
- package/dist/pet/bridge.d.ts +0 -73
- package/dist/pet/protocol.d.ts +0 -83
- package/dist/pet/state.d.ts +0 -28
- package/dist/plan/active.d.ts +0 -23
- package/dist/plan/active.js +0 -45
- package/dist/plan/index.d.ts +0 -13
- package/dist/plan/index.js +0 -33
- package/dist/plan/state.d.ts +0 -2
- package/dist/plan/state.js +0 -11
- package/dist/plan/store.d.ts +0 -78
- package/dist/plan/store.js +0 -429
- package/dist/project-skill/index.js +0 -95
- package/dist/project-skill/initializer.js +0 -178
- package/dist/project-snapshot/index.js +0 -85
- package/dist/project-snapshot/llm-snapshot.js +0 -150
- package/dist/project-snapshot/static-files.js +0 -87
- package/dist/repl/index.d.ts +0 -22
- package/dist/rollback/index.d.ts +0 -79
- package/dist/sandbox/command.d.ts +0 -3
- package/dist/sandbox/index.d.ts +0 -4
- package/dist/sandbox/jail.d.ts +0 -17
- package/dist/sandbox/policy.d.ts +0 -21
- package/dist/sandbox/root.d.ts +0 -5
- package/dist/session/compact.d.ts +0 -92
- package/dist/session/index.d.ts +0 -13
- package/dist/session/notes-plan.js +0 -41
- package/dist/session/persist.d.ts +0 -30
- package/dist/session/scheduler.d.ts +0 -53
- package/dist/skills/discover.d.ts +0 -33
- package/dist/skills/index.d.ts +0 -13
- package/dist/skills/skill-eval.js +0 -345
- package/dist/skills/skill-improve.js +0 -221
- package/dist/skills/stats.js +0 -102
- package/dist/tools/builtins/apply-patch.js +0 -174
- package/dist/tools/builtins/ask-human.d.ts +0 -9
- package/dist/tools/builtins/codegraph.d.ts +0 -2
- package/dist/tools/builtins/codegraph.js +0 -128
- package/dist/tools/builtins/edit-file.d.ts +0 -2
- package/dist/tools/builtins/glob.d.ts +0 -2
- package/dist/tools/builtins/grep.d.ts +0 -2
- package/dist/tools/builtins/index.d.ts +0 -2
- package/dist/tools/builtins/memory-forget.d.ts +0 -2
- package/dist/tools/builtins/memory-list.d.ts +0 -2
- package/dist/tools/builtins/memory-save.d.ts +0 -2
- package/dist/tools/builtins/memory-search.d.ts +0 -2
- package/dist/tools/builtins/memory-update.d.ts +0 -2
- package/dist/tools/builtins/project-skill-update.js +0 -102
- package/dist/tools/builtins/read-file.d.ts +0 -2
- package/dist/tools/builtins/run-command.d.ts +0 -2
- package/dist/tools/builtins/switch-mode.d.ts +0 -2
- package/dist/tools/builtins/switch-mode.js +0 -44
- package/dist/tools/builtins/task.d.ts +0 -2
- package/dist/tools/builtins/todolist.d.ts +0 -2
- package/dist/tools/builtins/todolist.js +0 -411
- package/dist/tools/builtins/use-skill.d.ts +0 -2
- package/dist/tools/builtins/web-fetch.d.ts +0 -2
- package/dist/tools/builtins/web-search.d.ts +0 -2
- package/dist/tools/builtins/write-file.d.ts +0 -2
- package/dist/tools/constants.d.ts +0 -52
- package/dist/tools/registry.d.ts +0 -18
- package/dist/tools/retry.js +0 -137
- package/dist/tools/types.d.ts +0 -42
- package/dist/ui/batch.d.ts +0 -67
- package/dist/ui/clipboard.d.ts +0 -9
- package/dist/ui/content.d.ts +0 -55
- package/dist/ui/diff.d.ts +0 -34
- package/dist/ui/intervention.d.ts +0 -46
- package/dist/ui/layout.d.ts +0 -225
- package/dist/ui/markdown.d.ts +0 -46
- package/dist/ui/mouse.d.ts +0 -29
- package/dist/ui/prompt.d.ts +0 -61
- package/dist/ui/render.d.ts +0 -60
- package/dist/ui/spinner.d.ts +0 -26
- package/dist/ui/theme.d.ts +0 -47
- package/dist/ui/thinking.js +0 -80
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 项目 Skill 初始化器 - 派生子 agent 带工具深度探索项目,生成高质量的初始 skill
|
|
3
|
-
*/
|
|
4
|
-
import { spawnAgent } from '../agent/spawn.js';
|
|
5
|
-
/**
|
|
6
|
-
* 子 agent 的系统提示后缀:角色与输出格式约束
|
|
7
|
-
*/
|
|
8
|
-
const SKILL_INIT_SUFFIX = `You are a project exploration agent. Your task is to deeply understand this project and generate a concise "Project Skill" document.
|
|
9
|
-
|
|
10
|
-
## ⚠️ Complementary to Snapshot — CRITICAL
|
|
11
|
-
The following information is ALREADY provided by Project Snapshot — **do NOT repeat**:
|
|
12
|
-
- ✗ Project one-liner description (快照已有)
|
|
13
|
-
- ✗ Module names and file locations (快照 srcTree 已有)
|
|
14
|
-
- ✗ Tech stack names and versions (快照 techStack 已有)
|
|
15
|
-
- ✗ Build/test commands (快照 keyCommands 已有)
|
|
16
|
-
- ✗ File lists and directory structure (快照 srcTree 已有)
|
|
17
|
-
|
|
18
|
-
**Your job is to capture INSIGHTS only:**
|
|
19
|
-
- **WHY** — design decisions, trade-offs, reasons for choosing X over Y
|
|
20
|
-
- **HOW** — module behaviors, data flows, call chains, non-obvious interactions
|
|
21
|
-
- **GOTCHAS** — pitfalls, edge cases, non-intuitive behaviors
|
|
22
|
-
- **CONVENTIONS** — naming patterns, code style, unwritten rules
|
|
23
|
-
|
|
24
|
-
## Exploration Strategy
|
|
25
|
-
1. Use \`codegraph\` to trace call chains and understand module interactions
|
|
26
|
-
2. Read core modules to understand **behaviors** (not just names)
|
|
27
|
-
3. Look for complex logic, error handling patterns, unusual configs
|
|
28
|
-
4. Identify naming conventions, common abstractions
|
|
29
|
-
|
|
30
|
-
## Output Format
|
|
31
|
-
Output the skill document between these exact delimiters:
|
|
32
|
-
|
|
33
|
-
\`\`\`skill-start
|
|
34
|
-
## 架构要点
|
|
35
|
-
### 核心模块及职责
|
|
36
|
-
- **\`path/to/module\`** — 行为描述(做什么 + 怎么做)
|
|
37
|
-
...
|
|
38
|
-
|
|
39
|
-
### 关键设计决策
|
|
40
|
-
- 决策:原因
|
|
41
|
-
...
|
|
42
|
-
|
|
43
|
-
## 项目约定
|
|
44
|
-
- 约定(具体的,有例子的)
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
## 开发流程
|
|
48
|
-
- 命令 + 注意事项/坑点(命令本身快照已有,这里只写注意什么)
|
|
49
|
-
...
|
|
50
|
-
|
|
51
|
-
## 常见坑点
|
|
52
|
-
- 坑:解法
|
|
53
|
-
...
|
|
54
|
-
\`\`\`skill-end
|
|
55
|
-
|
|
56
|
-
## Rules — MUST FOLLOW
|
|
57
|
-
1. 禁止列出文件名清单或目录结构
|
|
58
|
-
2. 禁止列出依赖名和版本
|
|
59
|
-
3. 禁止写项目概述/一句话描述
|
|
60
|
-
4. 禁止只写命令本身(快照已有),只写注意事项
|
|
61
|
-
5. 每个模块描述必须包含行为(做什么 + 怎么协作),不只是名称
|
|
62
|
-
6. **总长度目标 3000-4000 字符,硬上限 4000**
|
|
63
|
-
7. 写可操作的内容,用具体路径和例子
|
|
64
|
-
8. 如果某个 section 没有实质内容,省略它
|
|
65
|
-
`;
|
|
66
|
-
/**
|
|
67
|
-
* 生成初始项目 Skill(使用子 agent 深度探索)
|
|
68
|
-
* @param existingSkill 已有的 skill 内容(如果有,子 agent 会在其基础上优化)
|
|
69
|
-
* @param signal AbortSignal
|
|
70
|
-
*/
|
|
71
|
-
export async function generateInitialSkill(existingSkill, signal) {
|
|
72
|
-
const hasExisting = !!existingSkill;
|
|
73
|
-
let prompt;
|
|
74
|
-
if (hasExisting) {
|
|
75
|
-
prompt = `以下是当前的项目 Skill 内容,请深度探索项目并优化/完善它:
|
|
76
|
-
|
|
77
|
-
\`\`\`markdown
|
|
78
|
-
${existingSkill}
|
|
79
|
-
\`\`\`
|
|
80
|
-
|
|
81
|
-
## 优化方向
|
|
82
|
-
1. 补充缺失的信息(架构要点、设计决策、调用链等)
|
|
83
|
-
2. 修正过时或不准确的内容
|
|
84
|
-
3. 让描述更具体(用实际路径和例子)
|
|
85
|
-
4. 删除冗余或空泛的描述
|
|
86
|
-
5. 如果发现新的坑点或约定,添加进去
|
|
87
|
-
|
|
88
|
-
记住:保持精简,总长度控制在 4000-5000 字符。输出完整的优化后版本。`;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
prompt = `请深度探索当前项目,生成一份项目专属 Skill 文档。
|
|
92
|
-
|
|
93
|
-
## 探索步骤
|
|
94
|
-
1. 先用 glob 扫描项目结构(**/*.ts, **/*.json 等)
|
|
95
|
-
2. 读取 package.json、README.md、tsconfig.json 等关键文件
|
|
96
|
-
3. 用 codegraph 或 grep 找到主入口、核心模块、关键接口
|
|
97
|
-
4. 理解架构模式、数据流向、调用关系
|
|
98
|
-
5. 识别命名约定、代码风格、测试策略
|
|
99
|
-
6. 找出可能的坑点(复杂配置、特殊依赖、构建陷阱)
|
|
100
|
-
|
|
101
|
-
## 输出
|
|
102
|
-
探索完成后,将完整的 skill 文档输出在 \`\`\`skill-start 和 \`\`\`skill-end 之间。
|
|
103
|
-
|
|
104
|
-
记住:内容要精简、可操作、有具体路径和例子。总长度控制在 4000-5000 字符。`;
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const result = await spawnAgent({
|
|
108
|
-
prompt,
|
|
109
|
-
systemPromptSuffix: SKILL_INIT_SUFFIX,
|
|
110
|
-
tools: ['read_file', 'glob', 'grep', 'codegraph'],
|
|
111
|
-
maxSteps: 30,
|
|
112
|
-
signal,
|
|
113
|
-
});
|
|
114
|
-
if (!result.completed) {
|
|
115
|
-
return {
|
|
116
|
-
ok: false,
|
|
117
|
-
error: '子 agent 未完成(可能中断或超时)',
|
|
118
|
-
transcript: result.transcript,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
if (!result.summary) {
|
|
122
|
-
return {
|
|
123
|
-
ok: false,
|
|
124
|
-
error: '子 agent 未返回内容',
|
|
125
|
-
transcript: result.transcript,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
// 从 summary 中提取 skill 内容
|
|
129
|
-
const skillMatch = result.summary.match(/```skill-start\n([\s\S]*?)\n```skill-end/);
|
|
130
|
-
if (!skillMatch || !skillMatch[1]) {
|
|
131
|
-
// 如果没有找到 delimiters,尝试从最后一段 markdown 代码块提取
|
|
132
|
-
const codeBlockMatch = result.summary.match(/```(?:markdown|md)?\n([\s\S]*?)\n```/);
|
|
133
|
-
if (codeBlockMatch && codeBlockMatch[1]) {
|
|
134
|
-
return {
|
|
135
|
-
ok: true,
|
|
136
|
-
content: codeBlockMatch[1].trim(),
|
|
137
|
-
transcript: result.transcript,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
// 如果连代码块都没有,直接用 summary(但去掉开头的探索日志)
|
|
141
|
-
const lines = result.summary.split('\n');
|
|
142
|
-
const skillStartIdx = lines.findIndex((l) => l.startsWith('## '));
|
|
143
|
-
if (skillStartIdx > 0) {
|
|
144
|
-
return {
|
|
145
|
-
ok: true,
|
|
146
|
-
content: lines.slice(skillStartIdx).join('\n').trim(),
|
|
147
|
-
transcript: result.transcript,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
ok: false,
|
|
152
|
-
error: '无法从子 agent 输出中提取 skill 内容',
|
|
153
|
-
transcript: result.transcript,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const content = skillMatch[1].trim();
|
|
157
|
-
// 检查长度
|
|
158
|
-
if (content.length > 6000) {
|
|
159
|
-
return {
|
|
160
|
-
ok: false,
|
|
161
|
-
error: `生成内容过长 (${content.length} 字符),请手动精简后重试`,
|
|
162
|
-
transcript: result.transcript,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
ok: true,
|
|
167
|
-
content,
|
|
168
|
-
transcript: result.transcript,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
return {
|
|
173
|
-
ok: false,
|
|
174
|
-
error: e instanceof Error ? e.message : '未知错误',
|
|
175
|
-
transcript: '',
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getSandboxRoot } from '../sandbox/root.js';
|
|
5
|
-
/** 内存缓存:当前 session 的快照(避免重复 IO) */
|
|
6
|
-
let currentSnapshot = null;
|
|
7
|
-
/** 计算 sandboxRoot 的 hash,用作目录名(避免路径特殊字符) */
|
|
8
|
-
export function hashRoot(root) {
|
|
9
|
-
return createHash('sha256').update(root).digest('hex').slice(0, 16);
|
|
10
|
-
}
|
|
11
|
-
/** 快照存储目录 */
|
|
12
|
-
function snapshotDir() {
|
|
13
|
-
const root = getSandboxRoot() ?? process.cwd();
|
|
14
|
-
return path.join(process.cwd(), '.mocode', 'projects', hashRoot(root));
|
|
15
|
-
}
|
|
16
|
-
/** 快照文件路径 */
|
|
17
|
-
function snapshotPath() {
|
|
18
|
-
return path.join(snapshotDir(), 'snapshot.md');
|
|
19
|
-
}
|
|
20
|
-
/** 从磁盘加载快照(不存在/损坏返 null) */
|
|
21
|
-
export function loadSnapshot() {
|
|
22
|
-
if (currentSnapshot)
|
|
23
|
-
return currentSnapshot;
|
|
24
|
-
const p = snapshotPath();
|
|
25
|
-
if (!existsSync(p))
|
|
26
|
-
return null;
|
|
27
|
-
try {
|
|
28
|
-
const content = readFileSync(p, 'utf8');
|
|
29
|
-
// 从 markdown 文件头部的 YAML front matter 提取元数据
|
|
30
|
-
const match = content.match(/^---\nroot: (.+)\nbuiltAt: (.+)\nversion: (\d+)\n---\n\n([\s\S]+)$/);
|
|
31
|
-
if (!match)
|
|
32
|
-
return null;
|
|
33
|
-
const snap = {
|
|
34
|
-
version: parseInt(match[3]),
|
|
35
|
-
root: match[1],
|
|
36
|
-
builtAt: match[2],
|
|
37
|
-
content: match[4],
|
|
38
|
-
};
|
|
39
|
-
currentSnapshot = snap;
|
|
40
|
-
return snap;
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/** 获取当前快照(内存缓存优先,然后磁盘) */
|
|
47
|
-
export function getSnapshot() {
|
|
48
|
-
return currentSnapshot ?? loadSnapshot();
|
|
49
|
-
}
|
|
50
|
-
export async function buildSnapshot(signal, force = false) {
|
|
51
|
-
// 检查缓存:已有快照且不强制刷新,直接返回
|
|
52
|
-
if (!force) {
|
|
53
|
-
const cached = getSnapshot();
|
|
54
|
-
if (cached)
|
|
55
|
-
return { snapshot: cached };
|
|
56
|
-
}
|
|
57
|
-
const root = getSandboxRoot() ?? process.cwd();
|
|
58
|
-
// 动态导入避免循环依赖
|
|
59
|
-
const { generateLLMSnapshot } = await import('./llm-snapshot.js');
|
|
60
|
-
const result = await generateLLMSnapshot(root, signal);
|
|
61
|
-
if (!result.ok || !result.content) {
|
|
62
|
-
return {
|
|
63
|
-
snapshot: null,
|
|
64
|
-
error: result.error || 'LLM 未返回有效结果',
|
|
65
|
-
transcript: result.transcript,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
const snap = {
|
|
69
|
-
version: 1,
|
|
70
|
-
root,
|
|
71
|
-
builtAt: new Date().toISOString(),
|
|
72
|
-
content: result.content,
|
|
73
|
-
};
|
|
74
|
-
// 落盘为 markdown 格式,带 YAML front matter
|
|
75
|
-
const dir = snapshotDir();
|
|
76
|
-
mkdirSync(dir, { recursive: true });
|
|
77
|
-
const mdContent = `---\nroot: ${snap.root}\nbuiltAt: ${snap.builtAt}\nversion: ${snap.version}\n---\n\n${snap.content}`;
|
|
78
|
-
writeFileSync(snapshotPath(), mdContent, 'utf8');
|
|
79
|
-
currentSnapshot = snap;
|
|
80
|
-
return { snapshot: snap };
|
|
81
|
-
}
|
|
82
|
-
/** 清除内存缓存(强制下次重新加载) */
|
|
83
|
-
export function clearSnapshotCache() {
|
|
84
|
-
currentSnapshot = null;
|
|
85
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { spawnAgent } from '../agent/spawn.js';
|
|
2
|
-
/**
|
|
3
|
-
* 子 agent 系统提示:让 agent 自主探索项目并生成 markdown 快照
|
|
4
|
-
*/
|
|
5
|
-
const SNAPSHOT_LLM_SUFFIX = `You are a project analysis agent. Your task is to explore this project and generate a concise markdown snapshot for an AI coding assistant.
|
|
6
|
-
|
|
7
|
-
## Your Mission
|
|
8
|
-
Explore the project using available tools (read_file, glob, grep) and generate a structured markdown snapshot that captures:
|
|
9
|
-
- WHAT exists (project description, tech stack, commands, modules, directory structure)
|
|
10
|
-
- WHERE it is (paths, locations)
|
|
11
|
-
|
|
12
|
-
Do NOT explain WHY or HOW (that's Project Skill's job).
|
|
13
|
-
|
|
14
|
-
## Exploration Strategy
|
|
15
|
-
1. Read package.json, README.md, tsconfig.json (or equivalent for other languages)
|
|
16
|
-
2. List top-level directories to identify modules
|
|
17
|
-
3. Scan src/ directory structure (depth ≤3, directories only)
|
|
18
|
-
4. Extract key commands from package.json scripts
|
|
19
|
-
5. Identify tech stack from dependencies
|
|
20
|
-
|
|
21
|
-
## Output Format
|
|
22
|
-
Output only a markdown document between these exact delimiter lines. The delimiters are not Markdown fences and must each appear on their own line:
|
|
23
|
-
|
|
24
|
-
<snapshot-md>
|
|
25
|
-
# Project Snapshot
|
|
26
|
-
|
|
27
|
-
## Description
|
|
28
|
-
(一句话描述项目,≤80字,中文)
|
|
29
|
-
|
|
30
|
-
## Tech Stack
|
|
31
|
-
(关键技术栈,逗号分隔,含版本,≤15项)
|
|
32
|
-
|
|
33
|
-
## Commands
|
|
34
|
-
| Command | Description |
|
|
35
|
-
|---------|-------------|
|
|
36
|
-
| \`npm run build\` | 构建项目 |
|
|
37
|
-
| \`npm test\` | 运行测试 |
|
|
38
|
-
| ... | ... |
|
|
39
|
-
|
|
40
|
-
## Modules
|
|
41
|
-
- **module-name** — 一句话职责描述(≤20字)
|
|
42
|
-
- **module-name** — 一句话职责描述
|
|
43
|
-
- ...
|
|
44
|
-
|
|
45
|
-
## Source Tree
|
|
46
|
-
\`\`\`
|
|
47
|
-
src/
|
|
48
|
-
agent/
|
|
49
|
-
tools/
|
|
50
|
-
ui/
|
|
51
|
-
\`\`\`
|
|
52
|
-
</snapshot-md>
|
|
53
|
-
|
|
54
|
-
## Rules
|
|
55
|
-
1. **Description**: ≤80字,中文,从 README 和 package.json description 提炼。不要"一个..."开头。
|
|
56
|
-
2. **Tech Stack**: ≤15项,逗号分隔,含版本。格式如 "TypeScript 5.x, Node.js ≥18, openai@^4"。
|
|
57
|
-
3. **Commands**: 5-8个最重要的命令,从 package.json scripts 提取。Description ≤15字。用表格格式。
|
|
58
|
-
4. **Modules**: 每个顶层模块目录一条。职责≤20字,只写 WHAT(做什么)。
|
|
59
|
-
5. **Source Tree**: 只列目录(不列文件),深度≤3,用缩进表示层级,放在代码块中。
|
|
60
|
-
6. 不要包含:设计决策、注意事项、坑点、约定 → 这些是 Skill 的职责。
|
|
61
|
-
7. 总 markdown ≤ 2500 字符。
|
|
62
|
-
8. 使用中文。
|
|
63
|
-
9. 不要在 <snapshot-md> 和 </snapshot-md> 之外输出任何内容。
|
|
64
|
-
`;
|
|
65
|
-
/**
|
|
66
|
-
* 提取子 agent 输出中的 markdown。
|
|
67
|
-
* 优先使用无歧义标签,同时兼容历史自定义围栏和标准 Markdown 围栏。
|
|
68
|
-
*/
|
|
69
|
-
function extractSnapshotMarkdown(summary) {
|
|
70
|
-
const tagged = summary.match(/<snapshot-md>[ \t]*\r?\n?([\s\S]*?)\r?\n?[ \t]*<\/snapshot-md>/i);
|
|
71
|
-
if (tagged?.[1]?.trim())
|
|
72
|
-
return tagged[1].trim();
|
|
73
|
-
const legacy = summary.match(/```snapshot-md[ \t]*\r?\n([\s\S]*?)\r?\n```snapshot-md[ \t]*(?:\r?\n|$)/i);
|
|
74
|
-
if (legacy?.[1]?.trim())
|
|
75
|
-
return legacy[1].trim();
|
|
76
|
-
const opening = summary.match(/^```(?:snapshot-md|markdown)[ \t]*\r?$/im);
|
|
77
|
-
if (opening?.index === undefined)
|
|
78
|
-
return undefined;
|
|
79
|
-
let bodyStart = opening.index + opening[0].length;
|
|
80
|
-
if (summary[bodyStart] === '\n')
|
|
81
|
-
bodyStart += 1;
|
|
82
|
-
const body = summary.slice(bodyStart);
|
|
83
|
-
const closings = [...body.matchAll(/^`{3,}[ \t]*\r?$/gm)];
|
|
84
|
-
const closing = closings.at(-1);
|
|
85
|
-
if (closing?.index === undefined)
|
|
86
|
-
return undefined;
|
|
87
|
-
let content = body.slice(0, closing.index).trim();
|
|
88
|
-
// 有些模型会把内层和外层的结束围栏连成六个反引号。
|
|
89
|
-
if (closing[0].trim().length >= 6)
|
|
90
|
-
content = `${content}\n\`\`\``;
|
|
91
|
-
return content || undefined;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* 生成 LLM 快照(markdown 格式)
|
|
95
|
-
* @param root 项目根目录
|
|
96
|
-
* @param signal AbortSignal
|
|
97
|
-
*/
|
|
98
|
-
export async function generateLLMSnapshot(root, signal) {
|
|
99
|
-
const prompt = `请探索项目 ${root},生成项目快照(markdown 格式)。
|
|
100
|
-
|
|
101
|
-
## 探索步骤
|
|
102
|
-
1. 读取 package.json(或等效配置文件)
|
|
103
|
-
2. 读取 README.md
|
|
104
|
-
3. 列出顶层目录,识别模块
|
|
105
|
-
4. 扫描 src/ 目录结构(深度≤3,只列目录)
|
|
106
|
-
5. 从 package.json scripts 提取关键命令
|
|
107
|
-
6. 从 dependencies 识别技术栈
|
|
108
|
-
|
|
109
|
-
## 输出
|
|
110
|
-
严格按照系统提示的 markdown 格式输出,不要添加额外解释。`;
|
|
111
|
-
try {
|
|
112
|
-
const result = await spawnAgent({
|
|
113
|
-
prompt,
|
|
114
|
-
systemPromptSuffix: SNAPSHOT_LLM_SUFFIX,
|
|
115
|
-
tools: ['read_file', 'glob', 'grep'], // 允许探索工具
|
|
116
|
-
maxSteps: 15, // 给足够的探索步数
|
|
117
|
-
signal,
|
|
118
|
-
});
|
|
119
|
-
if (!result.completed) {
|
|
120
|
-
return {
|
|
121
|
-
ok: false,
|
|
122
|
-
error: '子 agent 未完成(可能中断或超时)',
|
|
123
|
-
transcript: result.transcript,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
if (!result.summary) {
|
|
127
|
-
return {
|
|
128
|
-
ok: false,
|
|
129
|
-
error: '子 agent 未返回内容',
|
|
130
|
-
transcript: result.transcript,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
const content = extractSnapshotMarkdown(result.summary);
|
|
134
|
-
if (!content) {
|
|
135
|
-
return {
|
|
136
|
-
ok: false,
|
|
137
|
-
error: '无法从子 agent 输出中提取 markdown',
|
|
138
|
-
transcript: result.transcript,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
return { ok: true, content, transcript: result.transcript };
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
return {
|
|
145
|
-
ok: false,
|
|
146
|
-
error: e instanceof Error ? e.message : '未知错误',
|
|
147
|
-
transcript: '',
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
/**
|
|
4
|
-
* 静态项目文件白名单 + 扫描逻辑。
|
|
5
|
-
* 这些文件通常是项目元信息,变动频率低、体积小、跨 session 稳定。
|
|
6
|
-
*/
|
|
7
|
-
/** 静态文件白名单(文件名或模式) */
|
|
8
|
-
const STATIC_FILE_PATTERNS = [
|
|
9
|
-
// 文档
|
|
10
|
-
/^README(\.[a-z]+)?(\.[a-z]+)?$/i, // README, README.md, README.zh-CN.md
|
|
11
|
-
'CHANGELOG.md',
|
|
12
|
-
'CONTRIBUTING.md',
|
|
13
|
-
'LICENSE',
|
|
14
|
-
'LICENSE.md',
|
|
15
|
-
'LICENSE.txt',
|
|
16
|
-
// Node.js
|
|
17
|
-
'package.json',
|
|
18
|
-
'tsconfig.json',
|
|
19
|
-
'jsconfig.json',
|
|
20
|
-
// Python
|
|
21
|
-
'pyproject.toml',
|
|
22
|
-
'setup.py',
|
|
23
|
-
'setup.cfg',
|
|
24
|
-
'requirements.txt',
|
|
25
|
-
'Pipfile',
|
|
26
|
-
// Go
|
|
27
|
-
'go.mod',
|
|
28
|
-
// Rust
|
|
29
|
-
'Cargo.toml',
|
|
30
|
-
// 环境变量模板
|
|
31
|
-
'.env.example',
|
|
32
|
-
'.env.template',
|
|
33
|
-
];
|
|
34
|
-
/**
|
|
35
|
-
* 判断文件名是否匹配白名单
|
|
36
|
-
*/
|
|
37
|
-
function matchesPattern(filename) {
|
|
38
|
-
for (const p of STATIC_FILE_PATTERNS) {
|
|
39
|
-
if (typeof p === 'string') {
|
|
40
|
-
if (filename === p)
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
if (p.test(filename))
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* 扫描项目根目录,收集匹配白名单的静态文件。
|
|
52
|
-
* 只扫顶层(不递归),避免 node_modules 等大目录。
|
|
53
|
-
* @param root 项目根目录(绝对路径)
|
|
54
|
-
* @returns key = 相对路径(正斜杠),val = { mtime, content }
|
|
55
|
-
*/
|
|
56
|
-
export function scanStaticFiles(root) {
|
|
57
|
-
const result = {};
|
|
58
|
-
let items;
|
|
59
|
-
try {
|
|
60
|
-
items = readdirSync(root, { withFileTypes: true });
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
for (const item of items) {
|
|
66
|
-
if (!item.isFile())
|
|
67
|
-
continue;
|
|
68
|
-
if (!matchesPattern(item.name))
|
|
69
|
-
continue;
|
|
70
|
-
const absPath = path.join(root, item.name);
|
|
71
|
-
try {
|
|
72
|
-
const stat = statSync(absPath);
|
|
73
|
-
// 体积限制:单文件 > 64KB 跳过(避免 README 巨长拖慢快照)
|
|
74
|
-
if (stat.size > 64 * 1024)
|
|
75
|
-
continue;
|
|
76
|
-
const content = readFileSync(absPath, 'utf8');
|
|
77
|
-
result[item.name] = {
|
|
78
|
-
mtime: stat.mtimeMs,
|
|
79
|
-
content,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
// 读取失败:跳过
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
|
-
}
|
package/dist/repl/index.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type ChatMessage } from '../llm/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* 把会话历史渲染成静态文本进内容区(回滚 / 续接 / --resume 后复显上下文,仿 Claude Code):
|
|
4
|
-
* user→❯ 回显、assistant→正文(+ tool_calls 折叠成 ● 摘要行)、tool→↳ 结果预览;system 跳过。
|
|
5
|
-
* 思考段不持久(history 只存正文),故无思考折叠。渲染后续写位在末尾,紧接 enterInputMode 画输入框。
|
|
6
|
-
* 内容长于屏时 viewport 显尾(最近轮次),PgUp 可看更早——与流式态一致。
|
|
7
|
-
* user 多模态:用 textOf 取 text parts;若侧 channel messageAttachments 有原文件名则追加 chip 行
|
|
8
|
-
* (避免 base64 解码不可逆,旧 session 没侧 channel 时只显文本,文件名 fallback 到 image/* mime)。
|
|
9
|
-
*
|
|
10
|
-
* 折叠策略:遇到 assistant + tool_calls 不立即打 ● 行,而是累积到 batchEntries;
|
|
11
|
-
* 跟随的连续 tool 消息按 tool_call_id 反查填 resultSummary;遇下一个非 tool 消息(或末尾)时,
|
|
12
|
-
* 用 batch.writeSummaryOnly 出单行摘要(与实时 runAgent 同一渲染器,UI 一致)。
|
|
13
|
-
* 回放默认全折叠;用户可鼠标点击摘要行展开(由 BatchRenderer 接管,见 ui/batch.ts)。
|
|
14
|
-
*/
|
|
15
|
-
export declare function renderHistory(history: ChatMessage[]): void;
|
|
16
|
-
/**
|
|
17
|
-
* 交互式 REPL:全屏 TUI(alt screen + 固定底栏)。INPUT 态底栏=状态行+输入框(raw mode 等按键);
|
|
18
|
-
* 提交后 enterRunningMode(底栏改 dim 占位、光标回内容续写位),命令分发与 runAgent 的流式输出经
|
|
19
|
-
* contentWrite 落入内容区(滚动区域内自动滚动,底栏不动)。history 由本模块持有,在轮次间持久;
|
|
20
|
-
* agent 只读取并追加(+ 经 session/ 压缩)。每轮成功结束后自动落盘,退出后可用 --resume / /resume 续接。
|
|
21
|
-
*/
|
|
22
|
-
export declare function startRepl(initialHistory?: ChatMessage[], sessionId?: string, updateNotice?: string | null, sandboxRootOverride?: string): Promise<void>;
|
package/dist/rollback/index.d.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { ChatMessage } from '../llm/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* 回滚子系统:`/rollback` 菜单(↑/↓)选轮次 → 选中第 X 轮 = 删该轮及之后 + 预填该轮 user 输入(仿 Claude Code
|
|
4
|
-
* rewind,Enter 重新跑);撤销被删轮次的文件改动(逐个「保留/撤销」询问)。
|
|
5
|
-
*
|
|
6
|
-
* 语义:选中下标 picked(0-based,= 第 picked+1 轮)→ `planRollback(picked)` 保 1..picked(删 picked+1 轮及之后),
|
|
7
|
-
* 预填 userTexts[picked](= 第 picked+1 轮 user 输入)。**不是**保 1..picked+1——选中第 X 轮即"从第 X 轮重跑"。
|
|
8
|
-
*
|
|
9
|
-
* 撤销方案 = 落盘快照(不用 git):write_file/edit_file 执行前由 executeTool 调
|
|
10
|
-
* recordMutation,把 before 内容存入快照(按轮次 turnId 打标)。回滚到第 n 轮时,
|
|
11
|
-
* 对每条选「撤销」的路径,恢复 turnId > cutoffTurnId 的最小 turnId 快照的 before
|
|
12
|
-
* (= 所选轮末状态;before===null = 当时不存在 → 删文件)。同文件多轮改动也能
|
|
13
|
-
* 精确回到所选轮边界,不误伤更早轮次。
|
|
14
|
-
*
|
|
15
|
-
* 快照 + 轮次日志随 saveSession 落盘到 <sessionDir>/<id>.snapshots.json,/resume 读回
|
|
16
|
-
* → 重启后仍可撤销。叶子模块:运行时只依赖 config(+ node:fs/path);ChatMessage
|
|
17
|
-
* 用 import type 擦除,不拉 llm 运行时;不反向依赖任何业务。
|
|
18
|
-
*
|
|
19
|
-
* v1 偏离 / 局限:
|
|
20
|
-
* - 撤销粒度 = 路径(非逐条 tool_call)——快照只能整路径回到轮末,无法只撤一条 edit。
|
|
21
|
-
* - run_command 不撤销(shell 可任意改文件,无法跟踪)。
|
|
22
|
-
* - 跨 /resume 后若文件被外部改过,恢复会用 before 覆盖(边缘情况,不处理)。
|
|
23
|
-
* - 快照文件随大文件多次改动膨胀;/clear 重置;日后可按「turn 已不可回滚」裁剪。
|
|
24
|
-
*/
|
|
25
|
-
export interface Turn {
|
|
26
|
-
turnId: number;
|
|
27
|
-
firstLine: string;
|
|
28
|
-
}
|
|
29
|
-
export interface Snapshot {
|
|
30
|
-
turnId: number;
|
|
31
|
-
path: string;
|
|
32
|
-
before: string | null;
|
|
33
|
-
}
|
|
34
|
-
export interface FileChange {
|
|
35
|
-
path: string;
|
|
36
|
-
ops: string[];
|
|
37
|
-
snapshotAvailable: boolean;
|
|
38
|
-
}
|
|
39
|
-
export interface RollbackPlan {
|
|
40
|
-
n: number;
|
|
41
|
-
cutoffIndex: number;
|
|
42
|
-
cutoffTurnId: number;
|
|
43
|
-
changes: FileChange[];
|
|
44
|
-
}
|
|
45
|
-
/** agent:runAgent 入口调——开新轮次。firstLine 已由调用方截断到 40。 */
|
|
46
|
-
export declare function beginTurn(firstLine: string): void;
|
|
47
|
-
/** tools/registry:write_file/edit_file 执行前调——记 before 快照(在 tool.execute 之前读)。 */
|
|
48
|
-
export declare function recordMutation(p: string): void;
|
|
49
|
-
/** 列出当前可回滚的轮次(1-based 序号由调用方显示)。 */
|
|
50
|
-
export declare function listTurns(): Turn[];
|
|
51
|
-
/**
|
|
52
|
-
* 规划回滚到第 n 轮(1-based):算截断点 + 被删轮次里涉及的文件改动(按 path 去重)。
|
|
53
|
-
* 调用方据 changes 逐 path 问保留 / 撤销;snapshotAvailable=false 的项无法撤销。
|
|
54
|
-
*/
|
|
55
|
-
export declare function planRollback(n: number, history: ChatMessage[]): RollbackPlan;
|
|
56
|
-
/**
|
|
57
|
-
* 执行回滚:原地截断 history 到 cutoffIndex + 按选择恢复文件 + 裁剪 turns/snapshots。
|
|
58
|
-
* revertPaths 为选「撤销」的相对路径集合。返回删除消息数 + 撤销文件列表。
|
|
59
|
-
*/
|
|
60
|
-
export declare function applyRollback(plan: RollbackPlan, history: ChatMessage[], revertPaths: Set<string>): {
|
|
61
|
-
deletedMsgs: number;
|
|
62
|
-
revertedFiles: string[];
|
|
63
|
-
};
|
|
64
|
-
/** compact 摘要成功后调:按存活轮次数裁剪(M = 新 history 里 user 消息数)。 */
|
|
65
|
-
export declare function pruneAfterCompaction(history: ChatMessage[]): void;
|
|
66
|
-
/** /clear 调:清空全部状态。 */
|
|
67
|
-
export declare function resetState(): void;
|
|
68
|
-
/**
|
|
69
|
-
* 无 snapshots 文件时(/resume 旧会话)从 history 重建 turns(扫 user 消息,1..M,
|
|
70
|
-
* 无快照 → 那些轮次的文件改动不可撤销)。turnIdCounter = M,后续新轮次从 M+1 续。
|
|
71
|
-
*/
|
|
72
|
-
export declare function rebuildFromHistory(history: ChatMessage[]): void;
|
|
73
|
-
/** 随 saveSession 调:把 turns + snapshots 落盘(turns 为空则跳过,不写空文件)。 */
|
|
74
|
-
export declare function persistSnapshots(id: string): void;
|
|
75
|
-
/**
|
|
76
|
-
* /resume / --resume 加载会话后调:读回 turns + snapshots。成功返 true(状态已覆盖);
|
|
77
|
-
* 失败 / 无文件返 false,调用方应改调 rebuildFromHistory(history) 兜底。
|
|
78
|
-
*/
|
|
79
|
-
export declare function loadSnapshots(id: string): boolean;
|
package/dist/sandbox/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { getSandboxRoot, setSandboxRoot } from './root.js';
|
|
2
|
-
export { jailResolve, jailGlobPattern, isInsideRoot } from './jail.js';
|
|
3
|
-
export { filterEnv, isCommandDenied } from './command.js';
|
|
4
|
-
export { SANDBOX_EXEMPT_TOOLS, SANDBOX_PATH_TOOLS, enforceSandbox, } from './policy.js';
|
package/dist/sandbox/jail.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* abs 是否在沙箱根内(含 == 根)。win32 下两边 toLowerCase 再比(node 的 path.relative 不做
|
|
3
|
-
* 大小写无关比较,盘符大小写差异会误判出圈)。**不做 realpath**——用于 glob/grep 结果后置过滤;
|
|
4
|
-
* 越界软链的拦挡靠 jailResolve 的 realpath。
|
|
5
|
-
*/
|
|
6
|
-
export declare function isInsideRoot(abs: string): boolean;
|
|
7
|
-
/**
|
|
8
|
-
* 把输入路径解析为沙箱根内的绝对路径;越界(../、绝对外圈、符号链接出圈)抛错。
|
|
9
|
-
* 供 enforceSandbox 重写 args.path(读/写/改)、readDiffContext 预读旧内容用。
|
|
10
|
-
* 同步:realpathSync 仅做 fs 元数据查询(微秒级),与既有 readFileSync/existsSync 用法一致。
|
|
11
|
-
*/
|
|
12
|
-
export declare function jailResolve(input: string): string;
|
|
13
|
-
/**
|
|
14
|
-
* glob pattern 校验:拒绝对路径(平台相关的 isAbsolute)与含 `..` 段的 pattern。
|
|
15
|
-
* 返 null = 通过;返 string = 拒绝原因。形如 *.ts 的正常 pattern 放行。
|
|
16
|
-
*/
|
|
17
|
-
export declare function jailGlobPattern(pattern: string): string | null;
|
package/dist/sandbox/policy.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 豁免 cwd 牢笼的工具:
|
|
3
|
-
* - memory_*:操作 ~/.mocode 与 <cwd>/.mocode(CLAUDE.md 明确不进回滚、在外圈),本就该在外圈
|
|
4
|
-
* - use_skill:读 ~/.claude/skills、~/.mocode/skills、<cwd>/.mocode/skills,部分在外圈
|
|
5
|
-
* - web_*:跨网络,非文件路径
|
|
6
|
-
* - ask_human / switch_mode:无文件路径
|
|
7
|
-
* - codegraph:只读 cwd 下 .codegraph/ 索引(只读、不写盘)
|
|
8
|
-
* - task:派生子 agent,继承全局 root(子 agent 同进程天然共享 getSandboxRoot)
|
|
9
|
-
*/
|
|
10
|
-
export declare const SANDBOX_EXEMPT_TOOLS: Set<string>;
|
|
11
|
-
/**
|
|
12
|
-
* 路径类工具:enforceSandbox 集中把 args.path 重写为牢内绝对路径(默认安全;工具内
|
|
13
|
-
* resolve(absolutePath) 原样返回)。**新加带 path 参数的工具须列入此集**,否则不会被牢笼挡。
|
|
14
|
-
*/
|
|
15
|
-
export declare const SANDBOX_PATH_TOOLS: Set<string>;
|
|
16
|
-
/**
|
|
17
|
-
* 工具执行前的沙箱校验。返 string = 拒绝(直接喂 LLM,不执行);返 null = 放行(可能已重写 args.path)。
|
|
18
|
-
* **不抛**——契约对齐「调度器永不抛错、永远返回字符串」(tools/registry.ts executeTool)。
|
|
19
|
-
* run_command 不在此处理(cwd / env / denylist 在其站点)。
|
|
20
|
-
*/
|
|
21
|
-
export declare function enforceSandbox(name: string, args: Record<string, unknown>): string | null;
|
package/dist/sandbox/root.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/** 当前沙箱根(绝对路径)。未初始化返 null,调用方 ?? process.cwd() 兜底(防御)。 */
|
|
2
|
-
export declare function getSandboxRoot(): string | null;
|
|
3
|
-
/** 设置沙箱根。repl startRepl 启动时调一次(默认 process.cwd();--sandbox-root / SANDBOX_ROOT 可覆盖)。
|
|
4
|
-
* 返回之前的值,供未来 save/restore(子 agent worktree 隔离)用。 */
|
|
5
|
-
export declare function setSandboxRoot(root: string | null): string | null;
|