codemini-cli 0.1.17 → 0.1.18

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.1.17",
3
+ "version": "0.1.18",
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.1.17';
7
+ const VERSION = '0.1.18';
8
8
 
9
9
  function printHelp() {
10
10
  console.log(`codemini ${VERSION}
@@ -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 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. For existing code files, prefer locate -> open_target -> edit_target and only use write_file with full_file_rewrite=true when a whole-file rewrite is truly intended. Avoid unnecessary tool calls.`;
122
122
  }
package/src/core/tools.js CHANGED
@@ -37,6 +37,25 @@ const TEXT_EXTENSIONS = new Set([
37
37
  '.sh',
38
38
  '.ps1'
39
39
  ]);
40
+ const CODE_WRITE_GUARD_EXTENSIONS = new Set([
41
+ '.js',
42
+ '.jsx',
43
+ '.ts',
44
+ '.tsx',
45
+ '.mjs',
46
+ '.cjs',
47
+ '.py',
48
+ '.rb',
49
+ '.go',
50
+ '.rs',
51
+ '.java',
52
+ '.cs',
53
+ '.css',
54
+ '.scss',
55
+ '.html',
56
+ '.sh',
57
+ '.ps1'
58
+ ]);
40
59
  const LANGUAGE_FILE_TYPES = {
41
60
  js: ['js', 'jsx', 'mjs', 'cjs'],
42
61
  ts: ['ts', 'tsx'],
@@ -100,6 +119,10 @@ function detectTextFile(filePath) {
100
119
  return TEXT_EXTENSIONS.has(path.extname(filePath).toLowerCase());
101
120
  }
102
121
 
122
+ function isCodeLikePath(filePath) {
123
+ return CODE_WRITE_GUARD_EXTENSIONS.has(path.extname(String(filePath || '')).toLowerCase());
124
+ }
125
+
103
126
  function normalizeFileTypes(args = {}) {
104
127
  const explicit = Array.isArray(args?.file_types) ? args.file_types.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean) : [];
105
128
  const language = String(args?.language || '').trim().toLowerCase();
@@ -490,6 +513,11 @@ async function writeFile(root, args) {
490
513
  } catch {
491
514
  existed = false;
492
515
  }
516
+ if (existed && !args?.append && !args?.full_file_rewrite && isCodeLikePath(rawPath)) {
517
+ throw new Error(
518
+ 'write_file blocks full overwrite for existing code files by default. Use locate -> open_target -> edit_target for minimal edits, or pass full_file_rewrite=true when a full-file rewrite is truly intended.'
519
+ );
520
+ }
493
521
  await fs.mkdir(path.dirname(target), { recursive: true });
494
522
  if (args?.append) {
495
523
  await fs.appendFile(target, args?.content || '', 'utf8');
@@ -1404,13 +1432,15 @@ export function getBuiltinTools({ workspaceRoot = process.cwd(), config }) {
1404
1432
  type: 'function',
1405
1433
  function: {
1406
1434
  name: 'write_file',
1407
- description: 'Write a UTF-8 text file in workspace. Always provide a full file path, not a directory.',
1435
+ description:
1436
+ 'Write a UTF-8 text file in workspace. Always provide a full file path, not a directory. Existing code files require full_file_rewrite=true for whole-file overwrites.',
1408
1437
  parameters: {
1409
1438
  type: 'object',
1410
1439
  properties: {
1411
1440
  path: { type: 'string' },
1412
1441
  content: { type: 'string' },
1413
- append: { type: 'boolean' }
1442
+ append: { type: 'boolean' },
1443
+ full_file_rewrite: { type: 'boolean' }
1414
1444
  },
1415
1445
  required: ['path', 'content']
1416
1446
  }