mocode-ai 1.2.3 → 1.2.5

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.
@@ -1,10 +1,12 @@
1
1
  import { saveEntry } from '../../memory/store.js';
2
+ import { addTriple } from '../../memory/graph.js';
2
3
  // ---------- memory_save ----------
3
4
  // 存一条长期记忆(跨会话)。启动只把标题/摘要注入索引(几百 token);详情按需 memory_search 取。
4
5
  // 撞库(name→id 已存在)拒绝,引导用 memory_update。
6
+ // 可选 links:把本条记忆挂进知识图谱(memory-graph.json)——src 省略时默认以记忆 name 为主实体。
5
7
  export const memorySaveTool = {
6
8
  name: 'memory_save',
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.',
9
+ 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. Optionally attach knowledge-graph links (triples) to relate this memory to entities.',
8
10
  risk: 'confirm',
9
11
  parameters: {
10
12
  type: 'object',
@@ -23,6 +25,20 @@ export const memorySaveTool = {
23
25
  enum: ['project', 'global'],
24
26
  description: 'Store at project level (<cwd>/.mocode/) or global (~/.mocode/), default project',
25
27
  },
28
+ links: {
29
+ type: 'array',
30
+ description: 'Optional knowledge-graph triples relating this memory to entities, e.g. [{"src":"mocode","relation":"depends_on","dst":"JSONL store"}]. src defaults to the memory name when omitted.',
31
+ items: {
32
+ type: 'object',
33
+ properties: {
34
+ src: { type: 'string', description: 'Source entity name (defaults to the memory name)' },
35
+ relation: { type: 'string', description: 'Relation, snake_case, e.g. depends_on / decided_by / conflicts_with' },
36
+ dst: { type: 'string', description: 'Target entity name' },
37
+ fact: { type: 'string', description: 'Optional one-line statement for the edge' },
38
+ },
39
+ required: ['relation', 'dst'],
40
+ },
41
+ },
26
42
  },
27
43
  required: ['name', 'summary', 'body'],
28
44
  },
@@ -37,16 +53,37 @@ export const memorySaveTool = {
37
53
  if (!body)
38
54
  return '错误:缺少 body。';
39
55
  const type = typeof args.type === 'string' ? args.type : undefined;
56
+ const scope = args.scope === 'global' ? 'global' : 'project';
40
57
  const r = saveEntry({
41
58
  name,
42
59
  summary,
43
60
  body,
44
61
  type,
45
62
  pinned: args.pinned === true,
46
- scope: args.scope === 'global' ? 'global' : 'project',
63
+ scope,
47
64
  });
48
- if (r.ok)
49
- return `已保存记忆 [${r.id}] "${name}"(下次启动进索引)。`;
50
- return `已存在同名记忆 [${r.exists}]。改用 memory_update(id="${r.exists}", …) 更新,或换一个 name。`;
65
+ if (!r.ok) {
66
+ return `已存在同名记忆 [${r.exists}]。改用 memory_update(id="${r.exists}", …) 更新,或换一个 name。`;
67
+ }
68
+ // 知识图谱挂边:容错——图失败不影响记忆保存结果
69
+ const links = Array.isArray(args.links) ? args.links : [];
70
+ let linked = 0;
71
+ for (const l of links) {
72
+ if (!l || typeof l !== 'object')
73
+ continue;
74
+ const link = l;
75
+ const tr = addTriple({
76
+ src: typeof link.src === 'string' && link.src.trim() ? link.src : name,
77
+ relation: typeof link.relation === 'string' ? link.relation : '',
78
+ dst: typeof link.dst === 'string' ? link.dst : '',
79
+ fact: typeof link.fact === 'string' ? link.fact : undefined,
80
+ sourceEntry: r.id,
81
+ scope,
82
+ });
83
+ if (tr.ok)
84
+ linked++;
85
+ }
86
+ const linkNote = links.length > 0 ? `;知识图谱挂边 ${linked}/${links.length}` : '';
87
+ return `已保存记忆 [${r.id}] "${name}"(下次启动进索引)${linkNote}。`;
51
88
  },
52
89
  };
@@ -1,10 +1,13 @@
1
1
  import { searchEntries } from '../../memory/store.js';
2
+ import { searchGraph } from '../../memory/graph.js';
2
3
  // ---------- memory_search ----------
3
- // 关键词搜记忆正文(多词子串匹配,name 权重最高)。命中即 bump recallCount(遗忘衰减依据)。
4
+ // 唯一记忆搜索入口:关键词搜记忆正文(多词子串匹配,name 权重最高)+ 知识图谱事实段
5
+ // (命中实体的 active 边)。命中条目即 bump recallCount(遗忘衰减依据)。
4
6
  // 结果走 capToolResultForHistory 的放宽上限(同 use_skill,保正文完整)。
7
+ const GRAPH_FACTS_LIMIT = 10;
5
8
  export const memorySearchTool = {
6
9
  name: 'memory_search',
7
- description: 'Search memory entries by keyword (substring match), returning full body.',
10
+ description: 'Search memory entries by keyword (substring match), returning full body. Also surfaces knowledge-graph facts (active edges) for entities matching the query.',
8
11
  parameters: {
9
12
  type: 'object',
10
13
  properties: {
@@ -33,10 +36,28 @@ export const memorySearchTool = {
33
36
  : undefined,
34
37
  limit: typeof args.limit === 'number' ? args.limit : undefined,
35
38
  });
36
- if (r.length === 0)
37
- return `(无匹配记忆:query="${query}")`;
38
- return r
39
+ const entryText = r
39
40
  .map((e) => `# [${e.id}] ${e.name} (${e.type}, recalled ${e.recallCount})\nsummary: ${e.summary}\n\n${e.body}`)
40
41
  .join('\n\n---\n\n');
42
+ // 知识图谱事实段:命中实体的 active 边(容错:图坏了不连累条目搜索)。
43
+ let graphText = '';
44
+ try {
45
+ const g = searchGraph(query, 8);
46
+ if (g.edges.length > 0) {
47
+ const lines = g.edges
48
+ .slice(0, GRAPH_FACTS_LIMIT)
49
+ .map((e) => `${e.src} --[${e.relation}]--> ${e.dst}${e.fact ? ` (${e.fact})` : ''}`);
50
+ const more = g.edges.length > GRAPH_FACTS_LIMIT ? `\n…(共 ${g.edges.length} 条,其余用 memory_graph action=neighbors 展开)` : '';
51
+ graphText = `\n\n## 知识图谱事实\n${lines.join('\n')}${more}`;
52
+ }
53
+ }
54
+ catch {
55
+ // 静默:图谱段是增强,失败只降级为纯条目结果
56
+ }
57
+ if (!entryText && !graphText)
58
+ return `(无匹配记忆:query="${query}")`;
59
+ if (!entryText)
60
+ return `(无匹配记忆条目,但图谱有命中)\n${graphText.trimStart()}`;
61
+ return entryText + graphText;
41
62
  },
42
63
  };
@@ -0,0 +1,42 @@
1
+ import { runSkill } from '../../skills/runner.js';
2
+ // ---------- run_skill ----------
3
+ // 唯一新增的常驻工具(L2-①):把某个 skill 作为隔离工作流(fork 子 agent)执行并返回摘要。
4
+ // 无论装 100 个还是 1000 个 skill,常驻工具表只多这 1 个。上下文 / 工具面 / 副作用 / 中断
5
+ // 全部由 spawnAgent 现成能力承接(设计 §3.4)。
6
+ export const runSkillTool = {
7
+ name: 'run_skill',
8
+ description: 'Execute a skill as an isolated workflow (forked sub-agent) and return its summary. ' +
9
+ 'Use for skills marked [fork] in the skill list. Args are rendered into the skill body.',
10
+ parameters: {
11
+ type: 'object',
12
+ properties: {
13
+ name: {
14
+ type: 'string',
15
+ description: 'Name of the skill to execute (see the skill list in the system prompt).',
16
+ },
17
+ args: {
18
+ type: 'object',
19
+ description: 'Arguments rendered into the skill body ($ARGUMENTS, $1..$9). Optional.',
20
+ },
21
+ context: {
22
+ type: 'string',
23
+ description: 'Optional extra context/facts to inject into the sub-agent (authoritative; not rediscovered).',
24
+ },
25
+ },
26
+ required: ['name'],
27
+ },
28
+ risk: 'confirm',
29
+ capabilities: {
30
+ effect: 'process',
31
+ concurrency: 'serial',
32
+ delegatesResourceLocks: true, // 与 sub-agent 一致:锁由内层工具取,避免父子自锁
33
+ supportsAbort: true,
34
+ },
35
+ async execute(args, ctx) {
36
+ return runSkill({
37
+ name: String(args.name ?? ''),
38
+ args: args.args,
39
+ context: typeof args.context === 'string' ? args.context : undefined,
40
+ }, ctx);
41
+ },
42
+ };
@@ -1,10 +1,21 @@
1
- import { getSkillBody } from '../../skills/index.js';
1
+ import { findSkill } from '../../skills/index.js';
2
+ import { renderSkillBody, readSkillFile } from '../../skills/runner.js';
3
+ import { activateSkill } from '../../skills/activation.js';
2
4
  // ---------- use_skill ----------
3
5
  // 模型按需加载某 skill 的 SKILL.md 正文(渐进式披露第②层)。
4
6
  // 系统提示里已列出可用 skill 的 name + description(何时用),模型据此决定调用。
7
+ //
8
+ // 设计 §3.3 升级:
9
+ // - args:渲染 $ARGUMENTS / $1..$9 / ${SKILL_DIR}
10
+ // - file:读 skill 目录内附属文件(L2 披露,jail 约束)
11
+ // - context: fork 的 skill 不返回正文,改为引导调 run_skill(隔离白做才是真隔离)
12
+ // - inline skill 成功加载后激活会话级工具面约束(allowed/disallowed-tools)
13
+ const MAX_SKILL_FILE = 200_000;
5
14
  export const useSkillTool = {
6
15
  name: 'use_skill',
7
- description: 'Load the full SKILL.md instructions for a given skill. See the skill list in the system prompt for when to use each.',
16
+ description: 'Load the full SKILL.md instructions for a given skill. See the skill list in the system prompt for when to use each. ' +
17
+ 'Supports args (renders $ARGUMENTS / $1.. / ${SKILL_DIR}) and file (reads a bundled reference file). ' +
18
+ 'For skills marked [fork], this returns a guide to call run_skill instead of loading the body inline.',
8
19
  parameters: {
9
20
  type: 'object',
10
21
  properties: {
@@ -12,16 +23,43 @@ export const useSkillTool = {
12
23
  type: 'string',
13
24
  description: 'Name of the skill to load (see the skill list in the system prompt, or the /skills command)',
14
25
  },
26
+ args: {
27
+ type: 'object',
28
+ description: 'Arguments rendered into the skill body ($ARGUMENTS, $1..$9). Optional.',
29
+ },
30
+ file: {
31
+ type: 'string',
32
+ description: 'Optional bundled file inside the skill directory to read (e.g. references/api.md). Subject to jail bounds.',
33
+ },
15
34
  },
16
35
  required: ['name'],
17
36
  },
18
- async execute(args) {
37
+ async execute(args, ctx) {
19
38
  const name = String(args.name ?? '').trim();
20
39
  if (!name)
21
40
  return '错误:缺少 skill 名。用 /skills 查看可用 skill 列表。';
22
- const body = getSkillBody(name);
23
- if (body === null)
41
+ const skill = findSkill(name);
42
+ if (!skill)
24
43
  return `错误:未找到 skill "${name}"。用 /skills 查看可用 skill 列表。`;
44
+ // fork skill:不把正文读进主上下文,引导走 run_skill。
45
+ if (skill.context === 'fork') {
46
+ return (`# Skill: ${name}\n\n` +
47
+ `该 skill 以隔离工作流(fork)形式执行。请勿在此加载其正文——调用 ` +
48
+ `\`run_skill({ name: "${name}"${Object.keys(args.args ?? {}).length ? ', args: {...}' : ''} })\` ` +
49
+ `即可在隔离子 agent 中执行并返回摘要。`);
50
+ }
51
+ // file 优先:L2 渐进式披露
52
+ if (typeof args.file === 'string' && args.file.trim()) {
53
+ const content = readSkillFile(skill, args.file.trim(), MAX_SKILL_FILE);
54
+ if (content === null)
55
+ return `错误:无法读取 skill "${name}" 的文件 "${args.file}"(不存在 / 越界 / 过大)。`;
56
+ return `# Skill: ${name} · ${args.file}\n\n${content}`;
57
+ }
58
+ const body = await renderSkillBody(skill, args.args, ctx?.signal);
59
+ if (body === null)
60
+ return `错误:未找到 skill "${name}" 的正文。用 /skills 查看可用 skill 列表。`;
61
+ // 激活 inline skill 的工具面约束(allowed/disallowed),本轮内生效。
62
+ activateSkill(skill);
25
63
  return `# Skill: ${name}\n\n${body}`;
26
64
  },
27
65
  };
@@ -1,5 +1,6 @@
1
1
  /** 工具共享的截断 / 上限 / 忽略规则。 */
2
2
  import { isMemoryEnabled, isSubAgentEnabled, isFrontendToolsEnabled } from '../config/index.js';
3
+ import { getActiveSkill } from '../skills/activation.js';
3
4
  export const MAX_FILE_LINES = 2000;
4
5
  export const MAX_OUTPUT = 20000;
5
6
  export const MAX_RESULTS = 100;
@@ -22,6 +23,11 @@ export const DECAY_DAYS = 30;
22
23
  export const GC_DAYS = 90;
23
24
  /** memory_search 结果(召回的记忆正文)的放宽上限:指令性内容,中截破坏语义,对齐 use_skill。 */
24
25
  export const MAX_MEMORY_RESULT = 64000;
26
+ // ── 知识图谱层(memory-graph.json,单 scope 容量)──────────────────────────
27
+ /** 单 scope 实体封顶:超限先清孤儿实体(无 active 边相连),仍超则拒绝新建。 */
28
+ export const MAX_GRAPH_ENTITIES = 500;
29
+ /** 单 scope 边封顶:超限先清已失效边,仍超则拒绝新边。 */
30
+ export const MAX_GRAPH_EDGES = 2000;
25
31
  // .codegraph:codegraph 索引目录(codegraph.db 是 SQLite 二进制 + daemon.log),
26
32
  // grep/glob 扫它无意义且会产出数 KB 的超长「行」,污染 TUI 展开渲染。
27
33
  export const IGNORE = ['**/node_modules/**', '**/.git/**', '**/.codegraph/**'];
@@ -55,7 +61,9 @@ export const PLAN_DISABLED_TOOLS = new Set([
55
61
  'memory_save',
56
62
  'memory_update',
57
63
  'memory_forget',
64
+ 'memory_graph', // 混合工具(add 写图),plan 只读模式整体屏蔽
58
65
  'sub-agent',
66
+ 'run_skill', // fork 子 agent 执行面;plan 模式不应派生子工作流
59
67
  ]);
60
68
  /**
61
69
  * 按当前 isMemoryEnabled() 现算 plan 模式应屏蔽的工具。
@@ -89,5 +97,11 @@ export function getRuntimeDisabledTools() {
89
97
  for (const name of FRONTEND_TOOLS)
90
98
  disabled.add(name);
91
99
  }
100
+ // inline skill 激活态的 disallowed-tools:即便模型幻觉调用也执行不了(设计 §3.6)。
101
+ const active = getActiveSkill();
102
+ if (active?.disallowed) {
103
+ for (const name of active.disallowed)
104
+ disabled.add(name);
105
+ }
92
106
  return disabled;
93
107
  }
package/dist/ui/layout.js CHANGED
@@ -1361,8 +1361,8 @@ function composeSpinnerLine(status, cols) {
1361
1361
  leadW = 1 + 1 + displayWidth(status.status) + (elapsed ? 1 + displayWidth(elapsed) : 0);
1362
1362
  }
1363
1363
  else if (spinning) {
1364
- // 运行态心跳帧(流式输出中):帧 + 生成中 + 走时
1365
- const label = '生成中';
1364
+ // 运行态心跳帧(流式输出中 / 命令态如 /rollback /compact /resume):帧 + 状态文字(优先)或生成中(兜底) + 走时
1365
+ const label = status.status || '生成中';
1366
1366
  const ePart = elapsed ? ` ${ui.dim}${elapsed}${ui.reset}` : '';
1367
1367
  lead = `${ui.bold}${ui.accent}${RUNNING_FRAMES[runningFrame]}${ui.reset} ${ui.dim}${label}${ui.reset}${ePart}`;
1368
1368
  leadW = 1 + 1 + displayWidth(label) + (elapsed ? 1 + displayWidth(elapsed) : 0);
package/dist/ui/theme.js CHANGED
@@ -29,7 +29,7 @@ const DEFAULT = {
29
29
  brightCyan: '\x1B[38;2;86;182;194m',
30
30
  brightMagenta: '\x1B[38;2;198;120;221m',
31
31
  accent: '\x1B[38;2;86;182;194m', // 与 cyan 同源(One Dark):logo/标题/输入框顶线/选中项统一承载
32
- userBg: '\x1B[48;2;44;49;60m', // One Dark current-line bg,深色终端上微妙可辨
32
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
33
33
  // diff 行底色:One Dark bg #282c34 上加 ~14% 亮度的对应色,够辨识但不刺眼
34
34
  addBg: '\x1B[48;2;44;62;42m', // 偏暗绿(One Dark green(152,195,121)暗化)
35
35
  delBg: '\x1B[48;2;62;38;42m', // 偏暗红(One Dark red(224,108,117)暗化)
@@ -54,7 +54,7 @@ const THEMES = {
54
54
  brightCyan: '\x1B[38;2;42;161;152m',
55
55
  brightMagenta: '\x1B[38;2;108;113;196m',
56
56
  accent: '\x1B[38;2;38;139;210m', // Solarized blue:浅底下更醒目的强调
57
- userBg: '\x1B[48;2;238;232;213m',
57
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
58
58
  // diff 行底色:Solarized Light base2(238,232,213)上贴同色族浅 tint,
59
59
  // 比直接用 base1 更柔,跟深 fg(red/green)对比充足
60
60
  addBg: '\x1B[48;2;220;235;205m',
@@ -72,7 +72,7 @@ const THEMES = {
72
72
  brightCyan: '\x1B[38;2;147;161;161m',
73
73
  brightMagenta: '\x1B[38;2;108;113;196m',
74
74
  accent: '\x1B[38;2;42;161;152m', // Solarized cyan(深底版):暗底上跳出
75
- userBg: '\x1B[48;2;7;54;66m',
75
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
76
76
  // diff 行底色:Solarized Dark base03(0,43,54)上加对应色族暗 tint
77
77
  addBg: '\x1B[48;2;20;50;38m',
78
78
  delBg: '\x1B[48;2;55;30;30m',
@@ -89,7 +89,7 @@ const THEMES = {
89
89
  brightCyan: '\x1B[38;2;142;192;124m',
90
90
  brightMagenta: '\x1B[38;2;211;134;155m',
91
91
  accent: '\x1B[38;2;250;189;47m', // Gruvbox yellow(主题色)
92
- userBg: '\x1B[48;2;40;40;40m',
92
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
93
93
  // diff 行底色:Gruvbox dark bg(40,40,40)上贴暗 bg0_a 风格
94
94
  addBg: '\x1B[48;2;40;55;30m',
95
95
  delBg: '\x1B[48;2;70;35;30m',
@@ -106,7 +106,7 @@ const THEMES = {
106
106
  brightCyan: '\x1B[38;2;136;192;208m',
107
107
  brightMagenta: '\x1B[38;2;180;142;173m',
108
108
  accent: '\x1B[38;2;136;192;208m', // Nord 浅冰蓝(主题色)
109
- userBg: '\x1B[48;2;67;76;94m',
109
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
110
110
  // diff 行底色:Nord polar night(46,52,64)上贴对应色族暗 tint
111
111
  addBg: '\x1B[48;2;46;66;52m',
112
112
  delBg: '\x1B[48;2;72;46;52m',
@@ -126,7 +126,7 @@ const THEMES = {
126
126
  brightCyan: '\x1B[38;2;160;220;220m',
127
127
  brightMagenta: '\x1B[38;2;245;165;215m',
128
128
  accent: '\x1B[38;2;255;170;60m', // 南瓜橙(主题主色:logo/标题/输入框顶线/选中项)
129
- userBg: '\x1B[48;2;55;42;32m', // 暖深棕(略亮于 #231c16 背景)
129
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
130
130
  // diff 行底色:暖深棕底上贴对应色族暗 tint,跟 fg 配对柔和可辨
131
131
  addBg: '\x1B[48;2;55;70;35m',
132
132
  delBg: '\x1B[48;2;78;42;32m',
@@ -145,7 +145,7 @@ const THEMES = {
145
145
  brightCyan: '\x1B[38;2;170;220;220m',
146
146
  brightMagenta: '\x1B[38;2;250;160;200m',
147
147
  accent: '\x1B[38;2;230;90;150m', // 玫粉(主题主色)
148
- userBg: '\x1B[48;2;50;32;46m', // 深紫底
148
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
149
149
  // diff 行底色:深紫底贴对应色族暗 tint,绿暗化偏橄榄、红暗化偏紫红
150
150
  addBg: '\x1B[48;2;50;60;42m',
151
151
  delBg: '\x1B[48;2;75;40;52m',
@@ -164,7 +164,7 @@ const THEMES = {
164
164
  brightCyan: '\x1B[38;2;140;230;210m',
165
165
  brightMagenta: '\x1B[38;2;210;170;230m',
166
166
  accent: '\x1B[38;2;80;210;140m', // 翡翠绿(主题主色)
167
- userBg: '\x1B[48;2;18;42;34m', // 深绿底
167
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
168
168
  // diff 行底色:深绿底贴对应色族暗 tint,绿暗化偏深绿、红暗化偏暗红
169
169
  addBg: '\x1B[48;2;30;55;40m',
170
170
  delBg: '\x1B[48;2;60;40;40m',
@@ -183,7 +183,7 @@ const THEMES = {
183
183
  brightCyan: '\x1B[38;2;170;210;200m',
184
184
  brightMagenta: '\x1B[38;2;240;180;210m',
185
185
  accent: '\x1B[38;2;255;200;80m', // 琥珀金黄(主题主色)
186
- userBg: '\x1B[48;2;43;31;14m', // 深棕底(比 orange 略浅,避免撞色)
186
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
187
187
  // diff 行底色:深棕底贴对应色族暗 tint,绿暗化偏橄榄、红暗化偏暗棕红
188
188
  addBg: '\x1B[48;2;50;55;25m',
189
189
  delBg: '\x1B[48;2;70;40;28m',
@@ -202,7 +202,7 @@ const THEMES = {
202
202
  brightCyan: '\x1B[38;2;180;230;230m',
203
203
  brightMagenta: '\x1B[38;2;210;180;250m',
204
204
  accent: '\x1B[38;2;180;150;230m', // 薰衣草紫(主题主色)
205
- userBg: '\x1B[48;2;31;26;46m', // 深紫底(比 rose 更冷)
205
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
206
206
  // diff 行底色:深紫底贴对应色族暗 tint,绿暗化偏冷绿、红暗化偏冷紫红
207
207
  addBg: '\x1B[48;2;38;46;42m',
208
208
  delBg: '\x1B[48;2;60;40;55m',
@@ -221,7 +221,7 @@ const THEMES = {
221
221
  brightCyan: '\x1B[38;2;170;225;215m',
222
222
  brightMagenta: '\x1B[38;2;250;170;210m',
223
223
  accent: '\x1B[38;2;255;120;100m', // 珊瑚红(主题主色)
224
- userBg: '\x1B[48;2;42;22;20m', // 深棕红底
224
+ userBg: '\x1B[48;2;72;78;90m', // 统一灰白用户消息底(黑底终端可见)
225
225
  // diff 行底色:深棕红底贴对应色族暗 tint,绿暗化偏橄榄、红暗化偏暗棕红
226
226
  addBg: '\x1B[48;2;50;55;30m',
227
227
  delBg: '\x1B[48;2;75;32;30m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocode-ai",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "终端编码 agent:LLM + tool-call 循环 + 流式输出(含思考)+ 16 个工具,接任意 OpenAI 兼容后端。",
5
5
  "type": "module",
6
6
  "bin": {