codemini-cli 0.2.4 → 0.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemini-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
5
5
  "keywords": [
6
6
  "cli",
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.2.4';
7
+ const VERSION = '0.2.6';
8
8
 
9
9
  function printHelp() {
10
10
  console.log(`codemini ${VERSION}
@@ -36,6 +36,38 @@ function isMiniMaxModel(model) {
36
36
  return String(model || '').toLowerCase().includes('minimax');
37
37
  }
38
38
 
39
+ function normalizeToolCallArguments(argumentsText) {
40
+ const raw = typeof argumentsText === 'string' ? argumentsText : JSON.stringify(argumentsText ?? {});
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
44
+ return JSON.stringify(parsed);
45
+ }
46
+ } catch {}
47
+ return '{}';
48
+ }
49
+
50
+ function sanitizeGatewayMessages(messages) {
51
+ const source = Array.isArray(messages) ? messages : [];
52
+ return source
53
+ .filter((message) => message && typeof message === 'object')
54
+ .map((message) => {
55
+ if (!Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
56
+ return message;
57
+ }
58
+ return {
59
+ ...message,
60
+ tool_calls: message.tool_calls.map((toolCall) => ({
61
+ ...toolCall,
62
+ function: {
63
+ ...toolCall?.function,
64
+ arguments: normalizeToolCallArguments(toolCall?.function?.arguments)
65
+ }
66
+ }))
67
+ };
68
+ });
69
+ }
70
+
39
71
  function sanitizeMiniMaxMessages(messages) {
40
72
  const source = Array.isArray(messages) ? messages : [];
41
73
  const out = [];
@@ -64,10 +96,11 @@ function sanitizeMiniMaxMessages(messages) {
64
96
  }
65
97
 
66
98
  function buildPayload({ model, temperature, messages, tools, stream = false }) {
99
+ const sanitizedMessages = sanitizeGatewayMessages(messages);
67
100
  const payload = {
68
101
  model,
69
102
  temperature,
70
- messages: isMiniMaxModel(model) ? sanitizeMiniMaxMessages(messages) : messages
103
+ messages: isMiniMaxModel(model) ? sanitizeMiniMaxMessages(sanitizedMessages) : sanitizedMessages
71
104
  };
72
105
  if (stream) {
73
106
  payload.stream = true;
@@ -142,6 +142,10 @@ Some tools are loaded on demand. If a needed tool is not listed, call tool_searc
142
142
 
143
143
  # Doing tasks
144
144
 
145
+ - You are a terminal-first CLI coding agent, not a generic chat assistant
146
+ - The user shares your workspace with you; prefer inspecting the project yourself before asking them to paste files that should be discoverable
147
+ - Before substantial tool work, send a short progress update to the user about what you are about to inspect or do
148
+ - Do not jump straight into tools without a brief user-facing note when the task is actionable
145
149
  - Search or read before editing unless the exact target is already known
146
150
  - If a command or tool is blocked or fails, inspect the error and retry with allowed commands or tools
147
151
  - For AST-scoped edits, if edit rejects due to missing or stale ast_target, fix arguments and retry
package/src/core/tools.js CHANGED
@@ -1840,11 +1840,11 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config, onSyste
1840
1840
  function: {
1841
1841
  name: 'write',
1842
1842
  description:
1843
- 'Create a new file or overwrite a file. Use this for new files or explicit full rewrites. Prefer edit for existing code changes.',
1843
+ 'Create a new file or overwrite a file. Always include path and content. Use this for new files or explicit full rewrites only. If the file path is not decided yet, do not call write yet. Prefer edit for existing code changes.',
1844
1844
  parameters: {
1845
1845
  type: 'object',
1846
1846
  properties: {
1847
- path: { type: 'string', description: 'File path to create or overwrite' },
1847
+ path: { type: 'string', description: 'Required file path like src/app.js or pages/index.html. Never omit this.' },
1848
1848
  content: { type: 'string', description: 'Content to write' },
1849
1849
  append: { type: 'boolean', description: 'Append instead of overwrite' },
1850
1850
  full_file_rewrite: { type: 'boolean', description: 'Set true for whole-file rewrites' }
@@ -375,6 +375,39 @@ function isCodeGenerationActivityName(name) {
375
375
  return String(name || '').trim() === 'Code generation';
376
376
  }
377
377
 
378
+ export function buildPreToolNotice(name, copy) {
379
+ const parsed = parseToolDisplayName(name);
380
+ const base = parsed.base;
381
+ const target = parsed.target ? trimText(parsed.target, 48) : '';
382
+ const isEnglish = String(copy?.roleLabels?.coder || '').trim() === 'CODER' && String(copy?.roleLabels?.you || '').trim() === 'YOU';
383
+
384
+ if (isEnglish) {
385
+ if (base === 'read') return target ? `I'll inspect ${target} first.` : `I'll inspect the relevant file first.`;
386
+ if (base === 'list' || base === 'glob') return target ? `I'll inspect the ${target} directory first.` : `I'll inspect the relevant directory first.`;
387
+ if (base === 'grep') return `I'll search the relevant code first.`;
388
+ if (base === 'edit' || base === 'write' || base === 'patch' || base === 'generate_diff') {
389
+ return `I'll inspect the current code first, then make the change.`;
390
+ }
391
+ if (base === 'run') return `I'll verify the current project state first.`;
392
+ return `I'll check the relevant project context first.`;
393
+ }
394
+
395
+ if (base === 'read') return target ? `我先查看 ${target} 的内容。` : '我先查看相关文件内容。';
396
+ if (base === 'list' || base === 'glob') return target ? `我先查看 ${target} 目录里的内容。` : '我先查看相关目录内容。';
397
+ if (base === 'grep') return '我先搜索相关代码位置。';
398
+ if (base === 'edit' || base === 'write' || base === 'patch' || base === 'generate_diff') return '我先确认当前代码上下文,再动手修改。';
399
+ if (base === 'run') return '我先检查当前项目状态。';
400
+ return '我先查看相关上下文。';
401
+ }
402
+
403
+ export function shouldInjectPreToolNotice(msg) {
404
+ if (!msg) return false;
405
+ const text = String(msg.text || '').trim();
406
+ const segments = Array.isArray(msg.segments) ? msg.segments : [];
407
+ const hasTextSegment = segments.some((segment) => segment?.type === 'text' && String(segment.text || '').trim());
408
+ return !text && !hasTextSegment;
409
+ }
410
+
378
411
  function formatDurationMs(ms) {
379
412
  const safeMs = Math.max(0, Number(ms) || 0);
380
413
  return `${(safeMs / 1000).toFixed(1)}s`;
@@ -2280,11 +2313,12 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2280
2313
  } else {
2281
2314
  segments.push({ type: 'text', text: delta });
2282
2315
  }
2283
- const nextText = `${m.text}${delta}`;
2316
+ const nextText = m.syntheticPrelude ? delta : `${m.text}${delta}`;
2284
2317
  return {
2285
2318
  ...m,
2286
2319
  text: nextText,
2287
- segments
2320
+ segments,
2321
+ syntheticPrelude: false
2288
2322
  };
2289
2323
  })
2290
2324
  );
@@ -2634,8 +2668,15 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2634
2668
  prev.map((m) => {
2635
2669
  if (m.id !== targetId) return m;
2636
2670
  const nextMessage = isCodeActivityName(event.name) ? finishCodeGeneration(m, finishedAt) : m;
2671
+ const withPrelude = shouldInjectPreToolNotice(nextMessage)
2672
+ ? {
2673
+ ...nextMessage,
2674
+ text: buildPreToolNotice(event.name, copy),
2675
+ syntheticPrelude: true
2676
+ }
2677
+ : nextMessage;
2637
2678
  return {
2638
- ...nextMessage,
2679
+ ...withPrelude,
2639
2680
  loading: true,
2640
2681
  phase: 'tooling',
2641
2682
  liveStatus: detail