mocode-ai 1.2.8 → 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.
Files changed (129) hide show
  1. package/dist/agent/core.js +126 -43
  2. package/dist/config/index.js +31 -6
  3. package/dist/config/presets.js +36 -1
  4. package/dist/context/budget.js +0 -15
  5. package/dist/llm/index.js +33 -0
  6. package/dist/repl/index.js +57 -13
  7. package/dist/session/compact.js +237 -13
  8. package/dist/session/notes.js +63 -0
  9. package/dist/session/scheduler.js +5 -9
  10. package/dist/tools/constants.js +7 -0
  11. package/package.json +1 -1
  12. package/dist/__trace_manual_test__.js +0 -1
  13. package/dist/agent/core.d.ts +0 -112
  14. package/dist/agent/index.d.ts +0 -19
  15. package/dist/agent/mode.d.ts +0 -29
  16. package/dist/agent/retry-classifier.js +0 -64
  17. package/dist/agent/spawn.d.ts +0 -36
  18. package/dist/attachments/image.d.ts +0 -22
  19. package/dist/commands/config.d.ts +0 -8
  20. package/dist/commands/skill.js +0 -230
  21. package/dist/config/file.d.ts +0 -23
  22. package/dist/config/index.d.ts +0 -114
  23. package/dist/config/presets.d.ts +0 -57
  24. package/dist/context/budget.d.ts +0 -79
  25. package/dist/context/classifier.d.ts +0 -14
  26. package/dist/context/encoders/_util.d.ts +0 -11
  27. package/dist/context/encoders/code.d.ts +0 -2
  28. package/dist/context/encoders/doc.d.ts +0 -14
  29. package/dist/context/encoders/graph.d.ts +0 -11
  30. package/dist/context/encoders/graph.js +0 -81
  31. package/dist/context/encoders/index.d.ts +0 -2
  32. package/dist/context/encoders/log.d.ts +0 -2
  33. package/dist/context/encoders/log.js +0 -2
  34. package/dist/context/encoders/memory.d.ts +0 -2
  35. package/dist/context/encoders/passthrough.d.ts +0 -11
  36. package/dist/context/encoders/search.d.ts +0 -2
  37. package/dist/context/encoders/summary.d.ts +0 -12
  38. package/dist/context/encoders/table.d.ts +0 -2
  39. package/dist/context/encoders/tree.d.ts +0 -2
  40. package/dist/context/index.d.ts +0 -6
  41. package/dist/context/lifecycle.d.ts +0 -51
  42. package/dist/context/pipeline.d.ts +0 -9
  43. package/dist/context/registry.d.ts +0 -9
  44. package/dist/context/relevance.d.ts +0 -65
  45. package/dist/context/types.d.ts +0 -36
  46. package/dist/foo.js +0 -28
  47. package/dist/foo.test.js +0 -12
  48. package/dist/index.d.ts +0 -1
  49. package/dist/llm/capabilities.d.ts +0 -19
  50. package/dist/llm/index.d.ts +0 -111
  51. package/dist/memory/discover.d.ts +0 -15
  52. package/dist/memory/index.d.ts +0 -13
  53. package/dist/memory/reflect.d.ts +0 -34
  54. package/dist/memory/store.d.ts +0 -124
  55. package/dist/pet/bridge.d.ts +0 -73
  56. package/dist/pet/protocol.d.ts +0 -83
  57. package/dist/pet/state.d.ts +0 -28
  58. package/dist/plan/active.d.ts +0 -23
  59. package/dist/plan/active.js +0 -45
  60. package/dist/plan/index.d.ts +0 -13
  61. package/dist/plan/index.js +0 -33
  62. package/dist/plan/state.d.ts +0 -2
  63. package/dist/plan/state.js +0 -11
  64. package/dist/plan/store.d.ts +0 -78
  65. package/dist/plan/store.js +0 -429
  66. package/dist/project-skill/index.js +0 -95
  67. package/dist/project-skill/initializer.js +0 -178
  68. package/dist/project-snapshot/index.js +0 -85
  69. package/dist/project-snapshot/llm-snapshot.js +0 -150
  70. package/dist/project-snapshot/static-files.js +0 -87
  71. package/dist/repl/index.d.ts +0 -22
  72. package/dist/rollback/index.d.ts +0 -79
  73. package/dist/sandbox/command.d.ts +0 -3
  74. package/dist/sandbox/index.d.ts +0 -4
  75. package/dist/sandbox/jail.d.ts +0 -17
  76. package/dist/sandbox/policy.d.ts +0 -21
  77. package/dist/sandbox/root.d.ts +0 -5
  78. package/dist/session/compact.d.ts +0 -92
  79. package/dist/session/index.d.ts +0 -13
  80. package/dist/session/notes-plan.js +0 -41
  81. package/dist/session/persist.d.ts +0 -30
  82. package/dist/session/scheduler.d.ts +0 -53
  83. package/dist/skills/discover.d.ts +0 -33
  84. package/dist/skills/index.d.ts +0 -13
  85. package/dist/skills/skill-eval.js +0 -345
  86. package/dist/skills/skill-improve.js +0 -221
  87. package/dist/skills/stats.js +0 -102
  88. package/dist/tools/builtins/apply-patch.js +0 -174
  89. package/dist/tools/builtins/ask-human.d.ts +0 -9
  90. package/dist/tools/builtins/codegraph.d.ts +0 -2
  91. package/dist/tools/builtins/codegraph.js +0 -128
  92. package/dist/tools/builtins/edit-file.d.ts +0 -2
  93. package/dist/tools/builtins/glob.d.ts +0 -2
  94. package/dist/tools/builtins/grep.d.ts +0 -2
  95. package/dist/tools/builtins/index.d.ts +0 -2
  96. package/dist/tools/builtins/memory-forget.d.ts +0 -2
  97. package/dist/tools/builtins/memory-list.d.ts +0 -2
  98. package/dist/tools/builtins/memory-save.d.ts +0 -2
  99. package/dist/tools/builtins/memory-search.d.ts +0 -2
  100. package/dist/tools/builtins/memory-update.d.ts +0 -2
  101. package/dist/tools/builtins/project-skill-update.js +0 -102
  102. package/dist/tools/builtins/read-file.d.ts +0 -2
  103. package/dist/tools/builtins/run-command.d.ts +0 -2
  104. package/dist/tools/builtins/switch-mode.d.ts +0 -2
  105. package/dist/tools/builtins/switch-mode.js +0 -44
  106. package/dist/tools/builtins/task.d.ts +0 -2
  107. package/dist/tools/builtins/todolist.d.ts +0 -2
  108. package/dist/tools/builtins/todolist.js +0 -411
  109. package/dist/tools/builtins/use-skill.d.ts +0 -2
  110. package/dist/tools/builtins/web-fetch.d.ts +0 -2
  111. package/dist/tools/builtins/web-search.d.ts +0 -2
  112. package/dist/tools/builtins/write-file.d.ts +0 -2
  113. package/dist/tools/constants.d.ts +0 -52
  114. package/dist/tools/registry.d.ts +0 -18
  115. package/dist/tools/retry.js +0 -137
  116. package/dist/tools/types.d.ts +0 -42
  117. package/dist/ui/batch.d.ts +0 -67
  118. package/dist/ui/clipboard.d.ts +0 -9
  119. package/dist/ui/content.d.ts +0 -55
  120. package/dist/ui/diff.d.ts +0 -34
  121. package/dist/ui/intervention.d.ts +0 -46
  122. package/dist/ui/layout.d.ts +0 -225
  123. package/dist/ui/markdown.d.ts +0 -46
  124. package/dist/ui/mouse.d.ts +0 -29
  125. package/dist/ui/prompt.d.ts +0 -61
  126. package/dist/ui/render.d.ts +0 -60
  127. package/dist/ui/spinner.d.ts +0 -26
  128. package/dist/ui/theme.d.ts +0 -47
  129. package/dist/ui/thinking.js +0 -80
@@ -1,29 +0,0 @@
1
- /**
2
- * agent 模式的共享状态(零依赖纯叶子)。
3
- *
4
- * 把 `agentMode` 从 repl 的模块变量提到这里,让三方都能用、且不破坏依赖单向:
5
- * - `tools/builtins/switch-mode.ts` 写它(setAgentMode)——LLM 在 agent 循环里自切 plan↔auto。
6
- * - `agent/index.ts` 每步读它(getAgentMode)——决定 chat() 用全量 chatTools 还是 planChatTools 只读子集,
7
- * 以及串行分支的 plan 防御 backstop。这样 LLM 中途切模式后,下一次 chat() 立即看到新工具集。
8
- * - `repl/index.ts` 读写它 + 注册 onModeChange 监听器(做 applyMode 重写 history[0] + refreshStatusBase
9
- * 刷状态行 modeTag)。
10
- *
11
- * 依赖方向无环:本模块不 import 任何业务模块。`agent/index → tools/registry → tools/builtins/switch-mode
12
- * → agent/mode` 链终点是本叶子,不回到 agent/index。`tools → agent/mode` 与既有 `tools → ui`(ask-human)、
13
- * `tools → rollback`(registry)同性质——都是引叶子。
14
- *
15
- * listener 同步触发:setAgentMode 在变更模式后同步调 listener(无 async)。switch_mode 工具在 executeTool
16
- * 内调 setAgentMode → listener 同步 applyMode(重写 history[0])+ refreshStatusBase;execute 返回字符串后,
17
- * agent 串行分支继续,下一步 chat() 读到新模式 + 新系统提示,一致。
18
- */
19
- export type AgentMode = 'auto' | 'plan';
20
- /** 当前 agent 模式。auto=全工具执行;plan=只读探查 + 产出计划(写盘/命令/记忆写入工具被 schema 剔除)。 */
21
- export declare function getAgentMode(): AgentMode;
22
- /**
23
- * 设置 agent 模式。同模式 no-op(不触发 listener)。变更时同步触发 onModeChange 注册的监听器
24
- * (repl 在 startRepl 注册:applyMode 重写 history[0] + refreshStatusBase 刷状态行)。
25
- * 返回之前的模式,供调用方(如 runAgent 中断恢复)还原。
26
- */
27
- export declare function setAgentMode(m: AgentMode): AgentMode;
28
- /** 注册模式变更监听器(单一,后注册覆盖先注册)。repl 在 startRepl 启动时注册一次。 */
29
- export declare function onModeChange(cb: (m: AgentMode) => void): void;
@@ -1,64 +0,0 @@
1
- // RETRY-01: 错误反思分类器。
2
- //
3
- // 在 tools/retry.ts 之上叠加一层"反思"语义:把每个 ToolOutcomeCode 映射到
4
- // 一个 ErrorCategory,供 (a) PROMPT 反思 prompt 注入、(b) QUAL-01 trace 指标。
5
- //
6
- // 设计要点:
7
- // - 纯映射函数,无副作用 → 易测,易扩展(新 code → 改一处)。
8
- // - 6 类对齐路线图 L163:`TRANSIENT_RATE_LIMIT` / `TRANSIENT_TIMEOUT` /
9
- // `INVALID_ARGUMENTS` / `PERMISSION_DENIED` / `CONFLICT` / `UNKNOWN_FAILURE`。
10
- // - 提供"反思指针"模板(reflectionHint),与 PROMPT-01 hard rule
11
- // / PROMPT-02 checklist 共用同一套"先想清楚再动"语言,避免新造话。
12
- // - 不**决策**重试与否(`shouldRetry` 在 tools/retry.ts 已经稳定;本次不动),
13
- // 只**注解**每个错误"该反思什么"。
14
- /** code → category 的稳定映射。fixture 直接断言(避免漏改)。 */
15
- const CODE_TO_CATEGORY = {
16
- OK: 'UNKNOWN_FAILURE', // OK 不该走到这里;归 UNKNOWN 是为了编译期不漏。
17
- // rate-limit / timeout / network
18
- TIMEOUT: 'TRANSIENT_TIMEOUT',
19
- HTTP_ERROR: 'TRANSIENT_RATE_LIMIT', // 4xx/5xx 中除 408/429 之外的 5xx 视为 rate-limit 语义
20
- NETWORK_ERROR: 'TRANSIENT_TIMEOUT', // 网络层错(ECONNRESET 等)→ 与 timeout 同类反思
21
- // 参数 / schema(模型自己改,不是 retry 自己改)
22
- INVALID_JSON: 'INVALID_ARGUMENTS',
23
- INVALID_ARGUMENTS: 'INVALID_ARGUMENTS',
24
- INVALID_TOOL_SCHEMA: 'INVALID_ARGUMENTS',
25
- UNKNOWN_TOOL: 'INVALID_ARGUMENTS', // 调用了不存在的工具 → 模型改 tool name
26
- // 权限 / 沙箱 / 模式(永久拒绝)
27
- SANDBOX_DENIED: 'PERMISSION_DENIED',
28
- PERMISSION_DENIED: 'PERMISSION_DENIED',
29
- TOOL_DISABLED: 'PERMISSION_DENIED',
30
- MODE_DENIED: 'PERMISSION_DENIED',
31
- ABORTED: 'PERMISSION_DENIED', // 用户主动 abort,与 permission 同类(无 retry 价值)
32
- // 冲突 / 写失败(retry 之前必须重新读)
33
- EDIT_CONFLICT: 'CONFLICT',
34
- CHANGE_CONFLICT: 'CONFLICT',
35
- PATCH_INVALID: 'CONFLICT',
36
- POSTCONDITION_FAILED: 'CONFLICT',
37
- PROCESS_FAILED: 'CONFLICT',
38
- // 兜底
39
- EXECUTION_ERROR: 'UNKNOWN_FAILURE',
40
- MCP_ERROR: 'UNKNOWN_FAILURE',
41
- };
42
- export function classifyError(code) {
43
- return CODE_TO_CATEGORY[code];
44
- }
45
- /** 反思指针:每类错误给 LLM 一句"反思什么"的话。 */
46
- const REFLECTION_HINTS = {
47
- TRANSIENT_RATE_LIMIT: 'The remote is throttling or returned 5xx. Re-running the same call with identical arguments is unlikely to help — wait, downgrade call frequency, or reduce request size; do not retry the same args in tight loop.',
48
- TRANSIENT_TIMEOUT: 'A network or process timeout occurred. Re-running the same call may help once, but if it fails again with identical args, the path/host/argument is wrong; switch tool, reduce scope, or ask the user.',
49
- INVALID_ARGUMENTS: 'The tool rejected the call shape (JSON / schema / unknown tool). Read the tool description again and fix the arguments yourself; do not resend the same call and do not retry automatically — only the model can fix argument shape.',
50
- PERMISSION_DENIED: 'The call was denied (sandbox / permission / disabled / mode). Retry will not help; surface the constraint to the user and ask for guidance or a permission grant.',
51
- CONFLICT: 'A write/patch conflicted with the on-disk state. Do not resend the same args; re-read the file or change target, then re-derive the diff. If the conflict is structural, the plan itself is wrong — go back to the spec.',
52
- UNKNOWN_FAILURE: 'An unclassified error occurred. Do not retry blindly; capture the message, re-read the tool description, and decide whether the same call with a smaller scope or a different tool is appropriate. If unsure, ask the user.',
53
- };
54
- export function reflectionHint(category) {
55
- return REFLECTION_HINTS[category];
56
- }
57
- /**
58
- * 一站式:对 ToolOutcome 给出"反思 category + 反思 hint"。
59
- * 调用方一般是 tools/retry.ts 的 onRetry hook,把 hint 注入 retry 提示尾部。
60
- */
61
- export function reflectOn(code) {
62
- const category = classifyError(code);
63
- return { category, hint: reflectionHint(category) };
64
- }
@@ -1,36 +0,0 @@
1
- /** 子 agent 运行选项。 */
2
- export interface SpawnOptions {
3
- /** 子任务指令(作为子 agent 的 user 消息)。 */
4
- prompt: string;
5
- /** 附加系统提示(角色/约束),拼在 SUBAGENT_SUFFIX 后。 */
6
- systemPromptSuffix?: string;
7
- /** 允许的工具名白名单(可选)。无 = 全量工具;给则从 chatTools 过滤。 */
8
- tools?: string[];
9
- /** 步数上限(可选,默认 config.subAgentMaxSteps ?? 50)。 */
10
- maxSteps?: number;
11
- /** 主 agent 的 abort signal(可选)。透传给子 runAgentCore → chat/executeTool,
12
- * 主 Ctrl+C 树杀子 agent(chat 流式 abort + run_command/web_fetch 即时取消)。 */
13
- signal?: AbortSignal;
14
- }
15
- /** 子 agent 运行结果。 */
16
- export interface SpawnResult {
17
- /** 子 agent 最终文本回复;中断或无回复为 null。 */
18
- summary: string | null;
19
- /** 正常完毕 true;中断 false。 */
20
- completed: boolean;
21
- /** 子 agent 中间过程的人类可读日志(工具调用 + 结果摘要 + 流式正文片段)。主 agent 通常不看,调试用。 */
22
- transcript: string;
23
- }
24
- /**
25
- * 派生一个子 agent 执行独立子任务。
26
- *
27
- * 行为:
28
- * - 构造独立 history:[{system: 主系统提示 + SUBAGENT_SUFFIX + 自定义后缀}, {user: prompt}]
29
- * - 工具子集:按 opts.tools 白名单过滤 chatTools;无白名单 = 全量。
30
- * - 静默 hooks:流式正文 / 工具头 / 结果缓冲到 transcript;不写主屏(layout)。
31
- * - 返回 { summary, completed, transcript }。summary 给 task 工具回灌主 history。
32
- *
33
- * 中断:opts.signal 透传给子 runAgentCore——主 Ctrl+C 树杀子 agent(chat abort + 工具 abort)。
34
- * 子 agent 跑在主 signal 下,主 abort 即子 abort;子 agent 的 abortRestore 还原子 history + 模式。
35
- */
36
- export declare function spawnAgent(opts: SpawnOptions): Promise<SpawnResult>;
@@ -1,22 +0,0 @@
1
- export type ImageMime = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
2
- export interface ImageAttachment {
3
- id: string;
4
- path: string;
5
- name: string;
6
- bytes: number;
7
- mime: ImageMime;
8
- dataUrl: string;
9
- }
10
- export declare const MAX_INLINE_BYTES_DEFAULT: number;
11
- export declare function detectMime(p: string): ImageMime | null;
12
- export declare function renderChip(att: ImageAttachment): string;
13
- export type LoadImageResult = {
14
- ok: true;
15
- att: ImageAttachment;
16
- } | {
17
- ok: false;
18
- reason: string;
19
- };
20
- export declare function loadImageAttachment(input: string, opts: {
21
- maxBytes: number;
22
- }): Promise<LoadImageResult>;
@@ -1,8 +0,0 @@
1
- /**
2
- * 首跑配置向导:交互填 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL,写 ~/.mocode/config。
3
- * 只交互三键、保留文件里其它键(MAX_TOKENS / CONTEXT_WINDOW_TOKENS / MOCODE_THEME 等)。
4
- * prompt 全纯文本(readline 光标按字符算,不能含 ANSI)。
5
- * 由 index.ts 在 `mocode config` 时动态加载,故不 import config/index.ts(避免触发 config 单例初始化 / loadEnvFiles,
6
- * 直接退出);只 import config/file.ts(纯 I/O 叶子,无 env 校验 / process.exit)。
7
- */
8
- export declare function runConfigWizard(): Promise<void>;
@@ -1,230 +0,0 @@
1
- // `mocode skill` CLI 子命令(skill 自进化 Phase 0/1 的用户入口)。
2
- //
3
- // mocode skill eval <name> [--runs N] [--threshold x] [--timeout ms]
4
- // 触发评测:对 <skill-dir>/evals/trigger.json 的每条 query 跑单轮 agent,
5
- // 统计 use_skill/run_skill 触发率,输出 PASS/FAIL 报告并落盘 JSON。
6
- //
7
- // mocode skill improve <name> [--runs N] [--threshold x] [--iterations N]
8
- // [--holdout x] [--timeout ms] [--apply]
9
- // description 进化循环:train/holdout 切分 + LLM 提案 + 接受门,默认 dry-run
10
- // 只打印最佳 description;--apply 才写回 SKILL.md(写前显式确认;非 TTY 拒绝)。
11
- //
12
- // mocode skill usage [name]
13
- // 展示使用台账(Phase 0):按 skill 的调用次数/成功率/最近失败。
14
- //
15
- // 由 index.ts 在 `mocode skill …` 时动态加载(与 mocode config 同模式):
16
- // 此时才引入 config 单例(LLM 配置)+ agent 依赖图,缺 LLM 配置时给友好报错而非崩。
17
- // 纯打印/非 TUI:本进程不进 alt screen,输出走 stdout。
18
- import * as readline from 'node:readline';
19
- import path from 'node:path';
20
- import { config, isModelConfigured } from '../config/index.js';
21
- import { listSkills } from '../skills/index.js';
22
- import { loadSkillForEval, loadTriggerEvalSet, triggerEvalTemplate, triggerEvalPath, runTriggerEval, renderTriggerReport, saveTriggerReport, validateEvalParams, } from '../skills/skill-eval.js';
23
- import { runImproveLoop, applyImprovedDescription } from '../skills/skill-improve.js';
24
- import { loadSkillUsage, aggregateSkillStats, skillStatsPath } from '../skills/stats.js';
25
- function usage() {
26
- console.log(`mocode skill — skill 自进化(触发评测 / description 进化 / 使用台账)
27
-
28
- 用法:
29
- mocode skill eval <name> [选项] 触发评测(需要 <skill-dir>/evals/trigger.json)
30
- mocode skill improve <name> [选项] description 进化循环(默认 dry-run)
31
- mocode skill usage [name] 展示使用台账
32
-
33
- eval 选项:
34
- --runs <n> 每 query 运行次数(默认 3;1..10)
35
- --threshold <x> 触发率判定阈值(默认 0.5;>0 且 ≤1)
36
- --timeout <ms> 单 query 超时(默认 60000)
37
-
38
- improve 选项:
39
- --runs <n> 同 eval(默认 3)
40
- --threshold <x> 同 eval(默认 0.5)
41
- --iterations <n> 迭代上限(默认 5)
42
- --holdout <x> holdout 比例(默认 0.4;0 禁用)
43
- --timeout <ms> 单 query 超时(默认 60000)
44
- --apply 把最佳 description 写回 SKILL.md(默认 dry-run,仅打印)
45
-
46
- eval 集格式(<skill-dir>/evals/trigger.json,非空数组):
47
- [
48
- { "query": "应该触发该 skill 的真实请求", "should_trigger": true },
49
- { "query": "不应触发的相近请求", "should_trigger": false }
50
- ]
51
-
52
- 注: 进化对象仅限 ~/.mocode/skills 与 <cwd>/.mocode/skills 下的 skill(内置 skill 不可进化)。
53
- 没有 evals/trigger.json 的 skill 只能 eval 前手工补建,improve 一律拒绝(没验证门不优化)。`);
54
- }
55
- function numArg(args, name, fallback) {
56
- const i = args.indexOf(name);
57
- if (i === -1 || i + 1 >= args.length)
58
- return fallback;
59
- const v = Number(args[i + 1]);
60
- if (!Number.isFinite(v) || v <= 0) {
61
- throw new Error(`${name} 需要正数: ${args[i + 1]}`);
62
- }
63
- return v;
64
- }
65
- function parseCommon(args) {
66
- const runs = Math.round(numArg(args, '--runs', 3));
67
- const threshold = numArg(args, '--threshold', 0.5);
68
- const timeoutMs = Math.round(numArg(args, '--timeout', 60_000));
69
- const err = validateEvalParams(runs, threshold);
70
- if (err)
71
- throw new Error(err);
72
- return { runs, threshold, timeoutMs };
73
- }
74
- function requireModel() {
75
- if (!isModelConfigured()) {
76
- console.error('未配置 LLM(缺 LLM_BASE_URL / LLM_API_KEY)。先运行 `mocode config` 或设置环境变量。');
77
- process.exit(1);
78
- }
79
- }
80
- /** 定位 eval 集;缺失时打印模板并退出(不猜、不自动建)。 */
81
- function requireEvalSet(skillName) {
82
- const skill = loadSkillForEval(skillName);
83
- const cases = loadTriggerEvalSet(skill);
84
- if (!cases) {
85
- console.error(`未找到触发评测集: ${path.relative(process.cwd(), triggerEvalPath(skill))}`);
86
- console.error('手工创建该文件(格式):');
87
- console.error(triggerEvalTemplate(skill));
88
- process.exit(1);
89
- }
90
- return { skill, cases };
91
- }
92
- async function evalCommand(args) {
93
- const rest = args.slice();
94
- const name = rest.find((a) => !a.startsWith('--'));
95
- if (!name)
96
- throw new Error('缺少 skill 名。见: mocode skill eval --help');
97
- requireModel();
98
- const { skill, cases } = requireEvalSet(name);
99
- const { runs, threshold, timeoutMs } = parseCommon(rest);
100
- console.log(`评测 skill "${skill.name}" — ${cases.length} 条 query × ${runs} 次,模型 ${config.model}\n`);
101
- const startedAt = Date.now();
102
- const report = await runTriggerEval(skill, skill.description, cases, runs, threshold, {
103
- timeoutMs,
104
- onProgress: (_d, _t, line) => process.stdout.write(line + '\n'),
105
- });
106
- const saved = saveTriggerReport(report);
107
- console.log(`\n${renderTriggerReport(report, { runsPerQuery: runs, threshold })}`);
108
- console.log(`\n耗时 ${((Date.now() - startedAt) / 1000).toFixed(1)}s,结果已存: ${path.relative(process.cwd(), saved)}`);
109
- if (report.summary.passed < report.summary.total) {
110
- console.log('存在失败项。可用 `mocode skill improve ' + skill.name + '` 尝试自动优化 description(dry-run)。');
111
- process.exitCode = 1;
112
- }
113
- }
114
- function confirmApply(skillName) {
115
- if (!process.stdin.isTTY) {
116
- // 非 TTY fail closed:自动落盘必须有人在场。
117
- console.error('非 TTY 环境拒绝 --apply 自动落盘。请在终端运行,或手工编辑 SKILL.md 的 description。');
118
- process.exit(1);
119
- }
120
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
121
- return new Promise((resolve) => {
122
- rl.question(`确认把新 description 写回 skill "${skillName}" 的 SKILL.md?(y/N) `, (ans) => {
123
- rl.close();
124
- resolve(ans.trim().toLowerCase() === 'y' || ans.trim().toLowerCase() === 'yes');
125
- });
126
- });
127
- }
128
- async function improveCommand(args) {
129
- const rest = args.slice();
130
- const name = rest.find((a) => !a.startsWith('--'));
131
- if (!name)
132
- throw new Error('缺少 skill 名。见: mocode skill improve --help');
133
- requireModel();
134
- const { skill, cases } = requireEvalSet(name);
135
- const { runs, threshold, timeoutMs } = parseCommon(rest);
136
- const iterations = Math.round(numArg(rest, '--iterations', 5));
137
- const holdout = (() => {
138
- const i = rest.indexOf('--holdout');
139
- const v = i === -1 ? 0.4 : Number(rest[i + 1]);
140
- if (i !== -1 && (!Number.isFinite(v) || v < 0 || v > 0.9))
141
- throw new Error('--holdout 需要 0..0.9 的数');
142
- return v;
143
- })();
144
- const apply = rest.includes('--apply');
145
- console.log(`进化 skill "${skill.name}" — ${cases.length} 条 query,train/holdout=${1 - holdout}/${holdout},${apply ? '将写回(--apply)' : 'dry-run(不落盘)'}\n`);
146
- const result = await runImproveLoop(skill, cases, {
147
- maxIterations: iterations,
148
- runsPerQuery: runs,
149
- threshold,
150
- holdout,
151
- timeoutMs,
152
- onIteration: (_i, _max, line) => console.log(line),
153
- });
154
- console.log('');
155
- if (!result.improved) {
156
- console.log(`没有更优的 description(原 description 已是最佳或循环未产出改进)。`);
157
- console.log(`原: ${result.originalDescription}`);
158
- return;
159
- }
160
- console.log(`原 description: ${result.originalDescription}`);
161
- console.log(`新 description: ${result.bestDescription}`);
162
- if (apply) {
163
- const ok = await confirmApply(skill.name);
164
- if (!ok) {
165
- console.log('已取消写回(dry-run 结果见上,可手工应用)。');
166
- return;
167
- }
168
- applyImprovedDescription(skill, result.bestDescription);
169
- console.log(`已写回: ${skill.skillMdPath}`);
170
- console.log('提示: 内容哈希已变更,project 级 skill 下次 run_skill 会重新要求信任确认。');
171
- }
172
- else {
173
- console.log('dry-run:未写文件。确认满意后加 --apply 重跑,或直接手工把新 description 写进 SKILL.md。');
174
- }
175
- }
176
- function usageCommand(args) {
177
- const filter = args.find((a) => !a.startsWith('--'));
178
- const records = loadSkillUsage();
179
- const summaries = aggregateSkillStats(records);
180
- const list = filter ? summaries.filter((s) => s.skill === filter) : summaries;
181
- if (list.length === 0) {
182
- console.log(`(台账为空: ${path.relative(process.cwd(), skillStatsPath())})`);
183
- console.log('台账在模型调用 use_skill / run_skill 时自动记录,跑几个会话后这里有数。');
184
- return;
185
- }
186
- for (const s of list) {
187
- const rate = s.runSuccessRate === null ? '' : ` run 成功率 ${(s.runSuccessRate * 100).toFixed(0)}%`;
188
- console.log(`${s.skill} ×${s.total}(use ${s.uses} / run ${s.runs})${rate} 最近 ${s.lastUsedAt}`);
189
- if (s.lastFailure) {
190
- console.log(` 最近失败: ${s.lastFailure.status}${s.lastFailure.code ? ` (${s.lastFailure.code})` : ''} @ ${s.lastFailure.ts}`);
191
- }
192
- }
193
- }
194
- export async function runSkillCommand(args) {
195
- const sub = args[0];
196
- if (!sub || sub === '--help' || sub === '-h' || sub === 'help') {
197
- usage();
198
- return;
199
- }
200
- try {
201
- if (sub === 'eval')
202
- await evalCommand(args.slice(1));
203
- else if (sub === 'improve')
204
- await improveCommand(args.slice(1));
205
- else if (sub === 'usage')
206
- usageCommand(args.slice(1));
207
- else {
208
- console.error(`未知子命令 "${sub}"。`);
209
- usage();
210
- process.exitCode = 1;
211
- }
212
- }
213
- catch (e) {
214
- const msg = e instanceof Error ? e.message : String(e);
215
- if (/aborted/i.test(msg)) {
216
- console.error('\n已中断。');
217
- process.exitCode = 130;
218
- }
219
- else {
220
- console.error(`错误: ${msg}`);
221
- process.exitCode = 1;
222
- }
223
- }
224
- }
225
- // 供 index.ts 判断子命令名集合(避免它 parse 我们的参数)。
226
- export const SKILL_SUBCOMMANDS = ['eval', 'improve', 'usage', 'help', '--help', '-h'];
227
- // listSkills 在 eval/improve 未命中时供报错文案复用(保持与 /skills 一致的名字集)。
228
- export function listSkillNames() {
229
- return listSkills().map((s) => s.name);
230
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * ~/.mocode/config 全局 dotenv 配置的读写叶子(纯 node:fs / node:os / node:path + dotenv)。
3
- *
4
- * 独立于 `config/index.ts`——**不**触发 config 单例初始化 / loadEnvFiles,故 `commands/config.ts`
5
- * (首跑向导,刻意不 import config/index.ts)与 `repl/index.ts`(`/theme` 持久化)都能安全共享。
6
- * 主题等 UI 偏好走 `updateConfigKey` 单键写;向导多键一次性写走 `writeConfigKeys`。
7
- */
8
- /** ~/.mocode/config:全局配置(与 ~/.mocode/skills 同目录,任意终端 / 任意目录生效)。 */
9
- export declare const CONFIG_PATH: string;
10
- /** 读已有配置(没有则空对象),用于回填默认值与保留其它键。 */
11
- export declare function readConfigFile(): Record<string, string>;
12
- /**
13
- * 读-合并-写:把 entries 覆盖进 ~/.mocode/config,保留文件里其它键。
14
- * mkdirSync 兜底(首跑无 ~/.mocode)。不吞 I/O 异常——向导路径让其抛(与历史行为一致);
15
- * UI 偏好路径用 `updateConfigKey`(自带 try/catch 静默)。
16
- */
17
- export declare function writeConfigKeys(entries: Record<string, string>): void;
18
- /**
19
- * 单键持久化(主题等 UI 偏好):写失败静默——下次启动读不到则回退默认,不阻断 REPL。
20
- * 注意:shell env 设了同名键时,`config/index.ts` 的 loadEnvFiles 只 backfill undefined 的
21
- * process.env,故文件写对**下次启动**可能被 shell 盖——调用方据此给 dim 警告。
22
- */
23
- export declare function updateConfigKey(key: string, value: string): void;
@@ -1,114 +0,0 @@
1
- export interface Config {
2
- baseURL: string;
3
- apiKey: string;
4
- model: string;
5
- maxTokens?: number;
6
- systemPrompt: string;
7
- /** 模型上下文窗口(token)。须对齐真实模型窗口(GLM-4.6≈128k,DeepSeek-V3≈64k,Qwen 视版本)。 */
8
- contextWindowTokens: number;
9
- /** 自动压缩触发阈值(占窗口比例)。默认 0.85,保守偏早以吸收估算误差与下一步增长。 */
10
- compactThreshold: number;
11
- /** 流式请求里带 stream_options.include_usage 拿真实 usage。后端不认 stream_options 时关掉。 */
12
- includeUsage: boolean;
13
- /** 自动压缩总开关。关掉则只靠手动 /compact。 */
14
- autoCompact: boolean;
15
- /** Context Optimization Pipeline 总开关(工具结果进 LLM 前的类型化编码:tree/search/log/…)。
16
- * 关掉则工具结果原样进 LLM(仅长度裁剪),零行为变化。默认 true。 */
17
- contextOptimize: boolean;
18
- /** 相关性裁剪总开关(read_file 跨条裁剪:同 path 旧 read + 已被 mutation 覆写的旧 read
19
- * 自动替换为存根)。纯静态、不调 LLM;关掉则保留所有 read 结果(只受 capToolResultForHistory
20
- * 单条上限与 microcompact 旧区截短影响)。默认 true;
21
- * 设 MOCODE_CONTEXT_RELPRUNE=false 全局回退。 */
22
- contextRelprune: boolean;
23
- /** 观察者生命周期总开关(LIVE→REFERENCED→OBSOLETE→STUB 四态机;grep/glob/codegraph
24
- * producer ↔ read/edit/write consumer 引用追踪;孤立+老化非观察类工具自动 STUB;
25
- * 观察类工具永远只到 REFERENCED,不自动 STUB)。与 contextRelprune 并列,纯静态、不调 LLM。
26
- * 默认 true;设 MOCODE_LIFECYCLE=false 全局回退。 */
27
- contextLifecycle: boolean;
28
- /** Context Budget Scheduler 总开关(五区分账 + ROI 排序调度)。
29
- * 关掉则 agent 步前退化为 maybeCompact(history) 老路径(只看总占用 0.85 阈值),
30
- * 零行为变化。默认 true;设 MOCODE_BUDGET_SCHEDULER=false 全局回退。 */
31
- contextBudget: boolean;
32
- /** 后台反思 pass 总开关。关掉则只靠手动 /reflect + 机会主义 memory_update。 */
33
- autoReflect: boolean;
34
- /**
35
- * 记忆子系统总开关。关闭时:5 个 memory_* 工具不进工具表,buildSystemPrompt
36
- * 里的 Memory Index 段 + memory_* 工具使用说明整段不出现,plan 模式提示词里的
37
- * 工具名也跟着消失。运行时 /memory_switch 改;持久化 MEMORY_ENABLED。
38
- * 默认 false:新用户零侵入,想用记忆功能显式打开。
39
- */
40
- memoryEnabled: boolean;
41
- /** 每 N 个轮次触发一次后台反思 pass(与 agent 并发,不阻塞)。默认 5。 */
42
- reflectEveryN: number;
43
- /** 每轮 agent 循环最大步数(防无限循环)。默认 25。 */
44
- maxSteps: number;
45
- /** 子 agent(task 工具派生)默认步数上限。防子任务失控耗尽配额。默认 50。 */
46
- subAgentMaxSteps: number;
47
- /** 会话落盘目录(cwd 下)。 */
48
- sessionDir: string;
49
- /** AnySearch 联网搜索 API key(可选)。不配则走匿名免费额度(按 IP 限流)。 */
50
- searchApiKey?: string;
51
- /** 沙箱根目录(文件操作边界,可选)。未配则 startRepl 用 process.cwd() 兜底。优先级:--sandbox-root > 本项 > cwd。 */
52
- sandboxRoot?: string;
53
- /** AnySearch API base,默认官方端点。 */
54
- searchBaseUrl: string;
55
- /** 单张图片内联字节上限(base64 前的原始字节);超此大小拒绝并提示 TODO 走 URL 上传。默认 4MB。 */
56
- maxImageBytes?: number;
57
- /** 主题名(对应 src/ui/theme.ts 的 THEMES 表键)。默认 default;shell env MOCODE_THEME 覆盖文件。 */
58
- theme: string;
59
- /** MOCODE_THEME 是否由 shell 环境变量设置(非文件回填)。若是,/theme 写文件下次启动仍被 shell 盖。 */
60
- themeFromShell: boolean;
61
- /** 由 shell 环境变量设置的 LLM 键名列表(非文件回填)。若含某键,/model 写该键下次启动仍被 shell 盖。 */
62
- llmKeysFromShell: string[];
63
- }
64
- /**
65
- * 模型是否已配置(baseURL + apiKey 非空)。REPL 开场据此决定是否提示 /model。
66
- * 未配置时 config.model 仍回退 'gpt-4o-mini',但发消息会因 baseURL/apiKey 空而失败——由 runTurn catch 友好提示。
67
- */
68
- export declare function isModelConfigured(): boolean;
69
- /** 兼容旧名字:repl 的 buildSystemMessage 仍引 PLAN_MODE_SUFFIX(变量)。运行时按需现拼。 */
70
- export declare function buildBasePrompt(): string;
71
- /**
72
- * plan 模式追加到系统提示末尾的指令。
73
- * 历史曾是 `export const PLAN_MODE_SUFFIX`(顶层字面量);现改为按 isMemoryEnabled()
74
- * 动态拼:false 时不出现 memory_* 工具名,避免 LLM 想调不存在的工具。
75
- *
76
- * 注意:已改为 getter(每次访问现拼),让运行时切 /memory_switch 后立即生效。
77
- * 旧 import `PLAN_MODE_SUFFIX` 路径不变;repl 推荐改用 getPlanModeSuffix()(语义更清晰)。
78
- * 不能直接 `export const PLAN_MODE_SUFFIX = buildPlanModeSuffix()`:
79
- * 该表达式在模块初始化时立即求值,而 buildPlanModeSuffix 内部读 config,config 还未求值 → TDZ。
80
- */
81
- export declare function getPlanModeSuffix(): string;
82
- export declare const config: Config;
83
- /**
84
- * 运行时更新模型相关配置(/model 命令调)。
85
- * - 更新 config 对象字段(即时生效:chat() 读 config.model,reconfigureClient 读 config.baseURL/apiKey)。
86
- * - 同步 process.env(保持内存一致:其他读 process.env 的路径也拿到新值;且使新值在下次启动的
87
- * loadEnvFiles 中被视为"已设",不被文件回填覆盖——即"优先拿这里的")。
88
- * 持久化(写 ~/.mocode/config)由调用方走 writeConfigKeys,此处只管内存 + env。
89
- * 重建 OpenAI 客户端(baseURL/apiKey 是构造时固化的实例字段)由调用方走 reconfigureClient。
90
- */
91
- export declare function updateModelConfig(opts: {
92
- model?: string;
93
- baseURL?: string;
94
- apiKey?: string;
95
- contextWindowTokens?: number;
96
- }): void;
97
- /**
98
- * 记忆子系统总开关:单一来源。/memory_switch、/memory_status、buildSystemPrompt、
99
- * tools/builtins/index.ts、tools/constants.ts 的 plan-mode 列表都从这里查。
100
- * 默认 false(新用户零侵入)。
101
- */
102
- export declare function isMemoryEnabled(): boolean;
103
- /**
104
- * 切换记忆子系统开关(/memory_switch on|off 调)。
105
- * - 更新 config 单例字段(其它模块下次调 isMemoryEnabled() 即拿新值)。
106
- * - 同步 process.env.MEMORY_ENABLED(下次启动 loadEnvFiles 不被文件回填)。
107
- * 持久化(写 ~/.mocode/config 的 MEMORY_ENABLED 键)由调用方走 writeConfigKeys。
108
- *
109
- * 注:开关切换对当前会话的 tool list / 已拼好的 systemPrompt 不会自动重算 —
110
- * 工具表在 REPL 启动时构建,systemPrompt 在每轮 chat() 拼时按 isMemoryEnabled()
111
- * 现查现拼(关掉时该轮拼出来的 prompt 即不带 memory_* 段)。所以切换在「下一轮
112
- * agent 调用」起即时生效,本轮已发出的请求不会回滚。
113
- */
114
- export declare function updateMemoryConfig(enabled: boolean): void;
@@ -1,57 +0,0 @@
1
- /**
2
- * 多模型预设(`/model save <name>` 保存的命名配置)的纯 I/O 叶子。
3
- *
4
- * 存储:每个预设一个独立 JSON 文件 `~/.mocode/models/<name>.json`。
5
- * - 选用 per-file 而非单一 index.json:写不需要读-合并-写整本,天然原子(单文件 rename),
6
- * 符合 memory/store.ts 的惯例;并发写多个预设互不打架。
7
- * - 路径前缀与 CONFIG_PATH 共享 `~/.mocode/`,保证权限/位置一致。
8
- *
9
- * 不读 process.env、不触发 config 单例初始化——repl(/model 子命令)与 commands/config.ts
10
- * 之外的脚本都能安全 import。错误一律抛(写失败属异常路径,调用方决定怎么提示用户)。
11
- *
12
- * 命名约束:[a-zA-Z0-9_-]{1,32}。理由:
13
- * - 排除路径分隔符与 .. 防止越权写文件。
14
- * - 与 dotenv key 风格一致,便于未来扩展到同名 env override。
15
- * - 32 位上限防极长名撑爆文件名系统。
16
- */
17
- /** 预设目录:`~/.mocode/models/`(按需创建)。 */
18
- export declare const MODELS_DIR: string;
19
- /** 单个预设的内容:与 updateModelConfig 的可选字段对齐。 */
20
- export interface ModelPreset {
21
- name: string;
22
- baseURL: string;
23
- apiKey: string;
24
- model: string;
25
- contextWindow: number;
26
- }
27
- /** 名字是否合法(调用方复用,避免在多处重复同一正则)。 */
28
- export declare function isValidPresetName(name: string): boolean;
29
- /** 读单个预设;不存在抛错。 */
30
- export declare function getPreset(name: string): ModelPreset;
31
- /** 读单个预设;不存在返回 null(供列表/可选切换场景)。 */
32
- export declare function readPreset(name: string): ModelPreset | null;
33
- /** 写/覆盖一个预设(原子:写 tmp 再 rename)。 */
34
- export declare function savePreset(preset: ModelPreset): void;
35
- /** 删除一个预设;不存在返回 false,成功返回 true。 */
36
- export declare function deletePreset(name: string): boolean;
37
- /**
38
- * 重命名一个预设(原子:link+unlink,跨设备时退化为 copy+unlink)。
39
- * 用于 /model rename <old> <new>;不存在的旧名 / 已存在的新名返回 false,具体由调用方决定提示文案。
40
- */
41
- export declare function renamePreset(oldName: string, newName: string): boolean;
42
- /** 列出全部预设(按 name 升序);目录不存在返回空数组。 */
43
- export declare function listPresets(): ModelPreset[];
44
- /**
45
- * 把当前 config 的 LLM 四键(baseURL/apiKey/model/contextWindow)迁为命名预设。
46
- * 用于启动时一次性兜底老用户:加 /model 之前就已经在 ~/.mocode/config 写过的配置,
47
- * /model list 应该立刻能看到,而不是空。已有同名预设则不重复写。
48
- *
49
- * 返回新建的预设名;若未配置完整(baseURL/apiKey 缺失)或已有同名则返回 null。
50
- * 设计为幂等:重启调用一次也只会生效一次。
51
- */
52
- export declare function migrateCurrentToPreset(input: {
53
- baseURL: string;
54
- apiKey: string;
55
- model: string;
56
- contextWindow: number;
57
- }): string | null;