codemini-cli 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,13 +19,13 @@ It is designed for teams that want a coding assistant that feels practical, cont
19
19
 
20
20
  ### Highlights
21
21
 
22
- - Minimal default tools: `run_command`, `read_file`, `write_file`
22
+ - Minimal default tools: `run`, `read`, `write`
23
23
  - Windows-aware shell profile with PowerShell-focused defaults
24
24
  - Safe mode enabled by default
25
25
  - Built-in lite skills for planning, execution, and collaboration
26
26
  - Configurable reply language through `ui.reply_language` (`zh` / `en`)
27
27
  - Richer slash completion with priority sorting, inline descriptions, and left/right paging
28
- - Structured code tools for small models: `locate`, `open_target`, `edit_target`
28
+ - Structured code tools for small models: `grep`, `read`, `edit`
29
29
  - More conservative `plan auto` acceptance checks with reviewer/tester goal checklists
30
30
  - Tone presets through `soul`, without changing plans or code behavior
31
31
  - Example soul presets include `professional`, `playful`, `anime`, `pirate`, `caveman`, and `ceo`
@@ -66,7 +66,7 @@ codemini skill list|install|enable|disable|inspect|reindex
66
66
  - Slash completion now prioritizes important commands and config keys, shows short descriptions, and supports `←/→` page switching
67
67
  - Ambiguous feature requests can pause for lightweight brainstorming first, and `/brainstorm <question>` gives an explicit way to compare options before coding
68
68
  - `plan auto` now turns the original goal into an acceptance checklist, uses a lighter chain only for truly tiny tasks, and treats unmet checklist items as failure signals
69
- - Structured code tools reduce shell-noise for small models by preferring `locate -> open_target -> edit_target`
69
+ - Structured code tools reduce shell-noise for small models by preferring `grep/read -> edit`
70
70
 
71
71
  ### Skill Loading
72
72
 
@@ -134,13 +134,13 @@ CodeMini CLI 是一个为小模型工作流优化过的代码助手 CLI,重点
134
134
 
135
135
  ### 主要特点
136
136
 
137
- - 默认工具极简:`run_command`、`read_file`、`write_file`
137
+ - 默认工具极简:`run`、`read`、`write`
138
138
  - 面向 Windows 的 PowerShell 默认配置
139
139
  - safe mode 默认开启
140
140
  - 内置 lite skills,覆盖规划、执行和协作
141
141
  - 支持通过 `ui.reply_language` 配置回复语言,当前支持 `zh` / `en`
142
142
  - slash 补全支持优先级排序、右侧简短说明和左右分页
143
- - 为小模型补了结构化代码工具:`locate`、`open_target`、`edit_target`
143
+ - 为小模型补了结构化代码工具:`grep`、`read`、`edit`
144
144
  - `plan auto` 会基于原始目标生成验收清单,并更保守地处理 reviewer/tester 结果
145
145
  - `soul` 只影响语气,不影响计划和代码行为
146
146
  - 可用的 `soul` 示例包括 `professional`、`playful`、`anime`、`pirate`、`caveman`、`ceo`
@@ -181,7 +181,7 @@ codemini skill list|install|enable|disable|inspect|reindex
181
181
  - slash 补全会优先展示更重要的命令和配置项,显示简短说明,并支持 `←/→` 翻页
182
182
  - 对于需求仍不明确的功能请求,CLI 会先偏向轻量 brainstorm;也可以显式使用 `/brainstorm <问题>` 先比较方案再决定是否编码
183
183
  - `plan auto` 会先把原始目标展开成验收清单;只有真正很小的任务才会走轻量链路;如果 reviewer 或 tester 标记了未满足或未验证的验收项,就不会按成功处理
184
- - 为了减少小模型被 shell 原始输出干扰,新增了 `locate -> open_target -> edit_target` 这套结构化代码工具流
184
+ - 为了减少小模型被 shell 原始输出干扰,新增了 `grep/read -> edit` 这套结构化代码工具流
185
185
 
186
186
  ### Skill 加载位置
187
187
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemini-cli",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
5
5
  "keywords": [
6
6
  "cli",
@@ -34,16 +34,17 @@ Routing:
34
34
  - do not assume features, storage model, or scope unless the user already gave them
35
35
 
36
36
  Tool order:
37
- - prefer `locate` first for repo search and candidate discovery
38
- - use `open_target` to inspect the smallest useful code block with edit metadata
39
- - use `edit_target` for minimal validated edits
40
- - use `search_code`, `read_block`, and `read_symbol_context` when lower-level structured context is needed
37
+ - prefer `grep` first for content search and candidate discovery
38
+ - use `read` to inspect the smallest useful code block
39
+ - use `edit` for minimal focused edits or direct whole-file rewrites when you already have the replacement content
40
+ - use `generate_diff` and `patch` for larger edits or when you already have a diff
41
+ - use `glob` and `list` when you need file or directory discovery
41
42
  - use shell search such as `rg` only as a fallback when structured tools are not enough
42
43
 
43
44
  Core rules:
44
45
 
45
46
  1. Search first.
46
- Prefer structured search before broad file reads. Start with `locate`, then inspect with `open_target`, and only fall back to shell search such as `rg` when the structured tools are not enough.
47
+ Prefer structured search before broad file reads. Start with `grep`, then inspect with `read`, and only fall back to shell search such as `rg` when the structured tools are not enough.
47
48
 
48
49
  2. Keep context tight.
49
50
  Do not carry full conversation history into every step. Summarize, narrow scope, and work from the most recent relevant evidence.
@@ -62,18 +63,18 @@ If the requested behavior, scope, or acceptance is unclear, do not jump into imp
62
63
  - clear enough to build -> proceed
63
64
 
64
65
  5. Read and write with intent.
65
- Use `open_target`, `read_block`, and `read_symbol_context` before `read_file` when possible. Use `edit_target` for focused edits. Use `write_file` only for full-file writes. Avoid unnecessary tool calls and avoid rereading the same file without a reason.
66
+ Use `read` before broad reads when possible. Use `edit` for focused edits or when you already have the complete replacement content. Use `generate_diff` and `patch` for larger changes. Use `write` only for creating new files or explicit whole-file writes. Avoid unnecessary tool calls and avoid rereading the same file without a reason.
66
67
 
67
68
  6. Verify before claiming success.
68
69
  Run the relevant test, check, or command before saying work is fixed or complete.
69
70
 
70
71
  Default workflow:
71
- - Search with `locate`
72
- - Inspect local context with `open_target`
72
+ - Search with `grep`
73
+ - Inspect local context with `read`
73
74
  - If the request is unclear, first decide: ask one question, brainstorm, or proceed
74
75
  - Plan the next smallest step
75
76
  - Delegate if the work is independent
76
- - Edit with `edit_target`
77
+ - Edit with `edit`
77
78
  - Verify
78
79
  - Summarize briefly
79
80
 
package/src/cli.js CHANGED
@@ -4,7 +4,7 @@ import { handleConfig } from './commands/config.js';
4
4
  import { handleDoctor } from './commands/doctor.js';
5
5
  import { handleSkill } from './commands/skill.js';
6
6
 
7
- const VERSION = '0.1.17';
7
+ const VERSION = '0.1.18';
8
8
 
9
9
  function printHelp() {
10
10
  console.log(`codemini ${VERSION}
@@ -26,11 +26,19 @@ function summarizeToolResult(result) {
26
26
  const p = String(obj.path || '');
27
27
  const action = String(obj.action || 'write');
28
28
  const line = Number(obj.changed_line || 1);
29
- const preview = String(obj.diff_preview || '')
30
- .split('\n')
31
- .slice(0, 3)
32
- .join('\n');
33
- return `${action} ${p} @L${line}${preview ? `\n${preview}` : ''}`;
29
+ const suffix =
30
+ action === 'delete'
31
+ ? 'deleted'
32
+ : action === 'create'
33
+ ? 'created'
34
+ : action === 'patch'
35
+ ? 'patched'
36
+ : action === 'replace_block' || action === 'replace_text'
37
+ ? 'edited'
38
+ : action === 'append'
39
+ ? 'appended'
40
+ : 'updated';
41
+ return p ? `${suffix} ${p}${line > 0 ? ` @L${line}` : ''}` : suffix;
34
42
  }
35
43
  if ('path' in obj && 'phase' in obj) {
36
44
  const phase = String(obj.phase || '');
@@ -79,6 +87,13 @@ function summarizeToolResult(result) {
79
87
  const logs = Array.isArray(obj.recent_logs) ? trimInline(obj.recent_logs.slice(-1)[0] || '', 96) : '';
80
88
  return `${taskId || 'service logs'}${logs ? `\n${logs}` : ''}`;
81
89
  }
90
+ if ('files' in obj && Array.isArray(obj.files)) {
91
+ return `patched ${obj.files.length} file(s)`;
92
+ }
93
+ if ('diff' in obj && 'new_hash' in obj && 'path' in obj) {
94
+ const p = String(obj.path || '');
95
+ return p ? `diff preview for ${p}` : 'diff preview';
96
+ }
82
97
  if ('created' in obj && Array.isArray(obj.created)) {
83
98
  return `created ${obj.created.length} task(s)`;
84
99
  }
@@ -98,21 +113,45 @@ function trimInline(value, maxLen = 72) {
98
113
  return `${s.slice(0, maxLen - 3)}...`;
99
114
  }
100
115
 
116
+ function normalizeToolCallName(name) {
117
+ return String(name || '').trim();
118
+ }
119
+
101
120
  function formatToolDisplayName(name, args) {
102
- if (name === 'read_file' || name === 'write_file') {
121
+ if (name === 'grep') {
122
+ const query = trimInline(args?.pattern || args?.query || args?.symbol || '', 96);
123
+ return query ? `grep("${query}")` : 'grep';
124
+ }
125
+ if (name === 'glob') {
126
+ const pattern = trimInline(args?.pattern || '', 96);
127
+ return pattern ? `glob("${pattern}")` : 'glob';
128
+ }
129
+ if (name === 'list') {
130
+ const target = trimInline(args?.path || '.', 96) || '.';
131
+ return `list(${target})`;
132
+ }
133
+ if (name === 'read' || name === 'write') {
103
134
  const target = trimInline(args?.path || '.', 96) || '.';
104
- if (name === 'read_file') {
135
+ if (name === 'read') {
105
136
  const start = Number(args?.start_line);
106
137
  const end = Number(args?.end_line);
107
138
  const hasRange = Number.isFinite(start) && start > 0;
108
139
  const suffix = hasRange ? `:${start}-${Number.isFinite(end) && end >= start ? end : start}` : '';
109
- return `${name}(${target}${suffix})`;
140
+ return `read(${target}${suffix})`;
110
141
  }
111
- return `${name}(${target})`;
142
+ return `write(${target})`;
112
143
  }
113
- if (name === 'run_command') {
144
+ if (name === 'run') {
114
145
  const command = trimInline(args?.command || '', 96);
115
- return command ? `${name}(${command})` : name;
146
+ return command ? `run(${command})` : name;
147
+ }
148
+ if (name === 'edit') {
149
+ const target = trimInline(args?.path || args?.file || '.', 96) || '.';
150
+ return `edit(${target})`;
151
+ }
152
+ if (name === 'patch') {
153
+ const target = trimInline(args?.path || args?.file || args?.patch || '', 96) || '.';
154
+ return `patch(${target})`;
116
155
  }
117
156
  if (name === 'start_service') {
118
157
  const command = trimInline(args?.command || args?.cmd || '', 96);
@@ -125,20 +164,7 @@ function formatToolDisplayName(name, args) {
125
164
  const taskId = trimInline(args?.task_id || args?.taskId || '', 96);
126
165
  return taskId ? `${name}(${taskId})` : name;
127
166
  }
128
- if (
129
- name === 'locate' ||
130
- name === 'open_target' ||
131
- name === 'edit_target' ||
132
- name === 'search_code' ||
133
- name === 'read_block' ||
134
- name === 'read_symbol_context' ||
135
- name === 'validate_edit' ||
136
- name === 'replace_block' ||
137
- name === 'replace_text' ||
138
- name === 'insert_before' ||
139
- name === 'insert_after' ||
140
- name === 'generate_diff'
141
- ) {
167
+ if (name === 'read' || name === 'write' || name === 'run' || name === 'grep' || name === 'glob' || name === 'list' || name === 'edit' || name === 'patch' || name === 'generate_diff') {
142
168
  const target = trimInline(args?.path || args?.query || args?.symbol || '', 96);
143
169
  return target ? `${name}(${target})` : name;
144
170
  }
@@ -218,15 +244,16 @@ export async function runAgentLoop({
218
244
 
219
245
  for (const call of toolCalls) {
220
246
  const args = safeJsonParse(call.arguments);
221
- const displayName = formatToolDisplayName(call.name, args);
247
+ const toolName = normalizeToolCallName(call.name);
248
+ const displayName = formatToolDisplayName(toolName, args);
222
249
  const startedAt = Date.now();
223
250
  let approved = true;
224
- if (executionMode === 'normal' && !alwaysAllowSet.has(call.name)) {
251
+ if (executionMode === 'normal' && !alwaysAllowSet.has(toolName)) {
225
252
  approved = false;
226
253
  if (typeof requestToolApproval === 'function') {
227
254
  const decision = await requestToolApproval({
228
255
  id: call.id,
229
- name: call.name,
256
+ name: toolName,
230
257
  displayName,
231
258
  arguments: args
232
259
  });
@@ -255,7 +282,7 @@ export async function runAgentLoop({
255
282
  }
256
283
 
257
284
  if (onEvent) onEvent({ type: 'tool:start', name: displayName, id: call.id });
258
- const handler = toolHandlers[call.name];
285
+ const handler = toolHandlers[toolName];
259
286
  if (!handler) {
260
287
  throw new Error(`Unknown tool: ${call.name}`);
261
288
  }
@@ -94,6 +94,8 @@ function describeConfigKey(key, mode = 'set') {
94
94
  'shell.default': 'default shell',
95
95
  'shell.timeout_ms': 'shell timeout in milliseconds',
96
96
  'context.max_tokens': 'context token budget',
97
+ 'soul.preset': 'soul preset',
98
+ 'soul.custom_path': 'custom soul prompt path',
97
99
  'policy.safe_mode': 'safe mode switch',
98
100
  'policy.allow_dangerous_commands': 'dangerous command allowance'
99
101
  };
@@ -1116,12 +1118,28 @@ function effectiveMaxContextTokens(config) {
1116
1118
  }
1117
1119
 
1118
1120
  function buildRuntimeStateSnapshot({ currentSession, config, model, executionMode }) {
1119
- return {
1121
+ const currentContextTokens = estimateMessagesTokens(currentSession?.messages || []);
1122
+ const maxContextTokens = effectiveMaxContextTokens(config);
1123
+ const contextUsagePct = maxContextTokens > 0 ? Math.min(100, Math.max(0, (currentContextTokens / maxContextTokens) * 100)) : 0;
1124
+ const snapshot = {
1120
1125
  sessionId: currentSession?.id || '',
1121
1126
  mode: executionMode || config.execution?.mode || 'auto',
1122
1127
  model: model || config.model?.name || '',
1123
- maxContextTokens: effectiveMaxContextTokens(config)
1128
+ maxContextTokens
1124
1129
  };
1130
+ Object.defineProperties(snapshot, {
1131
+ currentContextTokens: {
1132
+ value: currentContextTokens,
1133
+ enumerable: false,
1134
+ writable: false
1135
+ },
1136
+ contextUsagePct: {
1137
+ value: contextUsagePct,
1138
+ enumerable: false,
1139
+ writable: false
1140
+ }
1141
+ });
1142
+ return snapshot;
1125
1143
  }
1126
1144
 
1127
1145
  function estimatePromptTokensForRequest(sessionMessages, userText = '') {
@@ -1345,7 +1363,7 @@ async function askModel({
1345
1363
  onEvent: wrappedAgentEvent,
1346
1364
  executionMode: executionMode || config.execution?.mode || 'auto',
1347
1365
  alwaysAllowTools:
1348
- alwaysAllowTools || config.execution?.always_allow_tools || ['run_command', 'read_file', 'write_file'],
1366
+ alwaysAllowTools || config.execution?.always_allow_tools || ['run', 'read', 'write'],
1349
1367
  toolResultMaxChars: config.context?.tool_result_max_chars || 12000,
1350
1368
  requestCompletion: async ({ messages, tools, model: selectedModel }) => {
1351
1369
  if (onAgentEvent) onAgentEvent({ type: 'assistant:start' });
@@ -1720,6 +1738,8 @@ export async function createChatRuntime({
1720
1738
  'sessions.retention_days',
1721
1739
  'shell.timeout_ms',
1722
1740
  'context.max_tokens',
1741
+ 'soul.preset',
1742
+ 'soul.custom_path',
1723
1743
  'policy.safe_mode',
1724
1744
  'policy.allow_dangerous_commands'
1725
1745
  ];
@@ -1851,9 +1871,21 @@ export async function createChatRuntime({
1851
1871
  const body = input.slice(1);
1852
1872
  const tokens = body.trim().split(/\s+/).filter(Boolean);
1853
1873
  const commandPart = tokens[0] || '';
1874
+ const commandHasSubcommands = new Set([
1875
+ 'config',
1876
+ 'compact',
1877
+ 'mode',
1878
+ 'tasks',
1879
+ 'checkpoint',
1880
+ 'plan',
1881
+ 'agents',
1882
+ 'history',
1883
+ 'debug'
1884
+ ]);
1854
1885
 
1855
1886
  const allCommandEntries = listCommandNames();
1856
1887
  const allCommands = allCommandEntries.map((c) => c.name);
1888
+ const exactCommand = Boolean(commandPart) && allCommands.includes(commandPart);
1857
1889
  for (const entry of allCommandEntries) {
1858
1890
  registerSuggestion(`/${entry.name}`, entry.description || '');
1859
1891
  }
@@ -1879,7 +1911,7 @@ export async function createChatRuntime({
1879
1911
  ));
1880
1912
  }
1881
1913
 
1882
- if (tokens.length === 1 && !hasTrailingSpace) {
1914
+ if (tokens.length === 1 && !hasTrailingSpace && !(exactCommand && commandHasSubcommands.has(commandPart))) {
1883
1915
  const direct = prioritizeByPreferredOrder(
1884
1916
  allCommands
1885
1917
  .filter((name) => name.startsWith(commandPart))
@@ -1891,25 +1923,26 @@ export async function createChatRuntime({
1891
1923
  }
1892
1924
 
1893
1925
  if (commandPart === 'config') {
1894
- if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
1895
- const sub = tokens[1] || '';
1926
+ const subcommand = tokens[1] || '';
1927
+ const subcommandIsExact = ['set', 'get', 'list', 'reset'].includes(subcommand);
1928
+
1929
+ if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace && !subcommandIsExact)) {
1896
1930
  return materializeSuggestions(prioritizeByPreferredOrder(
1897
1931
  ['set', 'get', 'list', 'reset']
1898
- .filter((s) => s.startsWith(sub))
1932
+ .filter((s) => s.startsWith(subcommand))
1899
1933
  .map((s) => registerSuggestion(`/config ${s}`, configSubcommandDescriptions[`/config ${s}`] || 'config command').value),
1900
1934
  configSubcommandPriority
1901
1935
  ));
1902
1936
  }
1903
1937
 
1904
- const sub = tokens[1] || '';
1905
- if (sub === 'get') {
1906
- const keyPrefix = tokens[2] || '';
1938
+ if (subcommand === 'get') {
1939
+ const keyPrefix = tokens.length >= 3 ? tokens[2] || '' : '';
1907
1940
  return configKeyHints
1908
1941
  .filter((k) => k.startsWith(keyPrefix))
1909
1942
  .map((k) => registerSuggestion(`/config get ${k}`, describeConfigKey(k, 'get')));
1910
1943
  }
1911
- if (sub === 'set') {
1912
- const keyPrefix = tokens[2] || '';
1944
+ if (subcommand === 'set') {
1945
+ const keyPrefix = tokens.length >= 3 ? tokens[2] || '' : '';
1913
1946
  return configKeyHints
1914
1947
  .filter((k) => k.startsWith(keyPrefix))
1915
1948
  .map((k) => registerSuggestion(`/config set ${k} `, describeConfigKey(k, 'set')));
@@ -1920,6 +1953,11 @@ export async function createChatRuntime({
1920
1953
 
1921
1954
  if (commandPart === 'compact') {
1922
1955
  const joined = tokens.slice(1).join(' ');
1956
+ if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
1957
+ return compactOptions
1958
+ .filter((opt) => opt.startsWith(joined) || joined === '')
1959
+ .map((opt) => registerSuggestion(`/compact ${opt}`, 'context compaction command'));
1960
+ }
1923
1961
  return compactOptions
1924
1962
  .filter((opt) => opt.includes(joined) || joined === '')
1925
1963
  .map((opt) => registerSuggestion(`/compact ${opt}`, 'context compaction command'));
@@ -1952,6 +1990,10 @@ export async function createChatRuntime({
1952
1990
  if (commandPart === 'checkpoint') {
1953
1991
  if (tokens.length <= 2 && !hasTrailingSpace) {
1954
1992
  const sub = tokens[1] || '';
1993
+ if (sub === 'list') {
1994
+ return ['--all']
1995
+ .map((v) => registerSuggestion(`/checkpoint list ${v}`, 'checkpoint command'));
1996
+ }
1955
1997
  return ['create', 'list', 'load']
1956
1998
  .filter((s) => s.startsWith(sub))
1957
1999
  .map((s) => registerSuggestion(`/checkpoint ${s}`, 'checkpoint command'));
@@ -1987,6 +2029,10 @@ export async function createChatRuntime({
1987
2029
  if (commandPart === 'agents') {
1988
2030
  if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
1989
2031
  const sub = tokens[1] || '';
2032
+ if (sub === 'run') {
2033
+ return ['planner', 'coder', 'reviewer', 'tester']
2034
+ .map((r) => registerSuggestion(`/agents run ${r} `, 'sub-agent command'));
2035
+ }
1990
2036
  return ['list', 'run']
1991
2037
  .filter((s) => s.startsWith(sub))
1992
2038
  .map((s) => registerSuggestion(`/agents ${s}`, 'sub-agent command'));
@@ -2001,13 +2047,22 @@ export async function createChatRuntime({
2001
2047
  }
2002
2048
 
2003
2049
  if (commandPart === 'history') {
2050
+ const sub = tokens[1] || '';
2004
2051
  if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
2005
- const sub = tokens[1] || '';
2052
+ if (sub === 'resume') {
2053
+ const dynamic = historySessionCache
2054
+ .filter((session) => String(session.id || '').startsWith(''))
2055
+ .map((session) => ({
2056
+ value: `/history resume ${session.id}`,
2057
+ display: `/history resume ${session.id} · ${Number(session.messageCount || 0)} msgs`,
2058
+ description: 'resume a saved session'
2059
+ }));
2060
+ if (dynamic.length > 0) return dynamic;
2061
+ }
2006
2062
  return ['list', 'current', 'resume']
2007
2063
  .filter((s) => s.startsWith(sub))
2008
- .map((s) => `/history ${s}`);
2064
+ .map((s) => registerSuggestion(`/history ${s}`, 'history command'));
2009
2065
  }
2010
- const sub = tokens[1] || '';
2011
2066
  if (sub === 'resume') {
2012
2067
  const idPrefix = tokens[2] || '';
2013
2068
  const dynamic = historySessionCache
@@ -2025,7 +2080,15 @@ export async function createChatRuntime({
2025
2080
 
2026
2081
  if (commandPart === 'debug') {
2027
2082
  const sub = tokens[1] || '';
2028
- if (!sub) return debugTemplates;
2083
+ if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
2084
+ if (sub === 'keys') {
2085
+ return ['on', 'off', 'status']
2086
+ .map((v) => registerSuggestion(`/debug keys ${v}`, 'keyboard debug command'));
2087
+ }
2088
+ return ['keys']
2089
+ .filter((s) => s.startsWith(sub))
2090
+ .map((s) => registerSuggestion(`/debug ${s}`, 'debug command'));
2091
+ }
2029
2092
  if (sub === 'keys') {
2030
2093
  const action = tokens[2] || '';
2031
2094
  return ['on', 'off', 'status']
@@ -16,13 +16,13 @@ function suggestionForToken(token, config) {
16
16
  const shell = String(config?.shell?.default || '').toLowerCase();
17
17
  if (token === 'find' || token === 'grep') {
18
18
  return shell === 'powershell'
19
- ? 'Prefer structured tools like locate and open_target first. If you need shell fallback, use allowed search and context commands such as Get-ChildItem, Select-String, Get-Content, or rg when available.'
20
- : 'Prefer structured tools like locate and open_target first. If you need shell fallback, use allowed search and context commands such as rg, find, grep, sed, cat, or ls.';
19
+ ? 'Prefer structured tools like grep, list, read, and edit first. If you need shell fallback, use allowed search and context commands such as Get-ChildItem, Select-String, Get-Content, or rg when available.'
20
+ : 'Prefer structured tools like grep, glob, list, read, and edit first. If you need shell fallback, use allowed search and context commands such as rg, find, grep, sed, cat, or ls.';
21
21
  }
22
22
  if (shell === 'powershell') {
23
- return 'Prefer structured tools like locate, open_target, and edit_target first. If you need shell fallback, use allowed shell commands for search and local context such as Get-ChildItem, Get-Content, Select-String, or rg when available.';
23
+ return 'Prefer structured tools like read, edit, write, grep, and list first. If you need shell fallback, use allowed shell commands for search and local context such as Get-ChildItem, Get-Content, Select-String, or rg when available.';
24
24
  }
25
- return 'Prefer structured tools like locate, open_target, and edit_target first. If you need shell fallback, use allowed shell commands for search and local context such as rg, find, grep, sed, cat, or ls.';
25
+ return 'Prefer structured tools like read, edit, write, grep, glob, and list first. If you need shell fallback, use allowed shell commands for search and local context such as rg, find, grep, sed, cat, or ls.';
26
26
  }
27
27
 
28
28
  export function evaluateCommandPolicy(command, config, workspaceRoot = process.cwd()) {
@@ -34,26 +34,20 @@ const DEFAULT_CONFIG = {
34
34
  execution: {
35
35
  mode: 'auto',
36
36
  always_allow_tools: [
37
- 'locate',
38
- 'open_target',
39
- 'edit_target',
40
- 'search_code',
41
- 'read_block',
42
- 'read_symbol_context',
43
- 'validate_edit',
44
- 'replace_block',
45
- 'replace_text',
46
- 'insert_before',
47
- 'insert_after',
37
+ 'read',
38
+ 'grep',
39
+ 'glob',
40
+ 'list',
41
+ 'edit',
42
+ 'write',
43
+ 'run',
44
+ 'patch',
48
45
  'generate_diff',
49
46
  'start_service',
50
47
  'list_services',
51
48
  'get_service_status',
52
49
  'get_service_logs',
53
- 'stop_service',
54
- 'run_command',
55
- 'read_file',
56
- 'write_file'
50
+ 'stop_service'
57
51
  ],
58
52
  max_steps: 16
59
53
  },
@@ -134,26 +128,19 @@ function normalizePolicyLists(config) {
134
128
  : [];
135
129
  next.execution.always_allow_tools = uniqueStrings(
136
130
  [
137
- 'locate',
138
- 'open_target',
139
- 'edit_target',
140
- 'search_code',
141
- 'read_block',
142
- 'read_symbol_context',
143
- 'validate_edit',
144
- 'replace_block',
145
- 'replace_text',
146
- 'insert_before',
147
- 'insert_after',
131
+ 'read',
132
+ 'grep',
133
+ 'glob',
134
+ 'list',
135
+ 'edit',
136
+ 'write',
137
+ 'run',
148
138
  'generate_diff',
149
139
  'start_service',
150
140
  'list_services',
151
141
  'get_service_status',
152
142
  'get_service_logs',
153
143
  'stop_service',
154
- 'run_command',
155
- 'read_file',
156
- 'write_file',
157
144
  ...rawTools
158
145
  ].filter((name) => String(name) !== 'list_files')
159
146
  );
@@ -118,5 +118,5 @@ export function getEffectivePolicy(config) {
118
118
 
119
119
  export function getShellSystemPrompt(value) {
120
120
  const profile = getShellProfile(value);
121
- return `You are CodeMini CLI working in a ${profile.label} shell environment. Prefer the high-level structured workflow first: use locate to find candidates, open_target to inspect the smallest useful block and receive edit metadata, and edit_target to apply minimal edits. When you need lower-level control, use search_code, read_block, read_symbol_context, validate_edit, replace_block, replace_text, insert_before, insert_after, and generate_diff. Use start_service, list_services, get_service_status, get_service_logs, and stop_service for long-running servers or watchers. Use run_command only for one-shot commands that should exit on their own. Use read_file only when structured reads are not enough. Use write_file only for full-file writes and always provide a concrete file path, not a directory. Avoid unnecessary tool calls.`;
121
+ return `You are CodeMini CLI working in a ${profile.label} shell environment. Prefer OpenCode-style primary tools first: use read to inspect files, grep to search file contents, glob to find files by pattern, list to inspect directories, edit to modify existing files, write to create or fully rewrite files when appropriate, patch to apply unified diffs, and run for one-shot shell commands. Treat edit as the default editing path for existing code. Internal low-level edit strategies such as target resolution, block replacement, exact text replacement, and anchored inserts are handled inside edit rather than exposed as separate tools. Use generate_diff when you need a structured preview of a proposed file change. Use start_service, list_services, get_service_status, get_service_logs, and stop_service for long-running servers or watchers. Use run only for one-shot commands that should exit on their own. For existing code files, prefer grep/read/edit and only use write with full_file_rewrite=true when a whole-file rewrite is truly intended. Avoid unnecessary tool calls.`;
122
122
  }