Reliably write large files or files with CJK/special characters in AI coding environments — skeleton + Edit strategy to avoid Write tool JSON truncation
SaferSkills independently audited llm-safe-write (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Use this skill when:
Unterminated string or JSON truncation errorsDo NOT use this skill when:
The Write tool serializes file content as a JSON string for transport. CJK characters occupy 3-4 bytes each in UTF-8. When a multi-byte character happens to cross an internal buffer boundary, the JSON string gets truncated, causing Unterminated string errors. ASCII-only content avoids this because each character is exactly 1 byte.
Key facts:
Scope clarification: The Read and Edit tools are not affected by this issue — Read only reads (never writes), and Edit'soldString/newStringare short strings (< 30 lines each) that never trigger truncation. Only the Write tool has this limitation.
Evaluate the file first: if any condition below is met, go directly to skeleton + Edit — do NOT try Write first:
>
- File > 50 lines → skeleton + Edit - Content contains CJK / special characters → ASCII skeleton + Edit to fill CJK - Write already failed → switch immediately, never retry
>
Only for files that are short (< 50 lines) and pure ASCII may you try Write directly — but if it fails, switch immediately.
>
Never retry a failed Write with the same content — this is the hard constraint of this Skill.
| Scenario | Strategy |
|---|---|
| < 50 lines + pure ASCII | You may try Write directly; for files > 50 lines or with CJK, go directly to skeleton + Edit |
| > 50 lines / contains CJK / Write failed | Skeleton + Edit |
| Small edit to existing file (< 30 lines change) | Edit tool directly |
| Large rewrite of existing file / CJK content | Read file → Edit in small chunks with unique anchors |
When unsure, try the simpler approach first and fall back to skeleton+Edit if it fails. Never retry a failed Write.
Trigger condition: Creating a new file expected to be > 50 lines, or containing CJK content.
Steps:
PLACEHOLDER_xxxExample — writing a 200-line Python file:
Step 1 — Write the skeleton:
import argparse
import base64
import json
from paddleocr import PaddleOCR
from openai import OpenAI
API_BASE = "PLACEHOLDER_API_BASE"
API_KEY = "PLACEHOLDER_API_KEY"
PROMPT = "PLACEHOLDER_PROMPT"
def get_ocr():
pass # PLACEHOLDER_get_ocr
def detect_texts(image_path):
pass # PLACEHOLDER_detect_texts
def extract_json(text):
pass # PLACEHOLDER_extract_json
def main():
pass # PLACEHOLDER_main
if __name__ == "__main__":
main()Step 2 — Fill each function with Edit (one at a time):
Edit(filePath="/path/to/file.py",
oldString="pass # PLACEHOLDER_get_ocr",
newString="ocr = PaddleOCR(use_angle_cls=True, lang='ch')\n return ocr")
Edit(filePath="/path/to/file.py",
oldString="pass # PLACEHOLDER_detect_texts",
newString="result = ocr.ocr(image_path, cls=True)\n return result")Trigger condition: Modifying an existing file that is already large or contains CJK.
Trigger condition: Need to add content at the end of an existing file.
The Edit tool can only replace, not append. To add content at the end of a file:
oldString, then repeat it in newString after the new content.if __name__ == "__main__":, use that line as the anchor and replace it with new_function_code\n\nif __name__ == "__main__":.# END_OF_FILE) via Edit, use it as anchor for subsequent appends, then remove it with a final Edit. ⚠️ Prefer stable anchors (like if __name__ == "__main__":) whenever available — if the agent is interrupted before the final cleanup Edit, the marker will remain in the file as residue.Trigger condition: File content includes Chinese, Japanese, Korean, or other non-ASCII characters.
"\u8bc6\u522b" instead of "识别") — this keeps the file ASCII-safe but less readable.Example — Python file with Chinese prompts:
Step 1 — Skeleton with placeholders:
PROMPT_SYSTEM = "PLACEHOLDER_prompt_system"
PROMPT_USER = "PLACEHOLDER_prompt_user"
ERROR_MSG = "PLACEHOLDER_error_msg"Step 2 — Fill with Edit:
Edit(filePath="/path/to/file.py",
oldString='PROMPT_SYSTEM = "PLACEHOLDER_prompt_system"',
newString='PROMPT_SYSTEM = "你是一个专业的AI助手,请用中文回答问题"')Trigger condition: Writing config/data files that lack code-style comment placeholders.
These files lack code-style comment placeholders. Strategy:
0, false, null) rather than a string — a string placeholder like "PLACEHOLDER" would break JSON type validity. Edit the field afterward with a short Edit call to replace the placeholder with the real value, or use a two-step Edit: first replace with a string, then fix the type.# PLACEHOLDER_key_name comments as anchors on the line above the value to replace.A good skeleton should include:
pass statements cause Edit tool Found multiple matches errors.For classes, use # PLACEHOLDER_ClassName_method_name format to ensure uniqueness across methods:
class MyHandler:
def __init__(self):
pass # PLACEHOLDER_MyHandler_init
def process(self, data):
pass # PLACEHOLDER_MyHandler_processFor JS/TS, use // PLACEHOLDER_funcName comments instead.
If an Edit call fails with oldString not found:
If an Edit call fails with Found multiple matches:
| Error | Cause | Fix |
|---|---|---|
Unterminated string | Write content contains CJK or is too long; JSON truncated | Switch to skeleton + Edit immediately; do not retry Write |
oldString not found | File content doesn't match oldString (stale after prior edit, whitespace differences) | Re-Read the file; construct oldString from actual content |
Found multiple matches | oldString appears multiple times (e.g., bare pass) | Add more surrounding context to make oldString unique; or use replaceAll: true |
File not found | Parent directory doesn't exist for new file | Create directory first with mkdir -p, then Write |
| Write succeeds but Read shows incomplete content | JSON truncation at end of file — Write didn't error but content was cut off | Use Edit to fill in missing parts, or rewrite with skeleton + Edit |
| PowerShell corrupts multi-line code | PowerShell interprets quotes/backticks/curly braces in strings | Write code to a .py/.js file first, then execute the file |
pass # PLACEHOLDER_func_name) — bare pass triggers Found multiple matches on every subsequent Edit.oldString not found.After completing all file writes/edits:
file.py (120 lines, verified)" — no more detail needed~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.