mocode-ai 0.4.8 → 0.4.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.
Files changed (123) hide show
  1. package/README.md +18 -0
  2. package/dist/agent/core.d.ts +112 -0
  3. package/dist/agent/core.js +56 -13
  4. package/dist/agent/index.d.ts +19 -0
  5. package/dist/agent/index.js +1 -1
  6. package/dist/agent/mode.d.ts +29 -0
  7. package/dist/agent/spawn.d.ts +36 -0
  8. package/dist/attachments/image.d.ts +22 -0
  9. package/dist/commands/config.d.ts +8 -0
  10. package/dist/config/file.d.ts +23 -0
  11. package/dist/config/index.d.ts +114 -0
  12. package/dist/config/index.js +61 -5
  13. package/dist/config/presets.d.ts +57 -0
  14. package/dist/context/budget.d.ts +79 -0
  15. package/dist/context/classifier.d.ts +14 -0
  16. package/dist/context/encoders/_util.d.ts +11 -0
  17. package/dist/context/encoders/code.d.ts +2 -0
  18. package/dist/context/encoders/doc.d.ts +14 -0
  19. package/dist/context/encoders/graph.d.ts +11 -0
  20. package/dist/context/encoders/index.d.ts +2 -0
  21. package/dist/context/encoders/log.d.ts +2 -0
  22. package/dist/context/encoders/memory.d.ts +2 -0
  23. package/dist/context/encoders/passthrough.d.ts +11 -0
  24. package/dist/context/encoders/search.d.ts +2 -0
  25. package/dist/context/encoders/summary.d.ts +12 -0
  26. package/dist/context/encoders/table.d.ts +2 -0
  27. package/dist/context/encoders/tree.d.ts +2 -0
  28. package/dist/context/index.d.ts +6 -0
  29. package/dist/context/lifecycle.d.ts +51 -0
  30. package/dist/context/pipeline.d.ts +9 -0
  31. package/dist/context/registry.d.ts +9 -0
  32. package/dist/context/relevance.d.ts +65 -0
  33. package/dist/context/types.d.ts +36 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/llm/capabilities.d.ts +19 -0
  36. package/dist/llm/index.d.ts +111 -0
  37. package/dist/llm/index.js +22 -1
  38. package/dist/memory/discover.d.ts +15 -0
  39. package/dist/memory/index.d.ts +13 -0
  40. package/dist/memory/reflect.d.ts +34 -0
  41. package/dist/memory/store.d.ts +124 -0
  42. package/dist/permissions/index.js +138 -0
  43. package/dist/pet/bridge.d.ts +73 -0
  44. package/dist/pet/protocol.d.ts +83 -0
  45. package/dist/pet/state.d.ts +28 -0
  46. package/dist/plan/active.d.ts +23 -0
  47. package/dist/plan/index.d.ts +13 -0
  48. package/dist/plan/state.d.ts +2 -0
  49. package/dist/plan/store.d.ts +78 -0
  50. package/dist/project-skill/index.js +180 -0
  51. package/dist/project-skill/initializer.js +178 -0
  52. package/dist/project-snapshot/index.js +85 -0
  53. package/dist/project-snapshot/llm-snapshot.js +127 -0
  54. package/dist/project-snapshot/static-files.js +87 -0
  55. package/dist/repl/index.d.ts +22 -0
  56. package/dist/repl/index.js +268 -2
  57. package/dist/rollback/index.d.ts +79 -0
  58. package/dist/sandbox/command.d.ts +3 -0
  59. package/dist/sandbox/index.d.ts +4 -0
  60. package/dist/sandbox/jail.d.ts +17 -0
  61. package/dist/sandbox/policy.d.ts +21 -0
  62. package/dist/sandbox/root.d.ts +5 -0
  63. package/dist/session/compact.d.ts +92 -0
  64. package/dist/session/drop.d.ts +19 -0
  65. package/dist/session/index.d.ts +13 -0
  66. package/dist/session/persist.d.ts +30 -0
  67. package/dist/session/scheduler.d.ts +53 -0
  68. package/dist/skills/discover.d.ts +33 -0
  69. package/dist/skills/index.d.ts +13 -0
  70. package/dist/tools/builtins/ask-human.d.ts +9 -0
  71. package/dist/tools/builtins/codegraph.d.ts +2 -0
  72. package/dist/tools/builtins/codegraph.js +17 -4
  73. package/dist/tools/builtins/drop-context.d.ts +18 -0
  74. package/dist/tools/builtins/edit-file.d.ts +2 -0
  75. package/dist/tools/builtins/edit-file.js +1 -0
  76. package/dist/tools/builtins/glob.d.ts +2 -0
  77. package/dist/tools/builtins/grep.d.ts +2 -0
  78. package/dist/tools/builtins/index.d.ts +2 -0
  79. package/dist/tools/builtins/index.js +7 -0
  80. package/dist/tools/builtins/memory-forget.d.ts +2 -0
  81. package/dist/tools/builtins/memory-forget.js +1 -0
  82. package/dist/tools/builtins/memory-list.d.ts +2 -0
  83. package/dist/tools/builtins/memory-save.d.ts +2 -0
  84. package/dist/tools/builtins/memory-save.js +1 -0
  85. package/dist/tools/builtins/memory-search.d.ts +2 -0
  86. package/dist/tools/builtins/memory-update.d.ts +2 -0
  87. package/dist/tools/builtins/memory-update.js +1 -0
  88. package/dist/tools/builtins/project-skill-update.js +104 -0
  89. package/dist/tools/builtins/read-file.d.ts +2 -0
  90. package/dist/tools/builtins/run-command.d.ts +2 -0
  91. package/dist/tools/builtins/run-command.js +1 -0
  92. package/dist/tools/builtins/switch-mode.d.ts +2 -0
  93. package/dist/tools/builtins/task.d.ts +2 -0
  94. package/dist/tools/builtins/task.js +1 -0
  95. package/dist/tools/builtins/todolist.d.ts +2 -0
  96. package/dist/tools/builtins/todolist.js +124 -8
  97. package/dist/tools/builtins/use-skill.d.ts +2 -0
  98. package/dist/tools/builtins/web-fetch.d.ts +2 -0
  99. package/dist/tools/builtins/web-search.d.ts +2 -0
  100. package/dist/tools/builtins/web-search.js +12 -1
  101. package/dist/tools/builtins/write-file.d.ts +2 -0
  102. package/dist/tools/builtins/write-file.js +1 -0
  103. package/dist/tools/constants.d.ts +52 -0
  104. package/dist/tools/registry.d.ts +18 -0
  105. package/dist/tools/types.d.ts +42 -0
  106. package/dist/ui/batch.d.ts +67 -0
  107. package/dist/ui/batch.js +19 -2
  108. package/dist/ui/clipboard.d.ts +9 -0
  109. package/dist/ui/content.d.ts +55 -0
  110. package/dist/ui/content.js +68 -3
  111. package/dist/ui/diff.d.ts +34 -0
  112. package/dist/ui/diff.js +15 -24
  113. package/dist/ui/intervention.d.ts +46 -0
  114. package/dist/ui/layout.d.ts +225 -0
  115. package/dist/ui/layout.js +67 -11
  116. package/dist/ui/markdown.d.ts +46 -0
  117. package/dist/ui/mouse.d.ts +29 -0
  118. package/dist/ui/prompt.d.ts +61 -0
  119. package/dist/ui/render.d.ts +60 -0
  120. package/dist/ui/spinner.d.ts +26 -0
  121. package/dist/ui/theme.d.ts +47 -0
  122. package/dist/updater/index.d.ts +22 -0
  123. package/package.json +1 -1
@@ -0,0 +1,4 @@
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';
@@ -0,0 +1,17 @@
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;
@@ -0,0 +1,21 @@
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;
@@ -0,0 +1,5 @@
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;
@@ -0,0 +1,92 @@
1
+ import { type ChatMessage, type ChatUsage } from '../llm/index.js';
2
+ /**
3
+ * 上下文压缩子系统(参考 Claude Code 的 auto-compact):
4
+ * 三层 —— ① push-time 单条上限(见 capToolResultForHistory,在 agent push 时调用)
5
+ * ② 微压缩:旧工具结果原地截短(保 tool_call_id,无 LLM 调用)
6
+ * ③ 摘要:旧的非工具消息由一次 chat() 压成一条 role:'system' 摘要(index 1)
7
+ *
8
+ * 不变量:
9
+ * - 原地修改:用 history.length=0; push(...) 重建,repl 持有同一引用。
10
+ * - tool_call_id 配对:按完整 group(assistant+其后连续 tool)切,永不劈开;
11
+ * 微压缩只改 .content 不删消息。
12
+ * - history[0] 永远是当前 systemPrompt;摘要插 index 1。
13
+ */
14
+ export interface CompactOptions {
15
+ window: number;
16
+ threshold: number;
17
+ /** 手动 /compact 的聚焦指令,会拼进摘要 prompt。 */
18
+ focus?: string;
19
+ /** 可注入的摘要器(测试用);缺省调 chat()。 */
20
+ summarize?: (older: ChatMessage[], focus?: string) => Promise<string | null>;
21
+ /** 手动触发(repl /compact)开关:强制走 microcompact/summarize,绕过 autoCompact 阈。
22
+ * 设为 true 后,即便无 ROI 触发(history 不超/totalOver=false)也压——对齐用户拍板的"真·强制"。
23
+ * force:在 manual 基础上再绕过「oldGroups 空 → 直接 noop」的硬边界,允许降 keepBudget 强压。
24
+ * 默认 manual=false / force=false(自动路径行为完全不变)。 */
25
+ manual?: boolean;
26
+ force?: boolean;
27
+ }
28
+ export interface CompactResult {
29
+ compacted: boolean;
30
+ summarized: boolean;
31
+ estimateBefore: number;
32
+ estimateAfter: number;
33
+ reason: 'noop-empty' | 'noop-protected' | 'noop-ml-only' | 'noop-shrunk-too-large' | 'noop-noold-noop' | 'microcompact' | 'summarize';
34
+ /** 调试字段:保护区占比(0-1),供 /compact 显示"为什么没压"。force 时降 keepBudget 后可能更小。 */
35
+ protectedRatio?: number;
36
+ /** 调试字段:旧区可压组数,供 UI 显示。 */
37
+ oldGroupCount?: number;
38
+ }
39
+ /** 跨模块共享的上下文状态:agent 写 lastUsage,compact 写 lastEstimate,repl 的 /context 读。
40
+ * scheduler.ts 写最近一次调度日志(可选,repl 可读不到时 no-op)。 */
41
+ export declare const contextState: {
42
+ lastUsage?: ChatUsage;
43
+ lastEstimate: number;
44
+ schedulerLog?: import('./scheduler.js').SchedulerRunLog;
45
+ };
46
+ /** 中截:text 太长时保 head + 标记 + tail,总长 ≤ max。 */
47
+ export declare function truncateMid(text: string, max: number): string;
48
+ /**
49
+ * push-time 第一层:工具结果进 history 前裁到 MAX_HISTORY_RESULT。
50
+ * 显示层(summarizeToolResult)仍用原 output,不受影响。
51
+ */
52
+ export declare function capToolResultForHistory(name: string, output: string): string;
53
+ /**
54
+ * 把多模态 content 拍平成纯文本(供摘要 transcript 用):text parts 拼接;image_url parts
55
+ * 替换为 `[图片已剥离: <mime>]` stub,避免 base64 进摘要 prompt(LLM 看到也无意义,反而撑爆 token)。
56
+ * 其它情况(string / 其它形状)原样返回。
57
+ */
58
+ export declare function stripImagesForSummary(m: ChatMessage): ChatMessage;
59
+ /**
60
+ * 压缩 history(原地)。手动 /compact 与自动 maybeCompact 都走这里。
61
+ * 不检查阈值——调用方(maybeCompact)决定是否调;/compact 直接调以强制压缩。
62
+ */
63
+ export declare function compactHistory(history: ChatMessage[], opts: CompactOptions): Promise<CompactResult>;
64
+ /**
65
+ * 自动压缩门槛:agent 每步调 chat() 前调用。
66
+ * 用全量启发式估算(始终可用、安全侧、无 stale-usage 问题);超阈则压缩。
67
+ *
68
+ * 升级到 Budget Scheduler:可传 `report: BudgetReport`。传了就**按 ROI 调度**:
69
+ * - 只有当 `report.layers.history.overBudget` 或 `report.totalOver` 时才压;
70
+ * - 冷工具超(Cold Tool ROI 最低)→ 不压 history,留给调度器的 cold tools 路径处理。
71
+ * 这样 cold tools 路径(L1 中截 / L2 relevance / L3 age stub)能先动,history
72
+ * 摘要(最贵)只在 cold tools 解不开时才触发。
73
+ *
74
+ * 不传 report 时退化为原行为:仅看总占用是否超 `compactThreshold * window`——
75
+ * 兼容老调用方(子 agent / 测试直接调时),零行为变化。
76
+ *
77
+ * manual 选项(repl /compact 用):true 时旁路 autoCompact 开关与 ROI 阈,
78
+ * 强制走 compactHistory(manual/force 参数透传)。返 CompactResult 给 caller 文案展示。
79
+ * 默认 manual=false 自动路径完全不变。
80
+ */
81
+ export declare function maybeCompact(history: ChatMessage[], report?: {
82
+ layers: {
83
+ history: {
84
+ overBudget: boolean;
85
+ };
86
+ };
87
+ totalOver: boolean;
88
+ }, manualOpts?: {
89
+ manual?: boolean;
90
+ force?: boolean;
91
+ focus?: string;
92
+ }): Promise<CompactResult | void>;
@@ -0,0 +1,19 @@
1
+ import type { ChatMessage } from '../llm/index.js';
2
+ import { estimateTokens } from '../llm/index.js';
3
+ import type { DropContextFilter, DropContextResult } from '../tools/types.js';
4
+ /**
5
+ * 剔除历史里命中的旧 tool 结果(原地修改 history)。
6
+ *
7
+ * 筛选(各维度 AND 组合):
8
+ * - toolNames:只剔除这些工具名的结果(空 = 不限)
9
+ * - contains:只剔除内容包含所有这些词(AND、大小写不敏感)的结果(空 = 不限)
10
+ *
11
+ * 保护:history[0](system)+ 当前轮(最后一个 user 及其之后)永不剔除。
12
+ * 已是存根的 tool 消息(含「已剔除」标记)不重复剔除(幂等)。
13
+ *
14
+ * 永不抛错;无匹配返 dropped=0。
15
+ */
16
+ export declare function dropContextFromHistory(history: ChatMessage[], filter: DropContextFilter): DropContextResult;
17
+ /** 给 drop_context 工具结果格式化人类可读摘要(回灌给 agent)。 */
18
+ export declare function formatDropResult(r: DropContextResult): string;
19
+ export { estimateTokens };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * session/ 入口:上下文压缩 + 会话落盘。
3
+ * - compact.ts:三层压缩(push-time 上限 / 微压缩 / 摘要)+ 自动门槛
4
+ * - persist.ts:history 序列化到磁盘 + --resume / /resume
5
+ * 依赖方向:session → {llm(摘要复用 chat), config, ui};llm 不反向依赖 session。
6
+ */
7
+ export { compactHistory, maybeCompact, capToolResultForHistory, truncateMid, contextState, } from './compact.js';
8
+ export type { CompactOptions, CompactResult } from './compact.js';
9
+ export { runScheduler, manualCompact, createBudgetScheduler, } from './scheduler.js';
10
+ export type { BudgetScheduler, SchedulerRunLog } from './scheduler.js';
11
+ export { dropContextFromHistory, formatDropResult, } from './drop.js';
12
+ export { newSessionId, saveSession, loadSession, listSessions, sessionDir, } from './persist.js';
13
+ export type { SessionMeta, SessionRecord } from './persist.js';
@@ -0,0 +1,30 @@
1
+ import type { ChatMessage } from '../llm/index.js';
2
+ /**
3
+ * 会话落盘:把 history 序列化到 <cwd>/.mocode/sessions/<id>.json,支持 --resume / /resume。
4
+ * session = 「这次对话说了啥」;与 memory/(跨会话长期事实)区分。
5
+ * 同步 fs(小文件,匹配 config 的同步风格);任何读/解析失败都返 null,不抛。
6
+ */
7
+ export interface SessionMeta {
8
+ id: string;
9
+ createdAt: string;
10
+ model: string;
11
+ firstUser: string;
12
+ }
13
+ export interface SessionRecord extends SessionMeta {
14
+ history: ChatMessage[];
15
+ }
16
+ /** 会话目录(确保存在)。 */
17
+ export declare function sessionDir(): string;
18
+ /** 新会话 id:YYYYMMDD-HHmmss(运行时 Date 可用)。 */
19
+ export declare function newSessionId(): string;
20
+ /** 保存会话到磁盘(history.length<=1 时跳过写盘,只返 meta)。 */
21
+ export declare function saveSession(history: ChatMessage[], id: string): SessionMeta;
22
+ /** 加载会话;不存在 / 损坏返 null(不抛)。 */
23
+ export declare function loadSession(id: string): SessionRecord | null;
24
+ /** 列出最近会话,按 createdAt 降序。损坏文件跳过。
25
+ * - limit?: 仅返回前 N 条。会话文件名是 YYYYMMDD-HHmmss.json,字典序=时间序;
26
+ * 先按文件名降序取前 N,再只解析这 N 个文件(history 大字段全部跳过不读),避免
27
+ * /resume 在 sessions 目录堆了几百个文件时 readdirSync + 全量 JSON.parse 慢。
28
+ * - 不传 limit 时读全部(向后兼容,供裸 --resume 列全表用)。
29
+ */
30
+ export declare function listSessions(limit?: number): SessionMeta[];
@@ -0,0 +1,53 @@
1
+ import { evaluateBudget, scheduleActions, formatReport, type BudgetReport, type ScheduleAction } from '../context/budget.js';
2
+ import type { ChatMessage } from '../llm/index.js';
3
+ /** 一次调度的执行日志(供 /context 命令显示与调试)。 */
4
+ export interface SchedulerRunLog {
5
+ step: number;
6
+ report: BudgetReport;
7
+ actions: ScheduleAction[];
8
+ /** 实际触发的闸('compact_history' 调过 maybeCompact → true;其它已在 push-time 自动跑)。 */
9
+ compactHistoryCalled: boolean;
10
+ ts: number;
11
+ }
12
+ /** Scheduler 实例状态:压一份最近日志,方便 /context 看到上一次决策。 */
13
+ export interface BudgetScheduler {
14
+ observePush: (history: ChatMessage[], idx: number) => void;
15
+ runStep: (history: ChatMessage[], step: number) => Promise<void>;
16
+ /** 暴露最近一次决策(供 /context)。 */
17
+ lastRunLog: SchedulerRunLog | null;
18
+ }
19
+ /** compact_history action 的执行明细(供 repl 文案展示"为什么没压")。 */
20
+ export interface CompactHistoryDetail {
21
+ reason: 'microcompact' | 'summarize' | 'noop-empty' | 'noop-protected' | 'noop-ml-only' | 'noop-shrunk-too-large' | 'noop-noold-noop';
22
+ estimateBefore: number;
23
+ estimateAfter: number;
24
+ /** 保护区占比 0-1(系统 + 当前轮 / 总 history)。 */
25
+ protectedRatio?: number;
26
+ /** 旧区可压组数。 */
27
+ oldGroupCount?: number;
28
+ /** focus 透传。 */
29
+ focus?: string;
30
+ }
31
+ /** 创建 runAgentCore 闭包持有的 scheduler(每次 agent 启动一个新实例)。
32
+ * observePush 当前只是占位:真正 L1/L2/L3 已由 cap / pruner / lifecycle 在 push 时跑;
33
+ * 保留接口为后续「调度器注入 hotBoundary 给 lifecycle」演进留接缝。 */
34
+ export declare function createBudgetScheduler(): BudgetScheduler;
35
+ /** 便捷:agent/core.ts 不需要每次 createBudgetScheduler,直接 runScheduler(history, step)。 */
36
+ export declare function runScheduler(history: ChatMessage[], step: number): Promise<void>;
37
+ /** 手动 /compact 入口(repl):与自动路径完全一致——五区 ROI 调度,但 history 摘要强制执行。
38
+ * 即便 layers.history.overBudget=false 或 totalOver=false,manual 仍产 compact_history action
39
+ * 把 focus 透传给 LLM 摘要 prompt。其它 ROI 决策(cold tools / cap hot / warn)按 scheduleActions 走。
40
+ *
41
+ * 关系:runScheduler 是「自动触发」,manualCompact 是「用户显式触发」,二者共享 scheduleActions。
42
+ *
43
+ * force=true:即便 oldGroups 空(history 全在保护区)也强行把早期消息降级压一次。
44
+ * 适合"history 太长,自动阈值从未触发,但用户想强制压"的场景。
45
+ *
46
+ * 退化:config.contextBudget === false 时直接调 compactHistory(history, { focus }),与改造前等价。
47
+ * 返回 SchedulerRunLog + compactDetail 字段,供 repl 文案展示"为什么没压"。 */
48
+ export declare function manualCompact(history: ChatMessage[], focus?: string, opts?: {
49
+ force?: boolean;
50
+ }): Promise<SchedulerRunLog & {
51
+ compactDetail?: CompactHistoryDetail;
52
+ }>;
53
+ export { evaluateBudget, scheduleActions, formatReport };
@@ -0,0 +1,33 @@
1
+ export interface Skill {
2
+ name: string;
3
+ description: string;
4
+ version?: string;
5
+ license?: string;
6
+ dir: string;
7
+ skillMdPath: string;
8
+ }
9
+ /**
10
+ * 解析 skill 目录列表。
11
+ * env SKILLS_DIRS 设则覆盖默认(用 path.delimiter 切分,与 PATH 同语义:
12
+ * win32 用 ';',POSIX 用 ':',规避盘符冒号问题);未设则默认三目录,
13
+ * 按优先级升序(低→高):~/.claude/skills → ~/.mocode/skills → <cwd>/.mocode/skills。
14
+ */
15
+ export declare function resolveSkillsDirs(): string[];
16
+ /**
17
+ * 极简 YAML frontmatter 解析(不引入 yaml 依赖)。
18
+ * 仅支持单行键值:按首个冒号切 key/value,trim,去首尾配对引号。
19
+ * 不支持块标量(| / >)与多行值——v1 限制(skill 的 description 实际多为单行)。
20
+ *
21
+ * 返回 { meta, body }:无 frontmatter 时 meta 为空、body 为原文。
22
+ */
23
+ export declare function parseFrontmatter(content: string): {
24
+ meta: Record<string, string>;
25
+ body: string;
26
+ };
27
+ /**
28
+ * 扫描所有 skill 目录,解析 frontmatter,按目录优先级去重。
29
+ * 同步 fs + 全程静默容错(目录不存在 / 读失败 / 解析失败 → 跳过,不抛),
30
+ * 风格对齐 src/session/persist.ts。按 resolveSkillsDirs 升序遍历,
31
+ * Map.set 后设覆盖先设 → 项目级优先。
32
+ */
33
+ export declare function discoverSkills(): Skill[];
@@ -0,0 +1,13 @@
1
+ import { type Skill } from './discover.js';
2
+ export type { Skill };
3
+ /** 已发现的 skill 列表(懒加载,首次调用触发扫描;启动期 repl 调一次)。 */
4
+ export declare function listSkills(): Skill[];
5
+ /**
6
+ * 读取某 skill 的 SKILL.md 正文(去掉 frontmatter)。
7
+ * 纯函数:找不到 / 读失败返 null(错误字符串交给调用方工具层生成)。
8
+ */
9
+ export declare function getSkillBody(name: string): string | null;
10
+ /** 拼进系统提示的 skill 段;无 skill 返空串(零行为变化)。 */
11
+ export declare function buildSkillsSection(): string;
12
+ /** base 系统提示 + skill 段;无 skill 时 === base。 */
13
+ export declare function effectiveSystemPrompt(base: string): string;
@@ -0,0 +1,9 @@
1
+ import type { Tool } from '../types.js';
2
+ /** 单个选项:label 是选项本身,detail 是选它意味着什么/取舍(可选)。 */
3
+ export interface ChoiceOption {
4
+ label: string;
5
+ detail?: string;
6
+ }
7
+ /** 公开以便 check-ask-human-options.ts 单元测试。 */
8
+ export declare function coerceOptions(raw: unknown): ChoiceOption[];
9
+ export declare const askHumanTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const codegraphTool: Tool;
@@ -10,8 +10,9 @@ import { MAX_OUTPUT } from '../constants.js';
10
10
  function hasCodegraphIndex() {
11
11
  return existsSync(path.join(process.cwd(), '.codegraph'));
12
12
  }
13
- /** 跑 codegraph CLI 子进程,返回合并 stdout+stderr(带退出码),超时 60s。 */
14
- function runCodegraph(args) {
13
+ /** 跑 codegraph CLI 子进程,返回合并 stdout+stderr(带退出码),超时 60s。
14
+ * signal 透传(用户 Ctrl+C)→ 即时杀子进程,不等 60s 超时。 */
15
+ function runCodegraph(args, signal) {
15
16
  return new Promise((done) => {
16
17
  const isWin = process.platform === 'win32';
17
18
  // win32 必须走 cmd.exe /c:Node 自 CVE-2024-27980 修复后,shell:false 直接 spawn
@@ -26,6 +27,7 @@ function runCodegraph(args) {
26
27
  return;
27
28
  finished = true;
28
29
  clearTimeout(timer);
30
+ signal?.removeEventListener('abort', onAbort);
29
31
  done(s);
30
32
  };
31
33
  const onChunk = (chunk) => {
@@ -46,6 +48,17 @@ function runCodegraph(args) {
46
48
  child.kill();
47
49
  finish(`[超时,已终止]\n${out.trim()}`);
48
50
  }, 60000);
51
+ // abort(用户 Ctrl+C,经 executeTool ctx.signal 透传)→ 杀子进程 + 返[已中断]
52
+ const onAbort = () => {
53
+ child.kill();
54
+ finish(`[已中断]\n${out.trim()}`);
55
+ };
56
+ if (signal) {
57
+ if (signal.aborted)
58
+ onAbort();
59
+ else
60
+ signal.addEventListener('abort', onAbort, { once: true });
61
+ }
49
62
  });
50
63
  }
51
64
  export const codegraphTool = {
@@ -84,7 +97,7 @@ export const codegraphTool = {
84
97
  },
85
98
  required: ['action', 'query'],
86
99
  },
87
- async execute(args) {
100
+ async execute(args, ctx) {
88
101
  if (!hasCodegraphIndex()) {
89
102
  return ('当前目录无 .codegraph/ 索引。codegraph 工具不可用。\n' +
90
103
  '建索引:运行 `codegraph init`(需要 codegraph CLI:`npm i -g @colbymchenry/codegraph`)。\n' +
@@ -110,6 +123,6 @@ export const codegraphTool = {
110
123
  else {
111
124
  return `错误:未知 action "${action}",可选 explore 或 node。`;
112
125
  }
113
- return runCodegraph(cliArgs);
126
+ return runCodegraph(cliArgs, ctx?.signal);
114
127
  },
115
128
  };
@@ -0,0 +1,18 @@
1
+ import type { Tool } from '../types.js';
2
+ /**
3
+ * 运行中上下文剔除工具:agent 检索到大量无关信息后,主动把历史里无关的 tool 结果替换为存根,
4
+ * 释放上下文空间(保 tool_call_id 配对不变量,只改 content)。
5
+ *
6
+ * 与 compact 的区别:compact 是阈值触发的整体压缩(微截 + 摘要);drop_context 是 agent 主动、
7
+ * 精准剔除"已判定无关"的具体 tool 结果。
8
+ *
9
+ * 保护:history[0](system)与当前轮(最后一个 user 及其之后)永不剔除——agent 还在用。
10
+ * 已是存根的不重复剔除(幂等)。永不抛错。
11
+ *
12
+ * 筛选(各维度 AND 组合,全部可选;不传 = 剔除所有可剔除的旧 tool 结果):
13
+ * - toolNames:只剔除这些工具名的结果(如 ["grep","read_file"])
14
+ * - contains:只剔除内容包含所有这些词(AND、大小写不敏感)的结果
15
+ *
16
+ * plan 模式不禁用:纯上下文管理,无文件 / 命令副作用。
17
+ */
18
+ export declare const dropContextTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const editFileTool: Tool;
@@ -4,6 +4,7 @@ import { resolve } from 'node:path';
4
4
  export const editFileTool = {
5
5
  name: 'edit_file',
6
6
  description: 'Replace a string in a file. old_string must occur exactly once and match exactly (including indentation/newlines). Use write_file for new files.',
7
+ risk: 'confirm',
7
8
  parameters: {
8
9
  type: 'object',
9
10
  properties: {
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const globTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const grepTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const builtinTools: Tool[];
@@ -18,6 +18,7 @@ import { memoryUpdateTool } from './memory-update.js';
18
18
  import { memoryForgetTool } from './memory-forget.js';
19
19
  import { taskTool } from './task.js';
20
20
  import { todolistTool } from './todolist.js';
21
+ import { projectSkillUpdateTool } from './project-skill-update.js';
21
22
  /**
22
23
  * 所有内置工具,按注册顺序排列。
23
24
  * 加新工具:在本目录新建 `xxx.ts` 导出一个 Tool,再在下面数组里加一行。无需改 agent / llm。
@@ -41,6 +42,11 @@ const _memoryTools = _memoryEnabledAtBoot
41
42
  memoryForgetTool,
42
43
  ]
43
44
  : [];
45
+ /** 项目专属 Skill 工具:仅在 MOCODE_PROJECT_SKILL=true 时注册,与 memory 同模式。 */
46
+ const _projectSkillEnabledAtBoot = process.env.MOCODE_PROJECT_SKILL === 'true';
47
+ const _projectSkillTools = _projectSkillEnabledAtBoot
48
+ ? [projectSkillUpdateTool]
49
+ : [];
44
50
  export const builtinTools = [
45
51
  readFileTool,
46
52
  writeFileTool,
@@ -56,6 +62,7 @@ export const builtinTools = [
56
62
  switchModeTool, // plan↔auto 自切(两模式都可见,不进 PLAN_DISABLED_TOOLS;副作用控制工具→串行分支)
57
63
  dropContextTool, // 运行中剔除无关 tool 结果(上下文管理,无副作用;两模式都可见,串行分支)
58
64
  ..._memoryTools,
65
+ ..._projectSkillTools,
59
66
  taskTool, // 派生子 agent(独立 history + 可受限工具集);plan 模式禁用(见 PLAN_DISABLED_TOOLS)
60
67
  todolistTool, // 工作记事本(plan 文件:复杂任务 checklist,落盘抗压缩);plan 模式可用(便于「先 plan 再 auto」时落地执行清单)
61
68
  ];
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const memoryForgetTool: Tool;
@@ -4,6 +4,7 @@ import { forgetEntry } from '../../memory/store.js';
4
4
  export const memoryForgetTool = {
5
5
  name: 'memory_forget',
6
6
  description: 'Forget a memory entry: default archive (hidden, revivable via memory_update); mode=delete hard-deletes. Pinned entries can\'t be deleted (unpin first).',
7
+ risk: 'confirm',
7
8
  parameters: {
8
9
  type: 'object',
9
10
  properties: {
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const memoryListTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const memorySaveTool: Tool;
@@ -5,6 +5,7 @@ import { saveEntry } from '../../memory/store.js';
5
5
  export const memorySaveTool = {
6
6
  name: 'memory_save',
7
7
  description: 'Save a cross-session long-term memory entry. Store only non-obvious, useful facts/decisions/pitfalls. Title enters the startup index; retrieve body via memory_search.',
8
+ risk: 'confirm',
8
9
  parameters: {
9
10
  type: 'object',
10
11
  properties: {
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const memorySearchTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const memoryUpdateTool: Tool;
@@ -4,6 +4,7 @@ import { updateEntry } from '../../memory/store.js';
4
4
  export const memoryUpdateTool = {
5
5
  name: 'memory_update',
6
6
  description: 'Update a memory entry in place (id unchanged). Use when facts changed / correcting outdated info / toggling pinned. Get id from memory_list or memory_search.',
7
+ risk: 'confirm',
7
8
  parameters: {
8
9
  type: 'object',
9
10
  properties: {
@@ -0,0 +1,104 @@
1
+ import { readProjectSkill, writeProjectSkillWithCompression, appendProjectSkillWithCompression, } from '../../project-skill/index.js';
2
+ /**
3
+ * 项目专属 Skill 更新工具。
4
+ * 支持三种操作:
5
+ * - read: 读取当前 skill 内容
6
+ * - write: 全量覆盖(适合重构整个 skill)
7
+ * - append: 追加到末尾(适合发现新坑点/约定时增量更新)
8
+ *
9
+ * 使用场景:
10
+ * - 开发过程中发现项目特有的架构模式、命名约定
11
+ * - 踩坑后记录避坑指南
12
+ * - 总结项目关键决策和设计理由
13
+ * - 随着项目开发,skill 越来越了解项目,开发效率越来越高
14
+ */
15
+ export const projectSkillUpdateTool = {
16
+ name: 'project_skill_update',
17
+ description: 'Update the project-specific skill file (.mocode/project-skill.md). ' +
18
+ 'Use this to record architectural patterns, naming conventions, common pitfalls, ' +
19
+ 'and key decisions specific to this project. The skill persists across sessions and ' +
20
+ 'is injected into the system prompt when MOCODE_PROJECT_SKILL=true. ' +
21
+ 'Actions: "read" (view current content), "write" (replace all), "append" (add to end). ' +
22
+ '\n\n' +
23
+ '## Skill 维护指南\n' +
24
+ 'Skill 专注 Snapshot 无法自动提供的洞察知识(why/how/gotchas)。Snapshot 已提供文件结构和静态文件内容——Skill 不要重复这些信息。\n' +
25
+ '\n' +
26
+ '**Skill 该写什么(Snapshot 不能替代的)**:\n' +
27
+ '- 设计决策的 why(为什么这样设计、取舍是什么)\n' +
28
+ '- 模块行为和职责描述(怎么工作、数据流、调用链)\n' +
29
+ '- 常见坑点和解决方案(踩过的坑、非直觉行为、边界条件)\n' +
30
+ '- 项目约定(命名规范、代码风格、测试策略)\n' +
31
+ '- 开发流程(构建/测试/部署命令及注意事项)\n' +
32
+ '- 关键 API 的使用限制和特殊行为\n' +
33
+ '\n' +
34
+ '**Skill 不该写什么(交给 Snapshot)**:\n' +
35
+ '- 文件列表和目录结构(Snapshot 自动扫描 src 树)\n' +
36
+ '- 静态文件内容摘要(依赖、编译器选项等 → package.json / tsconfig.json)\n' +
37
+ '\n' +
38
+ '**何时更新**:发现新架构模式、踩坑后总结、学到新约定、完成重要重构、用户纠正理解时。\n' +
39
+ '\n' +
40
+ '**注意事项**:\n' +
41
+ '- 保持精简,硬上限 4000 字符(约 1000 token)\n' +
42
+ '- 写可操作的内容,避免空泛描述\n' +
43
+ '- 用具体路径和例子(不要写"有多个模块",要写"src/agent 负责 agent 循环")\n' +
44
+ '- 定期整理,删除过时信息\n' +
45
+ '- 更新前建议先 `read` 看一下现有内容,避免重复\n' +
46
+ '\n' +
47
+ '**自动压缩**:内容超限时会自动调用 LLM 压缩(最多 3 次),无需手动精简。',
48
+ risk: 'safe',
49
+ parameters: {
50
+ type: 'object',
51
+ properties: {
52
+ action: {
53
+ type: 'string',
54
+ enum: ['read', 'write', 'append'],
55
+ description: 'Action to perform: "read" to view current content, "write" to replace all, "append" to add to end',
56
+ },
57
+ content: {
58
+ type: 'string',
59
+ description: 'Content to write or append (required for "write" and "append" actions, ignored for "read")',
60
+ },
61
+ },
62
+ required: ['action'],
63
+ },
64
+ execute: async (args, ctx) => {
65
+ const action = String(args.action ?? '').trim();
66
+ const content = String(args.content ?? '');
67
+ const signal = ctx?.signal;
68
+ switch (action) {
69
+ case 'read': {
70
+ const current = readProjectSkill();
71
+ if (!current) {
72
+ return 'Project skill is empty or does not exist yet.';
73
+ }
74
+ return `Current project skill:\n\n${current}`;
75
+ }
76
+ case 'write': {
77
+ if (!content.trim()) {
78
+ return 'Error: "write" action requires non-empty content parameter.';
79
+ }
80
+ const result = await writeProjectSkillWithCompression(content, 3, signal);
81
+ if (!result.ok) {
82
+ return `Error: ${result.error}`;
83
+ }
84
+ return result.compressed
85
+ ? 'Project skill updated successfully (full replacement, auto-compressed to fit limit).'
86
+ : 'Project skill updated successfully (full replacement).';
87
+ }
88
+ case 'append': {
89
+ if (!content.trim()) {
90
+ return 'Error: "append" action requires non-empty content parameter.';
91
+ }
92
+ const result = await appendProjectSkillWithCompression(content, 3, signal);
93
+ if (!result.ok) {
94
+ return `Error: ${result.error}`;
95
+ }
96
+ return result.compressed
97
+ ? 'Project skill updated successfully (appended, auto-compressed to fit limit).'
98
+ : 'Project skill updated successfully (appended).';
99
+ }
100
+ default:
101
+ return `Error: Unknown action "${action}". Use "read", "write", or "append".`;
102
+ }
103
+ },
104
+ };
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const readFileTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const runCommandTool: Tool;
@@ -5,6 +5,7 @@ import { getSandboxRoot, filterEnv, isCommandDenied } from '../../sandbox/index.
5
5
  export const runCommandTool = {
6
6
  name: 'run_command',
7
7
  description: 'Run a shell command, merging stdout+stderr. Default timeout 120s. For tests, builds, git, etc.',
8
+ risk: 'dangerous',
8
9
  parameters: {
9
10
  type: 'object',
10
11
  properties: {
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const switchModeTool: Tool;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../types.js';
2
+ export declare const taskTool: Tool;